四足机器人强化学习实战:从Isaac Gym到RK3566部署全流程
实验室里那台四足机器人从仿真环境里的 Isaac Gym 到地上真实爬行中间隔着的远不止一根网线。我这次要分享的是把一套 25 厘米大小的 Microduck 强化学习机器人从英伟达 GPU 上的仿真训练一步步搬到 RK3566 实机上的完整过程。整个过程踩了不少坑包括 PyTorch 环境从零搭建、GPU 并行训练时的诡异崩溃、模型导出格式不匹配导致实机推理直接挂掉以及 RK3566 开发板在 Linux 下被识别成 ADB 设备而不是网卡这类幺蛾子。这篇手记会把这套流程从训练侧到部署侧完整拆开讲清楚适合正在做足式机器人强化学习部署、或者准备在低成本边缘设备上跑机器人算法的开发者参考。先说结论整个链路比想象中长但每个环节的坑都有迹可循。我用的是 Microduck 这个开源项目主控是 RK3566训练侧先用英伟达 GPU 跑仿真训练完的策略网络导出成 RKNN 格式部署到实机。这个项目最核心的卖点在于“低成本”和“端到端”——硬件成本控制在几百块算法层面从状态输入到动作输出是一个完整的神经网络不像传统 PID 那样需要手调一大堆参数。接下来我把从零开始到实机跑通的细节全部摊开讲。1. 先弄明白 Microduck 是什么以及为什么选 RK35661.1 项目核心拆解25 厘米机器人 强化学习Microduck 是一个开源的四足机器人项目机器人本体尺寸大约 25 厘米整机重量很轻采用 12 个舵机驱动四条腿。它的特殊之处在于——运动控制完全由强化学习策略网络生成而不是传统的运动学求解加 PID 跟踪。整个项目的软件栈分成三块仿真训练侧、模型导出侧、实机推理侧。训练侧跑在英伟达 GPU 上用 Isaac Gym后来迁移到 Isaac Lab做并行仿真训练一个 MLP 策略网络输入是机器人本体状态关节角度、角速度、朝向等输出是 12 个舵机的目标角度。模型导出侧把 PyTorch 的.pt权重转换成 RKNN 格式瑞芯微的 NPU 格式。实机推理侧跑在 RK3566 上加载 RKNN 模型以 100Hz 的频率做状态采集-推理-动作输出的闭环控制。为什么选 RK3566 而不是树莓派或者 Jetson Nano核心是成本、功耗和生态的三点妥协。RK3566 有 1 TOPS 的 NPU 算力专门做神经网络推理跑一个 3 层 MLP、单次推理 1ms 左右买一块核心板价格在百元级。树莓派没 NPU推理全靠 CPU虽然这个 MLP 也不大、CPU 跑也能到 100Hz但 NPU 的功耗优势更明显整板功耗控制在 2W 以内这对小体积机器人很关键。从实际体验看Microduck 最大的意义在于它把强化学习足式机器人的门槛拉到了一个大学生实验室就能复现的量级。不需要 MIT 那台 mini-cheetah 几十万的硬件成本也不需要专业的运动捕捉场地在普通办公室地板上就能验证算法。1.2 部署链路的五段式结构我把整条部署链路拆成五段训练侧环境Ubuntu 20.04/22.04 NVIDIA GPU CUDA PyTorch跑 Isaac Gym 仿真策略训练PPO 算法训练输出.pt权重文件模型导出.pt→ ONNX → RKNN这个环节最容易出问题实机推理端RK3566 开发板 Ubuntu 系统加载 RKNN 模型做实时控制联调状态估计、舵机控制、通信时序对齐这个五段式链路中每一段都有自己的知识门槛。训练侧要懂强化学习算法和仿真环境搭建导出环节要懂 ONNX 算子和 RKNN 的算子映射关系实机侧要懂嵌入式 Linux、设备树、舵机控制协议。任何一个环节卡住整个项目就停滞不前。我的经验是先不追求一套跑通而是分阶段验证比如先单独跑通仿真训练出权重再单独在 RK3566 上跑一个随机权重模型验证推理链路最后再做整机闭环。我在实际部署过程中最耗时的不是训练模型反而是环境搭建和模型格式转换这两个看起来“不核心”的环节。后面会把每个环节的踩坑点都列出来。2. GPU 训练侧环境搭建PyTorch 与多卡并行的细节2.1 训练机的完整软件栈配置先说我的训练机配置双路 RTX 309064GB 内存Ubuntu 20.04 LTS。训练所需的核心软件栈是 CUDA 11.7、PyTorch 1.13配套 Isaac Gym 的版本要求、Isaac Gym Preview 4以及 Microduck 官方训练仓库里的 legged_gym 依赖。强调一下版本匹配的重要性。Microduck 的训练仓库用的是 legged_gym这个框架对 Isaac Gym 的版本依赖非常严格。我用 CUDA 11.7 PyTorch 1.13 的组合实测下来能稳定跑通。如果你用更新的 PyTorch 2.xIsaac Gym 的 Python 绑定会直接报错根本起不来。这一点在 Microduck 的 GitHub issues 里被反复提及但很多新手还是在这里卡住。# 我的环境实测可用的安装顺序 conda create -n microduck python3.8 conda activate microduck # 安装 PyTorch 1.13 CUDA 11.7 pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117 # 安装 Isaac Gym # 从 Nvidia 官网下载 Isaac Gym Preview 4然后 pip install -e isaacgym/python # 安装 legged_gymMicroduck 的复现版本 git clone https://github.com/microduck-robot/microduck_train.git cd microduck_train pip install -r requirements.txt安装完成后先用 Isaac Gym 自带的示例环境验证 GPU 环境是否正常python isaacgym/python/examples/joystick.py如果这个示例能正常打开窗口并看到机器人模型说明 Isaac Gym 和 PyTorch 的 GPU 链路是通的。这里有个新手常见误区torch.cuda.is_available()返回 True 不代表 Isaac Gym 能用因为 Isaac Gym 是通过 PhysX 引擎直接调用 GPU和 PyTorch 的 CUDA 是两条独立的依赖链。所以验证环境必须用 Isaac Gym 自带的示例而不是简单的 PyTorch 张量测试。2.2 三个 GPU 同时测试并行训练配置与 crash 排查我这台机器有三块 GPU两块 3090 加一块 2080 Ti但 Isaac Gym 默认只会用CUDA_VISIBLE_DEVICES0指定的那块卡。如果你想并行跑多个训练任务或者用多卡训练需要特别注意两点。第一Isaac Gym 本身不支持数据并行DP/DDP它只在单卡上跑仿真。所谓“多卡”实际上是在多张卡上分别跑不同的随机种子或不同任务。我实测的做法是用CUDA_VISIBLE_DEVICES环境变量隔离多卡同时开两个训练进程# 终端 1: GPU 0 上训练 CUDA_VISIBLE_DEVICES0 python legged_gym/scripts/train.py --taskmicroduck # 终端 2: GPU 1 上训练不同配置 CUDA_VISIBLE_DEVICES1 python legged_gym/scripts/train.py --taskmicroduck --seed42第二多卡同时训时最容易踩的坑是 GPU crash dump triggered。我用三卡同时训练时经常在训练开始后几分钟看到终端输出GPU crash dump triggered然后进程卡死。这个报错的根因大概率是显存分配不均或 NCCL 通信冲突。我排查后的解决方案是每张卡分配的任务负载差异不要太大并且关闭 PyTorch 的自动混合精度AMP——Isaac Gym 对 AMP 的支持一直有 bug。另外检查一下是否是电源供电问题——三卡满载时瞬间功耗可能突破电源上限。如果在机房或实验室用服务器供电通常会好一些但如果是个人工作站建议先限制功耗墙# 限制每张 GPU 的最大功耗避免同时满载触发 crash nvidia-smi -i 0 -pl 250 nvidia-smi -i 1 -pl 250 nvidia-smi -i 2 -pl 2502.3 为什么强化学习的训练时间没有想象中长Microduck 的策略网络其实很小总共约 15 万参数一个 MLP输入 48 维 → 256 → 256 → 输出 12 维。这个网络规模在 GPU 上训练收敛非常快。我的实测数据单块 RTX 3090、4096 个并行环境、跑了大约 8000 轮iterations总耗时 40 分钟左右就能得到一个能稳定行走的策略。相比之下大语言模型动辄几周的训练时间这个效率是 Microduck 这类小型机器人项目能被广泛复现的重要原因。整个训练的核心计算量在于物理仿真——Isaac Gym 用 GPU 并行模拟 4096 个机器人同时学习这是传统 CPU 仿真完全做不到的。所以不是“算法压缩了时间”而是“并行仿真扩展了速度”。很多人第一次跑的时候会发现训练了十几分钟 reward 还在涨但期望它像教程里那样在半个小时内收敛是不可能的。因为收敛速度取决于随机种子、学习率、batch size 等多个因素。我的经验是先用官方的默认超参数跑通一次再去调参优化不要一上来就改一堆参数否则大概率训练不收敛还找不到原因。3. 训练细节与策略蒸馏PPO、IQL 与奖励设计3.1 PPO 算法在低成本机器人上的实际表现Microduck 官方训练默认用的是 PPOProximal Policy Optimization这也是机器人强化学习领域最常用的 baseline 算法。PPO 的核心逻辑是通过裁剪clip限制策略更新的幅度避免一次更新过大导致策略崩溃。在 Microduck 的 context 里PPO 的输入状态包括本体角速度、朝向、关节角度和角速度、上一步动作、以及一个可选的“速度指令”向量表示机器人应该往哪个方向走多快。输出是一个 12 维向量每一维对应一条腿上的舵机目标角度。这里的动作空间是连续的范围在 -1 到 1 之间控制代码会把 [-1, 1] 映射到实际的舵机角度范围。我在训练时试过调大 batch size 和 PPO clip 参数从默认的 0.2 改成 0.3发现收敛更快但稳定性变差偶尔会出现策略突然退化的情况。单次实验比较难说哪个组合最优但工程上的建议是如果你不是在做算法研究用默认参数就行要调参的话一次只动一个变量并记录每组参数的训练曲线方便对比。3.2 IQL 离线强化学习是备选路线如果你看 Microduck 的 GitHub 讨论会发现有人提到 IQLImplicit Q-Learning离线强化学习路线。这跟 PPO 是两种完全不同的训练范式。PPO 是在线学习策略边探索边学需要大量仿真交互IQL 是离线学习只用预先收集好的数据集训练不再与环境交互。IQL 在 Microduck 这类低成本机器人上的意义在于如果你不想跑仿真而是想让机器人模仿一段人工遥控行走的数据IQL 能直接从这些离线数据中学出一个策略。但实话说IQL 要真正跑到实机可用的水平对数据质量的要求很高——需要覆盖各种扰动、各种地形否则学出来的策略泛化能力很弱。我的建议是先跑通 PPO 再考虑 IQL毕竟 PPO 在仿真里可以无限探索成本接近零。3.3 奖励设计里的“错误奖励”陷阱训练阶段最隐晦的问题就是“强化学习遇到错误奖励”——奖励信号设计不当导致策略学会了投机取巧。我在 Microduck 训练初期就遇到过机器人学会了“原地抖动”而不是“行走”因为原地抖动时关节速度很大躯干保持水平能拿到很高的“存活奖励”但前进距离几乎是零。这类问题排查起来很头疼因为训练曲线显示 reward 在涨但是仿真里机器人根本没在走路。我的排查方法是打开训练日志里的episode_rew和mean_torque两个指标——如果episode_rew在涨但mean_torque异常高大概率是策略在“抖动刷分”。另一个更直接的方法是每隔几百轮把当前策略导出一次在仿真里跑一下看看实际表现。不要只盯 reward 曲线一定要看行为这是仿真训练和真实训练最大的区别。关于奖励权重Microduck 默认配置里前进速度奖励占大头其次是存活奖励和关节力矩惩罚。具体权重我不建议大改如果你改了前进速度的权重可能会收获一个“绕圈”机器人——策略发现原地打转也能拿前进奖励因为前进方向是机器人自身坐标系转圈时也在前进。这类 reward hacking 问题在足式机器人里非常常见。4. 模型导出从 PyTorch 到 RKNN 的工程化细节4.1 导出链路PyTorch → ONNX → RKNN训练完得到的是 PyTorch 的.pt权重文件。但 RK3566 上的 RKNN 运行时认的不是 PyTorch 格式而是 RKNN 格式。这中间需要先用 PyTorch 导出成 ONNX再用 RKNN-Toolkit2 把 ONNX 转成 RKNN。# step 1: 导出 ONNX import torch from policies.actor_critic import ActorCritic # 加载训练好的权重 model ActorCritic(num_obs48, num_actions12) checkpoint torch.load(model_8000.pt, map_locationcpu) model.load_state_dict(checkpoint[model_state_dict]) model.eval() # 构造 dummy input对应状态向量维度 dummy_input torch.randn(1, 48) torch.onnx.export( model.actor, dummy_input, microduck_actor.onnx, opset_version11, input_names[obs], output_names[actions] )这里我把model.actor策略网络单独导出不导出 critic价值网络因为实机推理只需要 actor 来输出动作。很多第一次做导出的同学会把整个 ActorCritic 模型导出结果 ONNX 里包含两个输出头转 RKNN 时算子映射报错。导出 ONNX 后用onnx-simplifier做一次简化。这一步很关键因为 PyTorch 导出的 ONNX 图里可能包含很多冗余的 reshape/transpose 算子这些算子在 RKNN 转换时可能导致不支持。简化后的 ONNX 能明显提高 RKNN 转换成功率。python -m onnxsim microduck_actor.onnx microduck_actor_sim.onnx4.2 RKNN-Toolkit2 转换流程与量化选择然后是用 RKNN-Toolkit2 把 ONNX 转成 RKNN。这里有一个核心决策做不做量化quantizationRK3566 的 NPU 支持 INT8 和 INT16 两种量化推理。INT8 速度最快但精度损失明显INT16 精度好一些但速度略慢。我的实测不量化FP16在 RK3566 的 NPU 上跑这个小 MLP单次推理约 1.8msINT8 量化后约 0.9ms。两种方案都满足 100Hz 控制频率10ms 周期的需求所以我建议用 FP16 或不做量化省掉量化校准数据集的麻烦。如果你非要量化需要准备一组“校准数据集”——从仿真训练时采样的状态数据中抽几百条喂给转换器做激活值范围统计。这块的不确定性较多我的经验是如果模型只有 3 层全连接量化掉点非常小可以放心用但如果加了 BatchNorm 层量化后可能会跳变。好在 Microduck 的策略网络是 MLP 不带 BN量化风险低。# RKNN 转换脚本参考 rknn-toolkit2 的 API from rknn.api import RKNN rknn RKNN() rknn.config(mean_values[[0, 0, 0]], std_values[[1, 1, 1]], target_platformrk3566) rknn.load_onnx(modelmicroduck_actor_sim.onnx) rknn.build(do_quantizationFalse) # 或 True rknn.export_rknn(microduck_actor.rknn)4.3 导出过程中最容易踩的三个算子坑第一个是Gather算子。PyTorch 导出的 ONNX 里如果有torch.gather相关操作一般是索引某个向量RKNN-Toolkit2 可能不支持或转换效率极低。解决方式是在 PyTorch 层改用torch.where或矩阵乘法替代。第二个是Slice算子在某些版本下的 bug。它的表现是转换成功但推理结果全是 0。排查方式是用 RKNN-Toolkit2 自带的模拟器做输入输出对比如果模拟器结果正确但板子结果错误大概率是算子版本兼容问题导致的。第三个是动态维度的问题。RKNN 对动态 shape 支持很弱ONNX 导出时一定要固定 batch size 为 1输入维度写死(1, 48)不要用dynamic_axes。5. RK3566 实机部署系统、识别与推理5.1 开发板选型与系统烧录我用的是一块 RK3566 核心板加底板方案。市面上的 RK3566 开发板形态很多有树莓派尺寸的单板电脑也有核心板加扩展底板的模块化方案。Microduck 的 GitHub 上推荐的是某个国产“泰山派”开发板——就是热词里提到的“泰山派识别到 rk3566 但是是 adb 设备”那个问题。这块板子用的是瑞芯微的 SoC官方支持 Debian/Ubuntu 系统也有 Android 系统。在系统选择上我的建议是使用 Ubuntu 20.04RK3566 的 BSP 里叫rk3566-ubuntu-focal镜像。烧录工具用瑞芯微的RKDevToolWindows 端或者 Linux 下的upgrade_tool。烧录过程比较关键的一步是让板子进入Loader 模式按住板上的 recovery 键再上电然后 USB 连接电脑这样才能被烧录工具识别。5.2 “识别到 RK3566 但是是 ADB 设备”问题全解这个热词这两天几乎刷屏了整个 Microduck 社区。很多人在 Linux 下用lsusb能看到瑞芯微设备了但系统把它识别成了 ADB 设备Android Debug Bridge导致无法用 adb 或 ssh 正常进入系统。这个问题出现的根本原因是板子出厂烧录的是 Android 系统或者 u-boot 里默认开启了 ADB 编译选项。RK3566 的 ROM 里包含了一个内置的 ADB 守护进程在 Android 模式下它会枚举成一个2207:0006的 USB 设备ADB interface。我的排查步骤确认板子的系统是不是 Android。如果是需要重新烧录 Ubuntu 镜像。如果已经烧了 Ubuntu 但 lsusb 还是显示 ADB 设备检查 u-boot 编译配置开发板 SDK 的BoardConfig里如果export RK_ADB_ENABLE1会导致 u-boot 阶段启用 ADB 功能需要改为 0 重新编译烧录。更省事的方案不折腾 USB 连接直接用串口UART接开发板的调试串口通过串口登录系统后把 USB 的 gadget 模式从 adb 改成 rndis网卡或者关闭。# 在板子的串口终端里执行关闭 ADB开启 RNDIS 网卡模式 # 具体路径以你使用的 SDK 为准 echo 0 /sys/class/android_usb/android0/enable echo rndis /sys/class/android_usb/android0/functions echo 1 /sys/class/android_usb/android0/enable这个操作在 Microduck 的部署文档里没有写清楚我是翻了瑞芯微的 kernel 源码才找到的。如果你遇到这个问题我建议先用串口救急再彻底用新镜像重烧解决。5.3 RK3566 上运行 RKNN 模型的完整代码系统就绪后部署 RKNN 推理程序到板子上的流程如下# 把 RKNN 模型和推理脚本拷贝到板子 scp microduck_actor.rknn rk3566ip:/home/rk3566/microduck/ scp rknn_infer.py rk3566ip:/home/rk3566/microduck/推理脚本的核心部分# rknn_infer.py from rknnlite.api import RKNNLite import numpy as np # 初始化 RKNN rknn RKNNLite() ret rknn.load_rknn(microduck_actor.rknn) rknn.init_runtime() # 加载一次模型后续推理循环直接调用 state np.random.randn(1, 48).astype(np.float16) outputs rknn.inference(inputs[state]) action outputs[0].flatten()注意这里我用的是rknnlite而不是rknn。RK3566 上运行时用的是 RKNNLite它是 RKNN API 的轻量版专门用于部署推理。如果你在板子上加载rknn包会报错“module not found”实际应该用rknnlite。整个推理循环的核心是控制频率。Microduck 实机代码用的是一个 100Hz 的定时器每个周期做三件事读 IMU 和关节编码器 → 组装状态向量 → 推理得到动作 → 通过串口/舵机控制板下发角度。我实测每次推理 1ms 以内在 10ms 的控制周期里完全够用剩余时间可以做一些状态估计和异常检测。5.4 NPU 与 CPU 的任务分配经验RK3566 的 CPU 是 4 核 Cortex-A55NPU 是 1 TOPS。实际部署时我建议把策略推理放在 NPU 上跑而把舵机控制、IMU 读取、通信协议这些“硬实时”逻辑放在 CPU 上跑。我用taskset把推理进程绑定到 CPU 2/3 上控制进程绑定到 CPU 0/1 上避免调度器来回切换导致抖帧# 在板子上分配 CPU 核心 taskset -c 2,3 python rknn_infer.py taskset -c 0,1 python control_loop.py 关于 IMU 的数据频率我用的 IMU 是 200Hz 输出控制循环是 100Hz所以每两帧 IMU 做一次平均合成一帧状态向量。这里千万别直接把 200Hz 的数据全部塞进模型否则状态分布和仿真训练时的不一致推理出的动作会抖。6. 真机调试从仿真到现实Sim-to-Real的关键跳变6.1 仿真参数与实机参数的一致性训练好的模型拿到实机上第一步测试相当刺激——你可能看到一个“站起来→立刻乱抖→摔倒”的过程。这很正常因为仿真和现实之间的 gap 是强化学习部署永远绕不开的问题。我遇到的第一个直接问题仿真里用的关节阻尼系数和实机舵机的实际阻尼差距太大。仿真里机器人关节阻尼很小、响应快实机舵机是有刷电机加减速齿轮阻尼大、响应慢。导致模型在仿真里能走出稳定步态到实机上输出剧烈摆动却跟不上。解决方式是在仿真配置里调大关节阻尼系数参考实机舵机的硬件参数重新校准。Microduck 的 train 配置文件microduck.yaml里有stiffness和damping两个参数把它们改成与实机舵机接近的数值重新训练一轮实机表现会有明显提升。6.2 强化学习也需要 PID 支持混合控制策略这里要澄清一个常见的误解不是说用了强化学习就不需要 PID 了。Microduck 的实机代码里强化学习网络输出的实际上是“目标关节角度”而不是直接输出 PWM 值。真正驱动舵机达到目标角度的是底层舵机自身的 PID 控制器或者主控里的位置环 PID。这其实是分层控制架构强化学习负责“上层决策”——决定每个关节应该转到什么角度PID 负责“底层执行”——快速准确的跟踪这个角度。两者不是替代关系而是合作关系。我在调试中把舵机的 PID 参数做了如下调整P 值调大响应更快D 值适当调小减少高频抖动。具体数值因舵机型号而异但方向是一致的。6.3 真机测试中最容易忽视的机械细节机械结构对部署成败的影响往往比软件大得多。我实机测试时踩过一个很蠢的坑四条腿的舵机零位没校准导致机器人在仿真里站得很稳到实机上站起来的瞬间就失衡摔倒。解决方式是给每个舵机加一个“零位校准”流程——上电后先把所有关节归零确认四条腿在同一平面再启动推理控制。另外一个是舵机供电问题。12 个舵机同时动作时瞬间电流很大用小功率电源会出现电压跌落导致主控重启。我这里用的是 2S 锂电池加 BEC 降压模块给舵机单独供电主控用独立 5V彻底隔离电源干扰后整机运行稳定多了。6.4 实机跑通的验收标准最后分享一下我做实机部署的验收标准这样你可以判断“部署成功了”到底是什么意思机器人上电后姿态稳定不抖不歪在平整地面上能以约 0.3 m/s 速度直线行走 10 米不掉线用手轻轻推一下机器人侧面它能恢复平衡继续走系统连续运行 5 分钟无死机、无舵机过热报警如果以上四点都满足那这套从 GPU 到 RK3566 的部署就算是基本成功了。后续可以优化的是步态质量、地形适应性、以及用 IQL 做更复杂的行为模仿。7. 常见问题速查表与最终经验总结阶段问题现象根因解决方案GPU 环境Isaac Gym 示例打不开窗口图形库缺失或驱动版本不对安装 libgl1、libegl1确认 nvidia-smi 正常GPU 环境三卡同时训练触发 GPU crash dump供电不足或 AMP 兼容问题限制功耗关闭 AMP错峰启动训练训练Reward 涨但机器人没在走奖励设计错误策略在刷分查看行为而非曲线检查 mean_torque 指标导出ONNX 转 RKNN 失败不支持的算子或动态维度用 onnx-simplifier 简化固定 batch size导出转换成功但推理结果全 0Slice 算子兼容问题换算子实现或升级 RKNN-Toolkit2 版本部署板子被识别为 ADB 设备固件默认启用了 ADB串口救急改 rndis 模式或重烧 Ubuntu 镜像实机机器人站起来就倒仿真与实机动态参数 gap校准阻尼、stiffness重新训练实机动作剧烈抖动舵机 PID 参数不合适调大 P、调小 D降低响应超调实机主控偶发重启舵机供电不足导致电压跌落独立电源给舵机供电与主控隔离照着我这份速查表能解决大部分问题剩下的一些偏门问题需要你去翻社区和源码。说实话从英伟达 GPU 到 RK3566 实机这条链路中每一步单独拎出来都不算难但串在一起就变成了一个系统性工程。训练侧的强化学习知识、导出侧的模型转换经验、部署侧的嵌入式优化任何一个环节的短板都会被成倍放大。我做这个项目的第三个晚上一边开会一边在电脑前重启 RK3566那股挫败感我现在还记得。但也就是这几次折腾下来我对强化学习从“仿真玩具”到“真实世界”的整个流通链条有了很具体的体感——这是光看论文完全得不到的。如果你也在复现 Microduck卡住的时候别急躁一个一个环节排查总有跑通的一天。最后一个小技巧把每轮训练好的模型都存下来标注上“第几轮 奖励值”别只留一个 final 模型。我在实机测试时就发现最后一轮训练出的模型不一定是实机表现最好的反而是某个中间轮次、reward 稍低一点的模型在实机上更稳定。这个经验可能对你也适用毕竟仿真里最好的不一定是现实中最稳的。