🤖 roboto_origin_03 Wiki
首页 / 部署 / URDF 与电机映射关系

在强化学习训练环境(如 Isaac Gym/Sim)中生成的 ONNX 策略模型,其输入输出的关节顺序通常由训练时加载的 USD/URDF 解析器决定;而实际机器人硬件的电机 ID 分配则受限于 CAN 总线拓扑和驱动板布局。为了让策略模型能够无缝驱动真实硬件,系统在推理节点与硬件抽象层之间引入了两组显式映射配置,将策略空间URDF 逻辑空间电机物理空间三者解耦。本文档将从索引空间定义、映射表语义、闭链关节解耦以及控制循环中的数据流四个维度,完整阐述这一映射体系的设计与实现。

三层索引空间

整个系统围绕 23 个主动自由度运转,但在不同模块中,这 23 个维度的排列顺序并不相同。代码中实际存在三个独立的索引空间,任何跨模块的数据流转都必须经过显式映射。

URDF 空间(逻辑关节空间) 是推理节点内部的状态统一表示。InferenceNode 中的 act_(目标动作)、RobotInterface 中的 joint_q_ / joint_vel_ / joint_tau_(关节反馈),以及 ROS Topic /joint_states/action 均使用该顺序。此顺序由 inference.yaml 中的 joint_default_anglejoint_limits 隐式定义,索引范围为 0–22。

电机物理空间(硬件空间) 对应 RobotInterface 构造函数中 motors_cfg_->motor_id_ 数组的索引。robot.yamlmotor_id: [1, 2, ..., 23] 定义了每个电机在物理总线上的真实 ID,同时 motor_interfacemotor_num 将其分组到 can0(6 个)、can1(7 个)、can2(5 个)、can3(5 个)四条总线。驱动层通信时,以此空间的索引定位具体电机对象。

策略/USD 空间(模型空间) 是 ONNX 模型输入输出所采用的关节顺序。由于训练环境对 URDF 的解析顺序可能与机器人本体的逻辑关节顺序不同,策略输出的第 i 维未必对应 URDF 的第 i 个关节。inference.yaml 中的 usd2urdf 数组专门负责将模型空间映射到 URDF 空间。

Sources: robot_interface.cpp, inference_node.hpp, robot.yaml, inference.yaml

核心映射配置与语义

urdf2motor:URDF 到电机数组索引

robot.yaml 中的 urdf2motor 数组定义了从 URDF 空间到 motor_id_ 数组索引的映射。当前配置为恒等映射 [0, 1, 2, ..., 22],表示 URDF 索引 i 直接取用 motor_id_ 数组的第 i 个元素(即物理电机 ID 为 i+1)。在 RobotInterface 构造函数中,系统遍历 urdf2motor 并反推其逆映射 motor2urdf_motor2urdf_[urdf2motor[i]] = i。该逆映射在后续电机状态读取和指令下发环节被高频使用,用于将物理电机数组索引转换为 URDF 逻辑索引。

Sources: robot_interface.cpp, robot.yaml

usd2urdf:策略空间到 URDF 空间

inference.yaml 中的 usd2urdf 数组定义了 ONNX 模型输出维度到 URDF 关节索引的映射,其值为 [0, 6, 12, 1, 7, 13, 18, 2, 8, 14, 19, 3, 9, 15, 20, 4, 10, 16, 21, 5, 11, 17, 22]。这意味着策略输出的第 0 维对应 URDF 关节 0,第 1 维对应 URDF 关节 6,第 2 维对应 URDF 关节 12,依此类推。该映射在观测构造(get_dof_pos_obsget_dof_vel_obs)和动作解析(inference())两个方向均被使用,确保训练环境的关节顺序与机器人本体的逻辑顺序正确对齐。

Sources: inference.yaml, inference_node.cpp, obs_manager.cpp

motor_sign:方向极性修正

由于电机在机械结构上的安装方向可能与 URDF 中关节正方向定义相反,系统在读取电机反馈和下发控制指令时统一应用 motor_sign 进行符号修正。在 apply_action() 中,joint_q_[motor2urdf_[idx]] = motor->get_motor_pos() * robot_cfg_->motor_sign_[idx] 保证上层拿到的关节角度与 URDF 定义一致;在指令下发阶段,motor_target_[idx] * robot_cfg_->motor_sign_[idx] 则将 URDF 空间的目标值转回电机所需的物理方向。

Sources: robot_interface.cpp, robot_interface.cpp, robot.yaml

闭链踝关节的额外映射层

Atom01 机器人采用并联闭链结构实现踝关节,左右脚踝各由 2 个电机共同驱动 pitch/roll 两个自由度。这 4 个电机(物理 ID 为 5、6、11、12)无法直接视为独立 1-DOF 关节,必须在电机空间与关节空间之间插入运动学解耦层。

robot.yaml 中的 close_chain_motor_id: [5, 6, 11, 12] 标识了闭链电机的物理 ID。RobotInterface 构造函数通过查找将其转换为两组内部索引:close_chain_motor_idx_(电机数组索引,值为 [4, 5, 10, 11])和 close_chain_joint_idx_(URDF 索引,同样为 [4, 5, 10, 11],因当前 urdf2motor 为恒等映射)。DecoupleAtom01 类基于机构的连杆几何参数,通过数值迭代正向运动学(forward_kinematics)和解析逆运动学(inverse_kinematics)实现双向映射:

Sources: robot_interface.cpp, robot_interface.cpp, close_chain_mapping.hpp, decouple_atom01.hpp, decouple_atom01.cpp, robot.yaml

控制循环中的映射数据流

在一次完整的控制周期内,数据需要穿越所有三层索引空间以及闭链解耦层。以下 Mermaid 序列图展示了从电机反馈到策略推理,再到电机指令下发的完整映射链路:

sequenceDiagram
    participant M as 电机物理层<br/>(Motor ID 1~23)
    participant RI as RobotInterface
    participant DC as DecoupleAtom01<br/>(闭链解耦)
    participant IN as InferenceNode
    participant ONNX as ONNX 策略模型

    Note over M,RI: 反馈读取阶段
    RI->>M: 批量读取 pos/spd/current<br/>(motor_id_ 数组顺序)
    RI->>RI: 应用 motor_sign 极性修正
    RI->>RI: 通过 motor2urdf_ 写入<br/>joint_q_[], joint_vel[], joint_tau[]<br/>(URDF 顺序)
    RI->>DC: 对 close_chain_joint_idx_<br/>调用 get_forwardQVT()
    DC-->>RI: 更新 ankle pitch/roll<br/>的 q, vel, tau

    Note over RI,IN: 观测构造阶段
    IN->>RI: get_joint_q() / get_joint_vel()
    IN->>IN: 通过 usd2urdf_ 重排为<br/>策略空间顺序的 dof_pos / dof_vel<br/>(输入 ONNX)

    Note over IN,ONNX: 推理阶段
    IN->>ONNX: 运行模型推理
    ONNX-->>IN: output_buffer[]<br/>(策略空间顺序)

    Note over IN,RI: 动作解析与下发阶段
    IN->>IN: output_buffer[i] → act_[usd2urdf_[i]]<br/>应用 action_scale + joint_default_angle
    IN->>RI: apply_action(act_)
    RI->>DC: 对 ankle 调用 get_decoupleQVT()<br/>将关节力矩映射为电机力矩
    DC-->>RI: 更新 action[] 中的电机目标
    RI->>RI: action[motor2urdf_[i]] → motor_target_[i]
    RI->>M: 应用 motor_sign 后下发 MIT 指令<br/>非闭链电机用 pos/kp/kd 模式<br/>闭链电机用 tau 模式

该流程中有三个关键的映射转换点:一是 motor2urdf_RobotInterface 内部隔离了电机物理顺序与 URDF 逻辑顺序;二是 usd2urdf_InferenceNode 内部隔离了策略模型顺序与 URDF 逻辑顺序;三是 DecoupleAtom01RobotInterface 内部对特定关节进行了电机空间与关节空间的双向运动学转换。

Sources: robot_interface.cpp, robot_interface.cpp, inference_node.cpp, obs_manager.cpp

映射配置速查表

下表汇总了涉及映射关系的核心配置项、所在文件及语义说明:

配置项 配置文件 数据类型 语义说明
urdf2motor robot.yaml int[] URDF 索引 → motor_id_ 数组索引的映射。当前为恒等映射。
motor_sign robot.yaml int[] 电机方向修正系数,取值 ±1。在读写时统一乘入。
close_chain_motor_id robot.yaml int[] 闭链机构电机的物理 ID 列表(如踝关节驱动电机)。
usd2urdf inference.yaml int[] 策略/USD 空间索引 → URDF 空间索引的映射。
joint_default_angle inference.yaml double[] URDF 空间中各关节的默认/初始角度,用于观测归一化和动作偏置。
joint_limits inference.yaml double[] URDF 空间中各关节的 [min, max] 极限,按 URDF 顺序成对排列。

需要特别注意的是,urdf2motorusd2urdf 虽然同为索引映射数组,但作用域完全不同:前者只在 RobotInterface 内部生效,用于桥接硬件层与逻辑层;后者只在 InferenceNode 内部生效,用于桥接模型层与逻辑层。两者以 URDF 空间为公共中间表示,彼此不直接耦合。

Sources: robot.yaml, inference.yaml

映射与相邻模块的边界

URDF 与电机映射关系并不是孤立存在的配置,它与系统其他模块存在清晰的职责边界。闭链踝关节的运动学解耦数学推导属于 闭链运动学与踝关节解耦 的范畴;电机驱动命令的实际封装与 CAN/CANFD 总线发送属于 通信协议层:CAN/CANFD 与串口 的范畴;而 RobotInterface 中映射表的初始化与线程安全封装则在 RobotInterface 硬件抽象层 中有更完整的说明。如果需要在新的机器人平台或新的训练环境中复用本推理框架,通常只需修改 robot.yamlinference.yaml 中的上述四组映射数组,以及对应的 joint_default_anglejoint_limits,即可实现零代码迁移。