ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ODrive电机控制固件定时器时基与8kHz控制环设计详解

ODrive电机控制固件定时器时基与8kHz控制环设计详解 1. 为什么一个电机控制固件要先聊定时器很多人第一次翻 ODrive 的源码注意力都会被 FOC 算法、电流环、编码器校准这些看起来更高级的东西吸走结果在Axis、Motor、Encoder这几个类里绕了半天最后卡在一个最朴素的问题上这些控制逻辑到底是谁在什么时候调用的答案就藏在定时器和时基配置里。ODrive 的控制环跑在 8 kHz也就是每 125 微秒执行一次这个数字不是随便拍脑袋定的它直接决定了电流采样的时机、PWM 更新的节奏、编码器数据的读取窗口甚至影响整个系统的噪声表现和发热水平。我接触 ODrive 是从一台自制的云台电机驱动开始的当时用现成的固件跑得挺顺但一旦想改点东西——比如把控制频率从 8 kHz 提到 16 kHz或者换一颗主频不同的 MCU——就发现完全无从下手。后来硬着头皮把固件源码从头捋了一遍才意识到整个系统的骨架其实是定时器触发中断中断里跑控制环这么一个简单结构剩下的复杂逻辑都是挂在这个骨架上的肉。这篇就专门聊这个骨架把定时器时基怎么配、8 kHz 怎么来的、控制环在中断里怎么组织这几件事讲透。适合读这篇的人大概有三类一是正在用 ODrive 但想深入定制固件的开发者二是自己写 FOC 控制、想参考成熟项目定时器架构的嵌入式工程师三是学过 STM32 定时器但不知道怎么把它用到实际电机控制项目里的学生或爱好者。不管你属于哪一类只要你对中断驱动的实时控制这件事有兴趣下面的内容应该都能给你一些可以直接抄作业的东西。需要先说明一点ODrive 固件经历了多个版本迭代早期基于 STM32F405后来有基于 STM32F722 等型号的版本不同版本在定时器选型和时钟树上会有差异。下面讲的核心思路是通用的具体寄存器名和参数我会以常见的 STM32F4 系列为主来举例你实际对照自己手上的版本时注意核对时钟树配置。2. 定时器时基的整体设计与选型思路2.1 为什么是 8 kHz 而不是更高或更低控制环频率的选择本质上是在控制精度和计算余量之间做权衡。8 kHz 意味着控制周期 125 微秒对于典型的无刷电机来说这个频率已经足够让电流环有不错的带宽。电流环的带宽一般取开关频率的十分之一到五分之一如果 PWM 开关频率是 24 kHz那电流环带宽大概在 2.4 kHz 到 4.8 kHz 之间用 8 kHz 的采样率去支撑这个带宽是合理的再高就有点浪费算力了。从另一个角度看8 kHz 也刚好和音频范围的下边界擦边。人耳能听到 20 Hz 到 20 kHz如果控制频率落在可听范围内电机会发出明显的啸叫。8 kHz 本身在可听范围内但它的谐波和 PWM 载波频率配合之后实际噪声主要集中在更高频段配合适当的 PWM 策略可以把可听噪声压得比较低。我实测过把控制频率降到 4 kHz电机在中低速时会有明显的嗡嗡声提到 8 kHz 之后就干净很多这也是很多商业驱动器默认选 8 kHz 的原因之一。再考虑计算量。STM32F405 主频 168 MHz一个 125 微秒的周期里有 21000 个时钟周期可用。一次完整的 FOC 计算Clarke 变换、Park 变换、两个 PI 调节器、反 Park 变换、SVPWM 生成大概需要几千个周期加上编码器读取、电流采样处理、通信任务8 kHz 下 CPU 占用率大概在 40% 到 60% 之间留有余量。如果提到 16 kHz占用率直接翻倍很容易在通信任务繁忙时出现控制周期抖动反而得不偿失。2.2 时基来源为什么不用 SysTick初学者容易想到用 SysTick 来做控制环时基毕竟它简单、每个 Cortex-M 内核都有。但 SysTick 有几个硬伤一是它的中断优先级通常被 RTOS 占用如果项目里跑了 FreeRTOS 之类的系统SysTick 已经被调度器拿走了二是 SysTick 只有一个没法同时给多个不同频率的任务提供时基三是它的计数精度和灵活性不如通用定时器比如你想在特定时刻触发 ADC 采样SysTick 做不到硬件触发。ODrive 用的是通用定时器TIM来做控制环时基具体来说通常选一个高级定时器或者通用定时器配置成向上计数模式自动重装载值ARR根据主频和目标频率算出来。这样做的好处是定时器可以同时输出多路信号一路作为中断触发控制环一路作为 TRGOTrigger Output去触发 ADC 采样还能顺便生成 PWM。一个定时器干三件事硬件层面就把时序对齐了这是 SysTick 完全做不到的。提示如果你自己写 FOC强烈建议控制环时基和 PWM 用同一个定时器或者至少让它们同源。否则采样时刻和 PWM 更新时刻之间会有随机相位差电流采样值会带上纹波控制效果打折扣。2.3 时钟树配置从晶振到定时器计数频率要算 ARR得先知道定时器的计数时钟是多少。以 STM32F405 为例外部晶振一般是 8 MHz经过 PLL 倍频到 168 MHz 作为系统时钟。APB1 总线最高 42 MHzAPB2 最高 84 MHz。定时器挂在 APB 上但有个规则如果 APB 预分频系数不为 1定时器时钟是 APB 时钟的两倍。所以 APB1 上的定时器实际计数时钟是 84 MHzAPB2 上的也是 84 MHz因为 APB2 预分频为 2 时定时器时钟翻倍到 168 MHz具体要看配置。假设我们选一个挂在 APB1 上的定时器计数时钟 84 MHz要得到 8 kHz 的中断频率计算过程是这样的先确定预分频系数 PSC让计数频率落在一个合理范围。84 MHz 直接分频到 8 kHz 需要分频 10500这个值超过 16 位定时器的 PSC 最大值 65535 吗没有超过但 PSC 和 ARR 都是 16 位如果 PSC 取 10500ARR 取 1那计数精度太差一个计数周期就是 125 微秒没法做更细的时序控制。更好的做法是让 PSC 小一点ARR 大一点比如 PSC 取 84计数频率变成 1 MHzARR 取 125这样每个计数是 1 微秒125 个计数就是 125 微秒正好 8 kHz。这个配置下定时器分辨率是 1 微秒对于需要微调死区时间、采样延迟的场景来说够用了。公式总结一下定时器中断频率 定时器时钟 / ((PSC 1) * (ARR 1))。注意 PSC 和 ARR 寄存器实际分频是值加一这是很多人第一次配定时器会踩的坑算出来的频率总是差一点。3. 8 kHz 控制环的核心细节与实操要点3.1 中断优先级与嵌套设计控制环中断的优先级设置是个容易被忽视但极其关键的细节。ODrive 里控制环中断的优先级通常设得比较高但不能是最高的。为什么因为还有比它更紧急的事情比如故障保护。如果电机出现过流硬件比较器触发刹车信号这个响应必须比控制环更快。所以典型的中断优先级安排是硬件故障NMI 或最高优先级 编码器索引信号捕获 控制环 通信USB、UART、CAN。在 STM32 的 NVIC 里优先级数值越小优先级越高。控制环中断一般设成中等偏上比如抢占优先级 1 或 2子优先级 0。这样它不会被通信中断打断但可以被故障中断抢占。我见过有人把控制环设成最高优先级结果 USB 通信一来就丢包因为控制环把 CPU 全占了通信中断排队等太久。还有一个细节是中断服务函数ISR里要做的事情要尽量精简。ODrive 的做法是在 ISR 里只做最核心的采样和计算把日志、通信、状态机更新这些放到主循环或者低优先级任务里。ISR 执行时间如果超过控制周期的 50%系统就会变得很脆弱稍微有点额外负载就抖动。3.2 采样时刻与 PWM 的相位关系这是整个控制环里最讲究的地方。电流采样必须在 PWM 波形的特定时刻进行才能采到准确的相电流。对于中心对齐的 PWM通常在一个 PWM 周期的中间点采样这时候开关管的状态是确定的电流纹波最小。具体来说如果 PWM 是中心对齐模式计数器从 0 数到 ARR 再数回 0中间那个点计数器等于 ARR 的时刻就是采样点。ODrive 用定时器的 TRGO 信号在计数器到达 ARR 时触发 ADCADC 转换完成后产生中断控制环在中断里读取 ADC 值并计算。这样采样时刻和 PWM 周期严格对齐不受软件延迟影响。注意如果你用的是下桥臂电流采样低侧采样采样时刻要稍微提前一点因为下桥臂导通时间有限太晚采样可能已经换相了。具体提前多少要看死区时间和电流上升时间一般提前 1 到 2 微秒。我踩过的一个坑是一开始用软件触发 ADC在控制环中断里手动启动转换结果采样时刻随中断响应延迟而漂移电流波形上能看到明显的毛刺电机低速时抖动厉害。改成定时器硬件触发之后波形立刻干净了。这个改动看起来小但对控制质量的影响非常大。3.3 控制环内部的任务编排一个 8 kHz 的控制环 ISR 里任务顺序是有讲究的。ODrive 的大致流程是读取 ADC 电流值 → 读取编码器位置 → 执行 FOC 计算 → 更新 PWM 占空比 → 处理故障检测。这个顺序不能乱因为每一步都依赖前一步的结果。读取编码器位置这一步如果是增量式编码器需要读取定时器的计数寄存器如果是绝对值编码器比如 AS5047需要通过 SPI 读取。SPI 读取比较慢可能要好几个微秒所以 ODrive 有时候会把编码器读取放在控制环之外用 DMA 或者单独的定时器触发控制环里只取最新值。这样做的好处是控制环 ISR 时间可控不会因为 SPI 传输慢而抖动。FOC 计算本身包括几个数学变换用浮点运算还是定点运算也有讲究。STM32F405 有硬件浮点单元FPU用浮点运算很方便但要注意 FPU 的上下文保存会增加中断开销。ODrive 早期版本用的是浮点后来有些版本为了降低中断延迟改用了定点或者混合方案。如果你自己写建议先用浮点把逻辑跑通再根据性能需求决定要不要优化。4. 从零配置一个 8 kHz 控制环的完整实操4.1 时钟树与定时器参数计算假设你用的是一块 STM32F405 的开发板外部晶振 8 MHz目标系统时钟 168 MHz。用 CubeMX 配置时钟树的话PLL 参数大概是 M8, N336, P2, Q7这样得到 168 MHz 的系统时钟。APB1 预分频设为 4得到 42 MHz定时器时钟翻倍为 84 MHz。选 TIM2 作为控制环时基挂在 APB1 上。目标中断频率 8 kHz计算 PSC 和 ARR定时器时钟 84 MHz目标频率 8 kHz分频比 84,000,000 / 8,000 10500取 PSC 83则计数频率 84 MHz / 84 1 MHzARR 1,000,000 / 8,000 - 1 124所以 PSC 83ARR 124得到 1 MHz 计数频率和 8 kHz 中断频率。验证一下1,000,000 / (124 1) 8000 Hz正确。配置定时器为向上计数模式开启自动重装载使能更新中断。如果需要中心对齐 PWM把计数模式改成中心对齐这时候中断频率会变成原来的一半因为一个完整周期上下各计一次需要把 ARR 减半或者调整 PSC。这个细节很多人第一次配中心对齐时会搞错以为频率没变结果控制环实际跑在 4 kHz。4.2 中断服务函数的骨架代码下面是一个简化的控制环 ISR 骨架用 HAL 库的风格写方便理解结构void TIM2_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(htim2, TIM_FLAG_UPDATE) ! RESET) { __HAL_TIM_CLEAR_FLAG(htim2, TIM_FLAG_UPDATE); // 1. 读取相电流假设 ADC 已由硬件触发完成 float ia adc_to_current(adc_buf[0]); float ib adc_to_current(adc_buf[1]); float ic -ia - ib; // 三相电流和为零 // 2. 读取编码器位置 int32_t encoder_count __HAL_TIM_GET_COUNTER(htim3); float theta encoder_to_angle(encoder_count); // 3. Clarke 变换 float i_alpha ia; float i_beta (ia 2.0f * ib) * ONE_BY_SQRT3; // 4. Park 变换 float cos_t arm_cos_f32(theta); float sin_t arm_sin_f32(theta); float i_d i_alpha * cos_t i_beta * sin_t; float i_q -i_alpha * sin_t i_beta * cos_t; // 5. 电流 PI 调节 float v_d pi_d_update(i_d_ref - i_d); float v_q pi_q_update(i_q_ref - i_q); // 6. 反 Park 变换 float v_alpha v_d * cos_t - v_q * sin_t; float v_beta v_d * sin_t v_q * cos_t; // 7. SVPWM 生成并更新占空比 svpwm_update(v_alpha, v_beta); // 8. 故障检测 check_faults(ia, ib, ic); } }这段代码里每一步都有优化空间比如三角函数可以用查表或者 CORDIC 替代PI 调节器可以用定点实现但作为理解结构的起点足够了。实际 ODrive 的代码比这个复杂得多有各种状态机、校准模式、错误处理但核心骨架就是这个样子。4.3 验证时基是否准确的几种方法配好之后怎么确认真的跑在 8 kHz最直接的方法是在 ISR 里翻转一个 GPIO用示波器量频率。如果量出来是 8 kHz说明时基对了。如果差一点检查 PSC 和 ARR 的计算注意那个加一的坑。第二种方法是用定时器的另一个通道做输入捕获捕获控制环中断翻转的 GPIO这样能同时看到频率和抖动。如果抖动很大超过几个微秒说明 ISR 执行时间太长或者被更高优先级中断打断了需要优化。第三种方法是在 ISR 里累加一个计数器主循环里每秒读一次看是不是 8000。这个方法不用示波器但精度受主循环读取时刻影响只能做粗略验证。我一般用第一种方法简单直接。有一次量出来是 7.98 kHz差了 0.25%查了半天发现是晶振实际频率有偏差换了个晶振就好了。所以如果你对频率精度要求高晶振的精度也要考虑进去。5. 常见问题与排查技巧实录5.1 控制环频率对不上的排查思路频率对不上是最常见的问题排查顺序建议这样先确认系统时钟配置对不对用 MCO 引脚输出时钟或者读 RCC 寄存器验证再确认定时器时钟有没有翻倍APB 预分频不为 1 时定时器时钟是 APB 的两倍这个规则容易忘然后检查 PSC 和 ARR 的计算注意加一最后看中断有没有被意外关闭或者被更高优先级中断长时间阻塞。下面这个表格整理了几种典型现象和对应原因现象可能原因排查方法频率正好是目标的一半定时器配成了中心对齐模式检查 CMS 位改回边沿对齐或调整 ARR频率偏差几个百分点晶振实际频率与标称不符用示波器量 MCO 输出或换晶振频率忽高忽低中断被高优先级任务阻塞检查 NVIC 优先级测量 ISR 执行时间完全没有中断定时器没使能或中断没开检查 CR1 的 CEN 位和 DIER 的 UIE 位频率是目标的两倍更新中断触发了两次检查是否同时开了更新和触发中断5.2 电流采样噪声大的处理经验采样噪声大通常不是定时器本身的问题而是采样时刻和 PWM 相位没对齐。先确认 ADC 是不是由定时器 TRGO 硬件触发如果是软件触发改成硬件触发。然后确认采样点是不是在 PWM 周期中间中心对齐模式下应该是计数器等于 ARR 的时刻。如果用的是低侧采样检查采样时刻是否在下桥臂导通窗口内。还有一个容易被忽视的点是 ADC 采样保持时间。如果采样保持时间太短采样电容没充够读数会偏低且噪声大。STM32 的 ADC 采样时间可以配置一般设成 15 到 28 个 ADC 时钟周期比较稳妥。我遇到过采样时间设成 3 个周期导致电流读数跳变的情况改成 15 个周期就稳定了。5.3 中断执行时间超标的优化手段如果 ISR 执行时间接近或超过控制周期的一半系统会变得不稳定。优化手段按性价比排序先把能移到主循环的事情移出去比如日志、通信、状态机更新再把三角函数换成查表或 CORDIC然后考虑用定点运算替代浮点最后才是提高主频或换更快的 MCU。ODrive 的一个做法是把编码器读取和部分计算放到 DMA 完成中断里控制环 ISR 只做最核心的 FOC 计算。这样控制环 ISR 时间能压到 20 微秒以内8 kHz 下 CPU 占用率很低。如果你自己写可以借鉴这个思路把任务分层核心层用高优先级中断辅助层用低优先级或主循环。提示测量 ISR 执行时间最简单的方法是在 ISR 入口拉高一个 GPIO出口拉低用示波器量高电平持续时间。注意 GPIO 翻转本身有几十纳秒开销测量结果会略偏大但作为参考足够了。5.4 从 8 kHz 改到其他频率要注意什么有时候项目需求变了要把控制频率从 8 kHz 改成 10 kHz 或 16 kHz。改的时候不只是改 ARR 那么简单还要考虑几件事电流环 PI 参数需要重新整定因为采样周期变了积分时间常数要跟着调PWM 频率如果和控制频率绑定改控制频率会影响 PWM 频率进而影响电机噪声和发热CPU 占用率会变化要重新评估余量如果用了前馈或者观测器里面的时间常数也要同步修改。我的建议是改频率之前先把所有和时间相关的参数列个清单改完之后逐项核对。我见过有人只改了 ARR忘了改 PI 参数结果电机一跑就振荡查了半天才发现是积分时间没跟着调。6. 时基设计对整机性能的实际影响把定时器时基这件事做扎实之后整机性能的提升是能明显感觉到的。最直接的是电流波形更干净用示波器看相电流毛刺和尖峰明显减少这意味着电机发热更低、噪声更小。其次是低速性能改善8 kHz 配合合适的编码器分辨率低速时位置抖动可以控制在很小的范围内做云台或者机械臂关节的时候特别明显。还有一个隐性好处是系统鲁棒性提升。时基稳定意味着控制周期稳定控制周期稳定意味着 PI 调节器的行为可预测不会因为周期抖动导致积分饱和或者微分冲击。我在做一台需要频繁启停的设备时时基稳定之后启停过程中的电流冲击小了很多母线电压波动也小了。从调试效率角度看一个清晰的时基架构让问题定位变得容易。控制效果不好时可以按时基对不对 → 采样时刻对不对 → 计算逻辑对不对 → 参数整定对不对这个顺序排查每一步都有明确的验证方法不会像无头苍蝇一样乱试。最后分享一个我在实际项目中总结的小技巧在固件里加一个时基健康监测功能用一个低优先级任务定期检查控制环中断的计数如果发现频率偏离超过阈值或者中断丢失就记录一个错误码。这个功能在实验室里可能用不上但在现场运行时能帮你快速定位是硬件问题还是软件问题。我有一台设备在现场偶尔抖动加了监测之后发现是电源纹波导致晶振不稳换了电源就好了如果没有这个监测可能要排查很久。这个内容后续还可以往几个方向扩展一是把编码器接口的定时器配置也讲一遍特别是正交编码器模式和索引信号捕获二是聊聊 PWM 死区时间的计算和配置这块和时基也紧密相关三是如果要做多轴控制多个控制环之间怎么同步时基这也是个有意思的话题。
返回列表