第 7 章 强化学习基础与 microduck_rl 的工作流
本章代码走读:RL 仓库
AGENTS.md的命令与仓库地图、tasks/目录、microduck_velocity_env_cfg.py头部。
7.1 技术栈三层楼
microduck_rl 站在三层成熟基础设施上:
- MuJoCo + MJCF:多体动力学仿真器;机器人以 MJCF(XML)描述,由 Onshape CAD 经
onshape-to-robot自动导出——仿真身体与真实装配同源(几何层的第一条 sim2real 契约)。 - mjlab(MuJoCo Warp 后端):把仿真步进搬上 GPU,数千环境并行;环境以"配置对象 + 管理器"(manager-based)风格组装:传感器、奖励项、事件、课程都是可插拔的 cfg 条目。
- rsl_rl 的 PPO:与 legged_gym/Isaac Lab 社区同源的策略梯度实现,机器人步态训练的事实标准之一。
概念上只需带走 PPO 的三要素:观测(61 维,第 1/8 章)、动作(14 维关节位置偏移,joint_pos_action.scale = 1.0)、奖励(数十项加权和,第 8 章逐项走读)。训练与部署都以 50 Hz 节拍运行——频率是两侧共同的呼吸。
7.2 代码走读:AGENTS.md 命令表——工作流即纪律
RL 仓库的 AGENTS.md 命令区是团队的肌肉记忆清单,每行都有讲究:
## Commands
```bash
uv run list-envs # live task registry
uv run train <TASK_ID> --env.scene.num-envs 4096 # train (add --hf-jobs for Hugging Face Jobs)
uv run train <TASK_ID> --env.scene.num-envs 64 --agent.max_iterations 5 # SMOKE TEST — always run first
uv run play <TASK_ID> --wandb-run-path <entity/project/run_id>
uv run scripts/export.py <TASK_ID> --wandb-run-path <...> # → ONNX (bakes obs normalizer — mandatory path)
uv run publish --task <TASK_ID> --wandb-run-path <...> --checkpoint N --repo <user>/microduck-<name> --kind episodic --duration-s 4.0
# → HF Hub repo (policy.onnx + schema-2 manifest.json + README) the daemon loads via `robotctl policy add`
uv run scripts/infer_policy.py --walking out.onnx # CPU MuJoCo deployment rehearsal (BAM M6 actuators as in training; --no-bam = XML PD)
uv run --with pytest pytest tests/
A 5-iteration smoke test at 64 envs catches ~95% of config errors for cents. Never launch a long run without one.
划线句:**5 迭代 × 64 环境的冒烟测试,花几分钱抓住约 95% 的配置错误;不冒烟不跑长训**。RL 实验最大的浪费不是 GPU 时费,是 90 分钟后才发现奖励项拼错字的一天。命令表还埋着完整链路的顺序:`train → play(回看)→ export(唯一合法转 ONNX 的路)→ infer_policy(CPU 部署彩排)→ publish(Hub)`,最后由机器人侧 `robotctl policy add` 消费——第 10 章逐环展开。
`infer_policy.py` 的注释点出一个细节:彩排用的执行器是 **BAM M6,与训练一致**(`--no-bam` 才退回 XML 自带 PD)——连"验证部署"这一步都在守护执行器保真度契约。
## 7.3 代码走读:仓库地图——一个 cfg 一个任务族
`AGENTS.md` 的 Repo map 节(节选):
```markdown
- `src/mjlab_microduck/tasks/mdp.py` — ALL custom MDP functions (rewards, events,
observations, commands, curricula). Add new functions here, grouped by task.
- `src/mjlab_microduck/tasks/microduck_*_env_cfg.py` — one cfg module per task
family. `microduck_velocity_env_cfg.py` is the main walking recipe AND the
shared base (robot, DR, obs, commands) other envs build on or mirror.
- `src/mjlab_microduck/tasks/__init__.py` — task registration (base + `-Backlash-` variants).
- `src/mjlab_microduck/tasks/backlash.py` — wraps any env cfg into its backlash twin.
- `src/mjlab_microduck/robot/microduck_constants.py` — robot cfgs, HOME frame, BAM actuator cfg.
- `src/mjlab_microduck/robot/microduck/` — MJCF exports from Onshape
... Collision families: `walk` (feet only), `groundcontact` (curated floor set),
`allcollisions` (every part; XL330 housings named `*_servo_collision` by
`name_servo_collision_geoms` → VelStand's servo-impact sensor). Each has a
`_backlash` twin generated by `add_backlash.py <xml> --backlash-deg 2.0`.
三件结构设计:
mdp.py(约 7400 行)收拢全部自定义 MDP 函数——奖励/事件/观测/命令/课程一个文件按任务分组;新函数只进这里。大而集中换来的是"找一个奖励项的实现不需要猜它在哪个文件"。- velocity 配置是主配方兼共享基座。行走任务的环境配置同时是其他任务族的模板:机器人配置、域随机化、观测、命令全部继承或镜像它。
AGENTS.md后文解释了为什么:"Building onmake_microduck_velocity*_env_cfgkeeps DR / obs / noise / delays in sync for free"(免费保持同步)——否则你要手工搬运整套 DR + 观测噪声 + NaN 防护栈。 - 碰撞族三分 + 齿隙孪生。
walk(只算脚,最快)、groundcontact(精选接地部位)、allcollisions(全部部件,XL330 舵机外壳命名为*_servo_collision,供 VelStand 的"舵机撞击传感器"用)——同一具身体三种碰撞精度,按任务需要选;每族再由脚本生成带 ±2° 齿隙的孪生版,-Backlash-变体因此能和基础任务做无混杂的 A/B 对照。
任务族清单(tasks/ 目录 14 个 env cfg):velocity(行走)、velstand(行走+恢复)、standup(起身)、sitstand(坐站)、ground_pick(俯拾)、ball_kick(踢球)、roulade(前滚翻)、rollers/swizzle/spin/crouch/slope/roller_standup(轮滑六变体)、testbench(台架)、distill(蒸馏)。
7.4 代码走读:velocity 配置的"开关面板"
每个环境配置文件顶部是一排大写常量开关——这份列表本身就是域随机化的菜单,注释里还留着调参史:
# Domain randomization toggles
ENABLE_COM_RANDOMIZATION = True
ENABLE_HEAD_COM_RANDOMIZATION = True # Randomize CoM of the head assembly bodies
ENABLE_KP_RANDOMIZATION = False # Was True
ENABLE_KD_RANDOMIZATION = False # Was True
ENABLE_MASS_INERTIA_RANDOMIZATION = True # Can enable once walking is stable
ENABLE_JOINT_FRICTION_RANDOMIZATION = True # Scales BAM's friction budget per-env via FrictionDRBamActuator.friction_scale
ENABLE_JOINT_DAMPING_RANDOMIZATION = False
ENABLE_ARMATURE_RANDOMIZATION = True # Reflected rotor inertia (microban-style). DOES affect BAM (armature is set, not zeroed).
ENABLE_VELOCITY_PUSHES = True # Velocity-based pushes for robustness training
ENABLE_IMU_ORIENTATION_RANDOMIZATION = True # Simulates mounting errors
ENABLE_ENCODER_BIAS = True # Per-env joint encoder calibration offset (actor obs sees joint_pos + bias)
ENABLE_BASE_ORIENTATION_RANDOMIZATION = False # Randomize initial tilt to force reactive behavior
读点有三:ENABLE_KP_RANDOMIZATION = False # Was True——被关掉的开关留着"曾是 True"的注释,这是负结果的记录(增益随机化试过、撤了);ENABLE_MASS_INERTIA_RANDOMIZATION 的注释写着"行走稳定后可开"——随机化强度跟着策略成熟度走;ENABLE_ENCODER_BIAS 让 actor 观测看到"关节角 + 标定偏差"——仿真里就把真实编码器的装配误差灌进观测。这些开关的具体实现散在 mdp.py 与 friction_dr_bam.py,第 9 章逐个走读。
7.5 无 GPU 怎么办
训练要 CUDA(4096 环境约 1–2 小时出可用步态),但整条学习路径并非必须有卡:--hf-jobs 把训练提交到 Hugging Face Jobs;infer_policy.py 与 tests/(cfg 不变量与 MDP 函数回归测试)全部 CPU 可跑。冒烟测试本身也只要几分钱级别的算力。
7.6 本章小结
microduck_rl 的工作流三律:先冒烟再长训、新任务从最近模板长出来、随机化强度跟着策略成熟度走。基础设施(mjlab/rsl_rl)都是社区成熟的轮子,这个仓库真正的产出是"配方"——下一章进入配方的核心:奖励与观测。