Robotics
PACE — 腿式机器人 Sim-to-Real
PACE 是什么
PACE(Precise Adaptation through Continuous Evolution,基于持续进化的精确自适应)不训练你的运动控制策略。 它只解决腿式机器人 sim-to-real 崩溃的那个关键问题:仿真器里的执行器/关节动力学和真实硬件对不上。
PACE 测量真实执行器的响应,然后用 CMA-ES(进化优化)找出一组物理参数,让 Isaac Lab 里仿真关节的运动方式和真机一致。之后你把这些参数烧进正常的强化学习训练里,策略就能顺利迁移到硬件。
它与 NVIDIA Isaac Lab 集成,并遵循其 task/environment 约定。
识别的参数
每个关节识别 5 类参数(ANYmal = 12 关节 × 4 + 1 个全局延迟 = 49 个参数):
| 参数 | 含义 | 范围(ANYmal) |
|---|---|---|
| Armature 电枢惯量 | 折算的转子惯量 | 1e-5 – 1.0 kg·m² |
| 粘性摩擦 | 与速度成正比的阻尼 | 0 – 7.0 Nm·s/rad |
| 静摩擦 | 库仑摩擦 | 0 – 0.5 |
| 编码器偏置 | 关节零点偏移 | ±0.1 rad |
| 延迟(全局) | 执行器延迟 | 0 – 10 仿真步 |
整个流程就是三个脚本:data_collection.py → fit.py → plot_trajectory.py。
前置条件
- 已安装 Isaac Lab(推荐 Isaac Sim 5.0+;4.5 可运行但精度下降)
- 用 Isaac Lab 的 Python 解释器以可编辑模式安装 PACE:
git clone https://github.com/leggedrobotics/pace-sim2real.git
cd pace-sim2real
python -m pip install -e source/pace_sim2real
工作流 1 — 内置 ANYmal 例子(纯仿真)
这是验证工具链能端到端跑通的"hello world",针对仿真激励数据做参数识别,无需硬件。
conda activate <isaaclab_env>
cd path/to/pace-sim2real
# 1. 生成 chirp 激励轨迹并记录关节响应
python scripts/pace/data_collection.py \
--task Isaac-Pace-Anymal-D-v0 --num_envs 1 \
--min_frequency 0.1 --max_frequency 10 --duration 20 --device cuda
# → 写入 data/anymal_d_sim/chirp_data.pt
# 2. 用 CMA-ES 拟合参数(4096 个并行环境 = 种群)
python scripts/pace/fit.py \
--task Isaac-Pace-Anymal-D-v0 --num_envs 4096 --headless
# → 写入 logs/pace/anymal_d_sim/<时间戳>/ (mean_XXX.pt, best_trajectory.pt, config.pt)
# 3. 检查拟合质量和收敛情况
python scripts/pace/plot_trajectory.py --robot_name anymal_d_sim --plot_trajectory
python scripts/pace/plot_trajectory.py --robot_name anymal_d_sim --plot_score
tensorboard --logdir logs/pace/anymal_d_sim # 观察直方图逐渐收窄
工作流 2 — 在你自己的机器人上做 Sim-to-Real
真正的目标。和工作流 1 的关键区别:步骤 B 把仿真数据换成真实硬件数据。
┌─ A. 在 PACE 里建模你的机器人 ──────────────┐
│ 新建 <robot>_pace_env_cfg.py: │
│ • 每种执行器一个 PaceDCMotorCfg │
│ • PaceCfg: joint_order, bounds_params │
│ • Scene: 你的机器人 USD + 初始姿态 │
│ • 注册 Isaac-Pace-<Robot>-v0 │
└────────────────────────────────────────────┘
│
┌─ B. 采集**真实**激励数据 ──────────────────┐
│ 在真机上跑 chirp 扫频 │
│ (激励信号设计同 data_collection.py) │
│ 保存测得的 q, q̇, τ → data/<robot>/ │
│ chirp_data.pt (顺序要对齐 joint_order)│
└────────────────────────────────────────────┘
│
┌─ C. 拟合(CMA-ES) ─────────────────────────┐
│ python scripts/pace/fit.py \ │
│ --task Isaac-Pace-<Robot>-v0 │
│ --num_envs 4096 --headless │
└────────────────────────────────────────────┘
│
┌─ D. 验证 ──────────────────────────────────┐
│ plot_trajectory.py + TensorBoard │
│ 在**不同**的 chirp/幅值/PD 增益上复验, │
│ 不能只看拟合用的那条轨迹 │
└────────────────────────────────────────────┘
│
┌─ E. 烧进 RL 训练 ──────────────────────────┐
│ 在你的策略环境里用 PaceDCMotor, │
│ 用识别出的参数设置: │
│ • joint_armature 电枢惯量 │
│ • joint_viscous_friction 粘性摩擦 │
│ • joint_friction 静摩擦 │
│ • encoder_bias, max_delay │
│ 训练策略 → 部署到硬件 │
└────────────────────────────────────────────┘
步骤 A — 机器人建模
复制 source/pace_sim2real/pace_sim2real/tasks/manager_based/pace/anymal_pace_env_cfg.py 作为模板。每种执行器类型定义一个 PaceDCMotorCfg(ANYmal 只有一种,你的机器人可能有多种——通过 joint_names_expr 关联对应关节)。
最关键的一点:joint_order 必须匹配你真实控制栈的关节顺序。Isaac Lab 用广度优先排序,通常和真机不同,顺序对不上会悄无声息地毁掉拟合结果。最后在 .../tasks/manager_based/__init__.py 里把环境注册为 Isaac-Pace-<Robot>-v0。
参考执行器配置:
ANYDRIVE_PACE_ACTUATOR_CFG = PaceDCMotorCfg(
joint_names_expr=[".*HAA", ".*HFE", ".*KFE"],
saturation_effort=140.0, # 峰值力矩能力
effort_limit=89.0, # 硬件力矩限制
velocity_limit=8.5, # 最大关节速度
stiffness={".*": 85.0}, # P 增益 [Nm/rad]
damping={".*": 0.6}, # D 增益 [Nm s/rad]
encoder_bias=[0.0] * 12, # 初始估计(全零)
max_delay=10, # 最大预期延迟 [仿真步]
)
bounds_params 中的参数排布(n_joints 为关节数):
- 电枢惯量:
[0 : n_joints] - 粘性摩擦:
[n_joints : 2*n_joints] - 静摩擦:
[2*n_joints : 3*n_joints] - 编码器偏置:
[3*n_joints : 4*n_joints] - 全局延迟:
[-1]
步骤 B — "真实"二字全靠这一步
仿真里的 data_collection.py 只是用来原型化激励信号。真正的 sim-to-real 要把同一个 chirp 跑在硬件上,把测得的轨迹放进 data/<robot>/chirp_data.pt。
激励信号设计参数(来自脚本):
trajectory_direction:对称 ± 关节运动,用于抵消基座反作用力trajectory_bias:每个关节的振荡中心trajectory_scale:振荡幅值
chirp 信号是可靠的基线,但更高级的轨迹优化方法可以进一步提升辨识质量。
步骤 E — 部署
把 utils/pace_actuator.py 和 utils/pace_actuator_cfg.py 拷贝或导入到你的学习项目里,然后:
导入执行器模型:
from pace_sim2real.utils import PaceDCMotorCfg, PaceDCMotor初始化时用识别出的值设置
max_delay和encoder_bias。训练前配置 articulation 的
joint_armature、joint_viscous_friction、joint_friction。
工作流 3 — 迭代精修循环
sim-to-real 很少一次到位。最佳实践循环:
设定保守、物理上合理的 bounds (查数据手册 > 理论极值)
↓
拟合 → 在一**族**轨迹上验证(不同频率/幅值/PD 增益)
↓
所有轨迹都拟合良好? ──否──► 调整 bounds、改进激励信号、重新拟合
│是
↓
**逐步**部署到硬件,监控 力矩/温度/速度/急停
↓
仍有残差? ──► 在失效工况下采新的硬件数据,重新拟合
关键规则:
- 不要对单条轨迹过拟合 —— 单个 chirp 拟合得再完美 ≠ 真机迁移成功。要在不同频率、幅值、相位偏移和 PD 增益上验证。
- 延迟被离散成整数仿真步 → 损失曲面不连续;收敛会有噪声,用保守的延迟 bounds,盯住延迟直方图。
- 种群大小:256–512 快速原型 · 512–2048 标准识别 · 2048–4096+ 发论文级别。
- 永远先在仿真里验证;绝不把未验证的参数直接推给硬件。
脚本参考
| 脚本 | 用途 | 主要参数 |
|---|---|---|
data_collection.py | 采集 chirp 激励数据用于系统辨识 | --task、--num_envs、--min_frequency、--max_frequency、--duration、--device、--headless |
fit.py | 运行 CMA-ES 参数辨识 | --task、--num_envs、--device、--headless |
plot_trajectory.py | 可视化轨迹和分数收敛 | --folder_name、--mean_name、--robot_name、--plot_trajectory、--plot_score |
优化日志写入 logs/pace/<robot_name>/<时间戳>/:
config.pt—— 完整配置和数据集元信息best_trajectory.pt—— 最优仿真关节轨迹mean_XXX.pt—— 每次迭代的平均参数估计progress.pt—— 最优分数和完整参数历史(当save_optimization_process = True时)
硬件安全清单
部署到真机之前:
- ✅ 仿真行为稳定,无关节振荡
- ✅ 力矩低于安全阈值
- ✅ 监控温度
- ✅ 已测试急停
- ✅ 参数已在未见过的运动上验证
心智模型
PACE = 用进化搜索对执行器做系统辨识,让你本来就在用的仿真器变得物理真实 —— 然后照常训练和部署。
用工作流 1 熟悉工具,工作流 2 用在你的机器人上,工作流 3 收尾闭合剩余差距。
引用
@article{bjelonic2025towards,
title = {Towards Bridging the Gap: Systematic Sim-to-Real Transfer for Diverse Legged Robots},
author = {Bjelonic, Filip and Tischhauser, Fabian and Hutter, Marco},
journal = {arXiv preprint arXiv:2509.06342},
year = {2025},
eprint = {2509.06342},
archivePrefix = {arXiv},
primaryClass = {cs.RO},
}