资讯动态

强化学习实操:microduck四足机器人仿真训练与ONNX部署全链路

发布时间:2026/10/29 17:09:57 来源:尧图企业网站定制
强化学习这个词听起来唬人但真正动手跑通第一个环境的人都知道最折磨人的往往不是算法推导而是环境装不上、模型导不出、仿真跑不起来这些脏活。我这次拿microduck做切入从零把一条完整的强化学习实操链路走了一遍——环境搭建、仿真调试、策略训练、模型导出、推理部署每一步都踩了坑也填了坑。microduck本身是个轻量级的四足机器人仿真项目结构清晰、依赖干净非常适合作为强化学习的入门载体。整条链路涉及mujoco物理引擎、PyTorch训练框架、ONNX模型导出与量化、以及最终的推理部署覆盖了从仿真到落地的核心环节。不管你是刚接触强化学习的新手还是想把手上的模型从实验室推到实际设备上的开发者这篇内容都能给你一条可复现的路径。1. 为什么选microduck作为强化学习的第一个实操项目1.1 四足机器人仿真在强化学习入门中的独特位置很多人入门强化学习的第一反应是CartPole或者LunarLander这类经典控制环境。这些环境确实简单几十行代码就能跑起来但它们有个共同问题状态空间和动作空间都太抽象了你很难把学到的东西和真实世界建立联系。而四足机器人不一样它的状态是关节角度、角速度、躯干姿态动作是关节力矩这些东西你在真实机器人上都能一一对应。microduck作为一个四足机器人仿真项目恰好卡在足够简单能跑通和足够真实有迁移价值之间。我自己的体会是用CartPole学强化学习你学的是算法本身用microduck学强化学习你学的是怎么让算法在物理世界里工作。后者的价值大得多因为真实项目里让你头疼的从来不是PPO的损失函数怎么写而是仿真步长设多少、奖励函数怎么设计、观测噪声怎么加、训练出来的策略能不能扛住模型误差。microduck把这些真实问题都摆在你面前但又不至于像完整的人形机器人那样复杂到劝退。另外从计算资源角度看microduck的模型规模适中在单张消费级显卡上就能完成训练。我实测下来用一块RTX 3060大概四到六个小时能训练出一个能稳定行走的策略。这个时间成本对于个人开发者来说是可以接受的不会出现跑了一周还没收敛的挫败感。1.2 microduck与mujoco的配合逻辑microduck的仿真后端用的是mujoco物理引擎。mujoco在机器人仿真领域的地位不用多说它的接触求解器和约束求解器精度很高四足机器人那种频繁的足地接触场景用mujoco跑出来的结果和真实世界的差距相对较小。但mujoco也有它的脾气安装配置这块坑不少后面我会专门讲。microduck的模型文件是标准的MJCF格式MuJoCo XML这意味着你可以直接修改XML里的几何体、关节、执行器参数不需要重新编译任何东西。这个特性在调试阶段特别有用——比如你发现机器人走路时前腿总是打滑可以直接在XML里调摩擦系数改完存盘重新加载就行。相比之下有些仿真环境改个参数要重新编译整个项目调试效率差很多。还有一点值得说microduck的观测空间和动作空间定义得很干净。观测包括躯干高度、姿态四元数、各关节角度和角速度、上一帧的动作动作就是各关节的目标角度。这种设计让奖励函数的编写变得直观——你可以直接根据躯干高度和姿态算奖励不需要做什么复杂的坐标变换。对于刚入门的人来说这种所见即所得的设计能大幅降低理解成本。1.3 从microduck到真实部署的路径可行性选microduck还有一个考虑它的策略网络规模小导出ONNX之后模型文件通常只有几MB量化成int8之后能压到1MB以内。这意味着你训练出来的策略完全有可能部署到算力有限的嵌入式设备上。我这次实操的完整链路就是microduck仿真训练 → PyTorch策略网络 → ONNX导出 → int8量化 → 推理引擎加载。这条路径走通之后换成其他机器人平台流程基本一致只是观测和动作的维度需要调整。2. 环境搭建mujoco安装的坑与microduck依赖梳理2.1 mujoco安装常见问题与版本选择mujoco从2.3版本开始开源安装方式发生了很大变化。网上很多教程还是老版本的安装方法照着做大概率会报错。我建议直接用pip安装pip install mujoco当前稳定版本是3.x系列API和2.x有一些不兼容的地方。microduck的代码如果写的是老版本API你需要确认一下兼容性。我这次用的是mujoco 3.1.0配合Python 3.10实测稳定。安装过程中最常见的问题是缺少系统依赖。在Ubuntu上你需要确保这些包已经装好sudo apt-get install libgl1-mesa-glx libglew-dev libosmesa6-devWindows上相对简单pip安装完基本就能用但如果要做无头渲染比如在服务器上训练需要额外配置EGL或者OSMesa。我一开始在Windows 11上跑渲染窗口正常但后来迁移到Linux服务器做批量训练时无头渲染折腾了好一阵。最后用的是OSMesa后端设置环境变量export MUJOCO_GLosmesa注意如果你在服务器上没有显示设备不设置这个环境变量的话mujoco初始化渲染时会直接报错退出而且报错信息不太直观容易误以为是模型文件的问题。还有一个坑是mujoco的许可证。虽然现在开源了但某些旧版本的二进制包可能还带有许可证检查逻辑。如果你遇到莫名其妙的license not found错误先确认一下安装的版本是不是3.x以上。2.2 microduck项目依赖的完整清单microduck的核心依赖不算多但版本匹配很重要。我整理了一份实测可用的依赖清单依赖包版本用途mujoco3.1.0物理仿真引擎pytorch2.1.0策略网络训练gymnasium0.29.0环境接口封装numpy1.24.0数值计算onnx1.15.0模型导出onnxruntime1.17.0推理验证tensorboard2.15.0训练可视化这里要特别说一下gymnasium和gym的区别。gym已经停止维护了gymnasium是它的继任者API有细微差别。microduck如果用的是老版gym接口你需要做适配。主要改动在reset()和step()的返回值上——gymnasium的reset()返回(obs, info)step()返回(obs, reward, terminated, truncated, info)。这个改动不大但如果不注意训练循环会直接报错。2.3 环境验证跑通第一个仿真步装完依赖之后别急着训练先验证仿真环境能不能正常跑。写一个最小的测试脚本import mujoco import numpy as np # 加载microduck模型 model mujoco.MjModel.from_xml_path(microduck.xml) data mujoco.MjData(model) # 仿真一步 mujoco.mj_step(model, data) print(关节数量:, model.njnt) print(执行器数量:, model.nu) print(躯干高度:, data.qpos[2])如果这段代码能跑通并打印出合理的数值说明mujoco和模型文件都没问题。如果报错大概率是XML文件路径不对或者模型文件本身有语法错误。mujoco的XML报错信息会指出具体哪一行有问题照着改就行。我建议在这个阶段多花点时间手动改改XML里的参数观察仿真结果的变化。比如把重力从-9.81改成-1.62月球重力看看机器人会怎么动。这种玩的过程能帮你快速建立对仿真环境的直觉后面调奖励函数的时候会很有用。3. 观测空间、动作空间与奖励函数的设计取舍3.1 观测向量里该放什么、不该放什么microduck的观测向量设计直接决定了策略网络能看到什么信息。放少了策略学不到东西放多了训练变慢还容易过拟合。我这次用的观测向量包含以下几类躯干状态高度、姿态四元数、线速度、角速度关节状态12个关节的角度和角速度四足各3个关节上一帧动作12维帮助策略学习动作平滑性相位信息一个标量表示步态周期中的位置总共大约50维左右。这个维度对于全连接网络来说刚好不会太宽也不会太窄。这里有个经验不要把所有能拿到的信息都塞进观测里。我一开始把足底接触力也放进去了结果训练时策略严重依赖接触力信号仿真里接触力很干净但真实传感器有噪声迁移过去直接崩了。后来把接触力去掉策略反而学得更鲁棒。这个教训说明观测空间的设计要考虑真实部署时的传感器能力仿真里能拿到的信息不代表真实系统里也能拿到。另外观测的归一化很重要。不同物理量的量纲差异很大——角度是弧度制数值在-3.14到3.14之间角速度可能到几十高度在0.2到0.5之间。如果不做归一化网络训练时梯度会被大量纲的维度主导。我的做法是维护一个运行均值方差对每个观测维度做标准化class RunningMeanStd: def __init__(self, shape): self.mean np.zeros(shape) self.var np.ones(shape) self.count 1e-4 def update(self, x): batch_mean np.mean(x, axis0) batch_var np.var(x, axis0) batch_count x.shape[0] # 更新逻辑...这个类在训练过程中持续更新确保观测始终在合理范围内。3.2 动作空间的连续控制与平滑约束microduck的动作空间是连续的12个关节的目标角度。连续动作空间比离散动作空间难训因为策略网络要输出一个12维的连续向量而且这个向量的每个维度都有物理含义。我用的方案是策略网络输出目标关节角度然后通过一个PD控制器转换成力矩。这样做的好处是动作空间有明确的物理意义而且PD控制器能保证一定的动作平滑性。PD参数的选择有讲究P太大机器人会抖P太小机器人会软。我实测下来P20、D0.5这组参数在microduck上表现比较均衡。动作平滑性是个容易被忽视但很重要的点。如果相邻两帧的动作差异太大真实舵机会受不了仿真里也会出现数值不稳定。我在奖励函数里加了一项动作变化率的惩罚action_smoothness_penalty -0.01 * np.sum((action - prev_action) ** 2)这个系数不能太大否则策略会变得过于保守走路畏畏缩缩。0.01是我试了好几组之后觉得比较合适的值。3.3 奖励函数的层次化设计思路奖励函数设计是强化学习实操中最需要经验的部分。我的思路是分层次设计第一层生存奖励。机器人每存活一个时间步给一个小的正奖励比如0.1。这一层保证机器人不会一开始就学会躺平。第二层姿态奖励。躯干高度接近目标高度给正奖励躯干倾斜给负奖励。这一层引导机器人保持直立。第三层运动奖励。前进速度接近目标速度给正奖励。这一层引导机器人往前走。第四层能耗和动作平滑惩罚。关节力矩过大给负奖励动作变化剧烈给负奖励。这一层让策略更优雅。这四层的权重需要仔细调。我一开始把运动奖励权重设得很大结果机器人学会了扑倒式前进——直接往前扑靠惯性滑行。后来把姿态奖励权重提上来机器人就学会了正常走路。权重调整没有万能公式我的经验是先用小权重跑几百步看看策略的倾向再根据倾向调整权重。提示奖励函数里尽量不要用绝对值太大的奖励。奖励值在-10到10之间比较合适太大的奖励会导致值函数估计困难训练不稳定。4. 训练过程中的调参与踩坑记录4.1 PPO超参数的初始设置与调整节奏microduck的训练我用的PPO算法。PPO的超参数不算多但每个都影响训练效果。我的初始设置如下超参数初始值调整方向学习率3e-4训练不稳定时降到1e-4裁剪系数0.2一般不动折扣因子0.99一般不动GAE lambda0.95一般不动每次迭代步数2048显存不够时降到1024批大小64一般不动训练轮数10过拟合时降到5学习率是最需要关注的。我一开始用3e-4前1000次迭代loss下降正常但之后开始震荡。降到1e-4之后训练曲线平滑了很多。这个现象在强化学习里很常见——策略梯度方法的方差本来就大学习率大了容易在最优解附近跳来跳去。还有一个经验不要频繁改超参数。我见过有人每跑几百步就改一次学习率结果训练曲线像心电图一样。强化学习的训练本来就有随机性单次实验的波动不代表超参数有问题。我的做法是至少跑完一个完整的训练周期比如50万步再判断超参数是否合适。4.2 训练不收敛时的排查链路训练不收敛是强化学习实操中最常见的问题。我总结了一条排查链路按顺序检查第一步确认环境本身没问题。用随机策略跑几百步看看机器人会不会直接穿模或者飞出去。如果随机策略都能让机器人飞起来说明仿真参数有问题先修仿真再谈训练。第二步检查奖励曲线。如果奖励一直不涨先看看是不是奖励函数设计有问题。把奖励函数的每一项单独打印出来看看哪一项在主导。我遇到过姿态奖励和运动奖励互相打架的情况——机器人往前走就必然低头低头就被扣姿态奖励结果策略学了个原地不动。第三步检查观测和动作的数值范围。打印观测向量的均值和方差看看有没有异常大的值。我遇到过角速度观测偶尔飙到几百的情况原因是仿真步长太大导致数值爆炸。把步长从0.01降到0.002就解决了。第四步检查策略网络的输出。如果策略网络输出的动作始终在某个小范围内说明网络初始化有问题或者梯度消失了。可以试试换激活函数或者加一层LayerNorm。第五步降低任务难度。如果以上都没问题但还是不收敛把目标速度从1.0降到0.3先让机器人学会慢慢走再逐步提高目标速度。这种课程学习的思路在四足机器人训练里很有效。4.3 训练加速并行环境与向量化单环境训练microduckPPO大概需要200万步才能收敛在单张显卡上要跑好几个小时。加速的方法是用并行环境——同时跑多个microduck实例收集更多的经验。我用的方案是SubprocVecEnv开8个进程并行跑仿真from stable_baselines3.common.vec_env import SubprocVecEnv def make_env(): def _init(): return MicroduckEnv() return _init env SubprocVecEnv([make_env() for _ in range(8)])8个并行环境能把训练时间压缩到原来的1/5左右。但要注意并行环境会占用更多内存每个mujoco实例大概占200MB左右8个就是1.6GB加上模型和优化器的显存整体资源消耗要提前算好。还有一个加速技巧是降低仿真渲染频率。训练的时候不需要每步都渲染可以设置每100步渲染一次或者干脆关掉渲染。mujoco的无头模式比渲染模式快很多我实测下来能快3到4倍。5. 从PyTorch到ONNX策略网络的导出与量化5.1 策略网络结构对导出友好性的影响训练好的策略网络要导出成ONNX网络结构的设计就要考虑导出友好性。有些PyTorch的操作在导出ONNX时会遇到问题比如动态控制流、自定义算子、某些形式的索引操作。我的策略网络用的是最朴素的全连接结构输入50维观测经过两个隐藏层256维输出12维动作。激活函数用ELU因为ELU在ONNX里有标准实现导出不会出问题。如果你用了Swish或者GELU需要确认ONNX opset版本是否支持。导出代码很直接import torch.onnx policy.eval() dummy_input torch.randn(1, 50) torch.onnx.export( policy, dummy_input, microduck_policy.onnx, input_names[observation], output_names[action], dynamic_axes{observation: {0: batch_size}}, opset_version17 )dynamic_axes这个参数很重要。如果不设置导出的ONNX模型会固定batch size为1后面想批量推理就不行了。设置成动态之后模型可以接受任意batch size的输入。导出之后一定要验证。用onnxruntime加载模型和PyTorch的输出对比import onnxruntime as ort sess ort.InferenceSession(microduck_policy.onnx) onnx_output sess.run(None, {observation: dummy_input.numpy()}) torch_output policy(dummy_input).detach().numpy() print(最大误差:, np.max(np.abs(onnx_output[0] - torch_output)))误差在1e-5以内就算正常。如果误差很大说明导出过程中有算子被错误转换了需要检查网络结构。5.2 int8量化的实操步骤与精度损失评估ONNX模型量化成int8能大幅减小模型体积、提升推理速度但会带来精度损失。对于microduck的策略网络我的经验是int8量化后精度损失在可接受范围内但需要做校准。量化流程分三步第一步准备校准数据。从训练环境里随机采样1000条观测数据保存成numpy数组。这些数据要覆盖观测空间的主要区域不能只采一种状态。第二步运行量化工具。ONNX Runtime提供了量化工具from onnxruntime.quantization import quantize_dynamic, QuantType quantize_dynamic( microduck_policy.onnx, microduck_policy_int8.onnx, weight_typeQuantType.QInt8 )第三步评估精度损失。用同样的测试数据对比量化前后的输出差异。我实测下来动作输出的平均误差在0.02弧度左右对于关节控制来说这个误差完全可以接受。但要注意动态量化quantize_dynamic只量化权重不量化激活值。如果要进一步压缩可以用静态量化quantize_static但需要更复杂的校准流程。对于microduck这个规模的网络动态量化已经够用了。5.3 ONNX模型在推理引擎上的部署验证导出的ONNX模型最终要部署到推理引擎上。我这次在onnxruntime上做了验证流程很简单sess ort.InferenceSession(microduck_policy_int8.onnx) obs env.reset() for _ in range(1000): action sess.run(None, {observation: obs.reshape(1, -1)})[0] obs, reward, done, info env.step(action.flatten()) if done: obs env.reset()这段代码跑通说明模型从训练到部署的链路是完整的。实际部署到嵌入式设备时推理引擎可能换成TensorRT或者OpenVINO但ONNX作为中间格式是通用的转换过去不会有太大问题。注意int8量化后的模型在某些推理引擎上可能需要额外的算子支持。如果部署时遇到unsupported operator错误先检查推理引擎的版本是否支持你用的ONNX opset。6. 仿真到现实的差距与应对思路6.1 域随机化让策略见过世面仿真里训练出来的策略直接放到真实机器人上大概率会失败。原因是仿真和现实之间存在差距摩擦系数不同、电机响应有延迟、传感器有噪声、模型质量分布有偏差。应对这个问题的标准做法是域随机化——在训练时随机改变仿真参数让策略学会适应各种条件。我在microduck上做的域随机化包括摩擦系数在0.5到1.5之间随机躯干质量在标称值的80%到120%之间随机电机延迟在0到20毫秒之间随机观测噪声给每个观测维度加高斯噪声标准差为标称值的5%外部扰动每隔一段时间给躯干施加一个随机方向的力这些随机化参数的范围需要仔细调。范围太小策略学不到鲁棒性范围太大策略可能学不会走路。我的经验是从小范围开始训练收敛后逐步扩大范围类似于课程学习。6.2 观测噪声与执行器延迟的建模观测噪声和执行器延迟是两个最影响迁移效果的因素。观测噪声的建模相对简单直接在观测上加高斯噪声就行。但要注意噪声的标准差应该和真实传感器的噪声水平匹配。如果你不知道真实传感器的噪声水平可以先设一个保守的值比如标称值的3%后面再根据实测调整。执行器延迟的建模稍微复杂一些。真实舵机从收到指令到执行到位有延迟这个延迟在仿真里需要显式建模。我的做法是维护一个动作缓冲区策略输出的动作不直接作用于机器人而是放入缓冲区延迟若干步之后再取出执行class ActionBuffer: def __init__(self, delay_steps, action_dim): self.buffer deque(maxlendelay_steps 1) self.action_dim action_dim def push(self, action): self.buffer.append(action) def get(self): return self.buffer[0]延迟步数根据真实硬件的响应时间来确定。如果真实舵机响应时间是10毫秒仿真步长是2毫秒那延迟就是5步。6.3 策略鲁棒性评估的实操方法训练完之后怎么判断策略够不够鲁棒我用了几个测试方法方法一参数扰动测试。把仿真参数设成训练时域随机化范围之外的值看看策略还能不能走。比如训练时摩擦系数范围是0.5到1.5测试时设成0.3和2.0观察策略的表现。方法二持续外力测试。在机器人躯干上持续施加一个固定方向的外力看看策略能不能抵抗。这个测试模拟的是真实环境中的风或者负载。方法三地形变化测试。在仿真里加一些小的台阶或者斜坡看看策略能不能适应。microduck的XML里可以直接加地形几何体。方法四长时间运行测试。让策略连续跑10分钟以上观察有没有累积误差或者发散。有些策略短时间看着没问题跑久了就慢慢偏了。这些测试的结果不一定要完美但能帮你了解策略的边界在哪里。知道边界在哪里比盲目相信策略能work要靠谱得多。7. 一些零散但重要的实操心得7.1 日志与可视化训练过程必须可观测强化学习训练是个黑盒如果不做日志和可视化出了问题你根本不知道从哪查。我建议至少记录以下内容每轮的平均奖励、最大奖励、最小奖励策略网络的输出均值和方差值函数的估计值观测向量的均值和方差动作的均值和方差训练损失这些数据用TensorBoard记录训练过程中随时可以看。我遇到过奖励突然掉下去的情况看TensorBoard发现是观测方差突然变大追查下去发现是仿真里有个关节卡住了。如果没有日志这个问题可能要排查很久。7.2 模型保存与恢复训练的注意事项训练过程中要定期保存模型但保存频率不能太高否则IO会成为瓶颈。我的做法是每10次迭代保存一次同时保留最近5个检查点。这样即使最新的模型坏了也能回退到之前的版本。恢复训练的时候要注意优化器的状态也要保存和恢复。如果只保存模型参数不保存优化器状态恢复训练后loss会有一个明显的跳变。PyTorch里保存优化器状态很简单torch.save({ model_state_dict: policy.state_dict(), optimizer_state_dict: optimizer.state_dict(), iteration: iteration }, checkpoint.pt)还有一个细节随机数种子。如果要做可复现的实验训练开始时就要固定所有随机数种子包括numpy、torch、以及仿真环境的种子。但要注意固定种子之后并行环境的随机性会降低可能影响策略的鲁棒性。我的做法是训练时用随机种子做对比实验时才固定种子。7.3 从microduck迁移到其他平台的思路microduck上跑通的这套流程迁移到其他机器人平台时核心逻辑不变需要改的主要是三个地方第一观测和动作的维度。不同机器人的关节数量不同观测向量的构成也不同。迁移时需要重新定义观测空间和动作空间并相应调整策略网络的输入输出维度。第二奖励函数。奖励函数和机器人的形态强相关。四足机器人的奖励函数不能直接用在机械臂上。迁移时需要根据新平台的运动特点重新设计奖励。第三域随机化的参数范围。不同硬件的噪声水平、延迟特性、摩擦特性都不同域随机化的范围需要根据目标硬件重新标定。除此之外训练流程、导出流程、量化流程都是通用的。这也是我推荐用microduck入门的原因——它让你把整条链路走通一遍后面换平台只是换零件不用重新学造轮子。7.4 关于训练时长和硬件选择的建议最后说一下硬件。microduck的训练对硬件要求不算高但有几个地方值得注意GPU至少6GB显存8GB比较舒服。并行环境多的时候显存占用会上去。CPU并行环境需要多核CPU8核以上比较好。每个仿真进程占一个核。内存16GB起步32GB推荐。mujoco实例和并行环境都比较吃内存。存储SSD是必须的训练日志和模型检查点写入频繁机械硬盘会成为瓶颈。训练时长方面单环境200万步大概4到6小时8并行环境大概1到1.5小时。这个时间对于调参来说是可以接受的。如果你要做大量的对比实验建议用多张显卡或者云端的GPU实例能大幅缩短实验周期。我在整个实操过程中最大的体会是强化学习的门槛不在算法而在工程。算法论文里的公式看起来很优雅但真正让机器人走起来靠的是对仿真参数的反复调试、对奖励函数的细致打磨、对训练过程的持续观察。microduck是个很好的起点它足够简单让你能快速跑通又足够真实让你能学到真东西。把这条链路走一遍后面再遇到新的机器人平台或者新的算法你心里就有底了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑