179 lines
18 KiB
Markdown
179 lines
18 KiB
Markdown
# Go2 基础策略迁移(普通训练、自调参、CLI)
|
||
|
||
普通训练和自调参均可选择经服务验证的基础策略。浏览器持续导航不再因20秒截止而停用;训练/三seed评估协议不变。不新增高程图,不声称短训已学会复杂障碍导航。
|
||
|
||
## 前端直接上传单个文件(推荐)
|
||
|
||
已安装训练依赖后,在仓库根目录执行一条默认启动命令:
|
||
|
||
```bash
|
||
.venv/bin/python training_server/server.py
|
||
```
|
||
|
||
1. 打开普通训练面板或自调参工作台,将终端显示的令牌填入访问令牌,连接`http://127.0.0.1:8765`。
|
||
2. 在“基础策略”勾选“确认Go2 legacy47模板”,点击“选择基础策略文件”,选择单个`.pt`或`.onnx`;无需注册JSON、服务器文件路径、YAML或其他sidecars。
|
||
3. 等待上传/验证结束;完成后自动选中该文件的内容ID,检查文件名、格式、原文件SHA及初始化说明。上传中无法启动;可取消等待,同一文件可再次选择重试。
|
||
4. 普通训练选择Flat47或Obstacle81/97,再点击“发起本地训练”;自调参保留逐轮审批/全自动选择,点击“启动自调参”。无DeepSeek配置时必须明确勾选Optuna fallback才能启动自调参;上传本身不调用Agent。
|
||
5. 失败、取消、切换任务或更换连接不会把旧基础策略默默清空;失效/不兼容选择会阻止启动。若要从头训练,明确选择“不选择(随机初始化)”。
|
||
|
||
.pt继承actor及其归一化/探索参数;ONNX只继承确定性推理网络,缺失的count采用合成1,000,000、探索std采用新训练默认1.0,两者critic/optimizer均全新、iteration0。不是恢复原PPO训练。旧管理员注册功能仅为可选高级入口,见下文。
|
||
|
||
### HTTP与安全契约
|
||
|
||
默认启动服务即可上传,**不需要管理员JSON、服务器路径或邻接文件**。旧注册来源仍兼容,见下文“旧CLI/注册来源”。HTTP接口沿用Host/Origin检查及Bearer令牌:
|
||
|
||
`POST /api/training/pretrained-sources/upload?format=pt|onnx&template=go2-legacy47-v1&name=<URL编码显示名>`
|
||
|
||
- body为原始文件字节,`Content-Type: application/octet-stream`,唯一`Content-Length`。不是JSON/base64/multipart/ZIP;普通JSON请求仍限制128KiB。
|
||
- 必须显式确认模板,不能只凭47维shape自动证明物理语义。模板按Go2 FL/FR/RL/RR各hip/thigh/calf解释关节,以及本文legacy47观测、50Hz、PD/default/action_scale。文件缺失的坐标系/噪声等语义来自用户确认,不声称验证了源env.yaml。
|
||
- `.pt`上限256MiB、ONNX上限64MiB;单个上传/解析槽,接收总限60秒、单次阻塞最多10秒;累计快照最多32个/2GiB(包括旧来源),额外预留16MiB派生产物。临时目录由服务随机生成,显示名去路径/控制字符。接收中断/超时/校验失败清理临时文件,不登记可选来源。崩溃遗留临时目录不列入目录且计入容量,需本机维护者清理。
|
||
- CPU子进程解析:60秒墙钟/40秒CPU、8GiB虚拟地址、16MiB输出文件、64文件描述符、无core dump、CPU单线程。`.pt`只用`weights_only=True`,检查actor完整键/shape/dtype/有限值、normalizer var/std/count、探索std及已知嵌入语义metadata;缺actor的ZIP/任意checkpoint拒绝。不是针对本机同账户恶意写入或原生库漏洞的完整沙箱。
|
||
- 单ONNX只支持opset17/18、自包含float32 `[1,47]→[1,12]` 的精确9节点链:Sub、Div、Gemm/ELU/Gemm/ELU/Gemm/ELU/Gemm。校验每条连边、算子属性、initializer、I/O、关节/观测/PD/action metadata,拒绝外部data/custom op/支路/重排/额外算子;随后CPU ORT对48个随机+物理probe对照重建actor(atol=rtol=2e-5)。不承诺任意ONNX可恢复PPO。
|
||
- `.pt`保留原count和探索std;ONNX只有确定性网络/mean/denominator,以已知模板`epsilon=.01`推导`std=denominator-.01>0`及`var=std²`,**合成count=1,000,000**,探索std使用经检查的目标默认1.0。两者critic/optimizer fresh、iteration0;ONNX source_iteration未知,不能称为恢复原.pt训练。
|
||
- 成功201返回与`health.pretrainedSources[]`同形的单个source record。ID为SHA256(`template:format:原文件SHA256`),文件名不参与;同时保存原文件SHA及服务派生`actor.pt` SHA。manifest明确`sourceFormat`、`derived_fields`、`template_confirmation`与`verified_facts`,不把派生artifact伪装成用户原checkpoint。
|
||
- 后续普通job和tuning session继续只传`pretrainedSourceId`,复用原binding/immutable SHA/CLI初始化协议。受控`actor.pt`加`--pretrained-upload-manifest`加载,不扫描邻接文件;81/97首层新增列为零、统计0/1/1且可学习。rung仍只resume自身checkpoint,保存的已更新count不会重置为1e6。
|
||
- 上传来源及manifest原子目录发布、只读快照;无注册参数重启仍能列出。重复上传返回旧record,不改label/artifact或旧job绑定;失效快照保持显式不可用,不退回随机初始化。不要删除仍被历史session引用的快照。
|
||
|
||
两面板共用PretrainedSourceSelect/LocalTrainingClient及上传控件,`accept=".pt,.onnx"`;成功后合并服务返回的已验证record并选择内容ID,连接/任务epoch变化会取消旧请求并忽略晚到响应,不能把旧endpoint结果写入新连接。上传状态显示上传并验证中(非百分比进度);取消只停止浏览器等待,服务可能已经完成验证,可刷新查看。
|
||
|
||
### 本轮真实CPU证据
|
||
|
||
两个真实文件分别在空临时根以单文件上传成功,均未要求或读取相邻`.pt`/ONNX/YAML。双方重建actor对原ONNX的48组eval最大误差均为`1.9073486e-6`。真实ONNX导入及81/97扩展、非零有限梯度、保存/恢复已更新统计通过;不是PPO收敛或安全行走证明。
|
||
|
||
| 来源 | 更新batch | 更新率 | 物理probe最大动作漂移 |
|
||
| ------------------- | -------------: | ---------: | --------------------: |
|
||
| pt,count=983138304 | 4096(UI默认) | 4.16623e-6 | 2.62260e-6 |
|
||
| pt | 16384(上限) | 1.66647e-5 | 1.04904e-5 |
|
||
| onnx,合成count=1e6 | 4096 | .00407929 | .00254631 |
|
||
| onnx | 16384 | .01611989 | .01006627 |
|
||
|
||
以上为一次正常统计更新的实测,后续PPO可能退化,不能用更新前数值等价保证训练稳定性。
|
||
|
||
双面板×真实.pt/ONNX均经独立默认服务空上传根的浏览器文件选择验证。额外CPU真实runner使用4环境:两来源初始化对MuJoCo实际观测的原ONNX最大误差1.19209e-7;ONNX-derived仅运行1次PPO iteration,新增81维输入列更新为非零,导出对runner误差4.76837e-7,同trial恢复完整actor/optimizer且count保持1000096,未重设为1e6。该检查仅证明链路,不声称导航收敛。
|
||
|
||
## 旧CLI/注册来源使用(仍需配套文件;不适用于单文件上传)
|
||
|
||
先激活仓库 `.venv`,安装 `training_server/rl/requirements.txt`。CPU 身份校验依赖 `onnxruntime==1.29.0`。
|
||
|
||
```bash
|
||
# SOURCE 是管理员认可的本地训练产物目录,不是浏览器传入的任意路径。
|
||
python training_server/rl/scripts/train.py Unitree-Go2-ObstacleAvoidance \
|
||
--pretrained-checkpoint "$SOURCE/model_10000.pt" \
|
||
--pretrained-allowed-roots "[\"$SOURCE\"]" \
|
||
--agent.logger tensorboard \
|
||
--output-dir "$OUTPUT"
|
||
```
|
||
|
||
目录必须有 `params/env.yaml`、`params/agent.yaml`、`policy.onnx` 和明确选定的 `.pt`。可用 `--pretrained-onnx` 指定允许根内的配对 ONNX;没有对应 `.pt` 拒绝,不把 ONNX 当成 PPO resume。**不扫描/按 mtime 自动认定 model_10000 对应 ONNX**;当前实际对应关系由 CPU ORT 数值验证建立。
|
||
|
||
- `--pretrained-checkpoint` 与 `--resume-checkpoint` / `--agent.resume` 互斥。
|
||
- warm-start:新 trial 的 iteration=0;只迁移 actor,critic 和 optimizer 保持新初始化。预算仍是新训练预算。
|
||
- resume:既有 runner.load 和 explicit-resume 的 `current+1`、`max_iterations-current` 行为完全不变;同 trial successive-halving 不应再次传 pretrained 参数。
|
||
- 原 Flat legacy47 支持迁移;81/97 支持扩展。Rough 普通训练/原 resume 不受影响,但本轮**不支持从 legacy47 warm-start 到含 height scan 的 Rough**,会明确拒绝。
|
||
- 校验发生在模拟器分配前;创建环境后另核对编译后的 joint 顺序、PD、默认位姿、观测顺序与动作 scale。成功初始化时在输出目录写 `initialization.json`。
|
||
|
||
## 严格迁移契约
|
||
|
||
源 actor 必须是 RSL `MLPModel`:47→512→256→128→12、ELU、经验归一化、Gaussian scalar std。所有 state key、dtype、shape、有限值及 std/var 一致性均验证;没有 `strict=False`。观测语义顺序为:
|
||
|
||
`base_ang_vel(3), projected_gravity(3), command(3), phase(2), joint_pos(12), joint_vel(12), actions(12)`。
|
||
|
||
校验 term 函数名、参数、噪声/缩放/裁剪/延迟/history、动作配置、机器人 spec 名、默认关节状态、actuator/armature/collision 配置、decimation=4 与 dt=.005。观测 corruption 开关和命令采样分布可以因导航任务改变,但基础观测的坐标/单位/顺序不变。phase 为 .6s sin/cos,命令 norm<.1 时清零;本地与源 phase 实现只差尾空行,robot XML 逐字节相同(本次只读比对)。并非宣称训练分布和导航地形完全相同。
|
||
|
||
迁移覆盖:
|
||
|
||
| 参数 | 行为 |
|
||
| ---------------------------------------------------- | ----------------------------------------------- |
|
||
| `mlp.0.weight[:, :47]` | 原值复制 |
|
||
| `mlp.0.weight[:, 47:]` | 零初始化,仍 requires_grad,可由 PPO 更新 |
|
||
| 所有 MLP bias、其余 weight、`distribution.std_param` | 原值复制 |
|
||
| normalizer mean/var/std 前47维及标量 count | 逐值复制 |
|
||
| normalizer 新维 | mean=0、var=1、std=1 |
|
||
| 源 critic/optimizer/iter | 不加载,保持全新目标 critic/optimizer/iteration |
|
||
|
||
归一化策略经批准为 **`preserve-source-count/unit-new-features`**。真实源 count=983138304,RSL 更新率为 batch_size / 更新后 count。清 count 会让首批覆盖旧47统计,故禁止清零。新 ray∈[0,1]、heading∈[-1,1]、distance∈[0,1] 原本有界;使用近 identity 初始化(实际除以 std+.01),继承原算法缓慢更新,**不是完全冻结**。不引入 split normalizer、不改变 checkpoint 格式。
|
||
|
||
初始化等价在 eval / 未更新统计时验证。统计更新后不是要求动作数学上不变,而是必须与原47 actor执行相同统计更新后的动作一致,且变化无突变。48组含分布外随机 gravity 的探针实测最大变化9.06e-5;真实4env首batch变化0。
|
||
|
||
## 可选旧管理员注册来源并在两种面板使用
|
||
|
||
注册JSON由管理员在本机配置,不是HTTP请求。以下直接使用本次用户提供的源目录(只读,不改原文件):
|
||
|
||
```bash
|
||
source .venv/bin/activate
|
||
export SOURCE=/home/cen/Embodied_Workspace/unitree_rl_mjlab/logs/rsl_rl/go2_velocity/2026-08-25_Go2
|
||
export SOURCE_CONFIG=/tmp/go2-pretrained-sources.json
|
||
python - <<'PYCONFIG'
|
||
import json, os
|
||
from pathlib import Path
|
||
root = Path(os.environ["SOURCE"])
|
||
Path(os.environ["SOURCE_CONFIG"]).write_text(json.dumps({
|
||
"allowedRoots": [str(root)],
|
||
"sources": [{"id": "go2-base", "label": "Go2已训练基础行走策略",
|
||
"checkpoint": str(root / "model_10000.pt"),
|
||
"onnx": str(root / "policy.onnx")}]
|
||
}, ensure_ascii=False, indent=2))
|
||
PYCONFIG
|
||
python training_server/server.py --trainer-python "$VIRTUAL_ENV/bin/python" \
|
||
--pretrained-sources "$SOURCE_CONFIG" \
|
||
--tuning-data-root "$HOME/.local/share/mujoco-go2-training"
|
||
```
|
||
|
||
1. 复制服务显示的访问令牌,在普通训练面板连接服务,或在自调参工作台连接同一服务。
|
||
2. 在“基础策略”选择已验证条目,检查`model_10000.pt`及checkpoint/ONNX完整SHA、兼容任务和观测维度。两面板默认不选,保留原随机训练行为。
|
||
3. 选择Flat47或Obstacle81/97;Rough显示不兼容并由服务拒绝。不兼容/失效来源须显式处理,不可静默清空后退回随机初始化。
|
||
4. 普通训练点击“发起本地训练”;自调参选择Approval/Automatic后启动。基础策略不是LLM可调字段;每个新trial(含baseline)使用同一快照与相同session seed的新critic/optimizer初始化协议。后续rung只加载该trial自己的checkpoint,不覆盖成原基础权重。
|
||
|
||
允许根只能来自管理员JSON,不能来自浏览器;最多32注册条目。服务对路径各级使用dir_fd/O_NOFOLLOW,拒绝symlink、穿越、FIFO、目录与超限文件(pt256MiB/ONNX64MiB/env2MiB/agent128KiB)。CPU子进程只使用受限YAML与`torch.load(weights_only=True)`验证形状、语义和ONNX数值身份。未知object/apply、重复键、不匹配配置或缺.pt均拒绝;不逆向ONNX恢复PPO,也不按mtime猜checkpoint。
|
||
|
||
验证后原始四件产物复制到`<tuning-data-root>/pretrained_sources/<source_id>/`内容寻址只读快照。浏览器提交的`pretrainedSourceId`是目录返回的内容SHA身份,不是管理员可读别名或文件路径;同一别名重新注册了不同文件时,旧UI的内容ID会拒绝,必须刷新。注册之后原文件变化不改变已有job/session;每次启动trial都重新核验快照SHA,CLI还核验期望source_id。勿删除有历史session依赖的快照。管理员仍须保护数据根;这是常规文件/反序列化防护,不是任意ONNX计算或同账户恶意写入的完整沙箱。
|
||
|
||
session的SQLite config持久化完整无路径来源描述,旧session无该字段仍走原默认。服务重启会将queued/running/evaluating/paused/awaiting_approval标记interrupted并记录原state,不自动训练或调用Agent;必须用户显式恢复。恢复只清理不完整trial,保留完整评估/来源/约束版本/mode。遗留pending proposal以`service_restart/recovery_invalidated`审计拒绝(非用户拒绝),重新提案后Approval仍需审批;已批准历史不改。来源快照丢失/损坏时恢复排队前拒绝。连续resume请求不会启动两个worker。
|
||
|
||
源manifest/迁移覆盖写入`initialization.json`;source-bound rung要求父checkpoint的来源记录匹配。最佳ONNX的`pretrained_initialization`metadata仅含SHA/source ID、源迭代、normalizer协议与initialIteration=0,不含本地路径或任意checkpoint infos。
|
||
|
||
## 持续点击导航
|
||
|
||
仅Obstacle81/97启用配套地图点击导航,不给普通Flat47增加非同构导航地图模式。浏览器没有20秒交互截止,但跌倒/越界仍安全停止;需用户重置并重新启用。设定新目标不重新加载策略、不改变单inflight/held-action机制,也不会自动启用已停止策略。面板明确显示策略未启用、仿真暂停或安全停止。训练与固定三seed/1000步(20秒)评估不变。
|
||
|
||
真实源warm-start81在WASM/ORT中21.02秒仍启用:从x=-5走到2.566;换目标使command从vx=.598/yaw=-.077变为vx=0/yaw=1。25.04秒仍启用,距新目标从2.828m降到2.243m;无teleport、脚本轨迹或constant actor。此为空旷plane单例,不证明复杂障碍泛化或导航收敛。
|
||
|
||
## 验证
|
||
|
||
```bash
|
||
python -m unittest discover -s training_server/tests -p test_pretrained.py -v
|
||
# 可选真实源;不硬编码用户个人目录到源码:
|
||
GO2_PRETRAINED_SOURCE="$SOURCE" \
|
||
GO2_PRETRAINED_EVIDENCE_DIR="$EVIDENCE" \
|
||
python -m unittest discover -s training_server/tests -p test_pretrained.py -v
|
||
# 可选仅4env×1iteration GPU初始化/优化/导出/重载烟测:
|
||
GO2_PRETRAINED_GPU_SMOKE=1 MUJOCO_GL=egl \
|
||
GO2_PRETRAINED_SOURCE="$SOURCE" GO2_PRETRAINED_EVIDENCE_DIR="$EVIDENCE" \
|
||
python -m unittest discover -s training_server/tests -p test_pretrained.py -v
|
||
```
|
||
|
||
本次真实源:checkpoint SHA `d94eebd23be8b3cc998b493fe056a9e60ceaf4705900320015f30387412a4ffb`;ONNX SHA `80150119e93ce2f656625fc3048ece43ec1281fcdfe9109e07a0157438d0df7c`。
|
||
|
||
- 源 ORT vs checkpoint 48 probe 最大误差 `1.9073486328125e-6`。
|
||
- 47→47/81/97、不同ray/goal:CPU初始化最大误差0;扩展81/97的CPU ONNX导出最大误差 `1.9073486328125e-6`。
|
||
- 真实4env源ONNX vs 扩展actor最大误差 `3.427267074584961e-7`;首batch基础mean变化 `4.190951585769653e-9`,动作变化0。
|
||
- 新列梯度最大 `.0273208`;单iteration PPO后新列weight最大 `.00291905`,critic/optimizer得到更新。
|
||
- 新runner加载保存checkpoint后全部actor张量精确恢复,count=983138404;导出ONNX最大误差 `4.76837158203125e-7`。
|
||
- 安全失败例:越界路径/逃逸symlink、unsafe YAML、未知观测语义/phase/scale/armature、坏shape/NaN/std、错误actor身份、warmstart+resume混用。
|
||
|
||
这些是初始化和优化步骤证据,**不等于导航收敛/到达率验证**。完整命令日志和源manifest在本次交接的 `/tmp/go2-pretrained-core-*/` 工件目录。
|
||
|
||
### 浏览器实产物复核(不再次训练)
|
||
|
||
本次保留的真实warm-start产物为 `/tmp/go2-pretrained-integration-Yqhm9K/train/policy.onnx`,source SHA及新迭代0可从同目录`initialization.json`核对。下列测试需Vite开发服务(动态导入真实PhysicsAdapter);另一个终端启动后运行:
|
||
|
||
```bash
|
||
npm run dev -- --host 127.0.0.1 --port 4174
|
||
# 在另一个终端:
|
||
GO2_PRETRAINED_DEV_URL=http://127.0.0.1:4174 \
|
||
GO2_PRETRAINED_NAV_POLICY=/tmp/go2-pretrained-integration-Yqhm9K/train/policy.onnx \
|
||
npx playwright test -c web_platform/playwright.config.ts web_platform/e2e/pretrainedNavigation.spec.ts
|
||
```
|
||
|
||
原始47维Flat UI兼容测试还可设置`GO2_PRETRAINED_FLAT_POLICY`和`GO2_PRETRAINED_FLAT_XML`。后者必须是带12个有效actuator、采用既有`FL_hip`等绑定名称的Go2模型;裸训练XML本来不含actuator,会明确拒绝。此次`/tmp/go2-pretrained-integration-Yqhm9K/go2-flat.xml`由本地`Entity(get_go2_robot_cfg())`导出,保留源PD/armature,仅把actuator标识改为`actuator.target.removesuffix('_joint')`以匹配既有浏览器命名;没有修改生产模型或为Flat新增点击导航。Flat此测试只证明加载/推理兼容;持续行走/换目标证据来自上述真实81维warm-start产物。
|