本帖最后由 王昱顺 于 2024-10-31 11:37 编辑
本篇文章致力于对STC-FOC Lite的原版代码进行详尽的原理解释,并且提出更改/移植/裁剪建议,方便大家对这个项目进行更加适配自己的个性化更改。 设计目的:取长补短,使用较低的算力实现高算力同样的控制效果。这部分功能专注于有感FOC驱动程序优化的文章,旨在通过优化控制算法,降低计算资源消耗,同时保持或提升控制性能。 对比正常的控制方式优点: 在计算速度方面,优化后的有感FOC驱动程序具有显著优势。全程只需要进行一次PI(比例-积分)计算,而无需像传统方法那样进行多次PID计算。这种优化大大减少了计算量,提高了计算速度,使得系统能够更快速地响应控制指令。 在速度环调参方面,优化后的驱动程序同样表现出色。由于采用预瞄位置点开环执行的方式,因此速度控制中没有引入观测误差。这种方式不仅简化了控制流程,还降低了系统震荡的风险。尤其在参数欠调的情况下,优化后的驱动程序能够保持明显的特点(给定速度0后仍然旋转一会,这就证明参数有些欠调),并且,在参数较大的时候,很大一个范围内都不会有异常,避免了传统方法中可能出现的,调节一点就剧烈震荡现象。 在性能方面,优化后的有感FOC驱动程序同样具有显著优势。由于其控制频率可以设置得非常高,因为位置传感器能够获得非常纯净的位置数据。这些数据没有异常抖动和误差,使得系统的控制精度和稳定性得到了极大的提升。这种高性能的控制方式对于需要精确控制的应用场景来说尤为重要。 PID介绍: 在优化后的有感FOC驱动程序中,PID控制起到了关键作用。为了确保PID控制能够随时根据实际情况进行自适应调整,PID中引入了时间dt。这样,PID控制就能够根据系统的运行时间进行动态调整,从而保持最佳的控制效果。 同时,为了尽量减少运算时间,驱动程序中采用了PI控制策略。PI控制是PID控制的一种简化形式,通过省略微分项来减少运算量。因为控制频率的提高,所以这种简化并没有牺牲控制性能,反而使得系统能够更快速地响应控制指令(d项通常用来补偿观测速度的不足,如果观测速度足够,d项反而是累赘)。
下面就是一些实际的代码的详细注释和讲解:
- // 根据速度调整位置设定@30us
- float postion_add = 0; // 小于分辨率编码器时的累加器
- long last_postion_add = 0; // 上次增量记忆
- void Timer0_Isr(void) interrupt TMR0_VECTOR
- {
- if (Mode == Speed_Mode && Run_Flag && moto.set_speed != 0 && I_Error_Cnt < I_Error_Dat)
- {
- postion_add += (float)moto.set_speed * 30.0 * (1.6384 / 6000.0); // us
- }
- }
-
复制代码
这段程序的主要功能是根据设定的速度调整位置。具体来说,它在定时器中断服务中检查是否处于速度模式、电机正在运行、设定速度不为零且堵转积分误差计数小于设定的积分误差数据。如果这些条件都满足,程序会计算位置增量并累加到一个浮点变量position_add中。这个增量是基于设定速度、时间间隔(30微秒)和一些单位转换因子计算得出的。
这个postion_add主要功能是为了保证积分累计没有误差,防止因为整数的四舍五入造成位置生成的误差。这个变量积累了一定的位置自增值后,会在每次的SPI位置采集中断中进行判断: - if (fabs(postion_add) > 1 && Mode == Speed_Mode && Run_Flag)
- {
- moto.set_postion += (long)postion_add;
- postion_add -= (long)postion_add; // 自减整数部分
- }
复制代码
首先,程序将对postion_add的绝对值是否大于1进行判断,同时确认Mode变量是否设定为Speed_Mode,并确保电机在运行状态。如果全都满足,则将位置自增变量的整数部分取出,并且加到设定位置上。同时,将自身的整数部分减掉,以重新开始新一轮的累计,并且保证不丢失精度(靠刚才没有清除掉的小数部分)。 然后,就是根据设定位置来做一个最基本的PID位置环,就可以完成高效的速度环和位置环一体运行了。这也就是所谓的单环PID。 实际使用中,只需要根据不同的模式调用不同的PID参数和不同的PID目标值即可。 - // 计算位置环的pid,输出为speed的控制
- if (Mode == Set_ID_Mode)
- moto.set_uq = (PID_Ctrl(moto.set_postion - moto.postion, &pid_setid) / 1000.0); // 调整为不同的PID
- if (Mode == Postion_Mode || Mode == Servo_Mode)
- moto.set_uq = (PID_Ctrl(moto.set_postion - moto.postion, &pid_postion) / 1000.0); // 分频输出
- if (Mode == Speed_Mode)
- moto.set_uq = (PID_Ctrl(moto.set_postion - moto.postion, &pid_speed) / 1000.0); // 分频输出
复制代码
|