前置知识:已阅读 HelloWorld 解读。本文深入解读摆动旋转(Swing)任务——MicroDuck 坐在两根软绳悬挂的摆座上,从静止出发,只靠自己的身体动作把摆幅越荡越大。
一、整体流程概览
这个 Notebook 演示了**摆动旋转(Swing)**任务。和之前让机器狗走路不同,这次机器狗坐在一个由两根软绳悬挂的摆座上,像一只坐在秋千上的鸭子,从最低点静止出发,靠自己的身体动作把秋千越荡越高。
环境配置 → 加载策略模型 → 创建仿真 → 泵摆演示 → ONNX 验证 → BPU 部署通俗理解:就像教一只坐在秋千上的机器狗,学会靠自己"泵"起来,越荡越高。
先看实际效果
图 1:使用公开alpha050.pt在本地导出得到的 36 秒 rollout 关键帧。它展示的是"从静止开始主动泵摆",不是外部给摆座施加初始冲量。
图 2:本地导出视频的六个时间点抽帧。可以观察到机器狗如何通过身体伸展和收缩来给秋千"加油"。
图 3:两根独立软绳和保留座椅结构。注意绳子只能受拉,不能把"绷直"和"压缩"都当作合法约束——这是任务的核心物理约束。
二、这个任务到底难在哪里
2.1 为什么不是"按固定节奏蹬腿"
摆座本身是一个会摆动的动力学系统。鸭子一开始位于最低点,速度和角度都接近零。如果策略只输出一个固定的周期动作——比如每隔 0.5 秒蹬一次腿——动作的相位很快就会和摆座的实际运动错开。
打个比方:想象你坐在秋千上,闭着眼睛胡乱蹬腿。有时候你往前蹬的时候秋千正在往前荡,这一下给了秋千额外的能量;但更多时候你往前蹬的时候秋千正在往回摆,这一下反而把秋千"刹"住了。结果就是秋千不会越荡越高,反而会停下来。
机器鸭要学的,是在每个控制周期(每秒 50 次)实时回答一个闭环问题:
当前摆座正在向哪边运动?我的身体应该提前伸展还是收缩?哪一组腿部和头部动作能把身体重心的运动投影到摆动方向,而不把能量泄露到横向和偏航方向?
2.2 绳子的物理约束
另一个关键难点:绳子只能受拉,不能受压。
你可以用绳子去拉一个东西,但不能用绳子去推一个东西。这意味着:
- 当绳子绷直时,它能传递力,让摆座沿着弧线运动
- 当绳子松弛时,它不传递任何力,摆座处于自由落体状态
策略不能让绳子"压缩"来产生推力,也不能让绳子过度拉伸。它必须在绳子始终处于合法张力范围的约束下,找到泵摆的方式。
2.3 没有"秋千角度传感器"
公开实现明确把精确的绳长、座椅状态和几何量留给 critic 与评估器;actor 主要依靠本体感觉(也就是身体自己的传感器)来完成判断。
也就是说,鸭子并不知道"秋千现在摆到了 30 度",它只能通过自己身体的陀螺仪、加速度计、关节角度等信息去推断当前的摆动状态。
三、任务架构:谁在控制,谁在评估
3.1 Actor(演员)——真正控制动作的神经网络
Actor就是最终部署到机器鸭身上的神经网络,负责把传感器数据变成关节动作。
传感器数据(61 维) → Actor 神经网络 → 关节动作(14 维)输入(61 维)包含什么?
| 输入组 | 通俗解释 |
|---|---|
| 基座角速度 | 鸭子身体现在转得多快,就像陀螺仪告诉你"你在旋转" |
| 投影重力 | 重力方向在鸭子身体坐标系里的投影,让鸭子知道"哪边是下" |
| 14 个关节位置 | 两条腿和头部的每个关节现在在什么角度 |
| 14 个关节速度 | 每个关节转得多快 |
| 上一时刻动作 | 刚才给了什么指令,让策略知道自己刚才做了什么 |
| 速度/头部/身体命令 | 想让鸭子往前跑、转弯还是做什么(保持与 MicroDuck 既有控制接口一致) |
输出(14 维)是什么?
14 个关节的目标位置。MicroDuck 每条腿有 7 个可控关节(髋关节 3 个 + 膝关节 1 个 + 踝关节 3 个),两条腿共 14 个。
关键问题:Actor 看不到"秋千角度",怎么知道该什么时候蹬腿?
秋千的运动不会以"swing_angle = 30°"这种特权信息直接出现,但会通过以下方式间接体现:
- 基座角速度:秋千往前荡时,身体会感觉到向前的旋转
- 重力方向:秋千倾斜时,"下"的方向在身体坐标系里会偏移
- 关节响应:腿在蹬伸时,如果秋千正在同方向运动,关节会感觉更"轻"
策略在时间上连续运行(每秒 50 次),就能从这些信号中推断自己处于上摆、过顶还是下摆阶段,从而在正确的时机做出动作。
3.2 Critic(评论家)——训练时的辅助评估者
Critic是训练时用来帮 Actor 学习的辅助神经网络。训练结束后,Critic 就被扔掉了,只保留 Actor。
传感器数据(61 维) + 特权信息(7 维) → Critic 神经网络 → 价值评估Critic 的输入比 Actor 多了 7 维,这多出来的就是特权信息:
- 秋千的精确摆角
- 绳子的精确长度
- 秋千的精确能量
但是!这些信息只在训练时用,真正部署到真实机器鸭上时,Critic 就不存在了。
这叫asymmetric actor-critic(非对称演员-评论家):
- Actor 只能用"真实传感器能拿到的数据"
- Critic 可以用"上帝视角数据"来更准确地评估"这个动作好不好"
打个比方:就像学开车,教练(Critic)能看到车速、转速、油耗、轮胎磨损,但司机(Actor)只能看仪表盘。教练根据更多信息判断"你刚才换挡换得好不好",但司机要学会只靠仪表盘就能开好车。
一个常见误读:critic 能看到绳长,不等于鸭子的策略在真机上装了一个理想绳长传感器。导出的 ONNX 只应使用 actor 的 61 维输入。
3.3 训练链路全景
图 1:训练链路。actor 负责最终部署动作,critic 可以在训练时使用更多仿真状态,但这些特权状态不能进入部署 ONNX。
四、"自己泵摆"是怎样学出来的
机器鸭学到的不是一个"按固定相位摆腿"的动画,而是一个把本体感觉和接触动力学闭环起来的泵摆控制器。这个行为可以拆成三个层次理解:
4.1 能量注入——怎么给秋千"加油"
身体和腿在摆动的特定相位发生伸展/收缩,把关节运动转化为摆座沿弧线方向的有效能量。
打个比方:
- 在秋千往上荡的时候,鸭子伸展身体,把重心往外推,增加势能(就像你站起来)
- 在秋千往下荡的时候,鸭子收缩身体,减少阻力(就像你蹲下去)
- 这样每次都能"偷"一点能量进去,秋千越荡越高
4.2 方向约束——怎么让秋千只在前后荡
奖励函数鼓励"前后摆动"(像荡秋千一样),却惩罚"左右晃动"(像扭秧歌一样)和"身体旋转"(像转圈一样)。
具体来说,奖励会惩罚:
- 横向位移(左右偏移)
- 横向速度(左右甩的速度)
- 滚转/偏航角速度(身体绕纵轴旋转)
- 挂点不对齐(绳子系点和理想位置的偏差)
这样鸭子学会的是"前后荡",而不是"乱晃"。
4.3 物理边界——不能靠"耍赖"来解决问题
当出现以下情况时,策略不能靠快速 reset(重置)来逃避问题:
- 软绳松弛(失去张力)
- 过度拉伸(超过绳子的物理极限)
- 身体脱离摆座几何(鸭子从座位上飞出去)
- 出现 NaN(数值计算崩溃)
结果:换初始噪声或执行器参数后,动作相位仍然要能在反馈中自行修正。策略必须真正学会泵摆,而不是找到一个"漏洞"来刷分。
五、奖励设计:不是"角度越大越好"
公开配置里最重要的奖励项不是单独的"角度越大越好",而是一组同时生效的目标:
| 类别 | 代表项 | 通俗解释 |
|---|---|---|
| 进度 | swing_peak_progress | 如果你荡到了新的高度,加分。防止你只在低幅度来回晃(那样虽然安全,但没进步) |
| 高度/能量 | swing_height、swing_late_height、swing_energy | 让你在整个 36 秒内都保持高摆幅,而不是前几秒冲高后就失控 |
| 平面约束 | swing_lateral、swing_lateral_velocity、swing_out_of_plane_angular_velocity | 让你保持在"前后摆动"的平面内,不要左右晃 |
| 机构约束 | swing_alignment、string_slack、string_extension_barrier | 让挂点(绳子系在秋千上的点)对齐,绳子不要松垮垮或拉太长 |
| 控制平滑 | action_rate_l2、joint_torques_l2、关节限位 | 不要靠"高频抖动"来换角度(像抽搐一样),动作要平滑 |
评估时还要检查:
- 完整时域是否跑完(36 秒内有没有中途 reset)
- 绳长是否越过几何包络(绳子有没有松掉或拉断)
- 侧向位移是否受控(有没有左右乱甩)
- BAM 执行器电压/电流/扭矩是否在安全范围内(电机有没有过载)
六、公开策略的真实指标:视频里的数字是怎么回事?
6.1 测试方法
使用固定源码版本:
https://github.com/Vottivott/microduck-playground commit: c5fcc50219fef01ac9931d0079c583ccbb29b689对应的alpha050策略,在 100 个不同的随机种子上各跑 36 秒。
6.2 真实数据
| 指标 | 数值 | 通俗解释 |
|---|---|---|
| 随机评估 | 100 个 seed × 36 秒 | 用 100 个不同的初始条件测试 |
| 严格完整时域通过 | 71/100 | 71 个种子完整跑完 36 秒,没有中途 reset |
| 无几何债务通过 | 73/100 | 73 个种子没有出现绳子松弛、拉伸过度等问题 |
| 峰峰摆幅中位数 | 163.03° | 一半以上的种子能荡到 163° 以上 |
| 最好严格 rollout | seed 27,173.20° | 最好的单次摆幅是 173.2° |
| seed 27 最大横向位移 | 10.35 mm | 横向偏移只有 1 厘米,说明摆动很"正" |
6.3 视频里的"2717.8°"是怎么回事?
视频中出现的60 s、7 圈、2717.8°是累计角度的展示指标,不是单次摆幅。
打个比方:
- 累计角度就像计步器:你今天走了 2 万步,这是把所有步数加起来
- 峰峰摆幅就像单次步幅:你一步能跨多远
2717.8° 是把 60 秒内每次摆动的角度不断累加的结果,适合做展示,但不等于摆座一直处于一个合法的 360° 环形运动。
公开选择策略的严格峰峰摆幅只有约173.2°。后来某次 60 秒回放达到 180.2°,但离开了准确几何包络(绳子超出了合理范围),因此不能把它当作通过物理门控的正式结果。
七、关键参数速查表
| 参数 | 值 | 含义 |
|---|---|---|
| 策略频率 | 50 Hz | 每秒做 50 次决策 |
| 仿真频率 | — | MuJoCo 物理引擎推进频率 |
| 关节数 | 14 | MicroDuck 有 14 个可控关节 |
| Actor 输入维度 | 61 | 本体感觉传感器数据 |
| Critic 输入维度 | 68 | 本体感觉 + 特权信息 |
| 网络结构 | 512-256-128 ELU | 三层全连接网络 |
| 评估时长 | 36 秒 | 每次 rollout 的时间 |
| BPU 推理 | 可选 | 需要 RDK 开发板 |
总结:摆动旋转任务的核心不是"按固定节奏蹬腿",而是"根据身体感觉实时调整动作,让秋千越荡越高"。它展示了强化学习如何让机器人在复杂动力学约束下,学会从本体感觉推断环境状态,并做出最优决策。
附:原始 Notebook 参考
- 04_摆动旋转_ONNX_BPU_MuJoCo.ipynb— 在 GitHub 上查看原始 Notebook,可对照代码逐行学习。