🤖 roboto_origin_03 Wiki
首页 / 电机子模块 / LeadRobot电机驱动详解

本文档深入解析 LroMotorDriver 的实现原理与协议细节,涵盖其继承架构、CAN-FD 帧格式、控制模式编解码、多电机批量控制机制以及错误码体系。阅读前建议先了解 MotorDriver抽象基类与接口设计CAN/CANFD协议抽象接口,以便理解驱动在整个 SDK 中的位置。

架构定位与继承关系

LroMotorDriver 继承自 MotorDriver 抽象基类,通过 MotorDriver::create_motor() 工厂方法统一实例化。它在架构中定位于"电机驱动实现层",直接依赖 MotorsCANFD 抽象接口完成帧收发,当前仅支持 CAN-FD 物理层(EtherCAT 预留接口但尚未实现)。与 DM、EVO 驱动并列,LRO 驱动是 SDK 支持的第三种品牌电机实现。

classDiagram
    class MotorDriver {
        +create_motor()$ shared_ptr~MotorDriver~
        +lock_motor()* void
        +unlock_motor()* void
        +init_motor()* uint8_t
        +motor_mit_cmd()* void
        +motor_pos_cmd()* void
        +motor_spd_cmd()* void
        #motor_id_: uint16_t
        #comm_type_: CommType
        #can_interface_: string
    }
    class MotorsCANFD {
        +transmit(frame) void
        +add_canfd_callback(cb, id) void
        +remove_canfd_callback(id) void
    }
    class LroMotorDriver {
        +LroMotorDriver(id, interface_type, can, model, offset)
        +canfd_rx_cbk(frame) void
        -write_register_lro(rid, value) void
        -save_register_lro() void
        -bus_registry_: unordered_map~string, vector~LroMotorDriver*~~$
        -motor_index_: uint8_t
        -limit_param_: LRO_Limit_Param
    }
    MotorDriver <|-- LroMotorDriver
    LroMotorDriver --> MotorsCANFD : uses (CAN-FD)

LRO 驱动在构造时通过 MotorsCANFD::get(can_interface) 获取单例总线后端,并将自身实例注册到 bus_registry_ 静态映射中。该注册机制是实现"一帧多机"批量 MIT 控制的基础:同一 can_interface 下的所有 LroMotorDriver 指针被收集到同一个向量中,在调用数组版 motor_mit_cmd(float*, ...) 时按 motor_index_ 填充 64 字节 CAN-FD 负载。

Sources: motor_driver.hpp, lro_motor_driver.hpp, motor_driver.cpp

支持的电机型号与参数限位

LeadRobot 系列当前支持四款电机,每款具有独立的物理限位参数,存储于全局静态数组 lro_limit_param 中。这些限位值同时用于 MIT 控制指令的量化和反馈数据的反量化。

枚举标识 型号含义 位置上限 (rad) 速度上限 (rad/s) 力矩上限 (Nm) Kp 上限 Kd 上限
LRO_5550 PJ2-55-5550 12.5 45.0 40.0 500 5
LRO_6562 PJ3-60-6562 12.5 45.0 40.0 500 5
LRO_8462 PJ3-75-8462 12.5 30.0 60.0 500 5
LRO_10062 PJ3-97-10062 12.5 25.0 80.0 500 5

在构造函数中,motor_model_ 决定 limit_param_ 的取值,后续所有 range_map 调用均依赖此结构完成浮点物理量与固定位宽整型之间的双向映射。值得注意的是,LRO 驱动的位置控制指令内部使用角度(degree)和转速(RPM)作为单位,与 SDK 公开的 rad / rad/s 接口存在显式换算。

Sources: lro_motor_driver.hpp, lro_motor_driver.cpp

CAN-FD 通信协议与帧格式

LRO 驱动采用标准 CAN-FD 帧进行通信,所有数据帧均置位 CANFD_BRS(比特率切换)标志以提升传输效率。协议中存在两种 CAN ID 语义:以电机独立 ID(1–0x7FF)为标识的目标控制帧,以及以固定 0x7FF 为标识的全局管理帧。

控制帧结构(以 MIT 模式为例)

MIT 阻抗控制帧长度为 8 字节,采用紧凑位域打包,总布局如下:

block-beta
    columns 8
    block:row1:8
        b63["bit 63-61<br/>motor_mode<br/>uint3"]
        b60["bit 60-49<br/>Kp<br/>uint12"]
        b48["bit 48-40<br/>Kd<br/>uint9"]
        b39["bit 39-24<br/>pos<br/>uint16"]
        b23["bit 23-12<br/>spd<br/>uint12"]
        b11["bit 11-0<br/>torque<br/>uint12"]
    end

量化规则为线性映射:pos 映射到 [-PosMax, PosMax] 区间占满 16 位;spd 映射到 [-SpdMax, SpdMax] 占满 12 位;Kp 映射到 [0, OKpMax] 占满 12 位;Kd 映射到 [0, OKdMax] 占满 9 位;torque 映射到 [-TauMax, TauMax] 占满 12 位。发送前驱动会先通过 limit() 将浮点输入裁剪到对应物理范围,再通过 range_map() 完成量化。

位置控制帧(POS 模式)采用 8 字节长度,位域分布为:模式位 3bit、目标位置 32bit(IEEE-754 float,但以 8 字节中的特定位存放)、限速 15bit、电流限制 12bit、应答位 2bit。速度控制帧(SPD 模式)为 7 字节,直接以大端序存放 RPM 浮点值的 4 字节原始内存,后接 16bit 电流限制。

Sources: lro_motor_driver.cpp, utils.hpp

全局管理命令(0x7FF 帧)

0x7FF 是 LRO 协议中预留的全局配置 CAN ID,用于执行不针对单条电机 ID 的管理操作。所有管理帧均携带 CANFD_BRS 标志。

命令码 (Byte3) 功能 帧长度 关键载荷说明
0x03 设置零位 4 字节 Byte0-1: motor_id
0x04 修改电机 ID 6 字节 Byte0-1: old_id, Byte4-5: new_id
0x05 复位 ID 为 0x01 6 字节 全 0x7F 广播复位
0x06 使能电机 4 字节 对应 lock_motor()
0x07 禁用电机 4 字节 对应 unlock_motor()
0x81 查询通信模式 2 字节 配合查询模式帧使用
0x82 查询电机 ID 2 字节 配合查询模式帧使用

init_motor() 的实现流程体现了典型的 LRO 上电初始化序列:先发送禁用指令进入只读状态,再切至 MIT 控制模式,随后使能电机,最后刷新一次状态并返回错误码。clear_motor_error() 则采用"先禁用再使能"的软复位策略,而非发送独立清错码,这是 LRO 协议与其他品牌电机的显著差异。

Sources: lro_motor_driver.hpp, lro_motor_driver.cpp

控制模式详解

MIT 阻抗控制模式

MIT 模式是 LRO 驱动的核心控制模式。单电机指令通过 motor_mit_cmd(float f_p, float f_v, float f_kp, float f_kd, float f_t) 发送。在首次进入 MIT 模式或从其他模式切换回来时,set_motor_control_mode() 会自动发送一条零指令(位置、速度、Kp、Kd、力矩均为 0)作为同步缓冲,防止模式跳变引发电机抖动。所有参数在量化前均会减去 motor_zero_offset_ 并经过 limit() 裁剪,确保不会溢出固定位宽。

位置控制模式

motor_pos_cmd(float pos, float spd, bool ignore_limit) 将目标位置从 rad 转换为 degree,速度从 rad/s 转换为 RPM(取绝对值后放大 10 倍并以 uint15 存放),然后按位域打包发送。ignore_limit 参数在 LRO 实现中未参与逻辑,属于基类接口的保留签名。

速度控制模式

motor_spd_cmd(float spd) 将目标速度转换为 RPM 后,以大端字节序写入帧中,并附带最大电流限制 0xFFFF。与位置模式类似,单位转换发生在驱动层,对上层用户透明。

Sources: lro_motor_driver.cpp, lro_motor_driver.cpp

多电机批量控制(One-to-Many)

LRO 驱动支持一种高效的多电机同步控制机制:当用户调用数组版 motor_mit_cmd(float* f_p, float* f_v, float* f_kp, float* f_kd, float* f_t) 时,驱动会构造一帧长度为 64 字节的 CAN-FD 报文,CAN ID 为 0x8080 | CAN_EFF_FLAG(扩展帧)。该帧被划分为 8 个 8 字节槽位(slot 0–7),每个槽位对应 motor_index_ 为 0–7 的电机。

sequenceDiagram
    participant User
    participant LroMotorDriver as 任意LRO实例
    participant Registry as bus_registry_
    participant Bus as MotorsCANFD

    User->>LroMotorDriver: motor_mit_cmd(arrays)
    LroMotorDriver->>Registry: 按 can_interface 查找同总线所有实例
    loop 遍历同总线电机
        LroMotorDriver->>LroMotorDriver: 按 motor_index_ 选择 slot
        LroMotorDriver->>LroMotorDriver: 量化该电机的 p/v/kp/kd/t
        LroMotorDriver->>LroMotorDriver: 将 8 字节打包进 data[slot*8 : slot*8+7]
    end
    LroMotorDriver->>Bus: transmit(64B frame, id=0x8080)

该机制要求同一总线上的电机 ID 必须落在 1–8 范围内,以便 motor_index_ = motor_id - 1 正确映射到帧槽位。若 motor_index_ >= 8,该实例在批量发送时会被静默跳过。由于整个填充过程受 bus_registry_mutex_ 保护,多线程环境下调用数组版指令是线程安全的。

Sources: lro_motor_driver.cpp, lro_motor_driver.hpp

反馈数据解析

canfd_rx_cbk() 是 LRO 驱动的接收回调入口,由 MotorsCANFD 后台线程在收到匹配 motor_id_ 的帧时触发。当前实现仅完整解析了 Type-1 反馈0x01,即 Byte0 的高 3 位),其布局如下:

字段 位宽 字节位置 物理量转换
feedback_type 3 bit Byte0 [7:5] 直接读取
error_id 5 bit Byte0 [4:0] 直接读取
position 16 bit Byte1–Byte2 range_map(u16, 0, 65535, -PosMax, PosMax) + zero_offset
speed 12 bit Byte3[7:4] + Byte4[7:4] range_map(u12, 0, 4095, -SpdMax, SpdMax)
torque/current 12 bit Byte4[3:0] + Byte5 range_map(u12, 0, 4095, -TauMax, TauMax)
temperature 8 bit Byte6 value - 25 °C
mos_temperature 8 bit Byte7 直接读取

温度字段采用 "原始值减 25" 的偏移编码,因此接收到的无符号数值 25 对应实际 0°C。每次回调还会将 response_count_ 清零,用于上层判断通信是否超时。若 error_id_ > 0,驱动会通过 spdlog 输出错误日志,包含总线名、电机 ID 和十六进制错误码。

Sources: lro_motor_driver.cpp

错误码体系

LRO 协议将错误信息存放在反馈帧 Byte0 的低 5 位,定义如下:

错误码 枚举名 含义
0x00 LRO_NO_ERROR 无错误
0x01 LRO_MOTOR_OVERHEAT 电机过热
0x02 LRO_OVER_CURRENT 过电流
0x03 LRO_UNDER_VOLTAGE 欠压
0x04 LRO_ENCODER_ERROR 编码器故障
0x06 LRO_BRAKE_OVERVOLT 制动过压
0x07 LRO_DRV_ERROR 驱动器错误

init_motor() 在初始化末尾会专门对 error_id_ 进行 switch-case 映射,将上述错误码原样返回给调用方,便于上层在上电自检阶段识别硬件故障。refresh_motor_status() 的实现策略是发送一条零力矩 MIT 指令并等待 Type-1 反馈,从而在不改变电机运行状态的前提下刷新 telemetry。

Sources: lro_motor_driver.hpp, lro_motor_driver.cpp

参数配置与查询

配置模式(Mode 0x06)

LRO 支持通过配置模式写入电机运行参数,帧格式为:Byte0 = (0x06 << 5)Byte1 = config_codeByte2-5 = float / int32 value。驱动封装了 write_register_lro(uint8_t rid, float value)write_register_lro(uint8_t index, int32_t value) 两个重载,内部自动完成 float 内存reinterpret 或 int32 大端拆分。

配置码 功能 数据类型
0x01 加速度 (rad/s²) float
0x02 减速度 (rad/s²) float
0x03 最大运行速度 (rad/s) float
0x04 力矩常数 (Nm/A) float
0x05 最大 Kp 限制 float
0x06 最大 Kd 限制 float
0x07 最大位置限制 (rad) float
0x08 最大速度限制 (rad/s) float
0x09 最大力矩限制 (Nm) float
0x0A 最大电流限制 (A) float
0x0B CAN 通信超时阈值 (ms) float
0x0C 电流环 PI float
0x0D 速度环 PI float
0x0E 位置环 PD float
0x0F 力矩常数校准 float

与 DM 和 EVO 驱动不同,LRO 协议没有独立的"写入 Flash"命令;参数在配置帧发送后即自动持久化。因此 write_motor_flash() 直接返回 truesave_register_lro() 仅输出警告日志说明该行为。

查询模式(Mode 0x07)

get_motor_param(uint8_t param_cmd) 发送查询帧请求电机内部状态。查询码涵盖制造商信息、实时位置/速度/电流、功率、环路与限位参数、MCU UUID、软件版本等。由于当前回调仅解析 Type-1 反馈,查询响应(Type-5)的详细解码需要用户在上层或后续版本中扩展。

Sources: lro_motor_driver.hpp, lro_motor_driver.cpp

与其他品牌驱动的差异对比

将 LRO 驱动与 DM、EVO 并列对比,可以帮助开发者快速选型并避免协议层面的混淆:

对比维度 LeadRobot (LRO) 达妙 (DM) EVO
物理层 CAN-FD( EtherCAT 预留) CAN 2.0 / CAN-FD CAN 2.0
管理帧 ID 固定 0x7FF 按型号/系列变化 按型号变化
MIT 位宽 Kp 12bit / Kd 9bit Kp 12bit / Kd 12bit 协议差异较大
多机批量 64B 扩展帧 0x8080 逐帧单播或 DM 特有广播 通常逐帧单播
Flash 持久化 配置自动保存,无显式 burn 命令 需要显式 write_motor_flash() 与 set_zero 捆绑
清错策略 禁用+使能软复位 独立清错命令 独立清错命令
温度编码 原始值 - 25 °C 直接线性映射 型号相关映射

Sources: lro_motor_driver.hpp

下一步阅读

理解 LRO 驱动的协议细节后,建议继续阅读以下页面以形成完整的控制闭环认知: