机器人的行为表现不仅取决于控制算法与模型权重,更深受配置参数系统的约束。本文档从第一性原理出发,系统梳理本项目全量配置文件的层级结构、参数语义、加载机制与自定义方法,帮助你在不修改源码的前提下安全地调整机器人行为、接入新策略或适配差异化硬件。
Sources: inference.launch.py, robot_interface.cpp, inference_node.hpp
配置文件体系总览
项目的配置系统采用分层解耦设计:推理参数由 ROS2 Parameter 机制托管,硬件参数由 yaml-cpp 直接解析,脚本参数由 PyYAML 独立加载。三类配置在运行时通过 InferenceNode 与 RobotInterface 两个核心类完成交汇,形成完整的控制闭环。
graph TD
subgraph "ROS2 Parameter 层"
A[inference.launch.py] -->|加载| B[inference*.yaml]
B -->|declare_parameter<br/>get_parameter| C[InferenceNode]
end
subgraph "硬件抽象层"
D[robot.yaml] -->|YAML::LoadFile| E[RobotInterface]
end
subgraph "Python 脚本层"
F[scripts/config/*.yaml] -->|yaml.safe_load| G[set_zero.py<br/>motion_player.py]
end
C -->|apply_action| E
G -->|robot_py.RobotInterface| E
三种配置的定位截然不同:inference*.yaml 决定策略如何思考(观测构造、模型选型、控制频率);robot.yaml 决定硬件如何响应(总线映射、关节限幅、阻抗参数);scripts/config/*.yaml 则服务于离线工具链(零点标定、动作回放)。理解这一边界是避免参数误配的前提。
Sources: CMakeLists.txt, inference_node.cpp, set_zero.py
推理节点配置:inference*.yaml
推理配置是整套系统中语义最密集的文件,所有以 inference 为前缀的 YAML 文件共享同一套参数模式,仅在数值或数组长度上体现策略差异。文件根节点必须为 inference_node.ros__parameters,这是 ROS2 declare_parameter API 所要求的命名空间。
参数全景
推理参数可分为六大语义组:
| 参数组 | 代表参数 | 作用域 |
|---|---|---|
| 策略加载 | model_names, motion_names |
多策略切换与动作序列绑定 |
| 观测构造 | obs_layouts, extra_obs_layouts, frame_stacks, obs_stack_orders |
神经网络输入张量的形状与内容 |
| 控制时序 | dt, decimation, act_alpha |
控制频率与动作平滑 |
| 归一化与裁剪 | obs_scales_*, clip_observations, action_scale, clip_actions |
数值安全与量纲统一 |
| 运动学映射 | usd2urdf, joint_default_angle, joint_limits |
关节顺序转换与物理约束 |
| 安全阈值 | gravity_z_upper, clip_cmd |
跌倒检测与速度指令限幅 |
Sources: ros_interface.cpp
观测布局语法
obs_layouts 与 extra_obs_layouts 是推理配置的核心,其语法为逗号分隔的 name:size 对。InferenceNode::parse_obs_layout 在初始化时严格校验该字符串,任何名称拼写错误或维度不匹配都会触发 std::runtime_error 异常。
当前系统支持的观测源及其语义如下:
| 观测源名称 | 维度 | 数据来源 | 适用场景 |
|---|---|---|---|
ang_vel |
3 | IMU 角速度 | 所有策略 |
gravity_b |
3 | IMU 姿态解算后的重力向量 | 所有策略 |
cmd_vel |
3 | 手柄或 /cmd_vel 指令 |
所有策略 |
dof_pos |
23 | 电机反馈关节位置(经 usd2urdf 映射) |
所有策略 |
dof_vel |
23 | 电机反馈关节速度 | 所有策略 |
last_action |
23 | 上一时刻策略输出 | 所有策略 |
motion_pos |
23 | 动作序列文件当前帧位置 | 动作模仿策略 |
motion_vel |
23 | 动作序列文件当前帧速度 | 动作模仿策略 |
interrupt |
1 | 手柄中断信号 | 中断策略 |
perception |
187 | /robot_0/elevation_data 地形高度图 |
地形感知策略 |
obs_layouts 中的字段将被 frame_stack 堆叠后作为 ONNX 模型的主输入;extra_obs_layouts 中的字段则不经堆叠,直接拼接在主输入之后。例如 inference_attn_enc.yaml 使用 frame_stacks: [5] 对基础观测堆叠 5 帧,再将 187 维地形感知数据作为 extra_obs_layouts 追加,最终输入尺寸为 (5 × 78) + 187。
Sources: obs_manager.cpp, inference_attn_enc.yaml
观测堆叠顺序
obs_stack_orders 控制多帧观测在内存中的排布方式,仅当 frame_stack > 1 时生效:
frame_major(默认):逐帧平铺,即[frame0_obs0, frame0_obs1, ..., frameN_obsM]。这种方式与多数强化学习仿真训练框架(如 Isaac Gym)的默认输出一致。obs_major:逐字段平铺,即[frame0_obs0, frame1_obs0, ..., frameN_obs0, frame0_obs1, ...]。适用于对特定观测维度做时序卷积的网络结构。
Sources: inference_node.cpp
多策略配置约束
当 model_names 包含多个策略(如 inference_beyondmimic.yaml 中的 4 个模型)时,以下数组必须严格等长,否则 load_config 会抛出异常并终止节点:
model_names(策略数量)obs_layoutsframe_stacksobs_stack_orders
而 motion_names 与 extra_obs_layouts 允许为空;若不为空,则长度也必须与 model_names 一致。空字符串的 motion_name 表示该策略不绑定动作序列。这一校验逻辑通过 require_policy_count 与 require_empty_or_policy_count 两个 lambda 实现。
Sources: ros_interface.cpp
机器人硬件配置:robot.yaml
robot.yaml 被 RobotInterface 构造函数以硬编码路径 ROOT_DIR + "config/robot.yaml" 加载,不经过 ROS2 Parameter 系统。这意味着运行时无法通过 ros2 param set 动态修改硬件参数;任何调整都必须重启节点才能生效。配置分为 imu、motors、robot 三个顶级节点。
IMU 配置
| 参数 | 类型 | 说明 |
|---|---|---|
imu_id |
int | IMU 逻辑编号,用于多 IMU 区分 |
imu_interface_type |
string | 通信方式,当前支持 "serial" |
imu_interface |
string | 设备路径,如 /dev/ttyUSB0 |
imu_type |
string | IMU 型号,如 "HIPNUC" |
baudrate |
int | 串口波特率,默认 921600 |
Sources: robot.yaml, robot_interface.cpp
电机配置
电机配置的核心是总线拓扑描述。motor_interface 列出所有 CAN 通道(如 ["can0", "can1", "can2", "can3"]),motor_num 描述每条通道挂载的电机数量(如 [6, 7, 5, 5]),二者长度必须一致。motor_id 则是按通道顺序展开的扁平数组,总长度等于 motor_num 之和。
| 参数 | 类型 | 说明 |
|---|---|---|
motor_id |
int[] | 电机物理 ID 列表,按总线顺序排列 |
motor_interface_type |
string | "can" 或 "canfd" |
motor_interface |
string[] | CAN 通道名称数组 |
motor_num |
int[] | 各通道电机数量数组 |
motor_type |
string[] | 各通道电机驱动类型,如 "DM" |
motor_model |
int[] | 电机型号编码,扁平数组 |
master_id_offset |
int | MIT 控制帧的主 ID 偏移量 |
motor_zero_offset |
double[] | 各电机零点偏移(弧度) |
motor_zero_offset 中仅第 13 号电机(2.093)在出厂配置中具有非零值,其余均为 0.0,这通常对应特殊机械结构的关节偏置。
Sources: robot.yaml, robot_interface.cpp
机器人运动学配置
robot 节点承载运动学与安全相关的关键参数:
| 参数 | 类型 | 说明 |
|---|---|---|
type |
string | 机器人类型,如 "atom01",用于选择闭链解耦算法 |
kp / kd |
double[] | 各关节 MIT 控制的刚度与阻尼系数 |
motor_sign |
int[] | 电机方向符号(1 或 -1),用于对齐 URDF 与电机正方向 |
close_chain_motor_id |
int[] | 闭链关节对应的电机物理 ID |
urdf2motor |
int[] | URDF 关节索引到电机索引的映射 |
extrinsic_R |
double[9] | IMU 到机体坐标系的旋转矩阵(行优先) |
close_chain_motor_id 与 urdf2motor 共同定义了踝关节闭链运动学的解耦关系。当 type 字段存在时,RobotInterface 会实例化对应的 Decouple 对象,在 apply_action 中对左右踝关节进行前向/逆向解耦计算。
Sources: robot.yaml, robot_interface.cpp, robot_interface.cpp
脚本配置:scripts/config/
scripts/config/ 目录下的配置文件服务于 Python 离线工具,与 ROS2 推理节点完全解耦。
motion_player.yaml
该文件是 robot.yaml 的精简子集,仅保留 motors 与 robot 节点,不含 imu。motion_player.py 通过 robot_py.RobotInterface(config_file) 直接传入该路径,实现不依赖 ROS2 的动作回放。值得注意的是,其 motor_zero_offset 全为 0.0,与 robot.yaml 中第 13 号电机的非零偏移不同——这是因为在动作回放场景下,关节偏置通常已内置在动作数据文件中。
Sources: motion_player.yaml, motion_player.py
set_zero.yaml
零点标定配置进一步精简,仅保留电机创建所需的必要字段,不含 robot 运动学参数。set_zero.py 独立通过 yaml.safe_load 解析,并使用 motors_py.MotorDriver.create_motor 逐个实例化电机。标定完成后,各电机的 motor_zero_offset 会被写入电机内部存储,而非修改此 YAML 文件。
Sources: set_zero.yaml, set_zero.py
配置加载机制与运行时行为
理解配置何时被加载、以何种优先级被解析,是排查启动故障的关键。
推理节点加载链
sequenceDiagram
participant User
participant Launch as inference.launch.py
participant Node as InferenceNode
participant Robot as RobotInterface
User->>Launch: ros2 launch inference inference.launch.py
Launch->>Node: 加载 inference.yaml<br/>注入 ros__parameters
Node->>Node: load_config()<br/>declare + get parameter
Node->>Robot: 构造 RobotInterface<br/>路径: ROOT_DIR/config/robot.yaml
Robot->>Robot: YAML::LoadFile<br/>解析 imu/motors/robot
Node->>Node: setup_model()<br/>校验 ONNX 输入尺寸
InferenceNode 构造函数中,load_config() 与 RobotInterface 的初始化顺序是固定的:先解析推理参数,再加载硬件配置。这意味着如果 robot.yaml 路径错误或格式非法,节点会在 load_config 成功之后、模型加载之前崩溃。
Sources: inference_node.hpp
参数校验与失败模式
load_config() 执行了多层防御性校验:
- 策略数量一致性:
obs_layouts、frame_stacks、obs_stack_orders必须与model_names等长。 - 观测源合法性:
parse_obs_layout拒绝任何未在obs_source_definitions中注册的名称。 - ONNX 输入尺寸匹配:
setup_model将obs_num × frame_stack + extra_obs_num与 ONNX 模型张量形状逐元素比对,任何不匹配都会抛出std::runtime_error。 - 关节限幅:运行时
get_dof_pos_obs持续检测各关节是否超出joint_limits,越界即调用rclcpp::shutdown()。 - 跌倒检测:
get_gravity_b_obs监测重力向量 Z 分量,若大于gravity_z_upper(默认-0.5),判定机器人跌倒并安全停机。
Sources: ros_interface.cpp, inference_node.cpp, obs_manager.cpp
多策略配置文件对比
项目中预置了 6 套推理配置,分别对应不同的能力场景。下表从策略数量、观测结构、特殊能力三个维度进行对比:
| 配置文件 | 策略数 | 帧堆叠 | 特殊观测源 | 典型用途 |
|---|---|---|---|---|
inference.yaml |
1 | 10 | 无 | 基础 locomotion 策略 |
inference_amp.yaml |
1 | 1 | 无 | AMP(Adversarial Motion Prior)风格策略 |
inference_attn_enc.yaml |
1 | 5 | perception:187 |
地形感知导航 |
inference_interrupt.yaml |
1 | 10 | interrupt:1 |
支持手柄中断信号的安全策略 |
inference_getup.yaml |
2 | 10 / 1 | motion_pos, motion_vel |
基础策略 + 起身动作序列 |
inference_beyondmimic.yaml |
4 | 10 / 1×3 | motion_pos, motion_vel |
基础策略 + 挥手/舞蹈/击打动作 |
inference_getup 与 inference_beyondmimic 均包含多个策略,运行时通过手柄 LB 键切换 active_policy_idx_。含 motion_loader 的策略(motion_path 非空)被记录到 motion_policy_indices_,可通过 RB 键在动作序列间循环。
Sources: inference_beyondmimic.yaml, inference_getup.yaml, ros_interface.cpp
配置自定义指南
切换推理配置文件
默认的 inference.launch.py 硬编码了 inference.yaml:
configs = [
os.path.join(
get_package_share_directory("inference"),
"config",
"inference.yaml",
),
]
若要切换至其他配置(如地形感知),修改该路径为 inference_attn_enc.yaml 后重新编译即可。更优雅的做法是在 launch 文件中通过 LaunchConfiguration 暴露命令行参数,但当前版本尚未实现。
Sources: inference.launch.py
接入新策略模型的最小步骤
假设你已训练得到一个 ONNX 模型 policy_custom.onnx 并放入 src/inference/models/:
- 复制基线配置:以
inference.yaml为模板,新建inference_custom.yaml。 - 更新模型名:将
model_names改为["policy_custom.onnx"]。 - 校验观测维度:确认
obs_layouts中各字段维度之和与模型训练时的观测维度一致。 - 调整帧堆叠:若模型需要时序信息,设置
frame_stacks与obs_stack_orders;若为单帧策略,设为1。 - 注册并编译:确保
CMakeLists.txt中的install(DIRECTORY config ...)会将新文件安装到share/目录,执行colcon build --symlink-install。
Sources: CMakeLists.txt
硬件参数调整的注意事项
修改 robot.yaml 中的 kp / kd 是调试机器人柔顺性的常见操作,但需遵循以下原则:
- 保持数组长度:
kp、kd、motor_sign、joint_default_angle的长度必须等于joint_num(23)。 - 闭链关节特殊处理:
close_chain_motor_id对应的电机在apply_action中会被发送力矩指令(tau)而非位置指令,其kp/kd仅在解耦计算中作为中间系数使用。 - 零点偏移与标定:若机械结构发生变动,应优先通过 电机零点标定 流程更新电机内部零位,而非直接修改
motor_zero_offset。motor_zero_offset更适合补偿无法通过标定消除的固定机械偏置。
Sources: robot_interface.cpp, robot.yaml
延伸阅读与下一步
配置系统的深层机制与周边系统紧密耦合。若需进一步理解以下内容,建议按顺序阅读:
- 观测数据如何从硬件层流向 ONNX 模型?→ 观测堆叠与多策略切换
RobotInterface如何将配置中的kp/kd转换为 MIT 控制帧?→ RobotInterface 硬件抽象层- 闭链运动学配置
close_chain_motor_id的数学原理?→ 闭链运动学与踝关节解耦 - 启动时 DDS 配置如何与推理节点协同?→ 启动脚本与 DDS 优化配置