如果你正在折腾无刷电机驱动,绕不开ODrive这块板子。它把FOC电流环、编码器反馈、速度/位置闭环打包成了非常清爽的控制接口,尤其在用串口命令操作的时候,你会觉得手里拿的是一台电机的“遥控器”。ODrive支持USB原生协议、CAN和ASCII三种控制方式,其中ASCII协议最原始,就是一行一行发字符串,但恰恰是这种原始方式,在调试和嵌入式主控对接时最顺手。一条命令就能读状态、改参数、触发校准、让电机转起来。我最初做某个桌面机械臂的电机动子方案时,用的就是ODrive配STM32主控,整个控制链路里最关键的部分就是这套串口命令。如果你也用树莓派、Arduino这类主控,想在项目里快速上手ODrive,这篇整理能省掉不少在文档里翻命令的时间。
下面的内容以ODrive v0.5.1固件为准,这是目前开源机器人项目里使用最广的版本,命令路径和状态码都基于这个版本。如果你刷了新固件,个别属性路径会有差异,到时候对着对应版本的文档核对一下就行。
1. 先搞清楚串口能干什么:协议框架与适用场景
1.1 三种通信方式怎么选
ODrive的控制接口一共有三类,很多人第一次接触会被文档绕晕,先弄明白它们的区别,才知道什么场景该用哪套。
USB原生协议是ODrive上位机(比如图形化配置工具)用的,底层基于USB批量传输,通信效率高,还能做固件升级。但它需要安装Python库或者图形工具,不适合作为嵌入式主控的最终控制通道,更多是调试配置的时候用。
CAN协议走的是工业总线路线,适合多电机、长距离、抗干扰要求高的场景。比如四足机器人一条腿上好几个关节,用CAN总线把多个ODrive串起来很合适。但CAN需要额外处理帧ID、报文解析这类事情,对新手来说学习曲线陡一些。
ASCII协议就是本文的主角。它走的是最普通的UART串口,文本格式,任何能发串口数据的工具都能控制它,不需要装任何依赖库。STM32串口发一行字符串,ODrive就能执行;读回来的也是一行字符串,解析非常容易。调试的时候,电脑上随便开个串口助手就能实时看状态,这种“所见即所得”的体验是另外两套协议给不了的。
所以我的建议是:如果是做项目原型、教学验证,或者主控资源有限,直接用ASCII协议;如果做多电机分布式系统,再上CAN;上位机图形化配置只在首次调参时用USB原生协议。三个东西不冲突,但大部分日常操作,ASCII足够应付。
1.2 参数树:一条命令背后是整个属性系统
ODrive的ASCII命令之所以只有“读”和“写”两个动词,是因为固件内部维护了一棵完整的参数树。每个可配置项和运行状态都是树上的一个节点,节点路径用点号分隔。
你可以把这棵树想象成一个仓库,每个参数有固定的货架编码。比如:
axis0.config.motor.pole_pairs:0号轴电机的极对数axis0.motor.is_calibrated:0号轴电机是否校准完成axis0.controller.config.vel_gain:0号轴速度环增益
r命令就是从货架上取东西,w命令就是往货架上换东西。这种设计的最大好处是:你不需要为每个功能单独记忆“特殊命令”,只要知道参数树结构,所有东西都能用同一个模式读写。遇到没见过的参数,顺着axis0.config或axis0.controller这样的路径慢慢猜,基本都能找到。
也正是因为这套属性树,ASCII命令才能覆盖ODrive的全部功能。配置、校准、控制、状态读取,本质上都是对树节点的读写。所以下面的命令整理,我不打算一条一条背流水账,而是按“你在用ODrive时到底要做什么”来分,这样你拿到手就能用。
2. 环境准备:接线、串口参数与三条保命命令
2.1 接线和串口参数
ODrive板子上有USB口,也有排针引出的UART引脚。如果你手头有USB转TTL模块,可以直接接UART引脚:ODrive的TX接USB转TTL的RX,ODrive的RX接USB转TTL的TX,GND接GND。很多新手第一次就栽在TX和RX接反了,记住这个交叉规则,接反了是完全没反应的。
如果直接用板载USB口,Windows下会识别成一个虚拟串口,Linux或macOS下是/dev/ttyUSB0或/dev/tty.usbserial-xxx这样的设备。注意,用USB虚拟串口的波特率无所谓,因为走的是USB协议;一旦走外部UART引脚,波特率就必须设置正确。
ODrive的串口参数是固定的:115200波特率、8数据位、1停止位、无校验。大多数串口工具默认就是115200-8-N-1,直接连就行。但有一个关键细节:每条命令必须以换行符结尾,推荐发送\n。如果你用的串口助手默认发送\r\n,大多数固件版本也能容忍,但遇到“命令发出去没反应”的情况,第一个要查的就是换行符设置。
2.2 三个系统级命令先记住
在所有参数操作之前,有三个最基础的命令几乎每次都会用到。
| 命令 | 作用 | 示例输出/结果 |
|---|---|---|
v | 查看固件版本 | ODrive v0.5.1 |
help | 打印命令行帮助 | 列出可用命令 |
ss | 保存配置到非易失存储 | 无输出,但配置写入Flash |
sr | 系统重启 | 串口会短暂断开,等待重启完成 |
ss是所有人最容易忽略的命令。ODrive有很多参数是内存中临时生效的,掉电就丢,只有执行ss才会写入固化存储。我的习惯是:改完一组配置后立刻ss,避免后面忘记。
sr相当于重启板子,重启时串口会重新初始化,所以发送之后不要马上发下一条命令,等1到2秒再继续。如果是用外部UART引脚,可能需要重新打开串口连接。
2.3 轴状态机:命令的开关
ODrive每个轴都有一个状态机,控制着电机当前处于什么状态。这点理解了,后面所有操作都会很顺。
状态切换的方式是写axis0.requested_state这个节点,状态值是一个整数。用来查询当前状态则是读axis0.current_state。最常用的几个状态码如下:
| 状态码 | 名称 | 含义 |
|---|---|---|
| 0 | IDLE | 空闲,电机不上电 |
| 1 | STARTUP_SEQUENCE | 上电自检序列 |
| 2 | FULL_CALIBRATION_SEQUENCE | 完整校准序列 |
| 3 | MOTOR_CALIBRATION | 仅电机校准 |
| 4 | ENCODER_INDEX_SEARCH | 编码器索引搜索 |
| 5 | ENCODER_OFFSET_CALIBRATION | 编码器偏移校准 |
| 6 | CLOSED_LOOP_CONTROL | 闭环控制,电机正常出力 |
| 7 | LOCKIN_SPIN | 锁定旋转,测试用 |
通俗点理解:requested_state是“你想让它干啥”,current_state是“它正在干啥”。写状态码相当于下达命令,读状态码则是确认是否执行到位。比如你发w axis0.requested_state 3,ODrive就开始电机校准;校准完成后,current_state会自动回到1或0,同时axis0.motor.is_calibrated会变成1。
3. 高频命令速查:配置、校准、控制一页纸
3.1 电机参数类命令
把一块新ODrive接到电机上,第一件事就是告诉它电机是什么类型、有什么特性。这些参数基本都在axis0.config.motor路径下面。
| 参数 | 命令路径 | 说明 |
|---|---|---|
| 电机类型 | axis0.config.motor.motor_type | 0为大电流FOC模式,1为低电流模式 |
| 极对数 | axis0.config.motor.pole_pairs | 电机磁钢对数,必须准确 |
| 软限流 | axis0.config.motor.current_soft_max | 持续运行电流上限,单位A |
| 硬限流 | axis0.config.motor.current_hard_max | 瞬时电流上限,单位A |
| 校准电压上限 | axis0.config.motor.resistance_calib_max_voltage | 校准时的电压限制,默认2 |
电机类型的设置多数情况下用0,也就是标准的大电流FOC模式。这个值写错了,后面电流环会乱套,电机根本转不起来。
极对数是最容易出错的地方。无刷电机的极对数等于磁钢数量除以2,比如一个电机有14块磁钢,极对数就是7。你可以拆开电机数磁钢,或者看卖家参数。极对数写错,最典型的症状是校准时听见电机发出刺耳的高频噪音,甚至转一下就开始堵转、报错。这个参数直接决定电流环的电角度计算,错一位都不行。
软硬电流限制要结合你的电源能力和电机额定电流来定。比如电源能稳定输出10A,电机额定是8A,那么current_soft_max设8A、current_hard_max设12A是合理的。软限是长时间能跑的值,硬限是短时间允许的峰值,不能反过来。
校准电压上限比较隐蔽。它决定了电机校准的时候驱动桥能给多高的电压。如果设得太低,比如0.5V以下,有些内阻较大的电机会因为电流不够导致相电阻测量不准,校准结果残次。一般保持默认2,除非你要校准的电机非常小、额定电流很低,才需要适当调低。
3.2 校准类命令
写完电机参数,接下来就是校准。校准的目的是让ODrive知道电机的电阻、电感、编码器零位偏移这些“底数”,没有这步,闭环控制没法做。
| 状态码 | 名称 | 执行内容 |
|---|---|---|
| 3 | MOTOR_CALIBRATION | 测量相电阻和相电感 |
| 4 | ENCODER_INDEX_SEARCH | 寻找编码器索引信号 |
| 5 | ENCODER_OFFSET_CALIBRATION | 测量并写入编码器零位偏移 |
| 2 | FULL_CALIBRATION_SEQUENCE | 依次执行3、4、5(如果支持) |
实际使用中,多数情况直接发w axis0.requested_state 2跑完整校准就行。它会先做电机校准,然后做编码器偏移校准。如果你的编码器没有索引信号(比如普通AB相增量编码器),索引搜索会自动跳过。但如果你的编码器带Index,一定要在配置里正确启用索引模式,否则上电后每次都找不到机械零位。
校准过程中电机轻微震动、发出嗡嗡声、转到某个角度后锁住,这些都是正常的。但有一点必须强调:校准时不要让电机带负载,尤其不要带着刚性连接的减速结构或机械臂,否则电机转不动,编码器偏移校准会失败。把电机轴完全空载,再执行校准。
校准完成后,用下面两条命令确认结果:
r axis0.motor.is_calibrated r axis0.encoder.is_ready两个返回值都是1,说明校准通过。别急着进闭环,先把这个校准状态保存下来:
ss否则重启后ODrive不知道你校准过,又会要求你重来。
3.3 控制器类命令
校准通过之后,电机就随时可以进入闭环控制状态了。这里涉及两类命令:控制模式设置和增益参数设置。
控制模式通过axis0.controller.config.control_mode设置,常用的四种如下:
| 模式码 | 模式名称 | 输入命令路径 | 典型用途 |
|---|---|---|---|
| 0 | 电压控制 | axis0.controller.input_voltage | 开环调试 |
| 1 | 电流控制 | axis0.controller.input_current | 力控、力矩输出 |
| 2 | 速度控制 | axis0.controller.input_vel | 恒速转动 |
| 3 | 位置控制 | axis0.controller.input_pos | 定位、跟踪轨迹 |
设置方式很简单:
w axis0.controller.config.control_mode 3 w axis0.controller.input_pos 1.5注意设置顺序:先切换到对应的控制模式,再发输入命令。有些朋友先给了位置目标,再切模式,结果电机没反应,就是因为目标值发送的时候还没处于位置模式下,被固件丢弃了。
增益参数直接影响控制手感。常用增益都在axis0.controller.config下面:
| 参数 | 默认参考值 | 作用 |
|---|---|---|
pos_gain | 20左右 | 位置环增益,决定位置误差对应多大速度指令 |
vel_gain | 0.16左右 | 速度环比例增益,决定速度误差对应多大电流 |
vel_integrator_gain | 0.32左右 | 速度环积分增益,消除稳态误差 |
vel_limit | 2 | 最大速度上限,单位圈/秒 |
这些默认值不是对所有电机都合适,但作为起始点是没问题的。位置模式下有个最容易踩的坑:vel_limit默认只有2,意味着电机最大只能以每秒2圈的速度移动,你给一个大目标位置,电机会“慢慢悠悠”过去,看起来就像坏了一样。所以位置控制调试时,先把vel_limit调大,比如10或者20,再试位置跟踪。
增益调大后会震荡甚至飞车,调小后会变得“懒洋洋”。我的经验是:先设一个保守值让电机能转,然后观察阶跃响应,有振荡就降pos_gain,跟踪太慢就加vel_gain和pos_gain。速度环积分增益最后再动,它主要用来消除长时间停不下来的静差,加太多会让系统低频振荡。
4. 完整实操:从全新板子到电机顺畅转动
4.1 检查固件版本与通信链路
先连接ODrive,打开串口工具,选择对应COM口,波特率115200。发送v,如果返回类似ODrive v0.5.1的字符串,说明通信链路没问题。
接着发送help,确认你能看到命令帮助列表。这一步看似多余,但可以一次性验证换行符和串口参数是否正确。如果help没有任何输出,后面所有操作都没意义,先回去查接线和串口设置。
4.2 写入电机/编码器配置
假设我手里这台电机是7对极、额定电流10A左右,编码器用的是霍尔传感器。那么配置流程是这样的:
w axis0.config.motor.motor_type 0 w axis0.config.motor.pole_pairs 7 w axis0.config.motor.current_soft_max 10 w axis0.config.motor.current_hard_max 12 w axis0.config.motor.resistance_calib_max_voltage 2 w axis0.config.encoder_mode 1这里encoder_mode 1表示霍尔传感器。不同传感器有对应的模式值:0是增量AB编码器,1是霍尔,2是SPI绝对编码器,3是带索引的增量编码器。如果你用的是普通光电编码器,就设0;如果你用的是AS5047这类SPI绝对编码器,就设2。
写完后执行ss保存,然后sr重启。重启后串口会短暂断开,等1到2秒再继续发命令。为什么要重启?因为部分配置项在重启后才会真正生效,尤其是编码器模式这类涉及外设初始化的参数。
4.3 执行校准并确认标志位
保存重启完成后,开始校准:
w axis0.requested_state 2这命令会把0号轴切到完整校准序列。之后电机会自己动起来,整个过程大概几秒到十几秒,取决于电机参数。期间你可以反复读axis0.current_state,状态码会从2变成其他值,当它回到0或1时,说明校准流程跑完了。
接着确认结果:
r axis0.motor.is_calibrated r axis0.encoder.is_ready两个返回值都应该是1。如果is_calibrated是0,说明校准失败,先别急着往下走,把电机接线重新插紧,检查极对数,然后重新发送校准命令。
确认没问题后,再执行一次ss保存校准结果。这里一定要记住:校准结果如果不保存,下次上电还得重新校准。
4.4 进入闭环:从位置控制开始
校准完成后,让电机进入闭环:
w axis0.requested_state 6此时电机会锁住,用手去拧会有阻力,这是正常现象。接着切换到位置控制模式:
w axis0.controller.config.control_mode 3 w axis0.controller.config.pos_gain 20 w axis0.controller.config.vel_gain 0.2 w axis0.controller.config.vel_integrator_gain 0.3 w axis0.controller.config.vel_limit 5然后给一个位置目标,让电机转到指定位置:
w axis0.controller.input_pos 1这里的位置单位是“圈”。给1就是让电机转1圈。片刻之后读取编码器反馈:
r axis0.encoder.pos_estimate反馈值应该在1附近,比如0.9986,说明位置闭环工作正常。误差完全正常,位置环本身就会有小的跟踪误差。
到这里,整块板子的基础配置就算走通了。最后记得保存配置:
ss如果你有第二个电机接在M1口,操作方式完全一样,只要把命令里的axis0换成axis1即可。
5. 调试中最容易踩的四个坑与对应排查
5.1 命令发出去没反应,或者返回unknown command
这个问题出现频率最高,原因也很集中。
首先检查换行符。串口助手里如果设置成了“不发送新行”,ODrive收不到命令结束符,自然不会执行。把发送设置改成“追加换行符(\n)”或类似选项。
其次检查属性名大小写。ODrive的属性路径区分大小写,axis0.controller.config.vel_gain和axis0.Controller.config.vel_gain是两码事。复制粘贴的时候尤其注意,我之前在终端里手敲就经常把controller打成Controller。
再一个可能是串口号选错了。有些USB转TTL模块会虚拟出两个COM口,或者电脑上插了多个串口设备。在设备管理器里确认哪一个是ODrive对应的COM口,断开重连再试。
最后才是波特率。外部UART口如果波特率不对,通常会收到乱码,而不是完全没反应。看到乱码就先检查波特率是不是115200。
5.2 校准一直失败,或者校准时电机抖动刺耳
校准失败的原因九成出在极对数或者电机接线。
如果极对数写错了,电机在校准时会出现明显的“卡顿”感,声音刺耳,甚至电流直接超限报错。核对电机磁钢数量,重新写入pole_pairs。
如果极对数正确但依然失败,检查三相电机线A/B/C是否接好,接头有没有虚焊。ODrive的相线顺序无所谓,因为校准会自适应,但前提是三条线都可靠导通。用万用表量一下电机三根线两两之间的电阻,应该是对称的。
还有一个思路:校准时驱动电压不够。如果你调低了resistance_calib_max_voltage,试着恢复到默认值2,再校准一次。电压太低导致电流上不去,相电阻测量值就会离谱。
5.3 进闭环后电机抖动、飞车或者顶死
能进闭环说明校准至少通过了一部分,问题多半出在控制配置或者编码器设置上。
先确认编码器模式是不是设对了。霍尔传感器就设1,增量编码器就设0,如果模式选错,反馈数据完全是乱的,电机会不受控地乱转。
再排查编码器偏移是否真的校准成功。如果r axis0.encoder.is_ready返回1但位置反馈漂移,可能是编码器偏移保存后又被覆盖了。重新执行整定流程,校准完立刻ss。
如果电机能转但抖动,大概率是增益太高。把pos_gain和vel_gain先降到很低的值,比如pos_gain给5、vel_gain给0.05,能正常转了再逐步往上加。记住一个原则:先保证稳定,再追求快速。
5.4 配置重启后丢失,每次都要求重新校准
这是最常见的“假故障”,原因非常简单:改完参数和校准状态后没有执行ss。
ODrive的配置生命周期是这样的:你通过w命令改的是RAM里的值,实时生效,但掉电即失。只有ss才会把这些值写入Flash。我见过太多人调好了一台电机,没保存,断电后第二天从零开始。我的习惯是:任何一组参数改动确认无误后,第一时间ss,永远不要拖。
另外注意,ss保存的是当前轴和当前配置块的所有内容。如果你改完axis0的配置后保存,接着又改了axis1的配置,得再ss一次,因为每次保存的是那一刻的全局配置快照。
最后分享一个我自己的小习惯:在串口调试阶段,尽量不要用那种只发不收的工具,选带接收日志和时间戳的串口助手,把每次发送和返回都留痕。这样如果电机突发出人意料的行为,你还能翻回去看看刚才到底发了什么命令、什么顺序发的,排查起来会轻松很多。ODrive的ASCII协议看着简单,但正因为简单,所有问题都暴露在明面上,只要按着“参数配置、校准、闭环、保存”这条链路一步步走,基本不会卡太久。