🤖 roboto_origin_03 Wiki
首页 / 推理子模块 / 多策略 YAML 配置详解

本文档面向已具备 ROS 2 参数系统基础认知的开发者,系统拆解 config/ 目录下多组推理配置 YAML 的结构语义、校验规则与运行时映射关系。掌握这些配置项的精确含义后,你将能够安全地新增自定义策略、调整观测布局、或为不同 ONNX 模型匹配专用的帧堆叠与归一化参数,而无需改动 C++ 源码。

Sources: inference.yaml, ros_interface.cpp

YAML 配置在启动链路中的位置

推理节点并非直接读取磁盘上的 YAML 文件,而是通过 ROS 2 Parameter 机制接收配置。启动链路如下:Launch 文件将指定的 YAML 路径注入 Node::parameters,节点构造时调用 load_config(),将 ROS 参数转换为内部的 PolicyRuntime 向量。每个 PolicyRuntime 包含独立的模型上下文(ModelContext)、观测布局(obs_layout)、帧堆叠状态与可选的 MotionLoader。

flowchart LR
    A[inference.launch.py] -->|parameters=configs| B[ROS 2 Parameter Server]
    B --> C[InferenceNode 构造函数]
    C --> D[load_config]
    D --> E[PolicyRuntime 向量]
    E --> F[ONNX Session 初始化]
    E --> G[MotionLoader 初始化]

Sources: inference.launch.py, inference_node.hpp

核心参数字段分类详解

配置文件中所有参数均声明在 inference_node/ros__parameters 命名空间下。按照功能可划分为六大类。

策略列表参数

策略列表参数是多策略系统的核心,决定了节点启动时加载多少个 ONNX 模型以及是否绑定动作参考文件。

字段 类型 是否必填 说明
model_names string[] 每个元素对应 models/ 目录下的一个 ONNX 文件名,数组长度即策略总数
motion_names string[] 每个元素对应 motions/ 目录下的 .npz 文件;若为空数组,则所有策略均不加载动作参考

关键约束motion_names 要么为空,要么长度必须与 model_names 严格一致。运行时通过手柄 LB 键在基础策略与 motion 策略之间切换,通过 RB 键在多个 motion 策略之间轮询。若某策略绑定了 motion 文件,其观测布局中通常需要包含 motion_posmotion_vel 源,否则模型输入将缺少动作先验。

Sources: ros_interface.cpp, ros_interface.cpp, ros_interface.cpp

观测布局参数

观测布局参数定义了每个策略的 ONNX 输入向量由哪些语义字段拼接而成。

字段 类型 是否必填 说明
obs_layouts string[] 主观测布局,每个策略一个字符串,采用 name:size, name:size 格式
extra_obs_layouts string[] 额外观测布局,不参与帧堆叠,直接拼接在主观测之后

parse_obs_layout() 在启动时对每个布局字符串执行严格解析:字段名必须存在于预定义的 obs_source_definitions() 注册表中,尺寸必须是正整数,格式必须为 name:size。任何拼写错误或尺寸不匹配都会在构造阶段抛出异常并终止节点。

Sources: obs_manager.cpp, ros_interface.cpp

帧堆叠参数

帧堆叠参数控制时序观测如何组装成模型输入。对于需要历史帧信息的策略(如行走),通常设置较大的 frame_stack;对于基于参考动作的策略(如舞蹈、起身),通常设置为 1

字段 类型 是否必填 说明
frame_stacks int[] 每个策略的堆叠帧数,必须 > 0
obs_stack_orders string[] 堆叠顺序,支持 frame_majorobs_major

frame_major 将连续帧按时间顺序紧密排列:[frame0_obs0, frame0_obs1, ..., frameN_obs0, frameN_obs1]obs_major 则按观测字段分组排列:[frame0_obs0, frame1_obs0, ..., frame0_obs1, frame1_obs1, ...]。选择哪种顺序必须与训练时使用的 PyTorch 张量布局保持一致,否则模型输入将被错误解释。

Sources: inference_node.cpp, inference_node.hpp

运行时控制参数

字段 类型 典型值 说明
decimation int 5 控制线程与推理线程的频率比。控制线程周期为 dt,推理线程周期为 dt * decimation
dt float 0.004 控制周期(秒),对应 250 Hz
act_alpha float 1.0 动作平滑系数,last_act = alpha * act + (1-alpha) * last_act1.0 表示无平滑
intra_threads int 1 ONNX Runtime 全局线程池内的算子内并行线程数,-1 表示不设置

decimationdt 共同决定了推理频率。例如 dt=0.004decimation=5 时,控制频率为 250 Hz,推理频率为 50 Hz。这一参数必须与训练时的策略控制频率严格对齐。

Sources: ros_interface.cpp, inference_node.hpp

归一化与限幅参数

字段 类型 说明
obs_scales_lin_vel float 线速度指令缩放系数
obs_scales_ang_vel float 角速度(IMU 与指令)缩放系数
obs_scales_dof_pos float 关节位置偏差缩放系数
obs_scales_dof_vel float 关节速度缩放系数
obs_scales_gravity_b float 机体坐标系重力向量缩放系数
clip_observations float 观测值硬限幅绝对值上限
action_scale float 网络输出动作缩放系数
clip_actions float 动作输出硬限幅绝对值上限

这些系数必须与训练阶段的 env_cfg 保持一致。clip_observations 在推理线程的每次循环中通过 std::clamp 执行,防止异常传感器读数导致模型发散;action_scalejoint_default_angle 共同将网络输出映射为实际关节目标位置:act = output * action_scale + default_angle

Sources: ros_interface.cpp, inference_node.cpp

运动学与安全参数

字段 类型 说明
joint_num int 机器人主动关节总数,当前为 23
usd2urdf int[] ONNX 输出关节索引到 URDF/电机关节索引的映射表
joint_default_angle double[] 各关节默认站立姿态(弧度),长度须为 joint_num
joint_limits double[] 各关节限位,每两个元素构成 [min, max],共 joint_num
clip_cmd double[] 手柄/指令速度限幅,每两个元素构成 [min, max],顺序为 vx, vy, yaw
gravity_z_upper float 机体坐标系重力向量 Z 分量的安全上限。当 gravity_b.z > gravity_z_upper 时判定机器人摔倒,触发紧急停机

usd2urdf 是解决训练环境(USD/Isaac Gym)与真实机器人关节顺序不一致的关键映射。joint_limitsget_dof_pos_obs() 中实时检查,一旦越界立即调用 rclcpp::shutdown() 停止进程,属于最后一道硬件安全防线。gravity_z_upper 的数值需根据实际机器人结构调试:行走模式通常设为 -0.5 左右,而起身模式需要允许更大的姿态变化,故设为 1.0

Sources: obs_manager.cpp, ros_interface.cpp

外部感知参数

字段 类型 说明
perception_obs_topic string 外部感知观测的 ROS 话题名,默认 elevation_data
extra_obs_layouts 中的 perception:size 声明感知观测的维度,如 187

当策略需要地形高程图、深度特征等外部感知输入时,通过 perception_obs_topic 订阅 Float32MultiArray 消息。感知观测被归类为 extra_obs_layouts,不参与帧堆叠,仅在每次推理时取最新一帧。

Sources: ros_interface.cpp, inference_attn_enc.yaml

可用观测源注册表

obs_source_definitions() 以静态向量形式注册了所有合法的观测源名称。任何出现在 obs_layoutsextra_obs_layouts 中的字段名必须匹配下表。

观测源名称 尺寸 语义说明 依赖条件
ang_vel 3 IMU 角速度(机体坐标系),经 obs_scales_ang_vel 缩放 IMU 已初始化
gravity_b 3 世界坐标系重力向量旋转到机体坐标系,经 obs_scales_gravity_b 缩放 IMU 已初始化
cmd_vel 3 遥控器/指令的 [vx, vy, yaw] 速度指令,经 obs_scales_lin_vel/obs_scales_ang_vel 缩放
dof_pos joint_num 各关节相对默认位置的角度偏差,经 obs_scales_dof_pos 缩放 电机已初始化
dof_vel joint_num 各关节角速度,经 obs_scales_dof_vel 缩放 电机已初始化
last_action joint_num 上一时刻策略网络原始输出(未经 action_scale 缩放)
interrupt 1 中断标志位,0.01.0,用于支持外部关节覆盖 配置含 interrupt
perception 任意正整数 外部感知特征向量 perception_obs_topic 有数据发布
motion_pos joint_num 当前帧动作参考的关节目标位置 策略绑定了 .npz 动作文件
motion_vel joint_num 当前帧动作参考的关节目标速度 策略绑定了 .npz 动作文件

Sources: obs_manager.cpp, obs_manager.cpp

现成配置文件全景对比

config/ 目录下已提供六组经过验证的配置,分别对应不同的运行场景。理解它们之间的差异是编写自定义配置的最佳起点。

配置文件 策略数 模型文件 Motion 文件 帧堆叠 特殊观测源 典型用途
inference.yaml 1 policy.onnx [10] 标准六元组 基础行走
inference_amp.yaml 1 policy_amp.onnx [1] 标准六元组 AMP 风格行走,关节限幅更宽松
inference_attn_enc.yaml 1 policy_attn_enc.onnx [5] 标准六元组 + perception:187 注意力编码器地形感知行走
inference_getup.yaml 2 policy.onnx + policy_getup.onnx "" + getup.npz [10, 1] 策略1标准;策略2含 motion_pos/motion_vel 摔倒后自动起身
inference_beyondmimic.yaml 4 policy.onnx + policy_wave.onnx + policy_dance.onnx + policy_punch.onnx "" + wave.npz + dance.npz + punch.npz [10, 1, 1, 1] 策略1标准;其余含 motion_pos/motion_vel 交互式动作库(挥手/舞蹈/冲拳)
inference_interrupt.yaml 1 policy_interrupt.onnx [10] 标准六元组 + interrupt:1 支持外部关节中断覆盖

值得注意的规律是:凡是涉及动作参考(Motion)的策略,帧堆叠均为 1,因为动作序列本身已经提供了时序先验;基础行走策略的帧堆叠通常为 5 或 10,以补偿单帧观测缺少的历史动态信息。

Sources: config/

配置加载与一致性校验

load_config() 在节点构造阶段执行,其校验逻辑遵循"列表长度对齐"原则。核心校验规则可概括为:

flowchart TD
    A[读取 model_names] --> B{长度 N > 0?}
    B -->|否| C[抛出异常: 至少需要一个策略]
    B -->|是| D[读取 obs_layouts / frame_stacks / obs_stack_orders]
    D --> E{长度均等于 N?}
    E -->|否| F[抛出异常: 列表长度不一致]
    E -->|是| G[读取 motion_names / extra_obs_layouts]
    G --> H{为空或长度等于 N?}
    H -->|否| I[抛出异常: 可选列表长度不合法]
    H -->|是| J[遍历构建 PolicyRuntime]
    J --> K[校验 ONNX 输入尺寸]
    K --> L{尺寸匹配?}
    L -->|否| M[抛出异常: 模型输入维度不匹配]
    L -->|是| N[启动完成]

具体来说,obs_layoutsframe_stacksobs_stack_orders 被强制要求与 model_names 同长度;motion_namesextra_obs_layouts 允许为空,但若非空则也必须对齐。此外,setup_model() 会将解析后的观测维度 obs_num * frame_stack + extra_obs_num 与 ONNX 模型的实际输入尺寸进行比对,任何不匹配都会立即报错,避免在运行时出现难以调试的张量形状错误。

Sources: ros_interface.cpp, inference_node.cpp

运行时热切换的内部行为

当节点支持多策略(motion 策略或 interrupt 策略)时,手柄 LB 键触发策略切换。切换并非瞬时完成,而是遵循暂停-切换-重置-恢复的范式,以保证不同策略的观测缓冲区不会相互污染:

  1. is_running 原子交换为 false,推理线程进入休眠;
  2. 切换 active_policy_idx_is_interrupt_ 标志;
  3. 调用 reset_policy_runtime() 清空新策略的观测段、输入缓冲区、motion_frame 计数器,并将 is_first_frame 置为 true,使下一帧推理时 update_stacked_obs() 以相同观测填满整个历史窗口;
  4. 恢复 is_runningtrue

这意味着切换瞬间会有约 dt * decimation 的停顿,随后新策略以"满窗口重复首帧"的方式平滑启动。

Sources: ros_interface.cpp, inference_node.cpp

自定义策略配置指南

若需为新的 ONNX 模型新增一套 YAML 配置,建议按以下流程操作:

flowchart LR
    A[确认模型输入尺寸] --> B[编写观测布局字符串]
    B --> C[确定 frame_stack 与 stack_order]
    C --> D[复制并修改 YAML]
    D --> E[修改 launch 文件指向新配置]
    E --> F[启动验证 ONNX 尺寸匹配]
    F --> G[运行时测试策略切换]

步骤详解

  1. 确认模型输入尺寸:查看 ONNX 模型的输入张量形状,计算 obs_num * frame_stack + extra_obs_num 应等于该尺寸。
  2. 编写观测布局:根据训练时的 obs_buf 拼接顺序,选择对应的观测源并计算尺寸。注意 dof_posdof_vellast_actionmotion_posmotion_vel 的尺寸均为 joint_num(当前为 23)。
  3. 确定帧堆叠:若模型训练时使用了历史帧堆叠,则 frame_stack 应等于训练时的 num_histobs_stack_orders 必须与训练时的张量展开顺序一致(通常是 frame_major)。
  4. 复制模板:以 inference.yaml 或最接近的场景配置为模板,修改 model_namesobs_layoutsframe_stacks 等字段。若涉及 motion,将 .npz 文件放入 motions/ 目录并在 motion_names 中注册。
  5. 修改 Launch 文件:在 inference.launch.py 中替换 configs 列表中的 YAML 路径,或追加新路径实现参数覆盖。
  6. 启动验证:首次启动时观察日志中的 policy N: ...ONNX input size mismatch 信息。若校验通过,节点将正常进入等待状态。

Sources: launch/inference.launch.py, inference_node.hpp

常见配置错误排查

错误日志 根因 修复方法
model_names must contain at least one policy model_names 数组为空 检查 YAML 缩进,确保参数位于 ros__parameters
obs_layouts must have the same size as model_names 策略数与各 per-policy 列表长度不一致 核对 obs_layoutsframe_stacksobs_stack_orders 的元素个数
Unsupported obs source: xxx 观测字段名拼写错误或使用了未注册的源 对照"可用观测源注册表"检查字段名,确保使用 name:size 格式
ONNX input size mismatch ... 配置计算的总输入维度与 ONNX 模型期望不符 重新核算 sum(obs_layout.size) * frame_stack + sum(extra_obs_layout.size)
Motion file has no frames .npz 文件路径错误或文件损坏 确认 motion_names 中的文件名存在于 motions/ 目录,且包含有效的 pos/vel 数组
Robot fell down! Shutting down... 机器人姿态超出 gravity_z_upper 阈值 若确实处于起身或大幅度动作场景,应将该策略的 gravity_z_upper 放宽至 1.0 左右

Sources: ros_interface.cpp, obs_manager.cpp, obs_manager.cpp

下一步阅读指引

如果你希望深入理解观测值在代码层面的组装、归一化与帧堆叠实现细节,建议继续阅读 观测值组装、归一化与帧堆叠。若需了解如何在运行时通过手柄或 ROS 服务触发策略切换,请参阅 多策略运行时与热切换机制。对于新增自定义观测源(如激光雷达或视觉特征)并注册到 YAML 布局中的完整流程,则可参考 新增观测源与模型适配指南