Sim-to-Real 迁移实战
概念速览
- Sim-to-Real gap:仿真与真机在物理 / 视觉 / 控制上的差异,导致 sim 训练好的策略到真机 0% 成功率
- 三大子问题:
- 物理 gap:质量、摩擦、电机增益、减速比
- 视觉 gap:光照、纹理、相机噪声、镜头畸变
- 动力学 gap:机械臂柔顺、关节迟滞、线缆阻力
- 成功率基线:
- 不做任何处理:0-30%
- 域随机化 (DR) + 1 小时真机数据:70-90%
- DR + System ID + 50 episodes fine-tune:90-99%
Sim-to-Real 流水线
flowchart TD
A["1. 物理建模<br/>URDF 质量/惯量 → USD 物理参数"]
B["2. System ID<br/>测关节扭矩响应 → 仿真里调增益"]
C["3. 域随机化 DR<br/>物理 ±20% · 视觉 ±30% · 噪声 ±10%"]
D["4. 大规模训练<br/>4096+ envs · 12 h · 1×RTX 4090"]
E["5. 真机评估<br/>50 episodes · 记录失败案例"]
F["6. 失败驱动迭代<br/>改 DR 范围 / 补 System ID"]
G["7. 渐进 fine-tune<br/>50 真机 episodes · 4 h"]
H["8. 部署<br/>Jetson / 工控推理"]
A --> B --> C --> D --> E --> F --> G --> H
F -.反馈.-> C
F -.反馈.-> B
域随机化 (DR) 配方
物理 DR
| 参数 | 范围 | 采样方式 |
|---|---|---|
| 关节质量 | ±20% | Uniform |
| 关节摩擦 | ±30% | Uniform |
| 电机增益 (Kp/Kd) | ±25% | Uniform |
| 减速比 | ±5% | Uniform |
| 末端负载 | 0-2 kg | Uniform |
| 地面摩擦 | 0.5-1.5 | Uniform |
视觉 DR
| 参数 | 范围 | 备注 |
|---|---|---|
| 纹理 | 随机几何 + ImageNet 1k | 用 isaaclab.utils.warp |
| 光照方向 | 全方位随机 | HDR skybox |
| 光照强度 | ±50% | Uniform |
| 相机噪声 | 高斯 σ=0.01 | per-pixel |
| 运动模糊 | 0-5 px | 模拟快门 |
| 镜头畸变 | 桶形 ±2% | K1/K2 参数 |
控制 DR
| 参数 | 范围 |
|---|---|
| 动作延迟 | 0-50 ms |
| 观测噪声 | σ=0.005 (位置) / σ=0.05 (图像) |
| 关节迟滞 | 0-2 ms |
System ID 速查
# 用真实遥操作数据反推物理参数# 例: 测机械臂在负载 0/0.5/1 kg 下的关节扭矩响应import numpy as npfrom scipy.optimize import minimize
def sim_torque(params, real_torque, action): mass, friction, kp = params return (kp * action) - friction * 0.1 - mass * 9.81 * 0.1
real_data = load_dataset("real_joint_torque.npz") # 1000 stepsresult = minimize( lambda p: np.mean((sim_torque(p, real_data['tau'], real_data['action']) - real_data['tau'])**2), x0=[1.0, 0.1, 50.0], # initial guess bounds=[(0.5, 2.0), (0.01, 0.5), (10, 200)],)print(f"Identified: mass={result.x[0]:.2f}kg, friction={result.x[1]:.3f}, Kp={result.x[2]:.1f}")渐进式微调 (避免灾难性遗忘)
flowchart LR
A["Sim Checkpoint<br/>100%"]
B["Mixed Replay<br/>sim : real = 90 : 10"]
C["Fine-tune<br/>4 h"]
D["Mixed Replay<br/>sim : real = 50 : 50"]
E["Fine-tune<br/>4 h"]
F["Mixed Replay<br/>sim : real = 10 : 90"]
G["Real-only<br/>2 h"]
H["Deploy"]
A --> B --> C --> D --> E --> F --> G --> H
失败案例库
| 症状 | 根因 | 修法 |
|---|---|---|
| 机械臂 sim 100% · 真机 0% | URDF 质量错 3-5x | 重新测质量 + 校惯量 |
| 抓取 sim 90% · 真机 30% | 相机内参不对 | 标定 (Kalibr) |
| 移动 sim 95% · 真机 50% | 地面摩擦模型错 | 加 DR ±50% |
| 抓 sim 80% · 真机 10% | 视觉 sim 与真机背景差 | 加 ImageNet 随机纹理 |
| Sim 训练慢 / 不收敛 | DR 范围过大 | 缩小到 ±10% 起步 |
实战工具栈
| 阶段 | 工具 | 备注 |
|---|---|---|
| 物理仿真 | Isaac Sim 4.2+ / MuJoCo 3.2+ | Isaac Sim = GPU 并行强 |
| 域随机化 | Isaac Lab RslRlVecEnvWrapper | 内置 |
| System ID | scipy.optimize.minimize + 自定义代价 | 见上 |
| 数据采集 | LeRobot / ALOHA 遥操作 | 50-200 episodes |
| 微调 | Hugging Face transformers + LoRA | 4-bit 量化 |
| 部署 | TensorRT / ONNX Runtime | Jetson = TRT · x86 = ORT |
📋 复现条件
| 类别 | 必备 | 备注 |
|---|---|---|
| 硬件 | RTX 4090 (训练) · 真机 (G1 / SO-100 / Franka 任一) | Jetson Orin = 部署 |
| 操作系统 | Ubuntu 22.04 / 24.04 | 训练机 |
| 软件版本 | Isaac Sim 4.2+ · Isaac Lab 1.3+ · LeRobot 最新 | PyTorch 2.2+ |
| 网络 | 无 | 模型本地训 |
| 账号 | Hugging Face (上传 dataset) | 可选 |
| 时间 | System ID 1 天 · 训练 12 h · 评估 2 h · 调优 1 周 | 全流程 2-3 周 |
| 难度 | ⭐⭐⭐⭐ 高 | 需要 RL + 机器人 + 深度学习 |
| 预算 | ¥20,000+ 训练机 · ¥1,800 起真机 | 真机越贵越接近 |
⛔ 别这么做 (Anti-patterns)
| ❌ 错误做法 | 💥 后果 | ⚠️ 风险 | 范围 |
|---|---|---|---|
| 不要在没做 DR 的情况下跑 Sim-to-Real | Sim 100% 成功率 → 真机 0% | 🔴 | 任何 VLA 部署 |
| 不要用仿真里默认的相机内参 | 焦距 / 畸变全错 = 视觉策略直接挂 | 🟡 | 视觉策略 |
| 不要在 System ID 之前 fine-tune 真机数据 | 物理参数错 = 真机梯度误导训练 | 🟡 | 任何模型 |
| 不要把 DR 范围一开始设 ±50% | 训练不收敛 = 浪费时间 | 🟢 | 训练初学者 |
| 不要用单一纹理背景训练 | 背景一变策略就崩 | 🟡 | 视觉策略 |
| 不要在真机没接物理急停的情况下训策略 | 失控 = 撞人 / 烧板 | 🔴 | 真机实验 |
🚨 风险总览 (Risk Matrix)
| 风险等级 | 含义 | 例子 |
|---|---|---|
| 🟢 低 | 改错可回滚 / 几分钟恢复 | DR 范围过大 |
| 🟡 中 | 需要重新刷机 / 重装 / 1-2 小时恢复 | 相机内参错,DR 不足 |
| 🔴 高 | 不可逆 / 烧板 / 安全风险 | 没 DR 真机部署,没急停 |
参考资料
- Sim-to-Real Survey (OpenX):Open X-Embodiment
- NVIDIA Isaac Lab 文档:https://isaac-sim.github.io/IsaacLab/
- System ID 教程 (MuJoCo):https://mujoco.org/book/
- 域随机化论文 (OpenAI Rubik’s):arxiv 1803.07067
- 渐进 fine-tune (Octo):https://octo-models.github.io/