☰
强化学习生成机器人跑步姿态:优化目标函数而非模仿人类
2026/10/13 7:37:19 网站建设 项目流程

跑步姿态到底应该怎么控制?很多人下意识的想法是:让人形机器人“学人一样跑步”。但实际做项目时你会发现,如果把“像人”当成优化目标,模型很容易走偏——它会去模仿人类跑姿里的细枝末节,比如手臂摆动的幅度、脚掌落地的角度,却忽略了真正重要的事情:保持速度、保持稳定、别摔倒。

这不是某个团队的个案,而是强化学习在机器人运动控制中非常典型的“目标错位”问题。人形机器人需要跑的“样子”,更需要跑的“能力”。用强化学习生成跑步姿态,核心不是“模仿人类”,而是“优化一个目标函数”。姿态是结果,不是目标。

这篇文章想讲清楚三件事:

第一,什么叫“让机器人保持跑步姿态”,它和“让机器人长得像人在跑步”有什么本质区别; 第二,如何用强化学习把跑步姿态的生成问题定义成可训练的优化问题; 第三,从奖励设计、环境搭建到训练验证,一条完整的最小实现路径是什么。

本文面向有一定强化学习基础、但对机器人运动控制接触不多的开发者。你不需要有机器人硬件,仿真环境里就能跑通整个逻辑。如果你正准备用强化学习做机械臂、四足、人形机器人的运动控制,这篇文章的思路可以复用。

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_speed=2.0, dt=0.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_speed=2.0, dt=0.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, high=np.inf, shape=(self.obs_dim,), dtype=np.float32 ) self.action_space = spaces.Box( low=-1.0, high=1.0, shape=(self.action_dim,), dtype=np.float32 ) self.reward_fn = RunningReward(target_speed=target_speed, dt=dt) self.robot_state = None self.step_count = 0 self.max_steps = 1000 def reset(self, seed=None, options=None): super().reset(seed=seed) self.reward_fn.reset() self.step_count = 0 # 初始化机器人状态,实际项目中从仿真器获取 self.robot_state = np.zeros(self.obs_dim, dtype=np.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_speed=current_speed, base_orientation=(roll, pitch, 0.0), joint_actions=action, joint_torques=np.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, size=self.robot_state.shape) new_state = self.robot_state + delta return new_state.astype(np.float32)

这段代码的接口设计是基于通用流程的演示,不是完整的仿真器接入代码。你在实际项目中要做的是:

  1. 用 MuJoCo 加载机器人模型 XML 文件;
  2. 在reset里用mj_resetData重置仿真状态;
  3. 在step里调用mj_step推进仿真;
  4. 用传感器数据和关节状态填充self.obs_dim对应的状态向量。

5.3 训练脚本:使用 PPO 训练跑步策略

奖励函数和环境接口都准备好之后,训练阶段可以复用成熟的强化学习算法。目前机器人运动控制领域最常用的算法是 PPO(Proximal 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_speed=2.0) # 创建环境 env = make_env() # PPO 关键超参数 model = PPO( "MlpPolicy", env, learning_rate=3e-4, n_steps=2048, batch_size=64, n_epochs=10, gamma=0.99, gae_lambda=0.95, clip_range=0.2, ent_coef=0.0, verbose=1, tensorboard_log="./tb_logs/", ) # 定期保存模型 checkpoint_callback = CheckpointCallback( save_freq=10000, save_path="./models/", name_prefix="running_policy", ) # 开始训练 model.learn(total_timesteps=1_000_000, callback=checkpoint_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_speed=2.0) obs, _ = env.reset() episode_reward = 0.0 speed_log = [] fall_count = 0 for step in range(500): action, _ = model.predict(obs, deterministic=True) 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(f"Total reward: {episode_reward:.2f}") print(f"Number of falls: {fall_count}") print(f"Average speed: {np.mean(speed_log):.2f} m/s")

运行结果的关键判断标准是:

  • 平均速度是否接近目标速度(比如 2.0 m/s);
  • 500 步内摔倒次数为 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)

仿真里练好的策略迁移到实体机器人时,通常会遇到“现实差距”问题。常见做法有三种:

  1. 在仿真中加入随机化:随机化地面摩擦力、机器人质量、关节延迟等参数,让策略对参数变化不那么敏感。这种方法叫 domain randomization,是当前最实用的迁移手段。
  2. 使用更精确的接触模型和动力学参数:这能缩小仿真和现实的差距,但成本高。
  3. 在策略输出后加一层底层控制器缓冲:策略输出目标关节角度或速度,底层用高增益 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、以及基于模型的强化学习在样本效率和策略平滑性上各有优势。特别是“基于模型强化学习”最近在机器人领域讨论度很高,它能在训练中利用动力学模型预测未来状态,大幅减少真实仿真步数。

最后提醒一句:这类项目最忌讳“奖励函数一写,马上丢上去跑”。花在研究奖励设计、观察行为曲线、调整权重上的时间,通常比训练本身更多,但这部分工作决定了最终策略的质量上限。如果你正准备开始动手,建议先克隆一个简单的人形机器人模型,把本文的代码框架套上去,跑通一次完整的“仿真训练—评估—调整”循环。这个循环跑通之后,再谈更高阶的算法和硬件迁移会更有底。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询