
1. 从零理解机器人试错学走路的本质1.1 为什么“试错”是机器人学会走路的唯一出路很多人第一次接触机器人走路这件事脑子里冒出来的第一个念头是直接写一套规则不就行了吗抬腿多少度、迈步多长、重心怎么移全部写死。我一开始也是这么想的直到真正上手才发现这条路根本走不通。原因很简单双足机器人的动力学太复杂了。一个十二自由度的双足机器人它的关节角度、角速度、地面摩擦力、重心位置、惯性矩阵这些变量之间是高度耦合的。你改一个髋关节的角度膝盖和踝关节的受力立刻全变。用传统控制方法你得先建立精确的动力学方程然后做线性化再设计控制器。问题是地面摩擦系数你没法精确知道机器人自身的质量分布也有误差电机还有响应延迟。这些误差累积起来写死的规则一上真实地面就摔。强化学习解决这个问题的思路完全不同。它不去精确建模整个系统而是让机器人在仿真环境里反复尝试摔倒了就调整策略走稳了就强化这个行为。这个过程和人学走路一模一样——没有哪个小孩是看了力学教材才会走路的都是摔了无数次之后身体自己找到了平衡的感觉。强化学习的核心逻辑智能体在环境中执行动作环境返回新的状态和奖励信号智能体根据奖励调整策略目标是让长期累积奖励最大化。这里的关键在于奖励函数的设计。你不能只给一个“走到终点给满分”的稀疏奖励因为机器人随机探索到终点的概率几乎为零。你得设计稠密奖励往前走给正奖励摔倒给负奖励身体保持直立给正奖励关节力矩过大给负奖励。这些奖励项加权求和构成了机器人学习的“指挥棒”。1.2 强化学习与传统控制的分水岭在哪里我经常被问到这个问题强化学习和传统的PID控制、MPC模型预测控制到底有什么区别什么时候该用哪个传统控制方法的核心是基于模型的。你得知道系统的数学模型然后设计控制器。优点是稳定、可解释、计算量小。缺点是模型不准的时候性能急剧下降而且对于高自由度、强非线性的系统建模本身就极其困难。强化学习的核心是无模型的。它不需要你知道精确的动力学方程只需要你能提供一个仿真环境让智能体在里面试错。优点是适应性强能处理传统方法搞不定的复杂场景。缺点是训练时间长、样本效率低、奖励函数设计需要经验。对比维度传统控制PID/MPC强化学习RL模型依赖需要精确模型无需精确模型调试方式调参数设计奖励函数计算开销实时计算量小训练开销大推理开销小适应性环境变化需重新调参泛化能力较强可解释性强弱典型场景工业机械臂重复作业复杂地形行走、灵巧操作实际项目中我见过太多人一上来就想用强化学习搞定一切结果连仿真环境都没搭好就放弃了。我的建议是如果你的任务有成熟的传统控制方案先用传统方案跑通基线再用强化学习去提升。这样你至少有一个能工作的系统而不是从零开始赌强化学习能收敛。1.3 仿真环境选型为什么MuJoCo是绕不开的选择做机器人强化学习仿真环境的选择直接决定了你的开发效率。我试过PyBullet、Gazebo、Isaac Gym、MuJoCo这几个主流平台各有优劣但MuJoCo在双足行走这个场景下确实是最优解。MuJoCo的物理引擎对接触力的计算非常精确这对双足行走至关重要。机器人脚掌和地面的接触力如果算不准训练出来的策略迁移到真机上就会完全失效。PyBullet虽然免费开源但接触求解的精度差一些训练出来的策略容易出现“仿真里走得稳真机上抖成筛子”的情况。Gazebo更偏向于系统集成测试物理精度不是它的强项。Isaac Gym的卖点是GPU并行加速可以同时跑几千个环境训练速度极快但它的物理精度和MuJoCo比还是有差距而且对硬件要求高。我目前的配置是MuJoCo做算法开发和策略验证Isaac Gym做大规模并行训练加速。先用MuJoCo把算法调通确认奖励函数和网络结构没问题再迁移到Isaac Gym上做大规模训练。这样既保证了物理精度又兼顾了训练效率。实操心得MuJoCo从2.1版本开始已经完全开源免费安装方式很简单pip install mujoco就能搞定。但要注意Python版本兼容性建议用Python 3.8到3.10之间的版本太新的Python版本可能会有依赖冲突。仿真环境搭建好之后下一步就是定义机器人的模型文件。MuJoCo使用MJCF格式的XML文件来描述机器人包括连杆几何、关节类型、质量惯性、碰撞体、执行器等。这个文件写得好不好直接影响训练效果。我的经验是质量惯性参数一定要尽量准确如果不知道真实值至少要做合理的估计否则训练出来的策略在真机上会完全不一样。2. 奖励函数设计决定机器人能不能学会走路的关键2.1 奖励函数的组成与权重分配奖励函数是强化学习里最需要经验和直觉的部分。我见过太多人在这里踩坑奖励设计得不好机器人要么学会原地不动骗奖励要么学会疯狂抖动来获取微小的前进奖励要么直接摆烂摔倒因为负奖励太大导致梯度爆炸。一个能用的双足行走奖励函数通常包含以下几个部分前进速度奖励机器人躯干在前进方向上的速度乘以一个正权重。这是最主要的引导信号。保持直立奖励躯干高度和姿态的偏差惩罚鼓励机器人站直。关节力矩惩罚关节输出力矩的平方和乘以负权重防止机器人用蛮力。关节速度惩罚关节角速度的平方和乘以负权重让动作平滑。接触奖励脚掌与地面的接触模式奖励鼓励交替迈步。存活奖励每个时间步给一个小的正奖励鼓励机器人不要摔倒。这些项的权重需要仔细调节。我的经验值是前进速度奖励权重设为1.0到2.0直立奖励权重设为0.5到1.0力矩惩罚权重设为0.0001到0.001速度惩罚权重设为0.0001到0.0005。存活奖励一般设为0.1到0.5。注意奖励权重的量级差异很大前进速度可能是米每秒级别力矩可能是几十牛米级别。如果不做归一化力矩惩罚会完全主导整个奖励信号。建议在计算奖励之前先对每个分量做归一化处理。2.2 稀疏奖励与稠密奖励的取舍稀疏奖励是指只在任务完成时给奖励比如“走到终点给100分其他时候0分”。稠密奖励是指每个时间步都给反馈比如“往前走一步给0.1分”。理论上稀疏奖励更接近真实目标但实际训练中几乎不可行。因为机器人随机探索到终点的概率极低智能体在获得第一个正奖励之前可能已经探索了几百万步梯度信号完全消失。稠密奖励的问题是奖励塑形偏差。你设计的稠密奖励可能引导机器人学会一些你没想到的“作弊”行为。比如你给“脚掌接触地面”正奖励机器人可能学会一直拖着脚走因为这样接触时间最长。你给“躯干高度”正奖励机器人可能学会踮着脚尖走因为这样躯干最高。我的解决方案是课程学习先用稠密奖励让机器人学会基本的站立和迈步然后逐渐降低稠密奖励的权重增加稀疏奖励的权重最终让机器人以完成任务为主要目标。这个过程需要反复调试没有一劳永逸的参数。2.3 奖励函数代码实现与调试技巧下面是一个我实际项目中使用的奖励函数实现基于MuJoCo和Gym接口import numpy as np def compute_reward(env, action, obs): # 提取状态信息 torso_height obs[0] # 躯干高度 torso_angle obs[1] # 躯干倾角 forward_vel obs[2] # 前进速度 joint_angles obs[3:15] # 12个关节角度 joint_vels obs[15:27] # 12个关节角速度 foot_contacts obs[27:29] # 左右脚接触标志 # 前进速度奖励 reward_forward 1.5 * forward_vel # 直立奖励 reward_upright 0.8 * np.exp(-5.0 * torso_angle**2) # 高度奖励 target_height 0.75 reward_height 0.5 * np.exp(-20.0 * (torso_height - target_height)**2) # 关节力矩惩罚 torque env.get_joint_torques() penalty_torque -0.0002 * np.sum(np.square(torque)) # 关节速度惩罚 penalty_velocity -0.0001 * np.sum(np.square(joint_vels)) # 接触奖励鼓励交替接触 if foot_contacts[0] 0.5 and foot_contacts[1] 0.5: reward_contact 0.3 elif foot_contacts[1] 0.5 and foot_contacts[0] 0.5: reward_contact 0.3 elif foot_contacts[0] 0.5 and foot_contacts[1] 0.5: reward_contact -0.5 # 双脚同时着地惩罚 else: reward_contact -1.0 # 双脚离地惩罚 # 存活奖励 reward_alive 0.2 total_reward (reward_forward reward_upright reward_height penalty_torque penalty_velocity reward_contact reward_alive) return total_reward调试奖励函数的时候我习惯先把所有惩罚项设为0只保留前进奖励和存活奖励看机器人能不能学会往前走。如果能再逐步加入惩罚项观察行为变化。如果加入某个惩罚项之后训练不收敛了说明这个惩罚项的权重太大了需要降低。还有一个技巧是记录每个奖励分量的值训练过程中打印出来。如果某个分量的值远大于其他分量说明权重需要调整。理想情况下各个分量的值应该在同一个量级。3. 算法选型与网络结构设计3.1 PPO为什么成为机器人行走的首选算法强化学习算法有很多种DQN、DDPG、TD3、SAC、PPO每个都有自己的适用场景。但在机器人连续控制这个领域PPO近端策略优化是目前最主流的选择。PPO的核心优势是稳定。它通过限制每次策略更新的幅度避免了训练过程中的剧烈波动。机器人行走这个任务策略稍微变一点行为可能就完全不同所以稳定性比样本效率更重要。SAC虽然样本效率更高但在高维连续控制任务上容易不稳定。DDPG和TD3的探索能力不如PPO容易陷入局部最优。PPO的另一个优势是实现简单。它的核心思想就是收集一批数据计算优势函数然后做多次梯度更新但每次更新时限制新旧策略的KL散度不要太大。这个限制通过裁剪clip来实现代码量很少。# PPO核心更新逻辑 def ppo_update(policy, old_policy, optimizer, states, actions, old_log_probs, advantages, returns, clip_ratio0.2): for _ in range(epochs): # 计算新策略下的动作概率 new_log_probs, entropy policy.evaluate(states, actions) # 计算概率比 ratio torch.exp(new_log_probs - old_log_probs) # 裁剪目标函数 surr1 ratio * advantages surr2 torch.clamp(ratio, 1 - clip_ratio, 1 clip_ratio) * advantages policy_loss -torch.min(surr1, surr2).mean() # 价值函数损失 values policy.get_value(states) value_loss F.mse_loss(values, returns) # 总损失 loss policy_loss 0.5 * value_loss - 0.01 * entropy.mean() optimizer.zero_grad() loss.backward() optimizer.step()实操心得PPO的clip_ratio一般设为0.2这个值比较通用。如果训练不稳定可以降到0.1如果训练太慢可以升到0.3。但不要超过0.3否则裁剪就失去意义了。3.2 策略网络与价值网络的结构设计网络结构的设计对训练效果影响很大。我的经验是策略网络和价值网络共享底层特征提取层但顶层分开。这样既能共享特征又能让两个网络专注于各自的任务。对于双足机器人输入维度通常是躯干状态高度、角度、速度加上12个关节的角度和角速度总共大约30到40维。输出维度是12个关节的目标角度每个关节一个连续值。网络结构我一般用三层全连接第一层256个神经元第二层128个神经元第三层64个神经元。激活函数用Tanh或者ELU。Tanh的输出范围是-1到1适合控制任务ELU的梯度更平滑训练更稳定。我通常用ELU。import torch.nn as nn class ActorCritic(nn.Module): def __init__(self, obs_dim, action_dim): super().__init__() # 共享特征层 self.shared nn.Sequential( nn.Linear(obs_dim, 256), nn.ELU(), nn.Linear(256, 128), nn.ELU(), nn.Linear(128, 64), nn.ELU() ) # 策略头输出动作均值和对数标准差 self.actor_mean nn.Linear(64, action_dim) self.actor_log_std nn.Parameter(torch.zeros(action_dim)) # 价值头输出状态价值 self.critic nn.Linear(64, 1) def forward(self, obs): features self.shared(obs) action_mean torch.tanh(self.actor_mean(features)) action_std torch.exp(self.actor_log_std) value self.critic(features) return action_mean, action_std, value动作输出的tanh激活函数把动作限制在-1到1之间然后通过一个缩放因子映射到关节的实际角度范围。这个缩放因子需要根据机器人的关节限位来设定。比如髋关节的活动范围是-1.5到1.5弧度缩放因子就是1.5。3.3 观测空间与动作空间的设计细节观测空间的设计直接决定了机器人能感知到什么信息。太少的信息会导致机器人“盲人摸象”太多的信息会增加训练难度。我的观测空间包含以下几类信息躯干状态高度、俯仰角、滚转角、前进速度、侧向速度、偏航角速度。这些是机器人平衡的核心信息。关节状态12个关节的角度和角速度。这是机器人感知自身姿态的基础。接触状态左右脚的接触力大小。这是机器人感知地面接触的关键。上一帧动作上一时刻的关节目标角度。这给机器人提供了动作平滑性的参考。动作空间我选择目标关节角度而不是力矩。原因是角度控制更稳定训练更容易收敛。力矩控制虽然更灵活但容易导致机器人用蛮力而且对电机模型的要求更高。目标角度通过PD控制器转换成力矩PD参数需要仔细调节。注意观测信息一定要做归一化。不同物理量的量级差异很大比如高度是0.7米左右角速度可能是几弧度每秒接触力可能是几百牛顿。不做归一化的话网络很难学到有效的特征。我一般用运行均值方差来做归一化训练过程中动态更新。4. 训练流程与调参实战4.1 从零开始搭建训练环境搭建训练环境的第一步是安装依赖。我用的技术栈是Python 3.9 MuJoCo 2.3 Gym 0.26 PyTorch 1.13。这个组合经过多次验证兼容性最好。pip install mujoco2.3.0 pip install gym0.26.2 pip install torch1.13.1 pip install numpy1.23.5安装完成之后先跑一个简单的测试确认MuJoCo能正常渲染和仿真import mujoco import mujoco.viewer model mujoco.MjModel.from_xml_path(humanoid.xml) data mujoco.MjData(model) with mujoco.viewer.launch_passive(model, data) as viewer: while viewer.is_running(): mujoco.mj_step(model, data) viewer.sync()如果能看到机器人在窗口里掉下来说明环境没问题。接下来就是定义Gym环境接口把MuJoCo的仿真封装成标准的强化学习环境。4.2 训练过程中的关键参数调节训练参数对结果影响巨大。我整理了一份自己常用的参数表参数名推荐值说明学习率3e-4策略网络的学习率太大不收敛太小训练慢批量大小64每次更新的样本数经验回放大小2048每次收集的步数更新轮数10每批数据重复训练的轮数折扣因子0.99未来奖励的折扣行走任务用0.99比较合适GAE参数0.95优势估计的平滑参数裁剪比例0.2PPO的裁剪范围熵系数0.01鼓励探索太大导致策略随机价值损失系数0.5价值函数损失的权重这些参数不是绝对的需要根据具体任务调整。我的经验是先固定其他参数只调学习率和熵系数。学习率决定了训练能不能收敛熵系数决定了探索的充分程度。这两个调好了其他参数微调一下就行。训练过程中要监控几个关键指标平均回报、策略熵、价值损失、KL散度。平均回报应该稳步上升策略熵应该缓慢下降价值损失应该逐渐减小KL散度应该保持在0.01到0.05之间。如果KL散度突然变大说明策略更新太剧烈需要降低学习率或者增加裁剪比例。4.3 训练加速与并行化技巧纯CPU训练双足行走收敛需要几百万步可能要跑好几天。加速训练有几个方向第一用GPU加速网络前向和反向传播。PyTorch默认会用GPU但要注意把数据和模型都放到GPU上。对于小网络GPU的加速比可能不明显但对于大批量数据GPU优势很大。第二用向量化环境并行采集数据。Gym提供了VectorEnv接口可以同时跑多个环境实例。我一般用8到16个并行环境训练速度能提升5到10倍。from gym.vector import SyncVectorEnv def make_env(seed): def _init(): env HumanoidEnv() env.seed(seed) return env return _init envs SyncVectorEnv([make_env(i) for i in range(16)])第三用Isaac Gym做大规模并行训练。Isaac Gym可以在GPU上同时跑几千个环境训练速度比CPU并行快几十倍。但Isaac Gym的物理精度不如MuJoCo适合做前期的大规模探索后期再用MuJoCo做精细调优。实操心得并行环境不是越多越好。环境太多每个环境采集的数据就少策略更新频率降低反而可能影响收敛。我一般用8到32个环境根据任务复杂度调整。5. 仿真到真机的迁移与常见问题排查5.1 Sim-to-Real迁移的核心挑战仿真里训练好的策略直接放到真机上十有八九会摔。原因主要有三个第一物理参数不匹配。仿真里的摩擦系数、质量、惯性、电机响应特性都和真机有差异。仿真里地面摩擦系数是1.0真机可能是0.6机器人脚底一打滑就摔了。第二传感器噪声。仿真里的状态信息是精确的真机上的IMU有噪声关节编码器有量化误差接触传感器有延迟。这些噪声会让策略的输出抖动。第三执行器延迟。仿真里电机是瞬时响应的真机上有几十毫秒的延迟。这个延迟在快速行走时会导致相位错位机器人越走越晃。解决这些问题的核心思路是域随机化。在训练时随机改变仿真环境的物理参数让策略学会适应不同的条件。具体做法是每次重置环境时随机采样摩擦系数、质量、电机增益、传感器噪声等参数让策略在这些变化中学会鲁棒的控制。def randomize_domain(env): # 随机化摩擦系数 env.model.geom_friction[:, 0] np.random.uniform(0.5, 1.5) # 随机化躯干质量 env.model.body_mass[1] * np.random.uniform(0.9, 1.1) # 随机化电机增益 env.model.actuator_gainprm[:, 0] * np.random.uniform(0.8, 1.2) # 随机化传感器噪声 env.noise_scale np.random.uniform(0.0, 0.05)域随机化的范围需要仔细调节。范围太小策略学不到鲁棒性范围太大策略学不会走路。我的经验是先从小范围开始训练收敛后逐步扩大范围。比如摩擦系数先从0.8到1.2稳定后再扩大到0.5到1.5。5.2 常见训练失败模式与解决方案训练过程中会遇到各种奇怪的现象我整理了一份速查表现象可能原因解决方案机器人原地不动前进奖励太小存活奖励太大增大前进奖励权重降低存活奖励机器人疯狂抖动动作惩罚太小熵系数太大增大动作惩罚降低熵系数机器人一直摔倒直立奖励太小初始探索太随机增大直立奖励降低初始动作标准差训练不收敛学习率太大奖励量级差异大降低学习率归一化奖励策略突然崩溃KL散度太大更新太剧烈降低学习率增大裁剪比例仿真稳真机摔域随机化不足物理参数不匹配扩大域随机化范围增加传感器噪声还有一个常见问题是机器人学会“作弊”。比如你给“脚掌接触地面”奖励机器人可能学会一直拖着脚走因为这样接触时间最长。你给“躯干高度”奖励机器人可能学会踮着脚尖走。解决方法是定期检查机器人的行为视频发现异常行为就调整奖励函数。5.3 真机部署的实操步骤与注意事项真机部署的流程一般是仿真训练 - 策略导出 - 真机推理 - 安全保护。策略导出就是把训练好的网络权重保存成ONNX或者TorchScript格式方便在真机上加载。真机推理的频率一般是100Hz到1000Hz取决于控制器的性能。安全保护包括关节限位保护、力矩限幅、跌倒检测、紧急停止。# 策略导出示例 policy.eval() dummy_input torch.randn(1, obs_dim) torch.onnx.export(policy, dummy_input, policy.onnx, input_names[obs], output_names[action])真机部署时先让机器人吊在架子上试确认策略输出的动作范围合理不会突然打满关节。然后再放到地面上从慢速开始逐步加速。第一次上真机一定要有人扶着随时准备急停。注意真机上的观测信息一定要做和仿真里一样的归一化。如果仿真里用了运行均值方差真机上也要用同样的均值和方差不能重新计算。否则观测分布不一致策略输出会完全错误。6. 进阶方向与个人经验总结6.1 从行走扩展到跑跳与复杂地形学会走路只是第一步。同样的框架可以扩展到跑步、跳跃、上下楼梯、跨越障碍。扩展的关键在于课程学习先学走路再学跑步再学跳跃逐步增加难度。跑步和走路的区别在于腾空相。走路时至少有一只脚着地跑步时有双脚同时离地的阶段。奖励函数需要增加“腾空时间”奖励鼓励机器人跳起来。跳跃则需要增加“跳跃高度”奖励和“落地稳定性”奖励。复杂地形需要地形随机化。在仿真里生成随机的高度场、斜坡、台阶、碎石地面让机器人在这些地形上训练。地形随机化的难度也要逐步增加从平地到小斜坡再到大台阶。6.2 多智能体与迁移学习的可能性多智能体强化学习可以用于多机器人协作场景比如两个机器人一起抬东西。每个机器人是一个智能体共享一个全局奖励函数。训练时需要注意信用分配问题哪个机器人的动作对全局奖励贡献大。迁移学习可以用于跨机器人形态的迁移。比如在小型机器人上训练的策略迁移到大型机器人上。由于动力学参数不同直接迁移效果不好但可以用小型机器人的策略作为初始化在大型机器人上微调能显著加快收敛速度。6.3 我在实际项目中的几点体会做了这么多机器人强化学习的项目我最大的体会是奖励函数设计比算法选择重要十倍。很多人花大量时间对比PPO和SAC哪个好却不愿意花时间仔细设计奖励函数。实际上一个好的奖励函数用最简单的算法也能训出不错的效果一个糟糕的奖励函数用最先进的算法也训不出来。第二个体会是仿真环境的质量决定上限。仿真里的物理参数不准训练出来的策略再好真机上也白搭。我现在的习惯是每训练一个新任务先花两天时间校准仿真环境确保仿真里的行为和真机尽可能一致。第三个体会是训练日志比训练结果更重要。不要只看最终的平均回报要仔细分析训练过程中的每一个指标。策略熵的变化、价值损失的变化、KL散度的变化这些曲线里藏着大量信息。我经常通过分析日志发现奖励函数的缺陷然后调整后重新训练。最后分享一个小技巧定期保存策略快照并录制视频。训练过程中每隔一段时间保存一次模型权重然后加载不同的快照录制视频。对比不同阶段的视频能直观地看到机器人的行为变化比看曲线图有用得多。我就是在对比视频时发现机器人学会了“拖着脚走”的作弊行为然后调整奖励函数才纠正过来的。