尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

强化学习生成机器人跑步姿态:优化目标函数而非模仿人类

强化学习生成机器人跑步姿态:优化目标函数而非模仿人类 跑步姿态到底应该怎么控制很多人下意识的想法是让人形机器人“学人一样跑步”。但实际做项目时你会发现如果把“像人”当成优化目标模型很容易走偏——它会去模仿人类跑姿里的细枝末节比如手臂摆动的幅度、脚掌落地的角度却忽略了真正重要的事情保持速度、保持稳定、别摔倒。这不是某个团队的个案而是强化学习在机器人运动控制中非常典型的“目标错位”问题。人形机器人需要跑的“样子”更需要跑的“能力”。用强化学习生成跑步姿态核心不是“模仿人类”而是“优化一个目标函数”。姿态是结果不是目标。这篇文章想讲清楚三件事第一什么叫“让机器人保持跑步姿态”它和“让机器人长得像人在跑步”有什么本质区别 第二如何用强化学习把跑步姿态的生成问题定义成可训练的优化问题 第三从奖励设计、环境搭建到训练验证一条完整的最小实现路径是什么。本文面向有一定强化学习基础、但对机器人运动控制接触不多的开发者。你不需要有机器人硬件仿真环境里就能跑通整个逻辑。如果你正准备用强化学习做机械臂、四足、人形机器人的运动控制这篇文章的思路可以复用。1. “保持跑步姿态”不是“保持人形”先说清楚标题里的概念。“保持跑步姿态”和“保持人形”是两件事保持人形意味着机器人同时要维持类似人类的身体结构和外形特征。这是形态学的约束。保持跑步姿态意味着机器人要维持一种动态状态——前向速度不为零、身体不失控、周期性地蹬地和落地。这是运动学的约束也是动力学的约束。换句话说“人形”描述的是静态长相“跑步姿态”描述的是动态行为。在强化学习框架里这两者的区别会直接体现为奖励函数的设计。如果你把“像人形”作为奖励项比如对关节角度和人类运动捕捉数据的相似度给予加分模型会把大量策略容量花在“形状相似”上。但如果你把“保持跑步姿态”作为目标比如给前向速度加分、给摔倒扣分、给能量消耗惩罚模型会自己找出一条能跑起来的策略。这条策略可能像人也可能只是“像一台能跑的机器”。从项目角度来看后一种做法才符合机器人的价值逻辑。落地应用关心的是机器人能不能稳定执行任务不是它的姿态是否具有人类美感。跑步姿态的“像人”属性只有在仿生研究、表演场景或特定交互场景里才有意义。这也是强化学习最适合机器人运动控制的地方它可以跳过人类示范直接从目标函数出发搜索策略空间。传统方法需要工程师手工设计控制律每一步都依赖对系统动力学的精确建模而强化学习用大量试错替代手工调参把“怎么跑”的问题转成“怎么定义奖励”的问题。但这恰恰是难点所在奖励定义得不好机器人会学到一堆“钻空子”的行为。比如你想让它跑得快它可能直接跳起来翻滚你想让它保持身体直立它可能站着不跑。后面我们会专门讨论奖励设计里的这些坑。2. 运动控制的两种方法论MPC 与深度强化学习在进入代码之前有必要理清强化学习在机器人运动控制里的定位。目前主流方法有两类一类是基于模型的预测控制MPC另一类是基于学习的深度强化学习DRL。MPC 的思想是在每个控制周期根据当前状态和系统动力学模型在线求解一个有限时域的最优控制问题把第一个控制量施加给机器人然后滚动优化。它的优点是稳定性有理论保证缺点是依赖精确的动力学模型而且在线求解代价高。深度强化学习的思路完全不同。它在仿真环境里让智能体不断试错通过奖励信号学习从状态到动作的映射。训练完成之后推理阶段只是一个神经网络前向传播速度极快而且不需要在线求解优化问题。它不依赖精确模型但对奖励设计、环境真实度、训练稳定性都有更高要求。两类方法不是互斥的。很多实际系统是混合架构用强化学习学出高层运动策略比如要不要跑、跑多快、要不要跳再用 MPC 或传统控制做底层关节力矩跟踪。对于跑步姿态生成这个任务深度强化学习的优势尤其明显对比维度MPC深度强化学习动力学模型依赖强依赖模型误差直接影响控制效果弱依赖可在仿真中自主学习复杂动作表达能力受限于优化问题建模策略网络可以表达高度非线性映射运行期计算开销在线求解开销大神经网络前向推理开销小调参方式手调节权重和预测时域调奖励函数和训练超参数适合场景单一动作、高可靠性要求多动作、复杂动态、难建模场景从材料看目前很多人形机器人项目包括智元 D1、以及各种四足机器人的跑步动作都在用强化学习做运动控制。原因就是跑步这种高频动态动作传统控制方法很容易在模型误差和延迟面前失效。3. 跑步姿态怎么描述成强化学习问题强化学习有一套标准范式智能体在环境中观察状态根据策略选择动作环境反馈奖励智能体更新策略。放到跑步姿态生成上我们要把物理世界的跑步动作映射到这个框架里。3.1 状态空间Observation Space状态空间是机器人“能看到的信息”一般包括机身姿态躯干的翻滚角、俯仰角、偏航角。角速度和线速度机身角速度、前向速度、垂向速度。关节状态各个关节的角度和角速度。上一时刻的动作用于平滑策略输出。外部参考想要的目标速度、目标方向等。特别要注意的是跑步是周期运动单帧状态不足以让策略判断“当前处于跑步周期的哪个相位”。所以实践中常把历史状态也拼进观测里或者用一个循环神经网络比如 GRU来编码时间信息。不过对于最小示例先用多帧状态拼接就能达到不错的效果。3.2 动作空间Action Space动作空间是策略网络输出的控制量。在跑步控制里有两种常见设计输出目标关节角度或目标关节速度由底层 PD 控制器转换成力矩。直接输出关节力矩。第一种更常见。原因是强化学习直接输出力矩时训练后期fine-tune难度更大动作空间维度高且尺度敏感输出目标位置/速度让PD控制做局部修正训练更稳定也更容易从仿真迁移到实物。3.3 奖励函数Reward Function这是整个项目最核心的部分。跑步姿态的好坏最终全由奖励函数定义。一个相对完整的跑步奖励至少包含以下项前向速度奖励实际前向速度与目标速度越接近奖励越高。存活奖励或摔倒惩罚机器人摔倒回合结束给一个较大的负奖励。躯干姿态约束躯干不能过度倾斜否则跑步会变成前扑或后仰。能量惩罚关节力矩或关节速度过大时给惩罚避免策略学到夸张动作。平滑性奖励相邻动作变化率不能太大否则仿真里的运动会很抖。关于奖励设计有一句工程口诀奖励要定义“目标达成”而不是定义“过程长相”。很多新手会把“像人跑”拆成很多关节角度的参考轨迹然后逐项给奖励结果策略网络学着学着就卡死在高维奖励迷宫里。更稳的做法是先定义几个关键物理量速度、稳定性、能耗让策略自己探索出符合这些物理量的动作。我们会在第五节的代码里给出一个具体实现。4. 仿真环境搭建与训练配置跑步控制不能直接在实体机器人上训练首次训练就大概率摔坏硬件。标准做法是先在仿真环境里训练和验证策略通过 sim-to-real 迁移到实体。常见的仿真平台有两个方向MuJoCo轻量、快速、接触建模好学术界用得很多适合单机器人运动控制。Isaac Gym / Isaac Lab支持 GPU 并行大规模训练适合大规模强化学习尤其适合人形、四足这种高维运动控制。本文的示例代码基于 MuJoCo 的通用接口来写这样无论你用的是 MuJoCo 自带环境还是自定义机器人模型代码结构都能复用。训练需要的基础依赖如下pip install mujoco gymnasium stable-baselines3这里要特别说明版本问题。MuJoCo 的版本迭代比较快不同版本的 API 有差异。本文不绑定某个具体版本演示的是通用思路。如果你用的是较新的 MuJoCo 2.3配合 gymnasium 的MujocoEnv是不错的选择。从热词里可以看到很多人也在搜“机器人仿真平台选择”这里给一个判断标准如果你的目标是快速验证跑步控制算法MuJoCo 就够用如果你的目标是工业级、大规模并行训练Isaac 系列更合适。仿真平台不是越高级越好关键看你的训练规模和调参周期。5. 核心代码跑步姿态奖励设计5.1 定义奖励函数先看最关键的奖励函数部分。下面这段代码演示了如何把“保持跑步姿态”的目标翻译成强化学习能理解的数值。# 文件路径reward.py import numpy as np class RunningReward: def __init__(self, target_speed2.0, dt0.02): self.target_speed target_speed self.dt dt # 奖励权重 self.w_speed 1.0 self.w_pose 0.5 self.w_energy 0.05 self.w_smooth 0.1 self.prev_actions None def reset(self): self.prev_actions None def compute( self, current_speed, base_orientation, joint_actions, joint_torques, ): reward 0.0 info {} # 1. 前向速度奖励越接近目标速度奖励越高 speed_error np.abs(current_speed - self.target_speed) speed_reward np.exp(-speed_error) reward self.w_speed * speed_reward info[speed_reward] speed_reward # 2. 躯干姿态奖励保持躯干基本水平但允许适度前倾 roll, pitch, _ base_orientation # 滚转角应接近0俯仰角允许前倾负值表示前倾但不超过15度 roll_penalty np.abs(roll) pitch_penalty max(0.0, abs(pitch) - 0.26) # 约15度 pose_reward np.exp(-(roll_penalty * 2.0 pitch_penalty * 3.0)) reward self.w_pose * pose_reward info[pose_reward] pose_reward # 3. 能量惩罚关节力矩过大时惩罚 energy np.mean(torques ** 2) energy_penalty energy * self.w_energy reward - energy_penalty info[energy_penalty] energy_penalty # 4. 动作平滑性惩罚相邻控制量突变时惩罚 if self.prev_actions is not None: action_diff np.mean(np.abs(joint_actions - self.prev_actions)) smooth_penalty action_diff * self.w_smooth reward - smooth_penalty info[smooth_penalty] smooth_penalty self.prev_actions joint_actions.copy() # 5. 摔倒惩罚躯干滚转角过大或速度过低视为失败 if abs(roll) 1.0 or current_speed 0.1: reward - 10.0 info[fall] True else: info[fall] False return reward, info这段代码的关键点有三处第一速度奖励用了指数函数而不是线性函数。指数函数在接近目标速度时梯度更明显策略网络更容易收敛到“保持目标速度”的行为而不是在一个宽泛的区间内摇摆。第二俯仰角允许一定的前倾空间。真正跑步时身体不可能绝对竖直。如果你把躯干角度约束得太死模型会学到僵硬的“直立跳步”看起来会非常不自然。第三摔倒的判定条件是“滚转角过大或速度过低”。这意味着机器人在试图保持跑步状态时如果它停下来、或者翻滚倒地都会受到大的惩罚。这个设计直接呼应标题我们要的是“保持跑步”不是“站在那里像个人”。5.2 定义观测空间和动作空间接着定义跑步任务的环境接口。这里我们用 gymnasium 的标准接口封装# 文件路径running_env.py import numpy as np import gymnasium as gym from gymnasium import spaces from reward import RunningReward class RunningEnv(gym.Env): 一个简化的跑步环境。 实际使用时需要接入 MuJoCo 或其他机器人仿真器。 这里的模拟计算只是演示接口设计思路。 metadata {render_modes: []} def __init__(self, target_speed2.0, dt0.02): super().__init__() self.target_speed target_speed self.dt dt # 简化状态躯干姿态(3)速度(3)关节角度(6)关节速度(6) self.obs_dim 3 3 6 6 # 简化动作6个关节的目标角度 self.action_dim 6 self.observation_space spaces.Box( low-np.inf, highnp.inf, shape(self.obs_dim,), dtypenp.float32 ) self.action_space spaces.Box( low-1.0, high1.0, shape(self.action_dim,), dtypenp.float32 ) self.reward_fn RunningReward(target_speedtarget_speed, dtdt) self.robot_state None self.step_count 0 self.max_steps 1000 def reset(self, seedNone, optionsNone): super().reset(seedseed) self.reward_fn.reset() self.step_count 0 # 初始化机器人状态实际项目中从仿真器获取 self.robot_state np.zeros(self.obs_dim, dtypenp.float32) return self.robot_state.copy(), {} def step(self, action): # 在实际项目中这里调用 MuJoCo 的 step 函数推进仿真 # 并从仿真器读取新的机器人状态 self.step_count 1 # 模拟从仿真器中读取状态仅用于示例 current_speed self.robot_state[3] # 假设索引3是前向速度 roll self.robot_state[0] pitch self.robot_state[1] # 计算奖励 reward, info self.reward_fn.compute( current_speedcurrent_speed, base_orientation(roll, pitch, 0.0), joint_actionsaction, joint_torquesnp.zeros_like(action), ) # 更新状态实际项目中由仿真器更新 self.robot_state self._simulate_forward(action) terminated info[fall] truncated self.step_count self.max_steps return self.robot_state.copy(), reward, terminated, truncated, info def _simulate_forward(self, action): # 简化状态推进仅演示。 # 实际项目中此函数不存在状态由 MuJoCo 推进。 delta np.random.normal(0, 0.01, sizeself.robot_state.shape) new_state self.robot_state delta return new_state.astype(np.float32)这段代码的接口设计是基于通用流程的演示不是完整的仿真器接入代码。你在实际项目中要做的是用 MuJoCo 加载机器人模型 XML 文件在reset里用mj_resetData重置仿真状态在step里调用mj_step推进仿真用传感器数据和关节状态填充self.obs_dim对应的状态向量。5.3 训练脚本使用 PPO 训练跑步策略奖励函数和环境接口都准备好之后训练阶段可以复用成熟的强化学习算法。目前机器人运动控制领域最常用的算法是 PPOProximal Policy Optimization它的优点是训练相对稳定超参数不敏感适合多数连续动作控制问题。# 文件路径train.py from stable_baselines3 import PPO from stable_baselines3.common.callbacks import CheckpointCallback from running_env import RunningEnv def make_env(): return RunningEnv(target_speed2.0) # 创建环境 env make_env() # PPO 关键超参数 model PPO( MlpPolicy, env, learning_rate3e-4, n_steps2048, batch_size64, n_epochs10, gamma0.99, gae_lambda0.95, clip_range0.2, ent_coef0.0, verbose1, tensorboard_log./tb_logs/, ) # 定期保存模型 checkpoint_callback CheckpointCallback( save_freq10000, save_path./models/, name_prefixrunning_policy, ) # 开始训练 model.learn(total_timesteps1_000_000, callbackcheckpoint_callback) # 保存最终模型 model.save(./models/running_policy_final.zip)代码中需要解释几个关键点n_steps是每次策略更新前收集的经验步数。跑步任务是连续控制任务2048是一个合理起点。clip_range是 PPO 的裁剪范围控制每次更新的幅度。0.2是稳定默认值。ent_coef是熵系数控制探索强度。机器人运动控制里通常设置为0.0或很小因为过高的探索会导致关节抖动明显。如果你的训练目标是多人形机器人并希望利用 GPU 并行加速建议换用 Isaac Gym 配套的 RSL RL 框架PPO 的基础逻辑是一致的只是并行化和仿真方式不同。6. 运行结果与效果验证训练跑起来之后不能只看 loss 曲线下降了就说成功。跑步任务要验证的是策略是否能真的维持跑步状态。验证方法分两步6.1 查看训练曲线在 TensorBoard 中查看 reward 曲线的趋势tensorboard --logdir ./tb_logs/正常情况下你会看到总奖励逐步上升最后稳定在一个平台期。速度奖励会收敛到接近 1.0表示实际速度接近目标速度摔倒频率会逐步下降。6.2 仿真中评估策略训练完成后写一个简单的评估脚本# 文件路径evaluate.py import time import numpy as np from stable_baselines3 import PPO from running_env import RunningEnv model PPO.load(./models/running_policy_final.zip) env RunningEnv(target_speed2.0) obs, _ env.reset() episode_reward 0.0 speed_log [] fall_count 0 for step in range(500): action, _ model.predict(obs, deterministicTrue) obs, reward, terminated, truncated, info env.step(action) episode_reward reward # 假设 obs 索引3是前向速度这里仅演示 speed_log.append(float(obs[3])) if terminated: fall_count 1 obs, _ env.reset() time.sleep(0.02) print(fTotal reward: {episode_reward:.2f}) print(fNumber of falls: {fall_count}) print(fAverage speed: {np.mean(speed_log):.2f} m/s)运行结果的关键判断标准是平均速度是否接近目标速度比如 2.0 m/s500 步内摔倒次数为 0 或极少速度曲线是否呈现周期性波动。跑步本身就是周期运动速度小幅波动是正常的但如果波动幅度过大说明策略没有形成稳定的跑步步态。如果失败第一步要看奖励曲线。奖励曲线一直上不去通常问题出在奖励函数设计上而不是算法上。优先检查速度奖励和摔倒惩罚的权重配比。7. 常见问题与排查方法跑步姿态生成是典型的高维连续控制问题训练过程中状况百出。下面这张表整理了我认为最常遇到的四类问题问题现象可能原因排查方式解决方案机器人原地不动或蹲下存活奖励太高模型学会“不动”检查奖励分解观察是否存在“存活奖励”压过速度奖励降低存活奖励权重提高摔倒惩罚取消stand-still的隐身奖励机器人翻滚前行或跳跃速度奖励被“钻空子”检查关节角度是否超出合理范围有没有非正常接触增加关节角度限制惩罚增加躯干姿态约束机器人跑步姿势僵硬、关节抖动作平滑惩罚过小或 ent_coef 过大查看相邻动作差异曲线增加平滑惩罚权重调低策略网络学习率训练后期奖励曲线震荡奖励函数权重不均衡或学习率过高查看各个奖励子项的独立曲线降低学习率调整奖励权重延长训练步数这里要特别说一个容易被忽略的坑奖励函数里不要加入容易导致“零梯度”的硬编码条件。比如你在摔倒判定里写if speed 0.1: reward - 10这个惩罚只在极少数状态生效策略很难从这个稀疏反馈里学到东西。更好的做法是用连续惩罚比如penalty max(0, 0.1 - speed) * k让惩罚随速度下降而连续增大。另一个和热词“强化学习遇到错误奖励”相关的点奖励设计本身就在定义“什么是对的”。如果目标速度写错了、姿态角解算符号反了、或者把当前速度传成了历史速度模型学到的一定是错误的跑步姿态。遇到这种情况别急着调模型超参数先检查奖励输入数据的计算链路是否正确。8. 最佳实践与工程建议跑步姿态生成从能跑到跑得稳、跑得像、跑得省中间还有很多工程细节。基于实际项目经验我把值得注意的点按优先级整理一下。8.1 奖励设计从简单到复杂逐步加项不要一开始就设计一个带七八个奖励项的复杂函数。先只用速度奖励和摔倒惩罚让模型跑起来再逐步加入姿态约束、能量惩罚、平滑惩罚。每加一项奖励单独跑一组实验观察它对行为的影响。这个过程可能很枯燥但非常必要。奖励设计不是拍脑袋写权重而是要做消融实验。8.2 状态输入注意延迟与历史信息如果你只把当前帧状态输入策略网络跑步时会出现明显的“节奏滞后”。解决方案很简单把最近 N 帧的状态拼成一个输入向量。这在代码层面只是改了观测维度但对步态的周期性表达帮助很大。8.3 仿真到实物的迁移sim-to-real仿真里练好的策略迁移到实体机器人时通常会遇到“现实差距”问题。常见做法有三种在仿真中加入随机化随机化地面摩擦力、机器人质量、关节延迟等参数让策略对参数变化不那么敏感。这种方法叫 domain randomization是当前最实用的迁移手段。使用更精确的接触模型和动力学参数这能缩小仿真和现实的差距但成本高。在策略输出后加一层底层控制器缓冲策略输出目标关节角度或速度底层用高增益 PD 控制跟踪避免仿真中“理想力矩”在实机上无法复现的问题。这三个做法可以组合使用。从材料看当前人形机器人公司普遍采用的是“大规模仿真训练 domain randomization 底层控制器”的组合路线。8.4 训练资源与时间强化学习训练跑步策略不是几分钟能完成的事。在单台 GPU 上MuJoCo 单环境跑 100 万步可能耗时数小时甚至更久。如果使用 Isaac Gym 的 GPU 并行版本多个环境并行训练可以把时间压缩到几十分钟。如果你预算有限先用 MuJoCo 把算法和奖励调通再切换到 GPU 并行框架做大规模训练这个策略最省钱也最能培养对算法细节的理解。8.5 记录实验配置跑步训练涉及超参数、奖励权重、机器人模型、仿真参数等大量配置。强烈建议用配置文件统一管理而不是在代码里写死。例如# 文件路径config.yaml target_speed: 2.0 dt: 0.02 reward_weights: speed: 1.0 pose: 0.5 energy: 0.05 smooth: 0.1 ppo: learning_rate: 0.0003 n_steps: 2048 batch_size: 64 n_epochs: 10 gamma: 0.99 gae_lambda: 0.95 clip_range: 0.2 sim: env_name: mujoco robot_model: humanoid.xml num_envs: 1这样每次实验跑完可以完整回溯是哪个参数变化导致的行为改变。对运动控制这类高度依赖实验记录的项目来说这比什么都重要。9. 总结与后续学习方向回到文章开头的问题强化学习让机器人保持跑步姿态而不是人形本质上是把“像人”的目标替换成了“跑起来、跑得稳、跑得省”的目标。这种目标错位的纠正是运动控制项目走向实用的关键一步。本文顺着一条完整链路展开了这个主题从运动控制的方法论对比到强化学习问题的状态、动作、奖励定义再到仿真环境的搭建、核心代码实现、训练验证、问题排查和工程化建议。如果只看一句话结论那就是先用简单的奖励函数让机器人学会跑再逐步加入约束让姿态变好最后通过 domain randomization 迁移到实物。如果你准备继续深入我建议按下面三个方向推进第一研究奖励塑形reward shaping的进阶技巧。跑步只是一个起点跳跃、转身、上下坡这些动态动作对奖励设计的要求更高。第二理解 sim-to-real 的更多方法。包括系统辨识、对抗扰动训练、在线自适应等。第三尝试更先进的算法和框架。PPO 是很好的基线但 SAC、TD3、以及基于模型的强化学习在样本效率和策略平滑性上各有优势。特别是“基于模型强化学习”最近在机器人领域讨论度很高它能在训练中利用动力学模型预测未来状态大幅减少真实仿真步数。最后提醒一句这类项目最忌讳“奖励函数一写马上丢上去跑”。花在研究奖励设计、观察行为曲线、调整权重上的时间通常比训练本身更多但这部分工作决定了最终策略的质量上限。如果你正准备开始动手建议先克隆一个简单的人形机器人模型把本文的代码框架套上去跑通一次完整的“仿真训练—评估—调整”循环。这个循环跑通之后再谈更高阶的算法和硬件迁移会更有底。
返回列表