Lazy loaded image
ODrive 源码分析(三):状态机、故障保护与调试闭环
Words 2643Read Time≈ 7 min
2026-9-19
2026-9-21
🛡️
本文目标
算法回答“怎样控制”;状态机和保护回答“什么时候允许控制、出错时怎样停、怎样知道为什么停”。能让电机转起来只是演示,能在异常条件下安全停机才是工程。

一、源码阅读边界

重点文件:
  • axis.cpp:任务链、校准前置条件、闭环启动、Idle与看门狗。
  • motor.cpp:arm/disarm、驱动故障、温度、电流和PWM错误。
  • low_level.cpp:母线电流、回生制动和全局安全停机。

二、Axis不是一个简单switch

Axis::run_state_machine_loop() 将请求转换为任务链。典型完整校准序列:
启动序列则由配置决定是否依次执行:电机校准、Index搜索、编码器偏移校准、回零和闭环控制。
每个状态都有前置条件。例如:
  • 电机未校准,不能做编码器偏移校准;
  • Hall极性未校准,不能继续Hall相位相关校准;
  • 电机未校准或编码器方向未知,不能进入有感闭环;
  • 回零未启用限位开关,则直接失败。
状态执行失败后,任务链被清空并回到 IDLE。这避免失败后继续执行后续危险动作。

三、进入闭环前发生什么

start_closed_loop_control() 不是只把状态改成“闭环”,它还会:
  1. 根据有感/无感模式选择位置、速度和相位来源;
  1. 连接 Controller、Motor、Encoder/Estimator 之间的数据路径;
  1. 把位置目标初始化为当前位置,避免启动位置突跳;
  1. 清空速度积分,避免历史状态带入;
  1. 连接转矩目标到电机;
  1. 连接 、 和相位信号;
  1. 等待至少一个完整控制周期;
  1. 最后才 arm 电机。
关键点:“数据源连接完成”和“PWM允许输出”被分开处理,避免控制器拿着未初始化数据直接驱动功率级。

四、错误如何传播

4.1 子模块错误上卷

  • Encoder::set_error():使位置/速度估计失效,同时设置 Axis::ERROR_ENCODER_FAILED;
  • Motor::disarm_with_error():记录Motor错误,设置 Axis::ERROR_MOTOR_FAILED,立即disarm;
  • Controller错误:无效估计、超速、Spinout等使控制更新失败,随后闭环退出或电机停机。
因此调试不能只看Axis的汇总错误。Axis告诉你“哪个子系统失败”,还要继续读取对应Motor、Encoder或Controller错误位查根因。

4.2 错误是否自动清除

错误大多是粘滞的,需要明确清除;部分临时状态例外。例如Controller中的无效估计在恢复正常后可以清除,避免正常的校准切换留下永久错误。
设计时必须区分:
  • 瞬态不可用:数据还未初始化,可以等待恢复;
  • 安全故障:过流、驱动器故障、过温等,必须停机并要求显式恢复。

五、PWM安全停机

Motor::disarm() 在临界区内:
  1. 关闭栅极驱动器;
  1. 清除armed状态;
  1. 禁止定时器自动重新使能输出;
  1. 无条件关闭主输出MOE;
  1. 断开控制律。
函数返回后,三相保证浮空,下一次定时器更新也不会偷偷重新打开PWM。
arm() 同样不是立即输出:只有控制律成功算出有效PWM,并经过更新事件锁存后,输出才真正使能。

六、主要保护链

6.1 电流保护

  • ADC接近模拟前端饱和:电流数据不可信;
  • 等效电流超过有效限值加容差:立即停机;
  • armed状态下缺失电流测量:无法保证过流检测,立即停机;
  • 母线电流超出硬上下限:立即停机。

6.2 温度与驱动器

Motor::do_checks() 检查:
  • Gate Driver是否ready;
  • 电机温度;
  • MOS/FET温度。
温度除了触发硬停机,也可以先通过 effective_current_lim() 动态降低允许电流,形成降额运行。

6.3 超速与Spinout

  • 超速:实际速度超过速度限制乘容差;
  • Spinout:控制器试图制动,但机械功率和电功率方向表现矛盾,通常暗示编码器偏移或相位错误。

6.4 看门狗与限位开关

Axis周期性检查通信/控制看门狗;计数耗尽后设置超时错误。非回零状态触发限位开关,也会形成Axis错误。

6.5 回生与制动电阻

update_brake_current() 汇总各轴母线电流,按允许回生电流和母线电压计算制动占空比:
  • 制动电阻配置无效:全局停机;
  • 占空比为NaN:全局停机;
  • 制动占空比限制在95%,给自举电容留充电时间;
  • 正向母线电流或负向回生电流越界:全局停机;
  • 制动上下管死区不合法:全局停机。

七、推荐故障定位顺序

⚠️
只清除错误码而不消除根因,等价于把烟雾报警器电池抠掉。系统安静了,火还在烧。

八、典型故障案例

案例1:请求闭环后立刻退回Idle

检查顺序:
  1. 电机是否完成校准;
  1. 有感模式下编码器方向是否有效;
  1. start_closed_loop_control() 数据源连接是否成功;
  1. arm后是否马上出现电流测量缺失、驱动器故障或控制器初始化错误;
  1. Axis、Motor、Encoder和Controller错误位分别是什么。

案例2:运行一段时间后随机停机

优先看:
  • 温度降额后是否最终过温;
  • 通信看门狗是否未及时喂;
  • SPI绝对编码器错误率是否累计越界;
  • 最坏控制执行时间是否偶发超期;
  • 回生时母线电压和负向电流是否越界。

案例3:减速时比加速更容易报错

重点检查回生链:负母线电流、制动电阻阻值与功率、母线过压、制动占空比饱和。不要只盯着速度PI,因为加速和减速面对的是不同的母线能量流向。

九、面试问题与参考答案

问题1:为什么校准要设计成状态机,而不是一个大函数从头跑到底?

参考答案:不同校准步骤有明确依赖,且每一步都可能被用户取消、保护中断或失败。状态机可以检查前置条件、记录当前阶段、失败后回到安全状态,并允许按配置复用不同任务链。大函数容易把错误恢复、并发请求和安全停机缠在一起。

问题2:ODrive进入闭环前如何避免目标突跳?

参考答案:先连接位置和速度估计数据源,把位置目标初始化为当前位置,清空速度积分,等待至少一个完整控制周期,再arm功率输出。这样闭环接管瞬间的位置误差和历史积分接近零。

问题3:为什么缺失电流测量时要停机?

参考答案:电流不仅是控制反馈,也是过流保护依据。armed状态下继续运行但没有可信电流,既无法保证转矩输出,也无法判断功率级是否过流,所以应按故障安全原则关闭PWM。

问题4:Axis错误和Motor错误有什么区别?

参考答案:Axis错误是编排层的汇总和状态决策依据,Motor错误保留功率级或电流控制的具体根因。Motor失败会向上设置Axis的 MOTOR_FAILED,调试时必须继续查看Motor错误,不能停在汇总层。

问题5:为什么disarm不仅要清MOE,还要禁止自动输出使能?

参考答案:只清MOE可能在下一次定时器更新事件被自动重新使能。ODrive同时清除armed状态、自动输出使能和控制律连接,确保退出函数后以及后续更新事件都不能重新打开PWM。

问题6:怎样设计一次有价值的故障注入测试?

参考答案:每次只注入一个可控故障,例如断开编码器、停止喂看门狗、降低过温阈值或制造回生过压。记录触发时间、子模块错误、Axis状态、PWM关闭延迟和恢复条件。测试目标不是“看到报错”,而是验证错误传播、停机动作和恢复路径符合设计。

十、建议实验

未做电机校准时请求编码器偏移校准,确认状态机拒绝并回Idle。
闭环运行时停止喂看门狗,测量从超时到PWM关闭的延迟。
断开或注入错误编码器数据,记录Encoder错误如何传播到Axis。
降低电流限值,验证过流保护和错误粘滞行为。
做一次快速减速,记录母线电压、回生电流和制动占空比。
建立“故障码→触发条件→停机动作→恢复步骤”的表格。

十一、结论

掌握ODrive状态机,不是记住状态名称,而是能回答:当前动作的前置条件是什么、失败会传播到哪里、PWM如何被可靠关闭、清错后必须重做哪些校准,以及如何用日志和波形证明故障闭环有效。
上一篇
ODrive 源码分析(二):实时采样、校准与控制时序
下一篇
ODrive 源码分析(一):闭环控制、限幅与参数整定

Comments
Loading...