本文档面向首次接触本项目的开发者,系统梳理机器人主控与电机、IMU 的物理连接方式,以及这些硬件在软件层中的映射关系。理解这一层映射是后续进行电机标定、策略推理和故障排查的前提。全文以 src/inference/config/robot.yaml 为核心配置入口,配合 udev 规则与驱动代码,建立从“插哪根线”到“代码里怎么寻址”的完整认知链路。
Sources: robot.yaml, 99-auto-up-devs-orangepi.rules
整体硬件拓扑
ATOM01 部署框架采用主控板(Orange Pi 5 Plus 或 RDK X5)作为计算核心,通过 USB 转 CAN 适配器与下肢、腰部、手臂的 23 个关节电机通信,同时通过串口连接 IMU 获取机身姿态。下图展示了这一拓扑的软件视角:
graph TD
A[主控板<br/>Orange Pi 5 Plus / RDK X5] -->|USB| B[USB-CAN 适配器 ×4]
A -->|USB| C[IMU 串口适配器<br/>CP210x]
B -->|can0| D[左腿 6 电机<br/>ID 1-6]
B -->|can1| E[右腿+腰 7 电机<br/>ID 7-13]
B -->|can2| F[左手 5 电机<br/>ID 14-18]
B -->|can3| G[右手 5 电机<br/>ID 19-23]
C -->|/dev/ttyUSB0| H[HiPNUC IMU<br/>ID 8]
从软件角度看,robot_interface.cpp 在构造时会读取 robot.yaml,并依次调用 setup_motors() 与 setup_imu() 完成驱动实例化。所有后续控制与反馈都通过这两个接口向下分发。
Sources: robot_interface.cpp, robot_interface.hpp
CAN 总线连接与电机映射
物理连接与 udev 规则
本项目使用 4 路 USB 转 CAN 适配器,默认建议按机身部位分配如下表所示。如果不使用 udev 规则,必须严格按照插入顺序绑定接口;更推荐的方式是通过 udev 规则将物理 USB 端口与接口名永久绑定。
| 总线接口 | 对应部位 | 电机数量 | 电机 ID 范围 | 默认 USB 端口属性 (Orange Pi) |
|---|---|---|---|---|
can0 |
左腿 | 6 | 1 ~ 6 | 5-1.4.1 |
can1 |
右腿+腰 | 7 | 7 ~ 13 | 5-1.4.2 |
can2 |
左手 | 5 | 14 ~ 18 | 5-1.4.3 |
can3 |
右手 | 5 | 19 ~ 23 | 5-1.4.4 |
udev 规则文件 assets/99-auto-up-devs-orangepi.rules 完成了三件事:第一,根据 idVendor/idProduct(1d50:606f)识别 CAN 适配器;第二,利用 KERNELS 匹配物理 USB 端口,将设备重命名为 can0 ~ can3;第三,在设备插入时自动执行 ip link set canX up type can bitrate 1000000,将总线波特率设为 1 Mbps 并启用队列。IMU 的 USB 串口芯片(10c4:ea60)也在同一条规则中被赋予 0666 权限并创建 /dev/ttyUSB0 符号链接。
Sources: 99-auto-up-devs-orangepi.rules
电机型号与总线分配详表
robot.yaml 中的 motors 段通过 motor_num 数组定义了每条总线上的电机数量。代码在 setup_motors() 中按顺序遍历 motor_interface 与 motor_num,将 motor_id 列表依次分配到对应总线。对于本项目默认配置,电机与总线的精确映射如下:
| 总线 | 电机 ID | 电机型号 | 型号数值 |
|---|---|---|---|
can0 |
1 | DM10010L_48V | 1 |
can0 |
2 | DM10010L_48V | 1 |
can0 |
3 | DM10010L_48V | 1 |
can0 |
4 | DM10010L_48V | 1 |
can0 |
5 | DM4340P_48V | 0 |
can0 |
6 | DM4340P_48V | 0 |
can1 |
7 | DM10010L_48V | 1 |
can1 |
8 | DM10010L_48V | 1 |
can1 |
9 | DM10010L_48V | 1 |
can1 |
10 | DM10010L_48V | 1 |
can1 |
11 | DM4340P_48V | 0 |
can1 |
12 | DM4340P_48V | 0 |
can1 |
13 | DM10010L_48V | 1 |
can2 |
14 | DM4340P_48V | 0 |
can2 |
15 | DM4340P_48V | 0 |
can2 |
16 | DM4340P_48V | 0 |
can2 |
17 | DM4340P_48V | 0 |
can2 |
18 | DM4340P_48V | 0 |
can3 |
19 | DM4340P_48V | 0 |
can3 |
20 | DM4340P_48V | 0 |
can3 |
21 | DM4340P_48V | 0 |
can3 |
22 | DM4340P_48V | 0 |
can3 |
23 | DM4340P_48V | 0 |
其中型号 0 对应 DM4340P_48V,型号 1 对应 DM10010L_48V。电机驱动代码根据该型号索引加载对应的电流、速度和力矩限制参数。
Sources: dm_motor_driver.hpp, dm_motor_driver.cpp, robot_interface.cpp
master_id_offset 与 CAN 帧过滤
在 DM 电机驱动中,每个电机对象在初始化时会计算 master_id = motor_id + master_id_offset。默认配置下 master_id_offset 为 16,因此 ID 为 1 的电机其 master_id 为 17。驱动层将 master_id 注册到 SocketCAN 接收回调的过滤键中,这样总线上回传的反馈帧就能准确路由到对应的电机实例。
Sources: dm_motor_driver.cpp
IMU 连接与配置
串口模式(默认)
robot.yaml 默认将 IMU 配置为串口通信:
imu:
imu_id: 8
imu_interface_type: "serial"
imu_interface: "/dev/ttyUSB0"
imu_type: "HIPNUC"
baudrate: 921600
RobotInterface 构造时会调用 setup_imu(),进而通过 IMUDriver::create_imu() 实例化 HipnucIMUDriver。该驱动打开 /dev/ttyUSB0,以 921600 波特率接收 HiPNUC 协议数据包,解析出四元数(w, x, y, z)、角速度(x, y, z)和线加速度。
Sources: imu_driver.cpp, robot_interface.cpp
CAN 模式(备选)
项目中同样预留了基于 CAN 的 IMU 配置脚本 src/imu/init_imu.sh。该脚本使用 J1939 协议,通过 cansend 向 can4 接口发送配置指令,将 IMU 波特率修改为 1 Mbps 并开启四元数输出。若改用 CAN 模式,需同步修改 robot.yaml 中的 imu_interface_type 与 imu_interface 字段。对于初学者,建议保持默认串口模式,因为 udev 规则已为串口设备自动完成了权限和别名配置。
Sources: init_imu.sh
robot.yaml 配置参数详解
robot.yaml 是连接硬件与上层策略推理的“枢纽文件”,它包含 imu、motors、robot 三个顶层字段。下面按字段说明其含义与修改注意事项。
motors 字段
| 参数 | 类型 | 说明 |
|---|---|---|
motor_id |
整数数组 | 全部电机的逻辑 ID,共 23 个 |
motor_interface_type |
字符串 | 通信协议,可选 "can" / "canfd" |
motor_interface |
字符串数组 | CAN 总线接口名,如 ["can0", "can1", ...] |
motor_num |
整数数组 | 每条总线上的电机数量,与 motor_interface 一一对应 |
motor_type |
字符串数组 | 每条总线上的电机品牌,如 ["DM", "DM", ...] |
motor_model |
整数数组 | 每个电机的具体型号索引,顺序与 motor_id 一致 |
master_id_offset |
整数 | 用于 CAN 回调过滤的偏移量,默认 16 |
motor_zero_offset |
浮点数组 | 每个电机的零点偏移量,顺序与 motor_id 一致 |
修改 motor_num 时务必保证数组之和等于 motor_id 的长度;修改 motor_interface 时务必保证数组长度与 motor_num 一致。motor_zero_offset 中的第 13 个值(对应腰部 yaw 电机)默认为 2.093,该值与机械限位标零方式有关,后续在 电机零点标定 中会详细说明。
Sources: robot.yaml
imu 字段
| 参数 | 类型 | 说明 |
|---|---|---|
imu_id |
整数 | IMU 设备逻辑 ID,默认 8 |
imu_interface_type |
字符串 | 通信方式,"serial" 或 "can" |
imu_interface |
字符串 | 设备路径或 CAN 接口名,如 /dev/ttyUSB0 |
imu_type |
字符串 | IMU 品牌/协议,当前仅支持 "HIPNUC" |
baudrate |
整数 | 串口波特率,默认 921600 |
Sources: robot.yaml
robot 字段
| 参数 | 类型 | 说明 |
|---|---|---|
type |
字符串 | 机器人类型,如 "atom01",用于选择运动学解耦器 |
kp |
浮点数组 | 各关节 MIT 控制的刚度增益 |
kd |
浮点数组 | 各关节 MIT 控制的阻尼增益 |
motor_sign |
整数数组 | 电机方向符号,用于对齐 URDF 与电机实际正方向 |
close_chain_motor_id |
整数数组 | 闭链关节对应的电机 ID,用于踝关节解耦 |
urdf2motor |
整数数组 | URDF 关节索引到电机数组索引的映射 |
extrinsic_R |
浮点数组 | IMU 到机体坐标系的 3×3 旋转矩阵(行优先) |
motor_sign 与 urdf2motor 是运动学一致性的关键。urdf2motor 默认恒等映射 [0, 1, 2, ..., 22],表示 URDF 的第 i 个关节直接对应 motor_id 列表的第 i 个电机;motor_sign 则在读取电机位置和下发指令时进行方向翻转。extrinsic_R 默认单位阵,表示 IMU 坐标系与机体坐标系完全重合,若安装姿态不同则需填入实际外参。
Sources: robot.yaml, robot_interface.cpp
连接验证 checklist
完成物理接线与 udev 规则部署后,建议按以下步骤验证映射是否生效:
- 检查 CAN 接口状态:执行
ip a,确认can0~can3均已UP且波特率为1000000。 - 检查串口设备:执行
ls -l /dev/ttyUSB0,确认文件存在且权限为crw-rw-rw-。 - 检查 udev 规则:执行
udevadm info -q property -n can0,确认NAME与KERNELS匹配规则文件中的配置。 - 验证电机回读:source 工作空间后,运行
python3 scripts/motors_py_example.py或调用 ROS2/read_joints服务,观察各电机是否能正常返回位置与状态。
若上述任一步骤异常,请先排查物理连接与 udev 规则,再进入 常见问题排查与调试技巧 进一步诊断。
下一步
确认硬件连接与映射配置无误后,请继续阅读 电机零点标定,完成首次上电前的电机零位校准。