MANUAL 05 · HD-1910 迁移篇 · LEARN NOTES

收进学习手册 · 2026-09-19 · duck.whatled.com

从 XL330 到 HD-1910:Microduck 仿真迁移与 PPO 训练教程

从“替换参数后能训练”,走到“能解释结果、能复现实验、知道下一步验证什么”。

编写日期:2026-09-19。本文整理用户提供的 HD-1910 迁移报告,并补充原理、操作顺序、评测方法和本地代码核对结果。

0. 先明确这次迁移完成了什么

本次案例将 Microduck 的 XL330 执行器配置替换为 HD-1910 的初步模型,在仿真中训练了一晚。基础站立和前行已完成初步仿真验证;横移、转向和真机自由行走尚未完成。

这里的“基础验证”有明确范围:在报告给出的短时仿真场景中,机器人能够保持支撑并产生前向移动。它还不代表长期站立不漂移、准确直线行走、各种方向都能跟踪,也不代表策略已经适合真机。

0.1 本文的三种信息来源

标记 含义 阅读方式
案例记录 用户提供的 HTML 报告及用户补充的训练状态 说明这次实验做过什么;原始 checkpoint 和完整日志未在本次写作中独立复跑
本地核对 当前 /Volumes/dev/dev/microduck 中实际读取的代码 说明当前工作区有什么;不等同于报告作者的 Linux 工作区版本
教程建议 为复现、排错和下一轮实验补充的方法 尚未执行的实验和建议门槛,不写成已经通过的结果

案例训练目录是 /home/july/microduck_rl,运行时目录是 /home/july/microduck。本文不会假设这两个目录存在于读者电脑上。HD-1910 专用任务和脚本是案例中的新增内容,普通上游仓库不一定包含。

**命名约定:**本文沿用报告的 HD-1910。工作区历史资料中的 HL-1910、已有实现中的 HL-2915,不能未经型号、后缀和厂商规格核对就认定为同一产品。后文的 HD 电压、质量、电流均是案例参数,不是对所有同名舵机的额定规格承诺。实物台架侧(接线、线序、只读探针、标定门控)见URT2 + HD-1910 接线、组装与测试指南。

0.2 读完后应当能够做到

  1. 说清楚更换舵机影响哪些层,哪些接口可以保留。
  2. 为每个参数标明单位、来源、作用位置和验证办法。
  3. 完成配置检查、小规模训练、回放、导出和独立评测。
  4. 区分训练正常、仿真能力形成、部署兼容和真机验证。
  5. 根据横移、转向、动作饱和等具体问题安排下一轮实验。

本文中的操作分为“只读检查”“仿真实验”和“未来硬件阶段”。阅读或生成这份教程没有触发训练、联网发布、串口操作或机器人运动。

1. 先认识整条控制链

速度、头部等命令
        ↓
传感器/仿真状态 → 拼装 61 维观测 → 观测归一化 → Actor 策略
        ↑                                      ↓
        │                                  14 维动作
        │                                      ↓
        │                            HOME + scale × action
        │                                      ↓
        │                         延迟、限幅、固件控制器模型
        │                                      ↓
        └──── 机器人运动 ← 接触与刚体动力学 ← 电机力矩与摩擦

训练时:额外用 Critic、奖励和 PPO 更新神经网络权重
部署时:用 ONNX 做推理,由 robotd 读取传感器、发出目标并处理安全状态

几个容易混淆的名词:

名词 负责什么 本例中的作用
MJCF / MuJoCo 身体结构、关节、质量、惯量、碰撞与动力学 机器人在仿真里怎样运动
BAM Better Actuator Models,执行器模型 目标角度怎样经过固件、电机和摩擦变成力矩
MDP 状态转移、观测、动作、奖励和终止条件 定义策略要学习的问题
Actor 根据观测输出动作的网络 部署时继续使用
Critic 估计状态的长期回报 帮助训练;本例部署不需要它
PPO 优化 Actor/Critic 的学习算法 利用仿真采样不断更新权重
ONNX 导出后的推理模型 把训练得到的 Actor 送入运行时
Sim-to-real 从仿真迁移到实物 检查模型假设在现实中是否成立

PPO 只会在它接触到的环境里学习。如果仿真中的舵机反应特别快、力矩特别大、没有实际摩擦,策略可能学到真机做不出的动作。换舵机首先要检查这个“环境中的身体”是否合理。

2. 采用什么重构方式

2.1 保留接口,为硬件差异建立独立配置

本案例的基本思路值得复用:在原任务外增加 HD 配置,复用观测、命令、奖励和 PPO,先把差异集中在身体与执行器层。

层 第一轮保留 第一轮替换或核查
机械 关节命名、顺序;装配等价时保留运动学 舵机质量、质心、惯量、外壳与碰撞
执行器 BAM 接口与任务调用方式 JSON 参数、固件控制律、电压、延迟、限速
任务 61 维观测、14 维动作、平地速度任务 robot entity、有效随机化项、任务 ID
PPO 网络和优化器基线 实验名、运行名;重新学习网络权重
运行时 RobotIo 上层、观测、策略与安全状态机 通信协议、寄存器、单位换算、零位、方向

“保留关节限位和 HOME”有前提:安装方向、机械零位、连杆几何和可用行程仍相容。机械结构改变后,应更新这些量并重新检查接口语义,不能为了少改代码保留错误数值。

2.2 推荐的职责划分

以下是教程建议的结构;同名文件是否存在,以读者自己的分支为准。

src/mjlab_microduck/
  robot/
    microduck_constants.py         XL330 基线
    hd1910_constants.py            HD 身体、执行器与初始姿态配置
    hd1910_params/
      preliminary_m1.json         案例初始估计
      parameter-provenance.json   建议新增:来源、单位、版本和适用条件
  tasks/
    microduck_velocity_env_cfg.py  共享速度任务
    microduck_hd1910_env_cfg.py    HD 任务包装
    __init__.py                    注册独立任务 ID
  actuator/
    friction_dr_bam.py             摩擦随机化与必要兼容处理
scripts/
  train_hd1910.sh                  案例训练包装入口
tests/
  test_hd1910_cfg.py               配置、计数、维度等检查

新增独立任务的好处是可以对同一份基础代码做 XL330/HD 对照。不要把“原版配置”直接覆盖成 HD,再让任务名仍然叫原版;否则日志和模型很容易失去硬件归属。

2.3 参数来源比参数文件名更重要

每个关键参数至少记录以下信息:

字段 示例或用途
参数名与单位 armature_kg_m2,避免只有一个无单位数字
对应硬件 完整型号、版本、序列号或批次
数值来源 规格书、称重、台架拟合、人工估计
条件 电压、温度、负载、固件、P 值
不确定性 拟合误差、范围或“尚未测量”
证据文件 原始日志、规格页、CAD 导出文件及 SHA-256
适用范围 仅仿真初版 / 单舵机验证 / 整机验证

preliminary_m1.json 可以让管线开始工作,但不应因为文件被成功加载就变成“实测 HD 模型”。

3. 准备环境与冻结基线

**本节目标:**明确“在哪个仓库、什么依赖版本、哪个硬件模型”上复现实验。

3.1 先准备完整的实验材料

要复现案例,除本文之外,还需要案例的 HD 专用源码、机器人资源、参数 JSON 和依赖锁文件。要复现那一晚的具体结果,还需要 checkpoint、resolved 配置、训练日志和评测脚本。

只拿到 HTML 报告时,可以学习迁移方法、编写适配和准备检查;不能声称已经复现案例。

计算资源 合理用途
本地开发电脑 阅读、代码检查、模型检查、ONNX 检查;后端支持时做 CPU 小实验
已配置 CUDA 的训练机 并行环境和长训练
Radxa 主控 后续运行策略与总线控制,不承担本案例的大规模 PPO 训练

上游训练入口和任务注册方法可参考 Microduck RL 仓库。GPU、依赖和 CLI 行为随版本变化;CPU smoke 在案例中成功,不代表任意上游版本、任意平台都具备同样支持。

3.2 只读确认仓库和依赖

在已进入的训练仓库根目录执行:

pwd
git rev-parse HEAD
git status --short
uv --version
uv run python -V
uv run python -c "import torch; print(torch.__version__); print(torch.cuda.is_available())"
uv run list-envs

最后一个命令用于检查当前注册的任务。如果版本没有这个入口,先看本仓库 README 和 pyproject.toml,不要用名称相近的其他程序代替。

**预期结果:**能够明确版本、解释工作区改动,并看到原任务;具备 HD 适配时,还应看到 Mjlab-Velocity-Flat-HD1910-MicroDuck。

uv run 可能按锁文件准备依赖,所以首次运行要预留安装时间和网络条件。已经能够运行的基线应保留其 uv.lock,避免为这次迁移顺手升级整个依赖栈。

3.3 留下可重建的快照

建议每轮实验保存:

experiment/
  source/                 对应提交与未提交代码的快照
  dependency-versions.txt  Python、Torch、MuJoCo、mjlab、BAM 等
  uv.lock
  resolved-env.json        最终环境配置,含真正启用的事件
  resolved-agent.json      最终 PPO 配置
  robot-assets/           MJCF、mesh、纹理和质量来源
  actuator.json
  provenance.json
  checkpoints/
  metrics/
  videos/
  export/

这里只定义推荐产物结构,不宣称工具已经自动生成它们。尤其不要只记录 Git commit 而漏掉未提交的 HD 参数文件。当前 Studio 的源码快照设计可作为参考,但其训练入口仍对非 XL330 执行器设有适配门槛。

3.4 跑通一个原版基线

先确认原版任务在当前环境能注册、构建并进入训练,再加入 HD 差异。这样,发生导入失败、GPU 初始化失败或模型资源缺失时,不会误认为是换舵机导致。

**本节验收:**基线能运行;版本、源码、资源和依赖可追踪;HD 实验有独立名称与输出目录。

4. 机械模型:先改质量,再理解惯量与几何

4.1 案例的质量替换

案例采用的数值是:

项目 XL330 基线 HD-1910 初版 差值
单舵机质量 0.018 kg 0.021 kg +0.003 kg
实体舵机数量 15 15 —
整机舵机增重 — — +0.045 kg

这些数值来自案例报告。采购件是否真是 21 g,要实测;线材、螺钉、舵盘或支架随之改变时,也要计入。

对一个含有 n 个舵机的刚体,案例近似为:

m_new = m_old + n × (0.021 - 0.018)
I_new = I_old × m_new / m_old

为什么按刚体分配?同样增加 45 g,放在躯干和放在两脚,对平衡与摆腿的影响不同。

4.2 避免把同一只舵机数两遍

案例按可视 mesh xl330 且 group == 2 计数,跳过重复的碰撞 mesh,并要求总数恰好为 15。这个方法适合它对应的模型版本。

更稳妥的长期办法是维护明确的“舵机实例 → 刚体”映射。mesh 名字、分组和导出方式会变化,不能把 group == 2 当作所有 MJCF 的固定规范。

检查顺序:

  1. 输出每个刚体包含哪些舵机实例。
  2. 核对总数为 15,其中嘴部不进入 14 维策略动作。
  3. 比较替换前后整机质量,期望本案例差值为 0.045 kg。
  4. 确认重复构建模型不会累计加重。
  5. 对训练实际使用的每个模型入口应用同一规则,避免 walk 已改、回放模型未改。

4.3 为什么同比缩放惯量只是近似

惯量描述物体对旋转加速度的抵抗。增加质量但保持原质心、把全部惯量按同一比例放大,相当于假设新增质量大致沿原分布增加。

实际换舵机可能让质量集中在不同位置。更准确的处理是根据各部件质量和位置重新计算组合质心,再把每个部件的惯量旋转、平移到共同坐标系后求和。

组合质心 c = Σ(m_i × c_i) / Σm_i

部件关于新质心的贡献:
I_i,new = R_i × I_i,local × R_i^T
          + m_i × (||d_i||² × E - d_i × d_i^T)

I_total = Σ I_i,new

其中 d_i 是部件质心到组合质心的位移,E 是单位矩阵。这说明“增加质量”与“修改惯量”不是任意选一个倍数就结束。

除正质量、正惯量外,还应检查惯量的物理可实现性、坐标系及模型编译结果;正定不是全部条件。MJCF 字段含义以 MuJoCo XML 参考 为准。

4.4 显示模型和碰撞模型要分开检查

外观能显示,不说明碰撞正确。核查舵机壳体、腿部间距、脚底接触面和关节轴;如果安装孔位不同,还会改变运动学。

**本节验收:**质量计数正确、不重复应用;HOME 姿态无明显穿模;脚底确实接地;连杆与碰撞来源可追踪。比例缩放阶段明确标为“机械近似”。

5. 执行器模型:从目标角度到真实力矩

5.1 不同舵机的 P 值不能直接比较

策略给出目标角度,舵机内部固件根据误差决定驱动强度。相同 P=32,在不同固件中可能对应不同的误差单位、PWM 增益、限流与目标滤波。

概念上可写成:

角度误差 → 固件比例增益与内部限幅 → PWM/电压
电压与反电动势 → 电流 → 电磁力矩
电磁力矩、齿轮摩擦、转子惯量 → 输出轴运动

这也解释了为什么“换了串口驱动,舵机能转”与“仿真执行器模型正确”是两件事。BAM 要求描述控制律、固件限制及待辨识参数;可参考 BAM 执行器建模。

5.2 案例初始参数表

下表用于理解实验,不是可以直接用于真机的参数推荐。

参数 案例值 单位或含义 来源等级
kt 0.73575 N·m/A,等效力矩常数 堵转规格估算
R 3.7 Ω,等效电阻 7.4 V / 2 A 估算
armature 0.00224 kg·m²,折算到关节侧的附加惯量 估计
q_offset 0.0 rad 初始设定
friction_base 0.015 基础库仑摩擦项,通常为 N·m 估计,按实际实现核对
friction_viscous 0.005 黏性项,通常为 N·m·s/rad 估计,按实际实现核对
error_gain_ratio 1.0 控制误差增益修正 初始设定
max_velocity 11.833 rad/s,约 113 RPM 规格换算,用作初始目标限速假设
model m1 简化摩擦模型 初版选择
actuator sts3215 借用 STS 固件模型 HD 等效性待测

RPM 换算:ω = RPM × 2π / 60。113 RPM 约为 11.833 rad/s。

q_offset 还要区分用途:在 BAM 辨识中,它可表示台架装配造成的角度偏置;整机运行时的每关节编码器零位是另一份校准。不能把一次摆锤拟合出的偏置统一写入整机所有关节。

需要注意:空载最大转速与固件的目标角度变化率上限不必相同。 将前者填入后者只能作为初始假设,应该通过阶跃和斜坡目标实验验证。

5.3 做一次数值自洽检查

案例由堵转扭矩与电流估算 kt,由电压与电流估算 R,属于粗略建模。

在简单电压电机模型且力矩/反电动势常数采用同一等效数值的假设下,空载速度上界近似为 V / kt。以案例的 7.4 V 和 0.73575 计算,约为 10.06 rad/s,低于另一条规格给出的 11.833 rad/s。PWM 上限和摩擦还会进一步影响速度。

这不是判定产品规格错误,而是提示:测量条件、减速器效率、等效参数定义或初值组合未必一致。应让实际数据约束模型,不要强行认为从规格表算出的每个数字同时成立。

5.4 为什么 M1 可以起步,但不能替代辨识

案例从 XL330 的较复杂 M6 模型切换到 HD 的初步 M1。M1 更容易建立基线,能检查接口、采样、训练和导出是否连通。

复杂模型未必自动更好:数据不足时,多参数模型也可能过拟合。建议用多负载、多轨迹数据比较候选模型,在留出日志上检查角度、速度和相位误差,再选择复杂度。

特别要区分:

单舵机采集与拟合的完整顺序见 BAM 辨识流程。

5.5 加载自定义 JSON 时防止悄悄回到 XL330

下面是配置模式示例,不是可直接运行的完整 HD 任务;需要已有 base_actuator_cfg 和有效 JSON 路径,字段以锁定版本为准。

from dataclasses import replace

hd_actuator_cfg = replace(
    base_actuator_cfg,
    motor_name=None,
    model=None,
    json_path=str(hd_json_path),
    kp_fw=32,
    vin_range=(6.6, 8.2),
    delay_min_lag=2,
    delay_max_lag=6,
)

json_path 与内置 motor_name/model 选择方式在本地 BAM 配置中互斥。加载后输出实际 actuator 类型、JSON 路径和关键数值,并检查加载器是否重新初始化、覆盖了拟合参数。

报告中的 sag 字段是 vin_drop_gain_range;不同 BAM 版本还可能使用电阻形式的 vin_drop_resistance_range,两者不能仅改名字就当成等价。先读当前类的定义,再迁移数值。

现成 JSON 更新(2026-10-11):飞特官方 Gitee 已发布 HD-1910 的 m1–m6 全套已辨识
BAM 参数(7.4 V,actuator: "hd1910" 身份正确),比陆吾/XGODUCK 参照更适合作为
json_path 的先验来源——逐参数对比与参照链插入位置见
飞特官方 BAM 发布解读。hd1910_our_measured 仍是终点。

6. 时间尺度与状态重置:两个容易漏掉的问题

6.1 同一个“step”可能有三种意思

案例给出物理步长 0.005 s、decimation 为 4,因此:

物理仿真:每秒 200 次,dt_physics = 0.005 s
策略决策:每秒 50 次,dt_policy = 4 × 0.005 = 0.020 s
一次 PPO rollout:每环境 24 个策略步 = 0.48 s
配置中的步数 应确认的更新位置 本案例条件下的换算
执行器 delay_min_lag/max_lag 物理循环还是策略循环 物理步时 2–6 = 10–30 ms;策略步时 = 40–120 ms
观测延迟 观测管理器多久取一次样 若每策略步更新,1 步 = 20 ms
课程 step 环境公共步计数,还是所有并行样本数 本报告使用 iteration × 24

**对原报告的修订说明:**报告将 2–6 步直接称为 40–120 ms。本次读取的本地 mjlab ActuatorCfg 明确写的是 physics timesteps,因此不能无条件沿用报告换算。要复现那一晚的物理延迟,必须取得当时依赖版本并确认调用点。

可在训练环境做只读检查:

uv run python - <<'PY'
import inspect
from mjlab.actuator.actuator import ActuatorCfg
print(inspect.getfile(ActuatorCfg))
print(inspect.getsource(ActuatorCfg))
PY

更可靠的验证是固定 lag,输入一次目标阶跃,记录“请求目标”和“实际送入固件模型的目标”的时间戳。不要只看关节开始运动的时间,因为机械响应会混入惯量与摩擦。

6.2 内部目标位置不能随便清零

STS 类执行器可能先对内部目标 q_target_smooth 做速率限制。这个状态必须有正确形状,并在环境重置时正确初始化。

案例为解决直接加载 JSON 的报错,创建了 [num_envs, num_joints] 张量并清零。这样可以消除“字段不存在”,但若 reset 后实际关节位于 HOME 非零角度,内部目标突然变成零,会引入额外的启动瞬态。

本地 BAM 当前实现是在首次控制时把内部目标设为当前关节位置 q,部分环境 reset 时只更新对应环境。迁移时先检查依赖是否已有这套生命周期,不要重复补丁。

建议验证四种情形:

  1. 首次构建多个并行环境,张量形状和设备正确。
  2. 只 reset 环境 0,其余环境状态保持连续。
  3. 非零 HOME reset 后,无人工的目标跳变。
  4. 同一步不重复推进有状态控制器;诊断调用不应额外改变内部状态。

**本节验收:**延迟统一写成毫秒并能追溯到更新步长;控制器在 reset 后没有跨环境串状态或无意的零位瞬态。

7. 61 维观测、14 维动作到底是什么

7.1 Actor 的输入

以下是案例契约,也与当前运行时的基本布局一致。每项仍需对齐单位、坐标系、正方向与顺序。

顺序 内容 维度 主要注意事项
1 躯干角速度 3 rad/s,明确躯干坐标系
2 重力投影 3 与训练一致的坐标系和正负号
3 相对 HOME 的关节位置 14 rad;嘴部排除
4 关节速度 14 rad/s;不能直接填寄存器计数
5 上次动作 14 raw action 还是处理后 action,按训练语义
6 速度命令 vx, vy, wz 3 m/s、m/s、rad/s
7 头颈命令 4 相对 HOME 的目标增量
8 身体位姿命令槽位 6 案例奖励权重为零,保留接口
合计 61

维度正确只证明容器大小正确。例如左右腿顺序对调,仍然是 61 维,但策略行为会完全不同。

7.2 Critic 为什么有 76 维

案例 Critic 额外使用仿真中容易获得的信息:躯干线速度、脚高、腾空时间、触地和接触力等。它帮助价值估计,不要求真机提供同样传感器。

本例组成是:线速度 3、角速度 3、重力 3、关节位置 14、关节速度 14、动作 14、速度命令 3、脚高 2、腾空时间 2、触地 2、接触力 6、头命令 4、身体命令 6,总计 76。

导出时要确认得到的是 Actor 推理链,不能把特权观测误加入部署输入。

7.3 动作是目标位置增量

案例 scale=1.0,使用 HOME/default 作为 offset:

q_target[j] = q_home[j] + action_scale[j] × action[j]

action=0 通常表示回到默认目标,而不是关闭力矩;14 个动作也不是 14 个电流命令。

嘴部的实体舵机仍影响重量和总线映射,但不进入该策略的 14 维输出。正则排除 passive_* 只是候选选择方法,最终应导出并核对实际关节名称列表。

7.4 缩放、裁剪与历史动作必须一起看

动作链建议逐级记录:raw action、缩放后目标、位置限制后目标、速率限制后目标、编码器目标、实际位置。

如果增加了 action clip,要确认训练中的上一动作观测究竟记的是哪个值。训练与运行时一边记 raw、一边记 clipped,就会产生语义差异。

8. 在一个任务里学习站立与行走

案例使用平地速度任务,没有把站立和行走拆成两个网络。

命令类型 案例设置 策略要学的行为
零速度 vx=vy=wz=0 保持支撑,尽量减少移动与转动
前后移动 vx ∈ [-0.4, 0.4] m/s 沿身体前后方向移动
横向移动 vy ∈ [-0.3, 0.3] m/s 左右移动且控制姿态
转向 wz ∈ [-1, 1] rad/s 跟踪偏航角速度
原地转向桶 15% 环境,线速度为零 避免训练数据很少包含纯转向
站立桶 2% 随课程增加到 25% 提高零命令行为的学习机会

“命令为零”不意味着自动拥有优秀的站立策略。还需要足够的零命令样本、合适的姿态与速度奖励,以及站立专项评测。

头部命令有 4 维,并随课程扩大范围;身体位姿 6 维命令在案例中保留输入,但对应 tracking 奖励为零。不能据此宣称已经训练出身体位姿控制能力。

当前任务也没有教随机趴倒后的起身或跌倒恢复。倒下后 reset,是环境给了新的一次机会,不是机器人自己站了起来。

9. 奖励函数:让策略知道什么算做好

9.1 先保留原奖励,再用分项指标决定调整

第一轮保持奖励不变,可以减少同时变化的因素。后续如果 HD 动力学使某种动作明显不可行,再有针对性修改。不要一边换电机、一边改网络、奖励和所有随机化范围,最后无法解释改善来自哪里。

9.2 案例奖励速查

奖励项 案例权重 作用与可能冲突
线速度跟踪 +2.0 跟踪 vx/vy;与其他姿态约束平衡
角速度跟踪 +2.0 跟踪 yaw rate
躯干竖直 +2.0 减少持续倾斜
腿部 pose +1.0 约束姿态;过强可能限制侧移与转向
躯干角速度 -0.05 减少抖动;检查是否也压制目标转向
全身角动量 -0.02 稳定整体运动
关节位置限位 -1.0 减少接近软限位
动作变化率 -0.1 → -1.0 减少目标跳变;过强可能降低反应速度
腾空时间 +3.0 鼓励合适的摆腿时段
脚离地高度 -2.0 目标约 0.02 m
摆脚高度 -0.25 减少拖脚或异常抬腿
脚底滑动 -0.1 减少滑移,但应兼顾 pivot 转向
自碰撞 -1.0 避免结构相撞
头部姿态跟踪 +2.0 跟踪头颈命令
身体位姿跟踪 0 案例中关闭
头部长期偏置 0 → +3 函数值本身为负,正权重仍形成惩罚

权重正负必须结合函数输出看。head_pose_bias 就是一个例子:只看 +3 会误以为它在奖励偏差。

动作变化率通常基于相邻动作之差,目标是平滑。它不直接保证角度合法,也不等于电流、温升或机械冲击已受控。

9.3 不用总 reward 代替能力验收

总 reward 上升,可能来自站得更直或头跟踪更好,而侧移仍然失败。课程改变了权重和采样比例时,不同阶段的 reward 也不一定具有完全相同含义。

评测应直接读取运动结果:速度误差、横向漂移、偏航误差、跌倒、动作饱和。后文会给出统一的评测矩阵。

10. 域随机化与课程:模拟变化,也要证明它生效

10.1 案例真正改了哪些范围

项目 XL330 案例基线 HD 案例 备注
BAM friction scale 0.9–1.1 0.5–1.5 报告记为已启用
armature scale 0.9–1.1 0.6–1.4 报告记为已启用
Kp scale 关闭 包装层写 0.75–1.25 基础 event 未创建,实际未启用
Kd scale 关闭 包装层写 0.8–1.2 同上
电压范围 6.5–8.2 V 6.6–8.2 V 仅案例仿真参数,不是 XL330 上电建议
命令延迟 3–6 步 2–6 步 必须按第 6 节确认时间尺度

扩大范围表示希望策略应对更多变化,但不能保证包含真实系统,更不能把错误控制律变成正确控制律。

本地 FrictionDRBamActuator 主要缩放速度无关的摩擦预算,黏性项保留原值。另一些 BAM 版本的内置 friction scale 定义可能不同。因此“摩擦乘 0.5–1.5”也应注明到底乘了哪些项。

10.2 继承的随机化也要记入实验配置

案例还包含脚底摩擦 0.7–1.3、躯干质量/惯量约 ±5%、躯干和头部质心偏移、编码器偏置 ±0.015 rad、最多 6° 的 IMU 安装失配、输入电压与负载压降、速度推扰等。

startup 与 reset 的含义不同:前者可能在整个训练过程中为每个环境固定抽一次样,后者每回合重采样。并行环境很少时,startup 抽样覆盖尤其有限。

10.3 用采样结果证明启用

对每个随机化项做四步检查:

  1. 配置中是否创建了 event。
  2. event 是否匹配到正确对象和参数。
  3. 执行后实际张量是否变化、范围是否正确。
  4. reset 后是否回到基准再采样,避免不断累乘或累加。

输出最小值、最大值、均值和样本数;少量样本不一定正好碰到范围端点,不要把未达到端点误判为故障。

报告还指出 bearing_roll 实际对应右 hip-yaw link,却被放入 head CoM 列表。保留历史行为有利于复现实验,但下一版应把“历史复现”和“修正 body 列表”的结果分别记录,不能默默改掉后混在同一曲线里。

10.4 课程进度的正确读法

本报告采用 step = iteration × 24,所以:

环境步数 对应 iteration 例子
12,000 500 动作变化率权重开始加强
14,400 600 头部偏置惩罚开始加入
24,000 1,000 多项课程继续扩大
36,000 1,500 动作平滑权重到 -1.0
48,000 2,000 站立比例到 25%

这里不能再乘 4096。4096 影响采集到多少条并行 transition,不一定影响课程管理器使用的公共步计数。

恢复训练时要区分“继续同一课程”与“重启课程”。案例的 MICRODUCK_WARM_START=1 是特定分支的显式行为,不是所有 PPO 框架的通用开关。

11. PPO 在这里如何工作

11.1 一轮训练发生了什么

PPO 反复执行“采样 → 估计哪些动作更好 → 更新网络”。它通过限制一次更新改变策略的程度,提高优化的稳定性;仍然需要合适的环境、奖励和数据。算法来源见 PPO 原始论文。

以案例参数解释:

  1. 4096 个环境各采集 24 个策略步。
  2. 收集观测、动作、奖励、终止状态和价值估计。
  3. 计算回报及优势估计,区分比预期好或差的动作。
  4. 把这批数据分成 4 个 mini-batch,重复学习 5 个 epoch。
  5. 进入下一轮采样,并按保存间隔保留 checkpoint。

因此每轮采样量为 4096 × 24 = 98,304 条 transition。它们是并行仿真经验,不是同一台机器人连续运动 98,304 步。

11.2 案例网络与超参数

配置 案例值 怎样理解
Actor 61 → 512 → 256 → 128 → 14 由观测到关节动作
Critic 76 → 512 → 256 → 128 → 1 估计长期回报
激活 ELU 网络非线性
观测归一化 Actor/Critic 各自使用 推理时保留 Actor 的统计量
动作分布 Gaussian,初始 std 1.0 训练探索;评测通常使用确定性输出
每环境 rollout 24 步 本例对应 0.48 s
epochs / mini-batches 5 / 4 每批采样数据的优化次数与切分
学习率 1e-3,自适应 根据策略变化调整
desired KL 0.01 自适应调节参考
PPO clip 0.2 限制策略更新幅度的一部分
value loss coef 1.0 价值损失权重
entropy coef 0.01 探索项权重
gamma / GAE lambda 0.99 / 0.95 回报与优势估计
梯度范数上限 1.0 梯度裁剪
seed 42 案例一次运行的随机种子
checkpoint 间隔 250 iterations 保留中间策略
最大 iterations 50,000 脚本上限,案例并未跑满

没有 RNN/CNN,案例 symmetry 配置关闭。PPO 的 clip=0.2 是优化目标中的策略比率裁剪,不是关节动作裁剪。 它不能解决输出角度过大的问题。

11.3 为什么第一轮不急着改 PPO

本例只是深拷贝原 RL 配置并改变实验名和 run 名。这样能检验:“在新身体模型下,原配方能否学到有用行为?”

当训练失败时,先排除关节顺序、力矩模型、延迟、接触、终止、奖励异常和配置未生效。只有这些都合理,而优化仍不稳定时,再用受控实验调整学习率、探索、采样长度等。

改变 rollout 或环境数时,要同时检查课程进度、batch 大小和采样效率,不能认为只改了一个性能开关。

12. 从 smoke 到一晚训练:分阶段执行

12.1 先明确命令的适用条件

以下命令分为“案例脚本”和“当前本地可核对的入口格式”。它们用于复现说明,本次写作没有执行训练。运行前应确认位于正确训练仓库,HD 任务已经注册,JSON/MJCF 和依赖版本已经匹配。

先检查帮助:

uv run train Mjlab-Velocity-Flat-HD1910-MicroDuck --help
uv run play Mjlab-Velocity-Flat-HD1910-MicroDuck --help
uv run scripts/export.py Mjlab-Velocity-Flat-HD1910-MicroDuck --help

如果报 Unknown task,先补齐任务适配和注册;不要改用原版任务后还把实验记为 HD。

12.2 阶段 A:配置与模型检查

若取得案例文件,可先执行:

uv run pytest tests/test_hd1910_cfg.py -q

案例报告记录该文件有 3 项测试通过。它们覆盖配置等工程条件,不证明步态已经学好。

除此之外,建议检查:

检查 预期结果
质量变化 15 只计数、总增重 0.045 kg,基于本案例数值
策略接口 Actor 61、action 14,名称顺序一致
执行器加载 实际读取 HD JSON,无 XL330 默认回退
初始姿态 HOME 支撑合理,无严重穿模
时间设置 物理步长、decimation、延迟单位明确
随机化 实际事件列表与采样值符合预期

12.3 阶段 B:小规模 smoke

案例包装脚本:

./scripts/train_hd1910.sh smoke

案例默认 64 环境、5 iterations。其目的只有:环境能构建、能采样、能反向更新并保存结果,且数值正常。5 iterations 不足以判断能否学会走路。

**检查输出:**没有维度错误、内部状态缺失、非有限损失;观察 reset 次数和原因;检查每个环境没有在第一步反复跌倒。

报告提到 CPU/GPU smoke 均成功。读者若在 CPU 失败,应先核查后端支持和依赖,而不是立刻调整 HD 参数。

12.4 阶段 C:中等规模观察行为

先运行足以观察学习趋势的中等实验,例如案例脚本支持的覆盖形式:

GPU_IDS=0 NUM_ENVS=512 MAX_ITERATIONS=1000 ./scripts/train_hd1910.sh train

这组规模是教程建议,不是案例已报告的新结果。环境数按显存和吞吐调整;若减小并行环境,startup 随机化的覆盖也会改变。

重点看:回合长度是否增长、跌倒是否减少、零命令是否能站、前行是否有方向性、输出是否长期饱和。第一轮出现重大异常就定位原因,避免把一个错误配置送去跑整晚。

12.5 阶段 D:长训练

案例默认入口:

./scripts/train_hd1910.sh train

案例默认 4096 环境、最大 50,000 iterations;本次实验约 8 小时停止,保存到 model_16250.pt。这不是“任何 GPU 跑 8 小时都会得到同样结果”的承诺。

脚本使用 CUDA_VISIBLE_DEVICES 配合 --gpu-ids all,用于解决其依赖版本中的参数解析问题。物理 GPU 被筛选后,进程内索引会重新编号;不要同时使用两套互相矛盾的设备编号。

训练期间保留原始日志,并固定间隔做相同条件下的评测。不要只在最后一刻看一个视频。

12.6 暂停、恢复与热启动

继续同一次优化,需要检查:网络、优化器、归一化统计量、iteration、课程状态和随机种子处理是否恢复。环境内部状态是否恢复取决于框架,不能假设 checkpoint 保存了所有仿真状态。

只加载 Actor 权重再重新训练属于另一种实验。重新开始课程也会改变训练分布。把二者分别命名,例如 resume-same-curriculum 与 warmstart-reset-curriculum,记录实际恢复了哪些状态。

**阶段验收:**smoke 通过之后才进入长训练;每次长训练都能找到对应的代码、配置、模型、checkpoint 和评测结果。

13. 怎样理解那一晚的结果

13.1 报告中的训练记录

项目 案例记录 可以说明什么
运行时间 约 8 小时 本次训练投入
保存 checkpoint iteration 16250 存在一个可供评测的候选策略
最终日志位置 约 iteration 16384 与保存点不是同一个概念
最后平均 reward 87.4 在该奖励与统计口径下的训练结果
最好平均 reward 93.026,约 iteration 13817 最新策略不一定具有最高训练 reward
平均回合长度 983.28 / 1000 平均接近时间上限,但不能推出所有回合不跌倒
NaN 报告记录为 0 仍应区分原始异常与防护处理后的统计

报告里其他 tracking metric 和 fall metric 没有完整的聚合定义,因此本文不把这些数值直接转换成成功率或跌倒率。

13.2 分项回放数据

场景 报告结果 教程解读
64 环境 × 10 秒,频繁推扰 4 falls,0 NaN,平均 tilt 5.678° 有抗扰能力,但仍有失败;未定义计数口径前不能直接算 4/64 成功率
32 环境 × 6 秒,无推扰 0 falls;站立 drift 0.0353 m/s,yaw 0.2778 rad/s 短时不倒,同时仍有平移和旋转漂移
同一基准中的行走 xy error 0.2535 m/s,yaw error 1.6179 rad/s 多方向跟踪仍然较弱
8 站立 + 8 前行,5 秒,无推扰 0 falls、0 NaN 支持基础短时站立与前行已形成
定向站立 speed 0.02112 m/s,绝对 yaw 0.16514 rad/s 仍存在漂移,后者约为 9.46°/s
定向前行,目标 0.2 m/s x 绝对误差 0.04501 m/s,横向速度 0.16774 m/s 前向误差约目标的 22.5%,横向速度约为目标的 84%

这些比例是根据报告数字做的算术解释。不能把绝对速度均值乘时间,直接当作真实净位移;机器人可能左右往复,必须积分带符号的世界坐标速度或读取位置轨迹。

13.3 如何准确介绍当前成果

可以这样写实验摘要:

已完成 XL330 到 HD-1910 初步动力学配置的仿真迁移,并沿用原 PPO 配方训练。基础站立和前向移动在短时仿真测试中得到验证;横向漂移、偏航跟踪、扰动鲁棒性和动作饱和仍需改进。执行器与机械参数尚有估计量,真机自由行走未完成验证。

这是可复用的阶段成果:已经证明管线和初步行为可行,同时清楚地留下下一阶段要解决的问题。

14. 建立独立评测,避免“看起来能走”

14.1 固定评测条件

训练环境经常自动切换命令、改变课程并施加推扰。正式评测应显式控制这些条件:

尤其注意报告的 play 配置推扰频率比训练更高。单纯执行 play,不一定得到“无推扰基准”。

14.2 推荐的评测矩阵

下面的时长与次数是下一轮实验建议,不是已经完成的验收。

组别 目标命令 (vx, vy, wz) 每次建议时长 用途
S0 (0, 0, 0) 30 s 站立漂移与转动
F1/F2/F3 (0.05/0.10/0.20, 0, 0) 20 s 不同前进速度
B1 (-0.05, 0, 0) 20 s 后退是否退化
L1/R1 (0, ±0.05, 0) 20 s 低速横移,两侧分别评测
Y1/Y2 (0, 0, ±0.2) 20 s 两个方向原地转向
T0 站立 → 前行 → 停止 30 s 命令切换瞬态与停稳
H0 保持速度,改变头部命令 20 s 头部动作是否破坏支撑
P0 S0/F2 加固定推扰 30 s 在已定义扰动下恢复

每个场景先用至少 3 组固定评测随机种子、每组 10 次独立尝试作为初步比较。它们不是安全认证样本量;关键是让不同候选策略面对同一组条件。另留一组不参与调参的随机种子做最后比较。

14.3 把指标定义写清楚

指标 建议定义
前向 MAE mean(abs(vx_actual - vx_command))
横向 MAE mean(abs(vy_actual - vy_command))
偏航 MAE mean(abs(wz_actual - wz_command))
站立平移速度 mean(sqrt(vx² + vy²))
路径偏移 世界坐标中相对计划路径的偏差;不能只看身体坐标速度
跌倒比例 发生跌倒的尝试数 / 全部尝试数
完成比例 无跌倒且达到预定时长的尝试数 / 全部尝试数
raw action p95/max 每关节分别统计,并报告聚合方式
饱和比例 实际触发某级限幅的样本比例,逐级统计
时序 控制周期均值、p95/p99、最大值及超时次数

跌倒判据应沿用并记录环境的明确条件,如倾角、躯干高度或非法接触。time_out 与跌倒终止分开。报告均值时同时给样本数和分布,避免均值掩盖某个关节或某一方向的失败。

过渡阶段可单独统计,稳态指标可排除预先约定的起始窗口;但被排除窗口内的跌倒和异常仍计入全程失败统计。提前结束的坏轨迹不能因为“数据不足”就不进入汇总。

14.4 一个可独立练习的统计示例

下面只演示单段轨迹的速度误差计算,可在安装 NumPy 的 Python 环境直接运行。输入明确为身体坐标系线速度、同一时间轴的命令,以及 rad/s 的 yaw rate;不能用于替代完整 episode 评测器。

import numpy as np

def tracking_metrics(v_xy, yaw_rate, command):
    v_xy = np.asarray(v_xy, dtype=float)
    yaw_rate = np.asarray(yaw_rate, dtype=float)
    command = np.asarray(command, dtype=float)
    if v_xy.ndim != 2 or v_xy.shape[1] != 2 or len(v_xy) == 0:
        raise ValueError("v_xy must have non-empty shape [T, 2]")
    if yaw_rate.shape != (len(v_xy),) or command.shape != (len(v_xy), 3):
        raise ValueError("yaw_rate/command must share the same T")
    if not all(np.isfinite(x).all() for x in (v_xy, yaw_rate, command)):
        raise ValueError("Non-finite data: record a failed evaluation; do not drop rows")
    error = v_xy - command[:, :2]
    return {
        "vx_mae_mps": float(np.abs(error[:, 0]).mean()),
        "vy_mae_mps": float(np.abs(error[:, 1]).mean()),
        "yaw_mae_radps": float(np.abs(yaw_rate - command[:, 2]).mean()),
    }

result = tracking_metrics(
    [[0.18, 0.02], [0.22, -0.02]],
    [0.01, -0.01],
    [[0.20, 0.0, 0.0], [0.20, 0.0, 0.0]],
)
assert np.isclose(result["vx_mae_mps"], 0.02)
assert np.isclose(result["vy_mae_mps"], 0.02)
assert np.isclose(result["yaw_mae_radps"], 0.01)
print(result)

预期输出三项误差约为 0.02、0.02、0.01。这些是人为构造的小例子,不是 HD 训练结果。真实日志还需要时间戳对齐、不等间隔加权、终止原因、尝试 ID 和完成率统计。

14.5 如何选择 checkpoint

先排除数值异常、严重动作饱和和关键场景失败的候选,再比较站立、前行和下一阶段目标。对当前项目,优先选站立与前行可靠的候选,比追逐一个总 reward 峰值更符合阶段目标。

报告的 reward 峰值在 iteration 13817,但默认每 250 次才保存一次,不保证存在 model_13817.pt。应列出实际文件,选择峰值附近存在的 checkpoint 及最新 checkpoint,在同一矩阵下比较。

15. 导出 ONNX 后,还要验证什么

15.1 保留观测归一化

Actor 训练时看到的往往是归一化观测。只导出网络主体、漏掉统计量,就会使真机把原始数值喂给一个期待另一种尺度的网络。

当前本地 Microduck 导出实现包含 actor(normalizer(obs))。因此应使用对应分支的导出入口,并检查结果;不要因为模型形状正确就认定导出完整。

15.2 导出命令

先进入具有 HD 任务的训练仓库。以下本地 checkpoint 格式已经在当前本地导出参数定义中核对,案例分支执行前仍需看 --help:

TASK_ID='Mjlab-Velocity-Flat-HD1910-MicroDuck'
CHECKPOINT_PATH='/absolute/path/to/model_16250.pt'

uv run scripts/export.py "$TASK_ID" \
  --checkpoint-file "$CHECKPOINT_PATH" \
  --onnx-file hd1910_model_16250.onnx

CHECKPOINT_PATH 是占位路径,必须改为真实文件。案例还提供了 W&B 形式:

WANDB_RUN='entity/project/run_id'
uv run scripts/export.py "$TASK_ID" --wandb-run-path "$WANDB_RUN"

W&B 形式需要真实 run 和访问条件;路径本身不是产物存在的证据。本节不会发布模型。

15.3 第一层:格式、维度和有限输出

在导出文件所在目录、具有 ONNX 依赖的环境执行:

uv run python - <<'PY'
import numpy as np
import onnx
import onnxruntime as ort

path = 'hd1910_model_16250.onnx'
model = onnx.load(path)
onnx.checker.check_model(model)
session = ort.InferenceSession(path, providers=['CPUExecutionProvider'])
inputs, outputs = session.get_inputs(), session.get_outputs()
assert len(inputs) == 1 and inputs[0].shape == [1, 61], inputs
assert len(outputs) == 1 and outputs[0].shape == [1, 14], outputs
x = np.zeros((1, 61), dtype=np.float32)
y = session.run(None, {inputs[0].name: x})[0]
assert np.isfinite(y).all(), y
print('Format and finite-output check passed; this is not a gait test.')
print('Metadata keys:', sorted(p.key for p in model.metadata_props))
PY

零向量只是格式测试输入,不是合法站立观测,也不能拿它控制真机。严格固定 batch 是本案例的部署约定;其他导出形式应按自己的运行时合同核对。

15.4 第二层:PyTorch 与 ONNX 数值一致性

保存一批实际 rollout 的原始观测,分别送入“确定性 Actor + 相同 normalizer”和 ONNX。比较每关节最大绝对误差、均值误差,并覆盖站立、前行和转向样本。

不要一边使用随机采样动作、一边使用均值动作。也不要给 ONNX 预先归一化一次,再由内置 normalizer 归一化第二次。

容差按数据类型和运行时确定;例如先以 1e-5 作为 float32 一致性检查的调查起点,超过时定位原因,而不是把这个值当作任何后端都必须达到的安全标准。

15.5 第三层:用 ONNX 做闭环回放

在同一 HD 动力学和同一观测处理下,用 ONNX 真正闭环控制仿真,再跑第 14 节矩阵。必要时再做独立 MuJoCo CPU 路径比较。

常见错误是训练用了 HD JSON,但回放脚本又加载默认 XL330 BAM。必须在回放日志中打印实际硬件配置及哈希。漂亮的视频无法证明它使用了正确执行器。

导出包建议包含:模型、配置、关节顺序、HOME、action scale、控制频率、执行器参数哈希、训练提交、评测条件和结果。

当前本地限制:manifest 中有些硬件描述字段仅用于展示;不能以为填写 robot.servos=hd1910 就自动获得硬件兼容性拦截。字段作用见本地 docs/policy-manifest.md,应明确区分现有校验与未来希望增加的校验。

16. 横移与转向尚未完成,该怎样改进

16.1 按可定位的顺序排查

顺序 检查 为什么先做
1 命令、实测速度的坐标系和正负号 错误坐标会把正确动作评成错误,也会给错误奖励
2 左右关节顺序、HOME、方向与限位 机械或映射不对称会制造单侧偏移
3 原地转向与纯横移是否被真正采样 随机组合命令不能代替足够的专项样本
4 动作/目标/力矩饱和、速率限制 想转但执行器做不到,与没学会是不同问题
5 脚底摩擦、碰撞、髋关节自由度 接触条件决定转向和侧移是否可行
6 pose、竖直、滑移、动作平滑等奖励冲突 过强约束可能压制有效动作
7 执行器响应、延迟与随机化难度 模型过慢或初期随机化过宽可能妨碍学习
8 PPO 超参数与训练量 前面的条件正确后,才更容易解释优化问题

16.2 用对照实验逐步修正

建议给每轮实验写一句假设,例如:“纯转向样本不足导致原地转向误差偏高。”只修改命令采样比例,其余保持一致,评测时同时检查站立和直行是否退化。

实验 改动 观察重点
E0 原始 HD 案例配置 固定参考点
E1 校正关节、单位、时间或 body 列表等确定性问题 配置正确性与行为变化
E2 增加低速纯横移/纯转向样本 对应 MAE 与跌倒是否改善
E3 针对证据调整一个冲突奖励 是否获得改善且不损害站立
E4 用实测 BAM 替换估计模型 跨参数评测和响应一致性
E5 逐步扩展命令和随机化范围 鲁棒性与其他能力退化

这是实验框架,不是必须严格线性执行的固定配方。确定性实现错误应优先修复;算法或奖励调参应保留对照,并在可能时用多个训练 seed 验证改善不是偶然。

16.3 对动作峰值建立专门记录

案例 raw action p95 约 6.759、最大约 13.37。若这些是单关节绝对输出,且 scale=1 直接映射为 rad 增量,就远超许多正常关节动作的量级;但没有原始统计代码前,不能排除它是向量范数或其他聚合值。

因此先核查统计定义,再逐关节记录:

关节名 → raw p95/max → 目标角度范围 → 位置限幅比例
       → 速率限幅比例 → 电机饱和比例 → 实际角度/速度

需要增加动作或目标限制时,把相同处理引入训练和回放;它改变了策略实际控制的环境,需要重新训练或微调并重新评测。不能只改运行时就认为动作问题已解决。

17. 从估计参数走向实测 BAM

这是下一阶段的方法说明,不包含自动驱动舵机的命令。硬件采集应在型号、电压窗口、接线、限流、机械固定和停止机制确认后进行。

17.1 采集前先定义问题

想确认什么 合适的数据
固件 P 值如何影响响应 多组已知 P 值的小幅阶跃
内部目标是否限速 不同幅度阶跃、斜坡目标
摩擦与反驱特性 正反方向、低速和不同负载轨迹
负载与惯量影响 已知摆长与质量的多组轨迹
电压变化影响 型号允许范围内的多个供电条件
通信与运动延迟 同一时钟的请求、发送、接收和响应时间戳

案例提出 6.6、7.4、8.2 V 三个点,这里仅作为报告计划保留;是否能用于实物,必须先核对完整型号规格。不要为了复制表格而给未知舵机上电,也不要把舵机电源接到 Radxa 的 5 V 输入。

17.2 日志必须包含实验条件

至少保留:舵机身份、固件、台架几何、负载质量、零位、P/I/D、目标序列、实测位置/速度、电压、温度、采样时间和错误计数。可测电流则记录其单位与换算依据。

不同采集工具的时间戳不一定同源。错误对齐会使模型用虚假的惯量或摩擦去补偿通信延迟。

17.3 分训练集与验证集

按完整轨迹或实验条件划分,避免把同一条轨迹的相邻点随机分到两边。先拟合简单模型,检查残差,再决定是否增加负载相关或非线性摩擦项。

验证时关注:角度误差、速度误差、相位差、方向不对称和未见过负载下的表现。残差与速度或方向明显相关时,可以指引下一步模型改进。

参数若长期停在允许范围边界,应检查边界是否不合理、数据是否缺少激励或模型是否漏掉关键机制。具体拟合入口与版本参数见 BAM Fitting 文档。

17.4 避免重复补偿延迟与惯量

如果拟合流程已经估计通信延迟,整机仿真又额外加入同一份延迟,就会重复计算。应区分台架通信延迟、整机通信延迟、固件内部滤波与物理响应,写成同一张时间预算表。

相似地,刚体结构惯量与 BAM 的转子折算惯量应有明确边界,避免对同一质量或转子贡献重复计入。

18. 真机部署需要另一个闭环

18.1 把“能通讯”和“能安全闭环”分开

协议层至少要核查:型号识别、寄存器表、字节序、符号表示、位置分辨率、速度单位、电流单位、工作模式、状态返回级别、ID、零位和方向。

案例写位置单位为 2π/4095,速度也采用对应换算;当前 HL-2915 实现使用自己的位置校准与 0.732 RPM/count 的速度定义。两套数值不能跨型号直接复用。 对 12 位编码器,4095 是最大码值还是整圈刻度分母,也要结合手册和已知角度实验确认。

单舵机验证后,再测试全总线读写周期、丢包、过期数据、断电/重启状态和温升。50 Hz 的周期只有 20 ms;案例串口超时 30 ms,一次超时就可能超过一个策略周期,需要统计异常情况下的周期与处理方式。

18.2 IMU 有两条不同路线

路线 实现方式 需要验证
案例方案 IMU 和舵机都走 Feetech v1,在地址 56 读取长度 15 的块 ID 200 的兼容固件、数据布局与真实时序;案例固件尚未交付
当前本地 HL-2915 代码 v2 读取 IMU,v1 读取舵机 同一物理总线上混合协议的设备行为、时序与容错

所以“必须重写 IMU 固件”是案例架构的前提,不是任何替换舵机方案的必然要求。当前代码的存在也不等于混合协议已经经过整机硬件验证。

18.3 分阶段提高运动权限

阶段 做什么 进入下一阶段前需要的证据
H0 只读身份、寄存器与传感器 型号、单位、状态、IMU 坐标与数据时效正确
H1 单关节固定台架、小幅动作 零位、方向、限位、停止与响应可控
H2 整机支撑下对齐 HOME 和目标链 15 个实体映射正确、14 维策略目标正确
H3 支撑条件下零命令策略 无异常跳变,时序、温升、电流和数据新鲜度符合预设条件
H4 有保护的低速前行 可重复启动、停止和故障退出
H5 在完成前置验证后评估自由行走 多次独立试验记录,而非单段成功视频

各阶段的电流、温度、速度和时间阈值应来自硬件规格及实测,本文不把仿真参数包装为真机阈值。保持 robotd 作为电机写入和安全状态管理的唯一实时入口。

19. 常见问题与定位方法

现象 优先查哪里 下一步
Unknown HD task 任务注册、源码版本、安装路径 打印 registry,确认加载的是当前分支
参数 JSON 找不到 工作目录、绝对路径、产物快照 打印解析后的路径与 SHA-256
JSON 有 HD 数值,运行仍像 XL330 内置参数优先级、加载器初始化 输出实际 actuator 和加载后的参数
q_target_smooth 缺失/形状错误 BAM 版本、构造和 reset 生命周期 检查向量化与部分环境重置
训练第一步就跌倒 HOME、脚接触、动力学、初始状态 先零动作/已知目标回放,观察是否可支撑
episode 很长但不走 命令未生效、奖励冲突、动作饱和 固定前行命令,看实际 vx/vy 和目标链
能前进但斜着走 左右映射、速度坐标、摩擦、采样 分左右/正反方向评测
原地转向时滑得很远 足底接触、滑移奖励、动作约束 看世界坐标轨迹和触地状态
改了随机化范围没有变化 event 未创建、对象名没匹配 输出真正启用的事件和采样张量
reset 后发生冲击 内部目标清零、上次动作、历史观测 对齐 reset 前后全部控制状态
ONNX 能加载但闭环异常 normalizer、关节顺序、HOME、执行器回退 先逐样本数值比较,再闭环回放
无 NaN 但行为突变 防护是否把异常替换成零 单独记录原始 NaN 与替换/终止次数
最新模型反而更差 课程变化、随机性、过度优化、评价偏差 对多个实际保存点运行固定评测矩阵

NaN 防护应阻止异常扩散,也应保留原因。把非有限奖励替换成零以后,日志显示“零 NaN”只能描述处理后的数据,不能证明上游没有异常。

20. 新手按六个练习完成一次学习闭环

这些练习按成果排序,不承诺固定天数。

练习 任务 交付物 完成标准
1:读懂接口 手算 61/76/14 维,画出控制链 一页接口说明 能解释 raw action、HOME 与目标角度
2:整理差异 建立 XL330/HD 参数和来源表 参数台账 每个数字有单位、来源与用途
3:检查身体 检查质量、惯量、关节和碰撞 模型检查记录 15 只计数、45 g 差值、HOME 和碰撞合理
4:运行小实验 原版基线 → HD 配置检查 → smoke 配置与日志快照 能进入 PPO 并解释 reset 与数值状态
5:训练与评测 一组受控训练,多个保存点评测 指标表和全部尝试记录 能区分不跌倒与准确跟踪
6:导出与复盘 ONNX 三层检查,写下一轮假设 模型包与实验结论 结果可追溯,下一轮只改有依据的因素

当前案例已经为练习 4–6 提供了一份参考,但复现者仍要在自己的环境产生证据。

21. 实验记录模板

复制下面的 Markdown,为每次训练单独填写。待填 表示尚未提供证据,不能在汇总中自动算作通过。

# HD-1910 实验记录

- 实验 ID:待填
- 实验目的与单一假设:待填
- 日期 / 操作者:待填
- 训练仓库 commit / dirty:待填
- 源码快照与依赖锁文件:待填
- GPU / Python / Torch / MuJoCo / mjlab / BAM:待填
- 完整舵机型号与规格来源:待填
- MJCF / mesh / BAM JSON 的 SHA-256:待填
- 参数来源等级:估计 / 规格推导 / 台架拟合
- task ID / seed / envs / iterations:待填
- physics dt / decimation / policy Hz:待填
- 执行器延迟步数 / 步单位 / 毫秒值:待填
- 实际启用的随机化及采样记录:待填
- 课程状态及恢复方式:待填
- checkpoint 文件与哈希:待填
- 本次相对基线的唯一或必要改动:待填

## 评测条件
- 确定性输出 / 头命令 / 噪声 / 随机化 / 推扰:待填
- 初始状态 / 时长 / 评测 seed / 尝试次数:待填
- 跌倒定义与 time_out 区分:待填
- 速度坐标系与指标聚合定义:待填

## 结果
| 场景 | 完成/总尝试 | 跌倒尝试 | vx MAE | vy MAE | yaw MAE | 逐关节饱和 | 证据 |
|---|---|---|---|---|---|---|---|
| 站立 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 |
| 前行 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 |
| 横移左/右 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 |
| 转向左/右 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 | 待填 |

- 原始 NaN/Inf / 防护触发次数:待填
- ONNX 格式 / 数值一致性 / 闭环回放:待填
- 原假设:被支持 / 被否定 / 证据不足
- 仍未完成的能力:待填
- 下一轮改动与理由:待填
- 真机状态:未验证 / 已完成的具体阶段与证据

22. 完成度清单与后续顺序

22.1 当前案例的位置

层级 当前结论 依据边界
工程管线 报告记录注册、训练、导出和回放已连通 本次未取得原始分支与产物独立复跑
初步仿真行为 基础站立和前行已验证 短时测试,仍有漂移
完整速度控制 未完成 横移和转向误差仍明显
HD 动力学精度 未完成 质量/惯量近似,执行器含估计参数
真机部署闭环 未完成 通信、IMU、标定、时序、热与运动仍待验证
真机自由行走 未完成 不能由仿真成功直接推出

22.2 下一阶段先做什么

  1. 整理那一晚的完整实验包。 找到实际 HD 分支、依赖、参数、checkpoint 和评测脚本。
  2. 核对确定性问题。 优先解决延迟时间尺度、状态重置、单位换算、关节映射和无效随机化。
  3. 重评现有候选。 用固定矩阵比较已保存的模型,选出当前最可靠的站立/前行基线。
  4. 用数据改进横移与转向。 先确认采样和约束,再做奖励或 PPO 对照实验。
  5. 补实测执行器与机械模型。 在合法电压和受控台架条件下采集,保留验证集。
  6. 完成 ONNX 一致性与 HD 闭环回放后,进入分阶段真机验证。

对当前案例,最值得保留的是独立硬件配置、稳定策略接口和分层证据。这套方法也能用于后续其他替换舵机,但每个型号都要重新确认控制律、参数来源和机械条件。

附录 A:案例差异速查

类别 案例改变 第一轮保留
身体 每只 +3 g、总 +45 g、惯量同比缩放 运动学、HOME、限位、原 mesh
执行器 XL330 M6 → HD 估计 M1 / STS 控制模型 BAM 调用接口
固件增益 训练 Kp 200 → 32;运行时提出 0.16 映射 上层增益调用形式
电压 仿真下界 6.5 → 6.6 V 上界 8.2 V、压降建模思路
延迟 3–6 → 2–6 步 必须核对实际时间单位
随机化 friction 0.5–1.5,armature 0.6–1.4 其他基础事件;Kp/Kd 实际未启用
任务 HD task ID 与 robot entity 61 维 Actor、76 维 Critic、14 动作
PPO 实验名、运行名 网络、学习率、clip、epochs 等基线
部署 Feetech 后端方案与校准 RobotIo 上层、观测和安全控制结构

附录 B:证据来源与版本提示

B.1 用户提供的原始材料

报告描述的 3 项训练配置测试、Rust 测试数量、checkpoint、reward 与回放数据均以“案例记录”方式引用,本次未重新执行或独立确认这些实验结果。

B.2 本次核对的本地文件

以下路径相对当前 /Volumes/dev/dev/microduck 根目录,用于定位版本差异,不表示其他电脑具备相同文件。

文件 核对事项
duck-control/src/obs.rs 61/14 契约、观测布局、上一动作语义
duck-control/src/hl2915.rs HL-2915 单位、校准、v2 IMU + v1 舵机读取
tools/microduck_learning/hl2915_bam_adapter.py HL-2915 专用配置,不能直接当作 HD 参数
bam/bam/feetech/actuator.py STS 内部目标按当前 q 初始化和部分 reset
bam/bam/mjlab.py 自定义 JSON、延迟说明、状态与版本字段
microduck-replica/upstream/microduck_rl/.venv/lib/python3.12/site-packages/mjlab/actuator/actuator.py 当前安装版本的执行器延迟使用物理步
microduck-replica/upstream/microduck_rl/src/mjlab_microduck/actuator/friction_dr_bam.py 摩擦随机化的具体作用项
microduck-replica/upstream/microduck_rl/src/mjlab_microduck/export.py 本地导出参数与 normalizer
microduck-studio/microduck_studio/training_pipeline.py 当前非 XL330 的任务适配门槛
docs/policy-manifest.md manifest 字段的校验与展示边界

B.3 官方与一手参考

在线文档可能继续更新。复现实验依赖锁定的源码与依赖版本;发现本文与运行代码不同,应记录差异并核对实际行为。

什么鸭 WHATDUCK · 复刻学习笔记 ← 全部手册 · 首页