活动介绍

foc查表法计算正弦余弦值.

时间: 2025-02-28 17:36:33 浏览: 76
### 使用查表法(Look-up Table)计算正弦和余弦值 #### 构建查找表 为了高效地计算正弦和余弦值,可以预先将这些函数在一个周期内的取值离散化并存入查找表中。对于正弦和余弦函数而言,由于其具有周期性和奇偶性特点,只需保存0至π/2范围的数据即可通过简单的变换得到其他区间的值。 在MATLAB/Simulink环境中创建这样的查找表时,可以通过`Simulink.Parameter`对象定义断点集(Breakpoints)和表格数据(Table Data),以便后续用于模型中的1-D Lookup Table模块配置[^2]。 下面是一个具体的例子展示如何建立一个包含正弦值的一维查找表: ```matlab % 定义角度区间(单位:弧度),这里选取了从0到pi/2之间的均匀分布采样点作为breakpoint theta_breakpoints = linspace(0, pi / 2, 65); % 包含端点共有65个样本点 % 计算对应的角度下的实际sine值形成table data sin_table_data = sin(theta_breakpoints); % 创建参数对象表示lookup table的breakpoints和data bp_param = Simulink.Parameter; td_param = Simulink.Parameter; % 设置属性 bp_param.Value = theta_breakpoints; td_param.Value = sin_table_data; % 将上述两个参数设置为全局工作空间变量供simulink使用 assignin('base', 'SinTheta_Breakpoints', bp_param); assignin('base', 'SinValues_TableData', td_param); ``` 同样地,也可以按照相同的方式构建另一个专门用来存储cosine值的查找表。 #### 应用查找表进行查询操作 当需要求解某个特定角α处的正弦或余弦值时,先判断该角度位于哪个完整的周期内,并转换成相应的标准形式β∈[0, π/2];接着利用之前准备好的查找表去获取近似的结果。如果所给定的角度恰好落在两个相邻采样点之间,则可通过线性插值得到更精确的答案。 假设现在要获得任意实数范围内某角度γ的正弦值: 1. **标准化处理**: 对于输入γ,找到它所属的标准区域k*π+δ (其中k为整数,-π<δ≤π),进而确定最终映射后的目标位置θ=|δ|(注意考虑符号影响)。 2. **索引定位与检索**: 利用二分搜索或其他快速算法,在已经设定好的`theta_breakpoints`数组里寻找最接近θ的那个元素的位置index,从而读取出对应的`sing_values[index]`即为我们想要估计出来的sin(γ)。 3. **插值优化**(可选): 如果追求更高的精度,可以在临近两点间实施一次简单的一次多项式拟合来逼近真实曲线形态,具体做法如下所示: ```matlab function y_interp = linear_interpolation(x, x1, y1, x2, y2) slope = (y2 - y1)/(x2-x1); intercept = y1-slope*x1; y_interp = slope * x + intercept; end ``` 此处仅提供了一种基础版本——线性插值方案,实际上还有更多高级别的方法可供选择如三次样条等。
阅读全文

相关推荐

我先在要使用Q格式的形式来归一化实现FOC内部算法。数据实现逻辑是通过ADC接口获取三相电流IA IB IC 和母线电压Q12格式(最大4095),通过clark变换得到Ialph Ibeta,通过Park格式得到Id Iq(其中有个角度变量thata 通过增量编码器获取),然后经过d和Q轴的两个PID或得Ud 和Uq,经过反park得到Ualph 和Ubeta;在通过SVPWM算法计算出三相互补PWM值其中(TPwm的值为10K 中心计数 周期0.2ms); 实际使用的电机母线电压为24V,测量电压与ADC获取电压值的对应关系是 测量电压=((ADC获取电压值/4095 * 3.3V)-1.24v)*37;实际电流与ADC获取电流值的对应关系是:实际电流=((实际电流*2mΩ)*8+1.24V/3.3V *4095);实际电机的参数 相电阻0.89Ω,相电感0.62mh 额定电流4A,额定转速3000rpm,编码器线1000线 极对数4 反电动势常数4.0V/Krpm;转动惯量0.028kg.cm2. 我现在已经使用Q12来进行Park和clark变换 角度和角度的sin和cos都使用Q12格式对应的函数是 /*************************************** 功能:Clark变换 形参:三相电流以及alpha_beta电流 说明:由三相互差120度变换到两相互差90度 Iabc(Q12格式) Ibeta Ialph Q12格式 */ void Clarke_Transf_Q12(CURRENT_ABC_DEF_Q12 Current_abc_temp, CURRENT_ALPHA_BETA_DEF_Q12* Current_alpha_beta_temp) { // 中间计算使用32位防止溢出 int32_t temp_Ib_Ic = (int32_t)Current_abc_temp.Ib - Current_abc_temp.Ic; // Ialpha = Ia Current_alpha_beta_temp->Ialpha = Current_abc_temp.Ia; // Ibeta = (Ib - Ic) * (√3/3) Current_alpha_beta_temp->Ibeta = (int16_t)(temp_Ib_Ic * SQR3_OVER_3_Q12 >> 12); } /*************************************** 功能:COS_SIN值计算 形参:角度以及COS_SIN结构体 说明:COS_SIN值计算 ***************************************/ void Angle_To_Cos_Sin(int16_t angle_temp,TRANSF_COS_SIN_DEF* cos_sin_temp) { //限制速度范围 int16_t anger = angle_temp%THETA_ANGER_Q12; cos_sin_temp->Sin = sin_table_q12[anger]; anger += THETA_ANGER_DIV4_Q12; anger = anger%THETA_ANGER_Q12; cos_sin_temp->Cos = sin_table_q12[anger]; } void Park_Transf_Q12(CURRENT_ALPHA_BETA_DEF_Q12 current_alpha_beta_temp,TRANSF_COS_SIN_DEF_Q12 cos_sin_temp,CURRENT_DQ_DEF_Q12* current_dq_temp) { // 所有中间计算使用32位防止溢出 int32_t temp1 = (int32_t)current_alpha_beta_temp.Ialpha * cos_sin_temp.Cos; int32_t temp2 = (int32_t)current_alpha_beta_temp.Ibeta * cos_sin_temp.Sin; int32_t temp3 = (int32_t)current_alpha_beta_temp.Ialpha * cos_sin_temp.Sin; int32_t temp4 = (int32_t)current_alpha_beta_temp.Ibeta * cos_sin_temp.Cos; // Id = Ialpha*Cos + Ibeta*Sin current_dq_temp->Id = (int16_t)((temp1 + temp2) >> 12); // Iq = -Ialpha*Sin + Ibeta*Cos current_dq_temp->Iq = (int16_t)((-temp3 + temp4) >> 12); } /** * @brief 将编码器计数值转换为Q12格式角度 * @param enc_count 编码器累计计数值(32位有符号整数) * @return uint16_t Q12格式角度值(0~4095对应0~2π) */ uint16_t encoder_to_q12(int32_t enc_count) { const int32_t counts_per_rev = 4000; // 1000线编码器四倍频 int32_t mod = enc_count % counts_per_rev; // 处理负余数 if (mod < 0) { mod += counts_per_rev; } // 计算: (mod*4096 + 2000)/4000 并四舍五入 uint32_t product = (uint32_t)mod * 4096 + 2000; return (uint16_t)(product / counts_per_rev); // 0~4095 } /*************************************** 功能:反PARK变换 形参:DQ轴电压、COS_SIN值、alpha_beta电压 说明:直流变交流 ***************************************/ /** * @brief 反Park变换 (Q12格式) * @param v_dq_temp: DQ轴电压(Q12格式) * @param cos_sin_temp: 包含Cos和Sin值(Q12格式) * @param v_alpha_beta_temp: 输出的αβ轴电压(Q12格式) * @retval 无 */ void Rev_Park_Transf_Q12(VOLTAGE_DQ_DEF_Q v_dq_temp, TRANSF_COS_SIN_DEF cos_sin_temp, VOLTAGE_ALPHA_BETA_DEF_Q* v_alpha_beta_temp) { // 使用64位中间变量防止溢出 int64_t temp_val; // Valpha = Cos*Vd - Sin*Vq (Q12*Q12=Q24, 右移12位得到Q12) temp_val = (int64_t)cos_sin_temp.Cos * v_dq_temp.Vd - (int64_t)cos_sin_temp.Sin * v_dq_temp.Vq; v_alpha_beta_temp->Valpha = (int32_t)(temp_val >> 12); // Vbeta = Sin*Vd + Cos*Vq temp_val = (int64_t)cos_sin_temp.Sin * v_dq_temp.Vd + (int64_t)cos_sin_temp.Cos * v_dq_temp.Vq; v_alpha_beta_temp->Vbeta = (int32_t)(temp_val >> 12); },现在需要你做的是1)分析一下这些函数有没有逻辑错误2)下面的函数是没有使用Q格式的电流的PID函数,改写成使用Q个数的形式,并将单位进行详细解释/*************************************** 功能:电流环PID 形参:电流参考、电流反馈、电压输出、PID结构体 说明:根据电流误差去调节电流输出 ***************************************/ void Current_PID_Calc(real32_T ref_temp,real32_T fdb_temp,real32_T* out_temp,CURRENT_PID_DEF* current_pid_temp) { real32_T error; real32_T temp; error = ref_temp - fdb_temp; temp = current_pid_temp->P_Gain * error + current_pid_temp->I_Sum; if (temp > current_pid_temp->Max_Output) { *out_temp = current_pid_temp->Max_Output; } else if (temp < current_pid_temp->Min_Output) { *out_temp = current_pid_temp->Min_Output; } else { *out_temp = temp; } current_pid_temp->I_Sum += ((*out_temp - temp) * current_pid_temp->B_Gain + current_pid_temp->I_Gain * error) *FOC_PERIOD; }

#include "MyProject.h" #include "DRV8313.h" #include "BLDC_tim.h" #include "tim.h" /************************************************ main中调用的接口函数都在当前文件中 ================================================= 本程序仅供学习,引用代码请标明出处 使用教程:https://blog.csdn.net/loop222/article/details/120471390 创建日期:20210925 作 者:loop222 @郑州 ************************************************/ /******************************************************************************/ extern float target; /******************************************************************************/ long sensor_direction; float voltage_power_supply; float voltage_limit; float voltage_sensor_align; int pole_pairs; unsigned long open_loop_timestamp; float velocity_limit; /******************************************************************************/ int alignSensor(void); float velocityOpenloop(float target_velocity); float angleOpenloop(float target_angle); /******************************************************************************/ void Motor_init(void) { // printf("MOT: Init\r\n"); // new_voltage_limit = current_limit * phase_resistance; // voltage_limit = new_voltage_limit < voltage_limit ? new_voltage_limit : voltage_limit; if(voltage_sensor_align > voltage_limit) voltage_sensor_align = voltage_limit; sensor_direction=UNKNOWN; //M1_Enable; // printf("MOT: Enable driver.\r\n"); } /******************************************************************************/ void Motor_initFOC(void) { alignSensor(); //检测零点偏移量和极对数 //added the shaft_angle update angle_prev=getAngle(); //getVelocity(),make sure velocity=0 after power on HAL_Delay(5); shaft_velocity = shaftVelocity(); //必须调用一次,进入主循环后速度为0 HAL_Delay(5); shaft_angle = shaftAngle();// shaft angle if(controller==Type_angle)target=shaft_angle;//角度模式,以当前的角度为目标角度,进入主循环后电机静止 HAL_Delay(200); } /******************************************************************************/ int alignSensor(void) { long i; float angle; float mid_angle,end_angle; float moved; // printf("MOT: Align sensor.\r\n"); // find natural direction // move one electrical revolution forward for(i=0; i<=500; i++) { angle = _3PI_2 + _2PI * i / 500.0; setPhaseVoltage(voltage_sensor_align, 0, angle); HAL_Delay(2); } mid_angle=getAngle(); for(i=500; i>=0; i--) { angle = _3PI_2 + _2PI * i / 500.0 ; setPhaseVoltage(voltage_sensor_align, 0, angle); HAL_Delay(2); } end_angle=getAngle(); setPhaseVoltage(0, 0, 0); HAL_Delay(200); // printf("mid_angle=%.4f\r\n",mid_angle); // printf("end_angle=%.4f\r\n",end_angle); moved = fabs(mid_angle - end_angle); if((mid_angle == end_angle)||(moved < 0.02)) //相等或者几乎没有动 { // printf("MOT: Failed to notice movement loop222.\r\n"); // M1_Disable; //电机检测不正常,关闭驱动 DRV8313_ENx_Off(); return 0; } else if(mid_angle < end_angle) { // printf("MOT: sensor_direction==CCW\r\n"); sensor_direction=CCW; } else { // printf("MOT: sensor_direction==CW\r\n"); sensor_direction=CW; } // printf("MOT: PP check: "); //计算Pole_Pairs if( fabs(moved*pole_pairs - _2PI) > 0.5 ) // 0.5 is arbitrary number it can be lower or higher! { // printf("fail - estimated pp:"); pole_pairs=_2PI/moved+0.5; //浮点数转整形,四舍五入 // printf("%d\r\n",pole_pairs); } else // printf("OK!\r\n"); setPhaseVoltage(voltage_sensor_align, 0, _3PI_2); //计算零点偏移角度 HAL_Delay(700); zero_electric_angle = _normalizeAngle(_electricalAngle(sensor_direction*getAngle(), pole_pairs)); HAL_Delay(20); // printf("MOT: Zero elec. angle:"); // printf("%.4f\r\n",zero_electric_angle); setPhaseVoltage(0, 0, 0); // delay_ms(200); return 1; } /******************************************************************************/ void loopFOC(void) { if( controller==Type_angle_openloop || controller==Type_velocity_openloop ) return; shaft_angle = shaftAngle();// shaft angle electrical_angle = electricalAngle();// electrical angle - need shaftAngle to be called first switch(torque_controller) { case Type_voltage: // no need to do anything really break; case Type_dc_current: break; case Type_foc_current: break; default: // printf("MOT: no torque control selected!"); break; } // set the phase voltage - FOC heart function :) setPhaseVoltage(voltage.q, voltage.d, electrical_angle); } /******************************************************************************/ void move(float new_target) { shaft_velocity = shaftVelocity(); switch(controller) { case Type_torque: if(torque_controller==Type_voltage)voltage.q = new_target; // if voltage torque control else current_sp = new_target; // if current/foc_current torque control break; case Type_angle: // angle set point shaft_angle_sp = new_target; // calculate velocity set point shaft_velocity_sp = PID_angle( shaft_angle_sp - shaft_angle ); // calculate the torque command current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if voltage torque control // if torque controlled through voltage if(torque_controller == Type_voltage) { voltage.q = current_sp; voltage.d = 0; } break; case Type_velocity: // velocity set point shaft_velocity_sp = new_target; // calculate the torque command current_sp = PID_velocity(shaft_velocity_sp - shaft_velocity); // if current/foc_current torque control // if torque controlled through voltage control if(torque_controller == Type_voltage) { voltage.q = current_sp; // use voltage if phase-resistance not provided voltage.d = 0; } break; case Type_velocity_openloop: // velocity control in open loop shaft_velocity_sp = new_target; voltage.q = velocityOpenloop(shaft_velocity_sp); // returns the voltage that is set to the motor voltage.d = 0; break; case Type_angle_openloop: // angle control in open loop shaft_angle_sp = new_target; voltage.q = angleOpenloop(shaft_angle_sp); // returns the voltage that is set to the motor voltage.d = 0; break; } } /******************************************************************************/ void setPhaseVoltage(float Uq, float Ud, float angle_el) { float Uout; uint32_t sector; float T0,T1,T2; float Ta,Tb,Tc; if(Ud) // only if Ud and Uq set {// _sqrt is an approx of sqrt (3-4% error) Uout = _sqrt(Ud*Ud + Uq*Uq) / voltage_power_supply; // angle normalisation in between 0 and 2pi // only necessary if using _sin and _cos - approximation functions angle_el = _normalizeAngle(angle_el + atan2(Uq, Ud)); } else {// only Uq available - no need for atan2 and sqrt Uout = Uq / voltage_power_supply; // angle normalisation in between 0 and 2pi // only necessary if using _sin and _cos - approximation functions angle_el = _normalizeAngle(angle_el + _PI_2); } if(Uout> 0.577)Uout= 0.577; if(Uout<-0.577)Uout=-0.577; sector = (angle_el / _PI_3) + 1; T1 = _SQRT3*_sin(sector*_PI_3 - angle_el) * Uout; T2 = _SQRT3*_sin(angle_el - (sector-1.0)*_PI_3) * Uout; T0 = 1 - T1 - T2; // calculate the duty cycles(times) switch(sector) { case 1: Ta = T1 + T2 + T0/2; Tb = T2 + T0/2; Tc = T0/2; break; case 2: Ta = T1 + T0/2; Tb = T1 + T2 + T0/2; Tc = T0/2; break; case 3: Ta = T0/2; Tb = T1 + T2 + T0/2; Tc = T2 + T0/2; break; case 4: Ta = T0/2; Tb = T1+ T0/2; Tc = T1 + T2 + T0/2; break; case 5: Ta = T2 + T0/2; Tb = T0/2; Tc = T1 + T2 + T0/2; break; case 6: Ta = T1 + T2 + T0/2; Tb = T0/2; Tc = T1 + T0/2; break; default: // possible error state Ta = 0; Tb = 0; Tc = 0; } SetPWMDutyPersent(&htim3, TIM_CHANNEL_1, Ta*100); SetPWMDutyPersent(&htim3, TIM_CHANNEL_2, Tb*100); SetPWMDutyPersent(&htim3, TIM_CHANNEL_3, Tc*100); // TIM_SetCompare1(TIM2,Ta*PWM_Period); // TIM_SetCompare2(TIM2,Tb*PWM_Period); // TIM_SetCompare3(TIM2,Tc*PWM_Period); } /******************************************************************************/ float velocityOpenloop(float target_velocity) { unsigned long now_us; float Ts,Uq; now_us = SysTick->VAL; //_micros(); if(now_us<open_loop_timestamp)Ts = (float)(open_loop_timestamp - now_us)/9*1e-6; else Ts = (float)(0xFFFFFF - now_us + open_loop_timestamp)/9*1e-6; open_loop_timestamp=now_us; //save timestamp for next call // quick fix for strange cases (micros overflow) if(Ts == 0 || Ts > 0.5) Ts = 1e-3; // calculate the necessary angle to achieve target velocity shaft_angle = _normalizeAngle(shaft_angle + target_velocity*Ts); Uq = voltage_limit; // set the maximal allowed voltage (voltage_limit) with the necessary angle setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, pole_pairs)); return Uq; } /******************************************************************************/ float angleOpenloop(float target_angle) { unsigned long now_us; float Ts,Uq; now_us = SysTick->VAL; //_micros(); if(now_us<open_loop_timestamp)Ts = (float)(open_loop_timestamp - now_us)/9*1e-6; else Ts = (float)(0xFFFFFF - now_us + open_loop_timestamp)/9*1e-6; open_loop_timestamp = now_us; //save timestamp for next call // quick fix for strange cases (micros overflow) if(Ts == 0 || Ts > 0.5) Ts = 1e-3; // calculate the necessary angle to move from current position towards target angle // with maximal velocity (velocity_limit) if(fabs( target_angle - shaft_angle ) > velocity_limit*Ts) { shaft_angle += _sign(target_angle - shaft_angle) * velocity_limit * Ts; //shaft_velocity = velocity_limit; } else { shaft_angle = target_angle; //shaft_velocity = 0; } Uq = voltage_limit; // set the maximal allowed voltage (voltage_limit) with the necessary angle setPhaseVoltage(Uq, 0, _electricalAngle(shaft_angle, pole_pairs)); return Uq; } /******************************************************************************/ #include "MyProject.h" /***************************************************************************/ // int array instead of float array // 4x200 points per 360 deg // 2x storage save (int 2Byte float 4 Byte ) // sin*10000 const int sine_array[200] = {0,79,158,237,316,395,473,552,631,710,789,867,946,1024,1103,1181,1260,1338,1416,1494,1572,1650,1728,1806,1883,1961,2038,2115,2192,2269,2346,2423,2499,2575,2652,2728,2804,2879,2955,3030,3105,3180,3255,3329,3404,3478,3552,3625,3699,3772,3845,3918,3990,4063,4135,4206,4278,4349,4420,4491,4561,4631,4701,4770,4840,4909,4977,5046,5113,5181,5249,5316,5382,5449,5515,5580,5646,5711,5775,5839,5903,5967,6030,6093,6155,6217,6279,6340,6401,6461,6521,6581,6640,6699,6758,6815,6873,6930,6987,7043,7099,7154,7209,7264,7318,7371,7424,7477,7529,7581,7632,7683,7733,7783,7832,7881,7930,7977,8025,8072,8118,8164,8209,8254,8298,8342,8385,8428,8470,8512,8553,8594,8634,8673,8712,8751,8789,8826,8863,8899,8935,8970,9005,9039,9072,9105,9138,9169,9201,9231,9261,9291,9320,9348,9376,9403,9429,9455,9481,9506,9530,9554,9577,9599,9621,9642,9663,9683,9702,9721,9739,9757,9774,9790,9806,9821,9836,9850,9863,9876,9888,9899,9910,9920,9930,9939,9947,9955,9962,9969,9975,9980,9985,9989,9992,9995,9997,9999,10000,10000}; /***************************************************************************/ // function approximating the sine calculation by using fixed size array // ~40us (float array) // ~50us (int array) // precision +-0.005 // it has to receive an angle in between 0 and 2PI float _sin(float a){ if(a < _PI_2){ //return sine_array[(int)(199.0*( a / (_PI/2.0)))]; //return sine_array[(int)(126.6873* a)]; // float array optimized return 0.0001*sine_array[_round(126.6873* a)]; // int array optimized }else if(a < _PI){ // return sine_array[(int)(199.0*(1.0 - (a-_PI/2.0) / (_PI/2.0)))]; //return sine_array[398 - (int)(126.6873*a)]; // float array optimized return 0.0001*sine_array[398 - _round(126.6873*a)]; // int array optimized }else if(a < _3PI_2){ // return -sine_array[(int)(199.0*((a - _PI) / (_PI/2.0)))]; //return -sine_array[-398 + (int)(126.6873*a)]; // float array optimized return -0.0001*sine_array[-398 + _round(126.6873*a)]; // int array optimized } else { // return -sine_array[(int)(199.0*(1.0 - (a - 3*_PI/2) / (_PI/2.0)))]; //return -sine_array[796 - (int)(126.6873*a)]; // float array optimized return -0.0001*sine_array[796 - _round(126.6873*a)]; // int array optimized } } /***************************************************************************/ // function approximating cosine calculation by using fixed size array // ~55us (float array) // ~56us (int array) // precision +-0.005 // it has to receive an angle in between 0 and 2PI float _cos(float a){ float a_sin = a + _PI_2; a_sin = a_sin > _2PI ? a_sin - _2PI : a_sin; return _sin(a_sin); } /***************************************************************************/ // normalizing radian angle to [0,2PI] float _normalizeAngle(float angle){ float a = fmod(angle, _2PI); return a >= 0 ? a : (a + _2PI); } /***************************************************************************/ // Electrical angle calculation float _electricalAngle(float shaft_angle, int pole_pairs) { return (shaft_angle * pole_pairs); } /***************************************************************************/ // square root approximation function using // https://reprap.org/forum/read.php?147,219210 // https://en.wikipedia.org/wiki/Fast_inverse_square_root float _sqrtApprox(float number) {//low in fat long i; float y; // float x; // const float f = 1.5F; // better precision // x = number * 0.5F; y = number; i = * ( long * ) &y; i = 0x5f375a86 - ( i >> 1 ); y = * ( float * ) &i; // y = y * ( f - ( x * y * y ) ); // better precision return number * y; } /***************************************************************************/ #include "MyProject.h" /******************************************************************************/ float shaft_angle;//!< current motor angle float electrical_angle; float shaft_velocity; float current_sp; float shaft_velocity_sp; float shaft_angle_sp; DQVoltage_s voltage; DQCurrent_s current; TorqueControlType torque_controller; MotionControlType controller; float sensor_offset=0; float zero_electric_angle; /******************************************************************************/ // shaft angle calculation float shaftAngle(void) { // if no sensor linked return previous value ( for open loop ) //if(!sensor) return shaft_angle; return sensor_direction*getAngle() - sensor_offset; } // shaft velocity calculation float shaftVelocity(void) { // if no sensor linked return previous value ( for open loop ) //if(!sensor) return shaft_velocity; return sensor_direction*LPF_velocity(getVelocity()); } /******************************************************************************/ float electricalAngle(void) { return _normalizeAngle((shaft_angle + sensor_offset) * pole_pairs - zero_electric_angle); } /******************************************************************************/ #include "MyProject.h" /******************************************************************************/ #define DEF_CURR_FILTER_Tf 0.005 //!< default currnet filter time constant #define DEF_VEL_FILTER_Tf 0.005 //!< default velocity filter time constant /******************************************************************************/ //unsigned long lpf_vel_timestamp; float y_vel_prev=0; /******************************************************************************/ /* float LPF_velocity(float x) { unsigned long now_us; float Ts, alpha, y; now_us = SysTick->VAL; if(now_us<lpf_vel_timestamp)Ts = (float)(lpf_vel_timestamp - now_us)/9*1e-6f; else Ts = (float)(0xFFFFFF - now_us + lpf_vel_timestamp)/9*1e-6f; lpf_vel_timestamp = now_us; if(Ts == 0 || Ts > 0.5) Ts = 1e-3f; alpha = DEF_VEL_FILTER_Tf/(DEF_VEL_FILTER_Tf + Ts); y = alpha*y_prev + (1.0f - alpha)*x; y_prev = y; return y; } */ float LPF_velocity(float x) { float y = 0.9*y_vel_prev + 0.1*x; y_vel_prev=y; return y; } /******************************************************************************/ #include "MyProject.h" #include "AS5600.h" #define AS5600_RAW_ANGLE_REG 0x0C #define AS5600_ANGLE_REG 0x0E /************************************************ 本程序仅供学习,引用代码请标明出处 使用教程:https://blog.csdn.net/loop222/article/details/120471390 创建日期:20210925 作 者:loop222 @郑州 ************************************************/ /******************************************************************************/ long cpr=4096; float full_rotation_offset; long angle_data_prev; unsigned long velocity_calc_timestamp; float angle_prev; /******************************************************************************/ /******************************************************************************/ void MagneticSensor_Init(void) { angle_data_prev = AS5600_Read_uint16(AS5600_ANGLE_REG); full_rotation_offset = 0; velocity_calc_timestamp=0; } /******************************************************************************/ float getAngle(void) { float angle_data,d_angle; //angle_data = I2C_getRawCount(I2C1); angle_data = AS5600_Read_uint16(AS5600_ANGLE_REG); // tracking the number of rotations // in order to expand angle range form [0,2PI] to basically infinity d_angle = angle_data - angle_data_prev; // if overflow happened track it as full rotation if(fabs(d_angle) > (0.8*cpr) ) full_rotation_offset += d_angle > 0 ? -_2PI : _2PI; // save the current angle value for the next steps // in order to know if overflow happened angle_data_prev = angle_data; // return the full angle // (number of full rotations)*2PI + current sensor angle return (full_rotation_offset + ( angle_data / (float)cpr) * _2PI) ; /* AS5600_Refresh_Angle(&has5600); return has5600.angle;*/ } /******************************************************************************/ // Shaft velocity calculation float getVelocity(void) { unsigned long now_us; float Ts, angle_c, vel; // calculate sample time now_us = SysTick->VAL; //_micros(); if(now_us<velocity_calc_timestamp)Ts = (float)(velocity_calc_timestamp - now_us)/9*1e-6; else Ts = (float)(0xFFFFFF - now_us + velocity_calc_timestamp)/9*1e-6; // quick fix for strange cases (micros overflow) if(Ts == 0 || Ts > 0.5) Ts = 1e-3; // current angle angle_c = getAngle(); // velocity calculation vel = (angle_c - angle_prev)/Ts; // save variables for future pass angle_prev = angle_c; velocity_calc_timestamp = now_us; return vel; } /******************************************************************************/ #include "MyProject.h" /************************************************ 本程序仅供学习,引用代码请标明出处 使用教程:https://blog.csdn.net/loop222/article/details/120471390 创建日期:20210925 作 者:loop222 @郑州 ************************************************/ /******************************************************************************/ float pid_vel_P, pid_ang_P; float pid_vel_I, pid_ang_D; float integral_vel_prev; float error_vel_prev, error_ang_prev; float output_vel_ramp; float output_vel_prev; unsigned long pid_vel_timestamp, pid_ang_timestamp; /******************************************************************************/ void PID_init(void) { pid_vel_P=0.1; //0.1 pid_vel_I=2; //2 output_vel_ramp=100; //output derivative limit [volts/second] integral_vel_prev=0; error_vel_prev=0; output_vel_prev=0; pid_vel_timestamp=SysTick->VAL; pid_ang_P=10; pid_ang_D=0.5; error_ang_prev=0; pid_ang_timestamp=SysTick->VAL; } /******************************************************************************/ //just P&I is enough,no need D float PID_velocity(float error) { unsigned long now_us; float Ts; float proportional,integral,output; float output_rate; now_us = SysTick->VAL; if(now_us 0.5) Ts = 1e-3f; // u(s) = (P + I/s + Ds)e(s) // Discrete implementations // proportional part // u_p = P *e(k) proportional = pid_vel_P * error; // Tustin transform of the integral part // u_ik = u_ik_1 + I*Ts/2*(ek + ek_1) integral = integral_vel_prev + pid_vel_I*Ts*0.5*(error + error_vel_prev); // antiwindup - limit the output integral = _constrain(integral, -voltage_limit, voltage_limit); // sum all the components output = proportional + integral; // antiwindup - limit the output variable output = _constrain(output, -voltage_limit, voltage_limit); // limit the acceleration by ramping the output output_rate = (output - output_vel_prev)/Ts; if(output_rate > output_vel_ramp)output = output_vel_prev + output_vel_ramp*Ts; else if(output_rate < -output_vel_ramp)output = output_vel_prev - output_vel_ramp*Ts; // saving for the next pass integral_vel_prev = integral; output_vel_prev = output; error_vel_prev = error; return output; } /******************************************************************************/ //P&D for angle_PID float PID_angle(float error) { unsigned long now_us; float Ts; float proportional,derivative,output; //float output_rate; now_us = SysTick->VAL; if(now_us 0.5) Ts = 1e-3f; // u(s) = (P + I/s + Ds)e(s) // Discrete implementations // proportional part // u_p = P *e(k) proportional = pid_ang_P * error; // u_dk = D(ek - ek_1)/Ts derivative = pid_ang_D*(error - error_ang_prev)/Ts; output = proportional + derivative; output = _constrain(output, -velocity_limit, velocity_limit); // limit the acceleration by ramping the output // output_rate = (output - output_ang_prev)/Ts; // if(output_rate > output_ang_ramp)output = output_ang_prev + output_ang_ramp*Ts; // else if(output_rate < -output_ang_ramp)output = output_ang_prev - output_ang_ramp*Ts; // saving for the next pass // output_ang_prev = output; error_ang_prev = error; return output; } /******************************************************************************/ /******************************************************************************/ 逐行解释

zip

最新推荐

recommend-type

langchain4j-anthropic-spring-boot-starter-0.31.0.jar中文文档.zip

1、压缩文件中包含: 中文文档、jar包下载地址、Maven依赖、Gradle依赖、源代码下载地址。 2、使用方法: 解压最外层zip,再解压其中的zip包,双击 【index.html】 文件,即可用浏览器打开、进行查看。 3、特殊说明: (1)本文档为人性化翻译,精心制作,请放心使用; (2)只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; (3)不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 4、温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件。 5、本文件关键字: jar中文文档.zip,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册。
recommend-type

TMS320F28335电机控制程序详解:BLDC、PMSM无感有感及异步VF源代码与开发资料

TMS320F28335这款高性能数字信号处理器(DSP)在电机控制领域的应用,涵盖了BLDC(无刷直流电机)、PMSM(永磁同步电机)的无感有感控制以及异步VF(变频调速)程序。文章不仅解释了各类型的电机控制原理,还提供了完整的开发资料,包括源代码、原理图和说明文档,帮助读者深入了解其工作原理和编程技巧。 适合人群:从事电机控制系统开发的技术人员,尤其是对TMS320F28335感兴趣的工程师。 使用场景及目标:适用于需要掌握TMS320F28335在不同电机控制应用场景下具体实现方法的专业人士,旨在提高他们对该微控制器的理解和实际操作能力。 其他说明:文中提供的开发资料为读者提供了从硬件到软件的全面支持,有助于加速项目开发进程并提升系统性能。
recommend-type

基于爬山搜索法的风力发电MPPT控制Simulink仿真:定步长与变步长算法性能对比 - 爬山搜索法 最新版

基于爬山搜索法的风力发电最大功率点追踪(MPPT)控制的Simulink仿真模型,重点比较了定步长和变步长算法在不同风速条件下的表现。文中展示了两种算法的具体实现方法及其优缺点。定步长算法虽然结构简单、计算量小,但在风速突变时响应较慢,存在明显的稳态振荡。相比之下,变步长算法能够根据功率变化动态调整步长,表现出更快的响应速度和更高的精度,尤其在风速突变时优势明显。实验数据显示,变步长算法在风速从8m/s突增至10m/s的情况下,仅用0.3秒即可稳定,功率波动范围仅为±15W,而定步长算法则需要0.8秒,功率波动达到±35W。 适合人群:从事风力发电研究的技术人员、对MPPT控制感兴趣的工程技术人员以及相关专业的高校师生。 使用场景及目标:适用于风力发电系统的设计与优化,特别是需要提高系统响应速度和精度的场合。目标是在不同风速条件下,选择合适的MPPT算法以最大化风能利用率。 其他说明:文章还讨论了定步长算法在风速平稳情况下的优势,提出了根据不同应用场景灵活选择或组合使用这两种算法的建议。
recommend-type

基于MatlabSimulink的风电场调频策略研究:虚拟惯性、超速减载与下垂控制的协调优化

内容概要:本文详细探讨了在Matlab/Simulink环境下,针对风电场调频的研究,尤其是双馈风机调频策略的应用及其与其他调频策略的协调工作。文中介绍了三种主要的调频策略——虚拟惯性、超速减载和下垂控制,并基于三机九节点系统进行了改进,模拟了四组风电机组的协同调频过程。研究指出,虚拟惯性的应用虽然可以提供短期频率支持,但也可能导致频率二次跌落的问题。因此,需要通过超速减载和下垂控制来进行补偿,以维持电网的稳定。此外,文章还展示了在变风速条件下,风电机组对电网频率支撑能力的显著提升,尤其是在高比例风电并网渗透的情况下,频率最低点提高了50%,验证了调频策略的有效性。 适合人群:从事电力系统、风电场运营管理和调频技术研发的专业人士,以及对风电调频感兴趣的科研人员和技术爱好者。 使用场景及目标:适用于希望深入理解风电场调频机制及其优化方法的人群。目标是掌握不同调频策略的工作原理及其协调工作的关键点,提高风电场的运行效率和稳定性。 其他说明:本文通过具体的案例研究和仿真数据,展示了调频策略的实际效果,强调了合理运用调频策略对于风电场稳定运行的重要意义。同时,也为未来的风电调频技术创新提供了理论依据和实践经验。
recommend-type

三菱QL系列PLC在3C-FPC组装机中的定位与伺服控制及触摸屏应用解析

三菱Q系列和L系列PLC在3C-FPC组装机中的具体应用,涵盖硬件架构、软件编程以及实际操作技巧。主要内容包括:使用QX42数字输入模块和QY42P晶体管输出模块进行高效信号处理;采用JE系列伺服控制系统实现高精度四轴联动;利用SFC(顺序功能图)和梯形图编程方法构建稳定可靠的控制系统;通过触摸屏实现多用户管理和权限控制;并分享了一些实用的调试和维护技巧,如流水线节拍控制和工程师模式进入方法。最终,该系统的设备综合效率(OEE)达到了92%以上。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是对三菱PLC有初步了解并希望深入了解其高级应用的人群。 使用场景及目标:适用于需要高精度、高效能的工业生产设备控制场合,旨在帮助用户掌握三菱PLC及其相关组件的应用技能,提高生产效率和产品质量。 其他说明:文中提供了详细的编程实例和操作指南,有助于读者更好地理解和实践。同时提醒使用者在调试过程中应注意伺服刚性参数调整,避免不必要的机械损伤。
recommend-type

Visual C++.NET编程技术实战指南

根据提供的文件信息,可以生成以下知识点: ### Visual C++.NET编程技术体验 #### 第2章 定制窗口 - **设置窗口风格**:介绍了如何通过编程自定义窗口的外观和行为。包括改变窗口的标题栏、边框样式、大小和位置等。这通常涉及到Windows API中的`SetWindowLong`和`SetClassLong`函数。 - **创建六边形窗口**:展示了如何创建一个具有特殊形状边界的窗口,这类窗口不遵循标准的矩形形状。它需要使用`SetWindowRgn`函数设置窗口的区域。 - **创建异形窗口**:扩展了定制窗口的内容,提供了创建非标准形状窗口的方法。这可能需要创建一个不规则的窗口区域,并将其应用到窗口上。 #### 第3章 菜单和控制条高级应用 - **菜单编程**:讲解了如何创建和修改菜单项,处理用户与菜单的交互事件,以及动态地添加或删除菜单项。 - **工具栏编程**:阐述了如何使用工具栏,包括如何创建工具栏按钮、分配事件处理函数,并实现工具栏按钮的响应逻辑。 - **状态栏编程**:介绍了状态栏的创建、添加不同类型的指示器(如文本、进度条等)以及状态信息的显示更新。 - **为工具栏添加皮肤**:展示了如何为工具栏提供更加丰富的视觉效果,通常涉及到第三方的控件库或是自定义的绘图代码。 #### 第5章 系统编程 - **操作注册表**:解释了Windows注册表的结构和如何通过程序对其进行读写操作,这对于配置软件和管理软件设置非常关键。 - **系统托盘编程**:讲解了如何在系统托盘区域创建图标,并实现最小化到托盘、从托盘恢复窗口的功能。 - **鼠标钩子程序**:介绍了钩子(Hook)技术,特别是鼠标钩子,如何拦截和处理系统中的鼠标事件。 - **文件分割器**:提供了如何将文件分割成多个部分,并且能够重新组合文件的技术示例。 #### 第6章 多文档/多视图编程 - **单文档多视**:展示了如何在同一个文档中创建多个视图,这在文档编辑软件中非常常见。 #### 第7章 对话框高级应用 - **实现无模式对话框**:介绍了无模式对话框的概念及其应用场景,以及如何实现和管理无模式对话框。 - **使用模式属性表及向导属性表**:讲解了属性表的创建和使用方法,以及如何通过向导性质的对话框引导用户完成多步骤的任务。 - **鼠标敏感文字**:提供了如何实现点击文字触发特定事件的功能,这在阅读器和编辑器应用中很有用。 #### 第8章 GDI+图形编程 - **图像浏览器**:通过图像浏览器示例,展示了GDI+在图像处理和展示中的应用,包括图像的加载、显示以及基本的图像操作。 #### 第9章 多线程编程 - **使用全局变量通信**:介绍了在多线程环境下使用全局变量进行线程间通信的方法和注意事项。 - **使用Windows消息通信**:讲解了通过消息队列在不同线程间传递信息的技术,包括发送消息和处理消息。 - **使用CriticalSection对象**:阐述了如何使用临界区(CriticalSection)对象防止多个线程同时访问同一资源。 - **使用Mutex对象**:介绍了互斥锁(Mutex)的使用,用以同步线程对共享资源的访问,保证资源的安全。 - **使用Semaphore对象**:解释了信号量(Semaphore)对象的使用,它允许一个资源由指定数量的线程同时访问。 #### 第10章 DLL编程 - **创建和使用Win32 DLL**:介绍了如何创建和链接Win32动态链接库(DLL),以及如何在其他程序中使用这些DLL。 - **创建和使用MFC DLL**:详细说明了如何创建和使用基于MFC的动态链接库,适用于需要使用MFC类库的场景。 #### 第11章 ATL编程 - **简单的非属性化ATL项目**:讲解了ATL(Active Template Library)的基础使用方法,创建一个不使用属性化组件的简单项目。 - **使用ATL开发COM组件**:详细阐述了使用ATL开发COM组件的步骤,包括创建接口、实现类以及注册组件。 #### 第12章 STL编程 - **list编程**:介绍了STL(标准模板库)中的list容器的使用,讲解了如何使用list实现复杂数据结构的管理。 #### 第13章 网络编程 - **网上聊天应用程序**:提供了实现基本聊天功能的示例代码,包括客户端和服务器的通信逻辑。 - **简单的网页浏览器**:演示了如何创建一个简单的Web浏览器程序,涉及到网络通信和HTML解析。 - **ISAPI服务器扩展编程**:介绍了如何开发ISAPI(Internet Server API)服务器扩展来扩展IIS(Internet Information Services)的功能。 #### 第14章 数据库编程 - **ODBC数据库编程**:解释了ODBC(开放数据库互联)的概念,并提供了使用ODBC API进行数据库访问的示例。 - **ADO编程**:介绍了ADO(ActiveX Data Objects)技术,讲解了如何使用ADO进行数据库的增删改查等操作。 #### 第15章 HTML帮助系统编程 - **创建HTML帮助文件**:讲解了如何创建HTML帮助文件(.chm),这是微软帮助系统的一种形式。 - **区分上下文帮助**:提供了在应用程序中实现上下文敏感帮助的技术示例。 #### 第16章 安装和部署项目 - **用安装程序向导创建安装项目**:介绍了如何使用Visual Studio中的安装和部署项目向导,快速创建安装程序。 - **部署应用程序**:阐述了部署应用程序到目标系统的过程,以及如何确保应用程序的正确安装和运行。 以上内容涵盖了Visual C++.NET编程技术体验一书中涉及的各个技术点,并对每个章节中的示例做了详细的说明。这些知识点对于理解和掌握Visual C++.NET编程非常重要,并能够帮助读者在实际项目开发中应用这些技术。
recommend-type

HarmonyOS内核深度探秘:优化自由行旅游系统的策略

# 1. HarmonyOS内核概述与旅游系统需求分析 ## 1.1 HarmonyOS内核概述 HarmonyOS(鸿蒙操作系统)是由华为开发的一个分布式操作系统,旨在提供跨多种设备的无缝体验。其内核设计理念强调高效、安全和模块化,以微内核架构和分布式能力
recommend-type

tkinter模块所有控件

<think>好的,我现在需要回答用户关于Tkinter模块中所有控件列表的问题。首先,我需要回忆Tkinter的基础知识,确保自己了解各个控件的名称和用途。用户希望得到所有支持的控件列表,所以必须全面覆盖,不能遗漏。 首先,我应该从Tkinter的标准控件开始。常见的控件包括Label、Button、Entry这些基础部件。然后是Frame,用于布局,还有Canvas用于绘图。接下来是Checkbutton、Radiobutton,这些属于选择类控件。Listbox和Scrollbar通常一起使用,处理滚动内容。还有Scale(滑块)、Spinbox、Menu、Menubutton这些可能
recommend-type

局域网五子棋游戏:娱乐与聊天的完美结合

标题“网络五子棋”和描述“适合于局域网之间娱乐和聊天!”以及标签“五子棋 网络”所涉及的知识点主要围绕着五子棋游戏的网络版本及其在局域网中的应用。以下是详细的知识点: 1. 五子棋游戏概述: 五子棋是一种两人对弈的纯策略型棋类游戏,又称为连珠、五子连线等。游戏的目标是在一个15x15的棋盘上,通过先后放置黑白棋子,使得任意一方先形成连续五个同色棋子的一方获胜。五子棋的规则简单,但策略丰富,适合各年龄段的玩家。 2. 网络五子棋的意义: 网络五子棋是指可以在互联网或局域网中连接进行对弈的五子棋游戏版本。通过网络版本,玩家不必在同一地点即可进行游戏,突破了空间限制,满足了现代人们快节奏生活的需求,同时也为玩家们提供了与不同对手切磋交流的机会。 3. 局域网通信原理: 局域网(Local Area Network,LAN)是一种覆盖较小范围如家庭、学校、实验室或单一建筑内的计算机网络。它通过有线或无线的方式连接网络内的设备,允许用户共享资源如打印机和文件,以及进行游戏和通信。局域网内的计算机之间可以通过网络协议进行通信。 4. 网络五子棋的工作方式: 在局域网中玩五子棋,通常需要一个客户端程序(如五子棋.exe)和一个服务器程序。客户端负责显示游戏界面、接受用户输入、发送落子请求给服务器,而服务器负责维护游戏状态、处理玩家的游戏逻辑和落子请求。当一方玩家落子时,客户端将该信息发送到服务器,服务器确认无误后将更新后的棋盘状态传回给所有客户端,更新显示。 5. 五子棋.exe程序: 五子棋.exe是一个可执行程序,它使得用户可以在个人计算机上安装并运行五子棋游戏。该程序可能包含了游戏的图形界面、人工智能算法(如果支持单机对战AI的话)、网络通信模块以及游戏规则的实现。 6. put.wav文件: put.wav是一个声音文件,很可能用于在游戏进行时提供声音反馈,比如落子声。在网络环境中,声音文件可能被用于提升玩家的游戏体验,尤其是在局域网多人游戏场景中。当玩家落子时,系统会播放.wav文件中的声音,为游戏增添互动性和趣味性。 7. 网络五子棋的技术要求: 为了确保多人在线游戏的顺利进行,网络五子棋需要具备一些基本的技术要求,包括但不限于稳定的网络连接、高效的数据传输协议(如TCP/IP)、以及安全的数据加密措施(如果需要的话)。此外,还需要有一个良好的用户界面设计来提供直观和舒适的用户体验。 8. 社交与娱乐: 网络五子棋除了是一个娱乐游戏外,它还具有社交功能。玩家可以通过游戏内的聊天系统进行交流,分享经验和策略,甚至通过网络寻找新的朋友。这使得网络五子棋不仅是一个个人娱乐工具,同时也是一种社交活动。 总结来说,网络五子棋结合了五子棋游戏的传统魅力和现代网络技术,使得不同地区的玩家能够在局域网内进行娱乐和聊天,既丰富了人们的娱乐生活,又加强了人际交流。而实现这一切的基础在于客户端程序的设计、服务器端的稳定运行、局域网的高效通信,以及音效文件增强的游戏体验。
recommend-type

自由行旅游新篇章:HarmonyOS技术融合与系统架构深度解析

# 1. HarmonyOS技术概述 ## 1.1 HarmonyOS的起源与发展 HarmonyOS(鸿蒙操作系统)由华为公司开发,旨在构建全场景分布式OS,以应对不同设备间的互联问题。自从2019年首次发布以来,HarmonyOS迅速成长,并迅速应用于智能手机、平板、智能穿戴、车载设备等多种平台。该系