
想让你那台宇树GO2在真正落地跑起来之前少摔几个跟头最现实的办法不是拿真机一遍遍试错而是在gym环境里先把运动策略练出来。这里说的gym训练不是简单搭个仿真界面看动画而是把环境交互、状态反馈、奖励曲线、策略导出这一整条链路跑通最后再把训练好的模型搬到真机上去。这篇教程就是来干这件事的我会把完整的配置文件、跑通流程、以及我在实际训练中踩过的坑全部摊开讲。这篇内容适合两类人一类是手里已经有GO2想让它学会稳定行走、抗扰动、跟速度指令的开发者另一类是刚开始接触四足机器人强化学习想知道从哪下手、什么配置能跑、奖励函数怎么写的人。我会默认你熟悉Python和基本的Linux操作但不会默认你懂强化学习因为我自己也是从一脸懵的状态一步步走过来的。1. 为什么是gym四足机器人训练链路全貌1.1 “gym”在GO2训练里到底指什么如果你去翻阅宇树官方文档或者开源社区的项目会发现几乎所有GO2强化学习训练都绕不开一个词gym。但严格来说“用gym训练”并不仅仅指OpenAI Gym这个单独的Python库而是指以Gym接口标准为基础搭建的一整套训练环境。Gym的核心设计特别朴素一个环境有几个固定接口比如重置环境让机器人回到初始状态执行一组动作让环境推进一个仿真步长返回新的状态和奖励信号。这种标准化封装的好处是不管你在训练宇树GO2、ANYmal还是自己拼装的四足机器人训练代码写起来都差不多。在实际项目里解决GO2训练任务的主流方案是英伟达的Isaac Gym加上开源框架legged_gym。Isaac Gym负责底层的高性能并行仿真legged_gym则把宇树GO2这类四足机器人封装成了Gym风格的环境。换句话说你平时写训练脚本时调用的reset、step这些接口就是Gym标准接口底层跑的是Isaac Gym的物理仿真引擎。这条组合路径在四足机器人圈子里已经是默认选择所以本教程也是沿着这条路走。1.2 为什么训练GO2不直接从真机开始很多新手会有个疑惑宇树GO2自带遥控器也能通过SDK接收控制指令为什么不直接在真机上调试原因很简单代价和风险完全不同。真机调试一个强化学习策略首先你得面对时间成本。GO2充满电后高负载运行时间有限每跑一轮实验可能只有几十分钟而训练一个能稳定行走的策略往往需要几百万个仿真步。你在真机上跑一辈子都凑不够足够的样本量。其次就是安全风险强化学习初期完全是乱试动作幅度大、方向随机轻则摔机损坏电机和外壳重则直接把人绊倒或撞坏东西。一台GO2的价格摆在那里真机试错的成本实在太高了。仿真训练的本质是把“失败的代价清零”。策略在仿真环境里摔几万次都无所谓电机不会烧、外壳不会刮花你只需要关注奖励曲线有没有上升就行了。等到策略跑出来的步态在仿真里已经像模像样再导出模型去真机做domain transfer这样真机测试主要是验证仿真与现实的差距而不是从零开始学走路。1.3 整个训练任务的核心链路我建议你在开始动手之前先把整条链路画在脑子里因为后面每一步都要回到这张图上来看自己走到哪了。创建Gym风格的四足机器人仿真环境加载宇树GO2的URDF模型。定义观察空间和动作空间让机器人能感知自身状态并生成控制指令。设计奖励函数告诉机器人什么样的行为是好的。用PPO这类强化学习算法在仿真环境里迭代训练。观察训练曲线判断模型是否收敛。导出策略模型部署到GO2真机控制器上。这条链路里我个人觉得最影响最终效果的两个环节是奖励函数设计和配置文件参数调整。前者决定机器人能不能学会行走后者决定训练能不能稳定收敛。接下来我会按这条链路往下走。2. 环境搭建与依赖安装第一天就卡住的三个地方2.1 硬件基线别拿轻薄本当生产力工具先说结论想用gym训练宇树GO2模型一台带NVIDIA独立显卡的电脑是必须的纯CPU版本虽然在某些简化环境里能跑但训练速度慢到你会怀疑人生。我自己的主力机器配置供你参考硬件项最低要求推荐配置GPUNVIDIA GTX 1660RTX 4080或更高显存8GB16GB以上内存16GB32GB系统Ubuntu 20.04Ubuntu 20.04 / 22.04硬盘20GB剩余空间50GB以上SSD为什么对显存和内存要求高因为Isaac Gym的核心竞争力是并行仿真它不是一个一个环境地跑而是同时创建数千个并行环境让GPU一次性处理几十万个刚体算力。环境数量越多样本采集速度越快训练收敛得也越快。如果你的显存只有8GB建议把环境数量从4096降到2048或1024否则很容易直接OOM报错。2.2 安装步骤尽量用conda管理环境我强烈建议你用conda创建一个独立Python环境不要直接装到系统Python里。因为Isaac Gym对Python版本有要求我自己用的是Python 3.8配合PyTorch 1.13版本这是经过大量项目验证过的组合。conda create -n go2_rl python3.8 conda activate go2_rl # 安装适合CUDA版本的PyTorch pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117接下来是Isaac Gym本体。你需要从英伟达官网下载Isaac Gym Preview 4版本这个版本目前开源社区用得最多资料也好找。下载后解压并安装cd ~/Downloads tar -xvf IsaacGym_Preview_4_Package.tar.gz cd isaacgym pip install -e python最后把legged_gym克隆下来安装训练所需的其他依赖git clone https://github.com/leggedrobotics/legged_gym.git cd legged_gym pip install -e .这里有个很多人会漏掉的细节legged_gym依赖rk_gym的一些编译组件安装时如果报缺少rlgpu相关模块通常是因为isaacgym没有正确安装或者环境变量没有配置。你需要在启动训练前确认Isaac Gym自带的测试能跑通cd ~/Downloads/isaacgym/python/examples python joint_pose_control.py如果弹出一个仿真窗口里面的机器人能被控制着摆动关节说明Isaac Gym本体没问题。2.3 新手最容易卡住的三个报错报错一ImportError: libpython3.8.so.1.0: cannot open shared object file这个大概率是系统缺少Python动态库执行sudo apt install libpython3.8-dev报错二ImportError: isaacgym requires a CUDA capable GPU查驱动nvidia-smi如果命令不显示任何信息说明NVIDIA驱动没装好需要先解决驱动问题。报错三PolySolver but no solver created这个错误很隐蔽通常是Python版本和Isaac Gym的兼容性问题。我试过Python 3.10就会稳定复现这个问题换成Python 3.8后一切正常。所以如果你遇到莫名其妙的底层错误别想着绕过去直接重建Python 3.8环境最省事。3. 手把手过一遍训练代码从make_env到step3.1 训练入口legged_gym的train脚本在做什么安装好环境之后你已经可以直接跑现成的训练脚本了。legged_gym仓库里的train.py是入口文件它会读取配置文件创建训练环境然后开始PPO迭代。from legged_gym.envs import * from legged_gym.utils import get_args, task_registry args get_args() env, env_cfg task_registry.make_env(namego2, argsargs) ppo_runner, train_cfg task_registry.make_alg_runner(envenv, namego2, argsargs) ppo_runner.learn(num_learning_iterationstrain_cfg.runner.max_iterations)这里你可能会好奇env和cfg是从哪来的在legged_gym的项目结构里go2环境不是一个单独的class而是通过配置文件生成。你在配置文件里指定模型路径、观测维度、控制频率等参数task_registry会根据名字找到对应配置并实例化环境。所以你拿到手的第一件事不应该是改代码而是改配置文件。3.2 观察空间和动作空间设计训练GO2运动策略最核心的一件事是把观察空间定义清楚。GO2本身是12个自由度四条腿每条腿有髋关节、大腿关节、小腿关节三个电机。动作空间也对应是12维每个维度代表目标关节位置。基础版本的观察空间通常包含以下几类信息机器人基座线速度和角速度维数是6重力方向在机器人坐标系下的投影维数是3当前速度指令通常包括x方向线速度、y方向线速度、偏航角速度维数是312个关节的当前角度12个关节的当前角速度上一帧的动作维数是12把这些加起来基础观察向量是54维。很多项目还会加入周期性步态相位、关节位置误差积分等扩展信息这些可以在配置文件里打开。这里有一个工程上的重要细节观察空间不是越大越好而是越稳越好。你加进去的每个量都必须能在真机上稳定获取。比如基座速度在仿真里可以直接读但在真机上要通过状态估计器融合IMU和关节编码器数据计算。如果仿真里用了真机拿不到的高精度量部署到真机上策略表现就会大打折扣。3.3 策略网络与PPO的核心逻辑策略网络采用的是Actor-Critic结构Actor负责根据观察向量输出12维动作Critic负责评估当前状态的价值。训练过程中大量并行环境会产生海量样本数据PPO算法用这些样本去更新网络参数。训练过程中需要注意一个重要的超参数控制频率。GO2真机的控制频率通常是50Hz到100Hz也就是说每20ms或10ms就要输出一次关节位置指令。仿真里的仿真步长一般设成0.005秒控制频率设为50Hz时相当于每10个仿真步执行一次策略推理。这个参数如果和真机不一致会产生严重的Sim2Real Gap。你仿真里策略已经适应了50Hz的决策节奏上了真机如果控制频率变成100Hz策略对相同输入的输出就会显得滞后或过于激进。3.4 写第一个自定义奖励函数这里我给一个最小可跑的奖励函数参考它会让GO2学会向前走def _reward_linear_velocity(self): # 希望机器人基座沿x方向前进 current self.base_lin_vel[:, 0] return self.commands[:, 0] * current def _reward_tracking_lin_vel(self): # 速度指令追踪误差 lin_vel_error torch.sum(torch.square(self.commands[:, :2] - self.base_lin_vel[:, :2]), dim1) return torch.exp(-lin_vel_error / 0.25)第一段是简单鼓励前向速度第二段是速度追踪的指数奖励二者配合能让机器人学出跟随指令前进的行为。你可能注意到第二段用了exp函数把误差映射到0到1之间这是四足机器人奖励函数设计的常见套路误差越小奖励越平滑地接近1梯度不会突然爆炸。4. 完整配置文件解析参数一个都不能乱动4.1 环境配置代码完整模板下面这份配置文件我是按“可以直接跑起来”的标准写的你先照抄跑通之后再根据自己的机器人需求改。class GO2RoughCfg(LeggedRobotCfg): class env: num_envs 4096 episode_length_s 20 num_observations 54 num_actions 12 reward_specs { tracking_lin_vel: {weight: 1.5}, tracking_ang_vel: {weight: 0.8}, linear_velocity_z: {weight: -2.0}, torque_limits: {weight: -0.0005}, joint_torques: {weight: -0.0002}, joint_velocity: {weight: -0.0002}, base_height: {weight: -30.0}, feet_air_time: {weight: 1.0}, } class terrain: mesh_type trimesh num_rows 5 num_cols 5 terrain_proportions [0.0, 0.0, 0.0, 1.0] difficulty_scale 0.0 class robot: asset_path urdf/go2/urdf/go2.urdf base_init_pos [0.0, 0.0, 0.35] stiffness 20.0 damping 0.5 class control: control_type P action_scale 0.25 decimation 10 class domain_rand: randomize_friction True friction_range [0.5, 2.5] randomize_base_mass True added_mass_range [-1.0, 1.0] randomize_com_displacement True com_displacement_range [-0.02, 0.02]这份配置里有几个地方重点解释一下。控制参数里的action_scale 0.25意思是策略网络输出的动作值要乘以0.25再叠加到目标关节位置上。为什么这样设计因为策略网络一般输出范围在-1到1之间但GO2的真实关节活动范围可能达到几十度直接输出绝对值不现实用缩放因子把网络输出映射到合适的关节增量更稳定。stiffness和damping是PD控制器的参数它们决定了关节跟踪目标位置的刚性和阻尼。GO2真机的默认PD参数和仿真里的不一定完全一致你可以在后续做域随机化时给它们加噪声让策略更鲁棒。4.2 PPO训练超参配置训练算法的超参在国家训练中同样关键放在runner_cfg.py里class GO2RoughCfgPPO(RunnerCfg): class policy: actor_hidden_dims [512, 256, 128] critic_hidden_dims [512, 256, 128] activation elu class algorithm: learning_rate 5e-4 num_learning_epochs 5 num_mini_batches 4 gamma 0.99 lam 0.95 class runner: policy_class_name ActorCritic algorithm_class_name PPO num_steps_per_env 24 max_iterations 10000 save_interval 100 resume False这里学习率5e-4是一个比较稳的起步值很多任务不需要动它。如果你发现训练后期损失震荡很严重可以把学习率降到2e-4或1e-4但要接受收敛速度变慢的代价。num_steps_per_env24配合num_envs4096意味着每轮迭代总共采样4096乘以24约98304条样本。这个量级对四足机器人任务来说是够用的。如果你显存不够减到1024个环境建议把num_steps_per_env提到32或48保证样本量别掉太多。4.3 从配置文件到启动直接跑起来配置写好后启动训练只需要一行命令python legged_gym/scripts/train.py --taskgo2 --num_envs4096 --headless如果一切正常你会看到终端不断打印iteration、平均奖励、学习率等信息。我第一次跑的时候看到终端开始跳动心里是挺兴奋的因为这意味着IMA等待的不是报错而是训练进程。训练过程中默认情况下会弹出一个仿真窗口用于可视化加上--headless参数则关闭渲染训练速度会有明显提升。日常调优我建议用headless模式每周看一次可视化就已经足够。5. 训练过程中怎么判断模型别被奖励曲线骗了5.1 奖励曲线的正确看法训练开始后最直观的反馈是终端打印的总奖励曲线。但我想提醒你总奖励数值本身并不是最重要的重要的是分项奖励的变化趋势。如果你开启了记录tensorboard里能看到torque、velocity、tracking等每一项的累计值。一个健康的学习过程是这样的速度追踪奖励稳步上升说明机器人在逐渐学会跟随指令关节加速度和关节力矩约束惩罚项保持在合理范围说明策略没有用极端动作刷奖励基座高度惩罚项收敛到零附近说明机器人没有趴下或弹跳我最担心的信号是总奖励在涨但速度追踪奖励在跌同时关节力矩惩罚在涨。这种情况通常意味着机器人学会了“投机取巧”用剧烈的腿部抖动骗取前向速度代价是电机负载过高。真机上这种策略一眼就能看出不对劲因为电机会嗡嗡作响。5.2 三个比奖励数值更重要的观测指标第一个是Action的平滑度。把策略输出的12维动作序列画出来如果曲线像锯齿一样高频抖动即使训练奖励很高这个模型也不适合部署到真机。真机电机的带宽和执行延迟会让这种高频抖动表现得更加剧烈。第二个是命令追踪误差。在训练环境里给随机速度命令统计实际速度与目标速度的误差。收敛好的模型前向速度追踪误差一般能控制在0.2m/s以内。如果这个数字大于0.4m/s说明策略的判断力还不够。第三个是对抗扰动的恢复能力。这是我在后期才学会的测试方法训练几千轮之后保存模型开一个Tester环境给机器人施加横向推力看它能不能在几秒内恢复稳定步态。很多时候奖励函数看起来收敛了但稍微来点外部干扰就原地翻车这就是典型的过拟合地形欠拟合扰动。5.3 配置里调什么来改善不理想的训练结果如果你的训练结果出现了上述问题优先调整这几个方向问题优先调整项训练不收敛或发散降低学习率增加环境数量步态诡异、漂移明显增大速度追踪奖励权重加入步态相位奖励关节抖动剧烈增大关节速度和关节力矩惩罚权重在平地上好、斜坡上差打开地形随机化和质量域随机化训练后期reward震荡降低PPO的epsilon或延长每轮采样步数这些不是唯一解但按这个顺序排查至少能让模型从不能用到能看。6. 从仿真到真机导出、部署与踩坑记录6.1 训练完成后怎么导出策略模型训练过程中每间隔save_interval轮会保存一个pt文件路径一般在logs/go2/exported目录下。这个pt文件包含Actor网络和Critic网络的状态字典但到了部署阶段只需要导出Actor网络的部分因为真机上推理时不需要价值网络。我建议使用ONNX格式做部署因为GO2运行环境对PyTorch的依赖比较重ONNX更轻量也更容易嵌入控制程序。import torch from legged_gym.utils import get_args, task_registry args get_args(go2, play) env, _ task_registry.make_env(namego2, argsargs) policy torch.load(logs/go2/exported/policy_1.pt) policy_pth logs/go2/exported/policy_1.onnx dummy_input torch.zeros(1, 54, dtypetorch.float32, devicecuda) torch.onnx.export( policy, dummy_input, policy_pth, input_names[obs], output_names[action], dynamic_axes{obs: {0: batch}, action: {0: batch}}, )导出这一步比较容易出问题的地方是dummy_input的维度必须和你配置文件里的num_observations一致。如果你改了观察空间却忘了重新安装环境导出时会直接报维度不匹配。6.2 部署到GO2真机复用官方SDK宇树官方SDK提供了模型部署的基本流程大致是把ONNX模型加载到运动控制板在控制循环里读取IMU和编码器状态拼接成54维观察向量送入ONNX模型推理得到12维动作再经PD控制器生成电机力矩指令。我自己的经验是初次联调时千万不要直接在真机上跑完整策略先做三个准备动作第一固定机器人让四条腿悬空手动把输入状态调整到接近零位观察策略输出是否接近零动作。这一步能确认模型加载和维度拼接没问题。第二把速度指令设成0让机器人在平地上尝试站立。注意遥控器要放在急停位置一旦出现异常抖动立刻断电。第三才把速度指令缓慢提升到0.2m/s观察步态是否稳定。6.3 我踩过的几个真机运行坑第一个坑是观察向量的数值范围不一致。仿真里IMU数据噪声大但范围正常真机上因为状态估计器滤波参数不同部分量可能出现数量级偏差。解决方法是真机采集一段数据统计均值和标准差在输入网络前做同样的归一化。第二个坑是控制频率不匹配。仿真里如果decimation10控制周期是50Hz但真机主循环如果跑到了100Hz策略会被重复调用动作输出参考会导致关节位置指令看起来混乱。务必确保真机主循环频率与训练配置一致。第三个坑最隐蔽PD增益差异。仿真里的stiffness和damping是理想模型真机电机驱动器的底层响应和仿真差距不小。常见的做法是把仿真里的PD增益设成真机实测值或者在仿真里对增益做一定程度随机化让策略对增益变化不敏感。我个人的强烈建议是第一次真机测试一定要有人在旁边控制急停速度从0.2m/s开始确认机器人重心稳定后再逐步提速。不要嫌慢真机摔一下的维修成本和时间成本远比你多花一个下午调参要高得多。等你跑通了这套gym训练链路后续再想改进步态或者加入新地形就能快速迭代了。