中国人形机器人运动会上,全自主机器人连续打破5项人类纪录,这个说法真正有技术价值的地方,不在于“速度比人快”或“跳得比人高”,而在于“全自主”三个字。让机器人在没有遥控、没有预设脚本、没有人工干预的情况下,自己感知环境、判断状态、规划动作、维持平衡,并完成高动态运动任务,这是一个典型的具身智能工程问题。文章不讨论赛事细节,而是拆解一套可以实现“全自主运动”的技术链路:从系统模块、仿真环境、强化学习策略,到量化验证、常见排错和生产落地。适合刚接触人形机器人控制、想用强化学习训练运动策略,或者在多个控制模块之间做集成的开发者阅读。
读完这篇文章,可以掌握四件事:一是理解人形机器人全自主运动需要哪些模块协同;二是用 MuJoCo 搭建一个最小运动控制实验环境;三是用 PPO 训练一个奔跑策略并设置合理的奖励函数;四是知道从仿真到真机时最容易出问题的地方,以及对应的排查顺序。
1. 全自主运动能力的技术拆解:从“会动”到“能赢”需要哪些模块
1.1 “全自主”不是遥控,也不是预设轨迹
很多人一听到机器人跑步,第一反应是“硬件扭矩够大就行”。如果把机器人放在跑步机上,给一组固定的关节角度轨迹,确实可以跑起来,但这只能叫“预设轨迹复现”,不能叫全自主运动。
全自主系统是一个完整的感知-决策-控制闭环:机器人需要知道自己在哪、周围是什么、前方目标方向怎么走、下一步脚落在哪里、当前姿态是否需要修正。在运动会场景里,赛道可能有光照变化、地面摩擦差异、观众席障碍物、临时凸起等不确定因素。机器人不能提前背下整条赛道的轨迹,必须在运动过程中实时感知并调整。
所以“全自主”有三个技术难点:
- 环境感知不确定:相机会受光照影响,深度传感器在透明或反光物上失效,地面材质无法提前全部标定。
- 高动态运动稳定性:奔跑、跳跃时质心速度变化快,脚底接触时间短,姿态稍微偏差就会摔倒。
- 上层规划与底层控制的配合:规划层给出落点,底层控制必须在一个控制周期内把力矩算出来,两者频率和延时不能错位。
1.2 人形机器人运动控制的四大模块
把“全自主运动”拆开看,通常包含四个模块:感知、状态估计、运动规划和运动控制。它们不是串行执行一次就结束,而是每个控制周期都在循环。
| 模块 | 输入 | 输出 | 常用方法 | 失效表现 |
|---|---|---|---|---|
| 感知 | 相机、激光雷达、IMU、关节编码器 | 障碍物位置、地面状态、目标方向 | 目标检测、语义分割、深度估计 | 撞到障碍物、踩空 |
| 状态估计 | 多传感器原始数据 | 关节位置、速度、质心位置、接触力 | 扩展卡尔曼滤波、无迹卡尔曼滤波、互补滤波 | 姿态漂移、落点误差大 |
| 运动规划 | 地图、目标位置、当前身体状态 | 足端落点序列、身体参考轨迹 | 模型预测控制、轨迹优化、步态生成器 | 动作僵硬、绕不开障碍物 |
| 运动控制 | 目标轨迹或高层速度命令 | 电机位置、速度、力矩指令 | PD控制、全身控制(WBC)、强化学习策略 | 抖动、摔倒、跟不上指令 |
模块之间最典型的接口数据流是这样的:
传感器数据 -> 感知 -> 障碍物/目标信息 ↓ 运动规划 -> 参考轨迹/落点 ↓ 运动控制 -> 电机指令 ↓ 真实执行 -> 新的传感器数据这里要注意,感知和状态估计容易混。感知回答“外部世界有什么”,状态估计回答“机器人自己是什么姿态”。在奔跑任务里,如果 IMU 数据没处理好,机器人会以为自己还站着,实际已经倾斜,最后反应慢半拍导致摔倒。
1.3 为什么“打破人类纪录”是合理的评估基准
运动会项目里的“人类纪录”本质是一组可量化的物理指标。奔跑比速度,跳跃比高度或距离,举重比重量。这些指标有一个共同点:可以跨设备、跨算法、跨时间对比。
对机器人运动算法来说,这套基准很有价值:
- 避免“看起来能走”的主观评价。一个机器人能不能稳定跑完 10 米,计时器会给出客观答案。
- 提供横向比较的难度标尺。同一套算法在不同硬件上的表现,可以折算成与人类纪录的百分比。
- 迫使开发者关注成功率。单次成绩刷出来没有工程价值,连续十次、百次都能完成才算稳定。
所以“打破人类纪录”不只是营销话术,它实际上把机器人运动控制从“演示验证”推到了“竞技量化和工程评估”层面。
2. 从零搭建一套类人形机器人运动控制实验环境
2.1 仿真平台与物理引擎选型
在实际项目里,不建议一上来就在真机上调试,尤其奔跑、跳跃这类高动态任务,摔几次就可能损坏硬件。先用仿真环境验证算法,确认策略基本稳定后再迁移到真机,是性价比最高的路径。
| 仿真平台 | 适用场景 | 优势 | 不足 | 推荐环境 |
|---|---|---|---|---|
| MuJoCo | 运动控制、强化学习 | 物理精度高、速度快、接触稳定 | 图像渲染较弱 | 学习入门、算法验证 |
| PyBullet | 机械臂、移动机器人 | 生态成熟、集成方便 | 高动态接触精度相对一般 | 教学、抓取实验 |
| Isaac Gym / Isaac Sim | 大规模并行强化学习 | 支持 GPU 并行、场景丰富 | 环境配置较重、显存占用高 | 训练大规模策略 |
如果目标是复现“全自主奔跑”类任务,推荐先用 MuJoCo 把状态、动作、奖励函数跑通,再迁移到 Isaac Gym 做大规模并行训练。专栏后续内容也以 MuJoCo 为例,因为它的安装和使用相对轻量。
注意:仿真平台只是实验环境,不是最终部署目标。仿真的物理参数再精确,也与真实世界有偏差。后面迁移真机时,必须加入领域随机化。
2.2 项目结构与配置示例
建议从一开始就使用结构化项目目录,避免后续训练、评估、部署时文件散落各处。
humanoid_sport/ ├── config/ │ └── robot.yaml ├── models/ │ └── humanoid.xml ├── scripts/ │ ├── stand_ctrl.py │ ├── train_ppo.py │ └── evaluate.py ├── logs/ ├── runs/ └── requirements.txtrequirements.txt是学习环境的最小依赖:
mujoco>=2.3.0 numpy>=1.24 pyyaml>=6.0 gymnasium>=0.29 stable-baselines3>=2.0 tensorboard>=2.14注意,stable-baselines3和mujoco的版本需要匹配,落地前先确认自己项目的版本。不要直接复制依赖版本到生产环境。
config/robot.yaml用来管理仿真和控制器参数:
sim: timestep: 0.002 control_frequency: 500 gravity: [0, 0, -9.81] ground_friction: 1.0 robot: model_path: "models/humanoid.xml" initial_height: 0.95 joint_names: - "hip_yaw_L" - "hip_pitch_L" - "hip_pitch_R" - "knee_pitch_L" - "knee_pitch_R" - "ankle_pitch_L" - "ankle_pitch_R" controller: kp: 200 kd: 10 target_height: 1.0这里几个参数需要重点理解:
timestep是物理仿真步长,越小精度越高,但计算越慢。奔跑任务通常取 0.002 秒到 0.005 秒。control_frequency是控制频率。500Hz 意味着每 0.002 秒执行一次控制计算,也就是每个物理步都控制一次。kp和kd是 PD 控制器的比例和微分系数。kp决定关节抗偏离能力,kd决定阻尼。kp过大会产生抖动,kp过小则机器人像“面条”一样松软。
2.3 最小运动控制脚本:让机器人保持站立
先不要直接训练奔跑,从“站立”开始。站立是稳定控制的基础,也是很多调试问题的暴露点。
下面的代码示例使用 MuJoCo 加载人形机器人模型,并用 PD 控制让机器人保持某个体态。代码是简化写法,展示控制逻辑,实际项目需要根据自己模型的关节和致动器映射关系调整:
import time import mujoco import numpy as np model = mujoco.MjModel.from_xml_path("models/humanoid.xml") data = mujoco.MjData(model) KP = 200.0 KD = 10.0 TARGET_HEIGHT = 1.0 # 示例关节名,按实际模型修改 joint_names = ["hip_pitch_L", "knee_pitch_L", "ankle_pitch_L", "hip_pitch_R", "knee_pitch_R", "ankle_pitch_R"] # 建立关节名到执行器索引的映射 actuator_index = {} for joint_name in joint_names: actuator_index[joint_name] = mujoco.mj_name2id( model, mujoco.mjtObj.mjOBJ_ACTUATOR, joint_name ) # 示例目标关节角度 target_angles = { "hip_pitch_L": -0.2, "knee_pitch_L": 0.4, "ankle_pitch_L": -0.2, "hip_pitch_R": -0.2, "knee_pitch_R": 0.4, "ankle_pitch_R": -0.2, } for step in range(5000): for joint_name in joint_names: jid = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_JOINT, joint_name) addr = model.jnt_qposadr[jid] # 简化速度索引映射,实际项目需要用 jnt_dofadr 准确映射 vel_index = jid current_q = data.qpos[addr] current_dq = data.qvel[vel_index] target_q = target_angles[joint_name] torque = KP * (target_q - current_q) - KD * current_dq actuator_id = actuator_index[joint_name] data.ctrl[actuator_id] = torque mujoco.mj_step(model, data) if step % 100 == 0: height = data.qpos[2] print(f"step={step}, height={height:.3f}")代码中的核心逻辑是经典的 PD 控制:
力矩 = 比例系数 * 目标角度与当前角度之差 - 微分系数 * 当前角速度比例项负责把关节拉向目标角度,微分项负责抑制速度,防止超调。
提醒:MuJoCo 中
data.qvel的速度索引和关节索引并不总是完全一致,依赖jnt_dofadr映射更准确。上面代码为了展示控制结构做了简化,正式工程里要补齐这一步。
运行命令:
cd humanoid_sport python scripts/stand_ctrl.py如果模型和配置正确,预期输出是一串逐步稳定的高度值,例如:
step=0, height=0.930 step=100, height=0.982 step=200, height=0.997 step=300, height=1.001如果高度持续下降或者数值出现NaN,优先检查三个方面:
- 模型是否存在自碰撞或自由度约束冲突。
- PD 增益是否过大,导致数值发散。
- 控制频率是否低于仿真频率,导致控制滞后。
3. 关键实现:用强化学习训练一个“全自主奔跑”策略
3.1 任务定义:目标速度、状态空间和动作空间
奔跑任务可以定义成:机器人从静止状态开始,在水平地面上达到并保持一个目标前进速度,例如 2.0m/s,并跑完一段固定距离。
强化学习环境需要明确三个要素:
- 状态空间:机器人自身的关键物理量。常用的是基座线速度、基座角速度、关节位置、关节速度、上一时刻动作,以及目标速度。
- 动作空间:策略输出的内容。常见设计是目标关节角度,再经由 PD 控制器转换成力矩;也可以直接输出力矩,但训练难度更大。
- 奖励函数:告诉策略什么行为是好的,什么是需要惩罚的。
用一个 Python 字典可以描述状态:
observation = { "base_linear_velocity": np.array([0.2, 0.0, 0.1]), "base_angular_velocity": np.array([0.0, 0.0, 0.0]), "joint_positions": np.array([...]), "joint_velocities": np.array([...]), "target_speed": 2.0, "last_action": np.array([...]), }实际训练时,字典会被展开成一维向量,作为策略网络的输入。
3.2 奖励函数设计:不是越快越好,而是“稳定地快”
奖励函数是全自主运动训练中最容易失控的部分。如果只奖励“前进速度快”,策略很可能学到向前冲然后摔倒,因为摔倒前瞬间速度最快。
一个比较稳的奖励函数至少包含四类信号:
import numpy as np def compute_reward(state, action, target_speed): vx = state["base_linear_velocity"][0] # 1. 速度跟踪奖励:速度越接近目标,奖励越高 speed_error = np.abs(vx - target_speed) / max(target_speed, 0.1) speed_reward = 1.0 - np.clip(speed_error, 0.0, 1.0) # 2. 存活奖励:每多坚持一步,获得一个小奖励 alive_reward = 0.005 # 3. 动作平滑惩罚:相邻动作变化越大,惩罚越大 action_rate_penalty = 0.01 * np.mean(np.square(state["last_action"] - action)) # 4. 高度惩罚:基座高度低于目标区间,惩罚增大 height_penalty = 1.0 * max(0.0, 1.0 - state["base_height"]) return speed_reward + alive_reward - action_rate_penalty - height_penalty各部分的含义:
- 速度跟踪奖励引导机器人朝目标速度前进,而不是盲目加速。
- 存活奖励让策略优先选择“不摔倒”的行为。
- 动作平滑惩罚抑制高频抖动,让动作更接近真实电机可以执行的范围。
- 高度惩罚是一个隐性的稳定性约束,基座塌下去了就惩罚。
3.3 用 PPO 训练奔跑策略
在运动控制领域,PPO(Proximal Policy Optimization)是常用的强化学习算法。下面的示例使用stable-baselines3训练一个简单的策略,实际大规模并行人形训练通常使用 Isaac Gym 系列环境,但 PPO 的算法思路一致。
from stable_baselines3 import PPO from envs.humanoid_runner import HumanoidRunnerEnv env = HumanoidRunnerEnv(target_speed=2.0) model = PPO( "MlpPolicy", env, n_steps=2048, batch_size=256, n_epochs=10, gamma=0.99, gae_lambda=0.95, clip_range=0.2, ent_coef=0.0, policy_kwargs=dict(net_arch=dict(pi=[256, 256], vf=[256, 256])), verbose=1, ) model.learn(total_timesteps=5_000_000) model.save("runs/runner_v1.zip")这里的关键超参数需要理解,不能直接照抄:
| 超参数 | 含义 | 调大影响 | 调小影响 |
|---|---|---|---|
n_steps | 每次更新前收集的步数 | 样本更多、更稳定,但更慢 | 更新频繁,但方差可能变大 |
batch_size | 每次梯度更新的样本量 | 更稳定,但内存占用高 | 收敛快但可能不稳定 |
n_epochs | 每个样本被复用的次数 | 更好利用数据,但可能过拟合 | 利用不充分 |
gamma | 折扣因子 | 更看长远收益 | 更看眼前收益 |
clip_range | 策略更新的最大步长限制 | 更新激进 | 更新保守 |
ent_coef | 探索熵系数 | 探索更多,收敛慢 | 更容易利用已有策略 |
3.4 从仿真到真机:sim-to-real 不能省
仿真训练出的策略在真机上直接运行,几乎一定会失败。学习环境里环境是固定的,而现实世界存在摩擦变化、电机延迟、硬件磨损、传感器噪声等不确定因素。
常用的改进手段包括:
- 领域随机化:在仿真里随机改变地面摩擦系数、机器人重量、电机力矩延迟、传感器噪声。
- 控制频率匹配:真机控制频率必须和仿真训练时的频率一致。
- 动作平滑:策略输出的动作要经过低通滤波或限制变化率,防止命令跳变。
- 紧急停止:真机实验必须设置急停按钮和安全绳,训练初期尤其重要。
例如,在仿真的每个 episode 开始前,随机摩擦系数:
import random random_friction = random.uniform(0.6, 1.4) env.sim.model.geom_friction[:, 0] = random_friction这样策略就不会过拟合到单一地面条件。
4. 验证与结果分析:如何量化“打破纪录”
4.1 实验指标与对比基线
训练完成后,不能只看训练记录里的 reward。还要在独立测试集上跑多轮,统计以下指标:
| 指标 | 含义 | 评估方式 |
|---|---|---|
| 平均速度 | 位移除以时间 | 运动捕捉或里程计 |
| 成功率 | 跑完固定距离且未摔倒的轮次占比 | 多次测试统计 |
| 稳定裕度 | 质心投影与支撑多边形的距离 | 根据接触力和关节数据计算 |
| 能量效率 | 关节总功耗除以距离 | 对关节功率积分 |
| 人工干预次数 | 需要人为接管或遥控的次数 | 系统日志记录 |
如果一项运动项目比拼的是“打破人类纪录”,那么最终报告至少要包含“最佳成绩”和“成功率”两类数据。只报告最佳成绩,无法说明策略稳定性。
4.2 数据记录与日志统计方法
建议在评估代码中持久化每个 episode 的回报、速度、成功与否,并定期输出统计结果:
import numpy as np from collections import deque history = deque(maxlen=100) success_flags = deque(maxlen=100) episode_return = 0.0 for step in range(total_steps): action, _states = model.predict(obs, deterministic=True) obs, reward, done, info = env.step(action) episode_return += reward if done: history.append(episode_return) success_flags.append(info.get("success", False)) print( f"avg_return={np.mean(history):.2f}, " f"success_rate={np.mean(success_flags):.2%}" ) episode_return = 0.0训练过程中,还可以通过 TensorBoard 查看 reward、episode length、成功率的曲线。出现曲线剧烈震荡时,先检查是否奖励函数某个项数值量级过大。
4.3 如何解读“打破纪录”的结果
假设测试场景是“机器人自主奔跑 10 米”,得到一组结果:
best_time: 4.8s success_rate: 92% avg_speed: 2.08m/s intervention_count: 0这说明策略在多数情况下可以全自主完成,且不需要人工接管。但如果成功率只有 60%,即使某一次跑出了特别快的时间,也不能认为系统已经具备“打破纪录”的能力。
可以把不同策略放入同一评估协议:
| 方案 | 平均速度 | 成功率 | 人工干预 | 部署成本 | 适用场景 |
|---|---|---|---|---|---|
| 预设轨迹 | 高 | 低 | 高 | 低 | 固定舞台表演 |
| 人工遥控 | 中 | 中 | 极高 | 中 | 演示、探索 |
| 强化学习全自主 | 中高 | 高 | 低 | 高 | 未知环境、生产任务 |
“全自主”的价值恰恰体现在低人工干预和高成功率上,而不是单一最高速度。
5. 常见问题排查:仿真站不稳、策略不收敛、真机抖动
5.1 仿真中机器人原地摔倒
现象:机器人刚启动就向前倒、向后倒,或者左右摇晃幅度越来越大。
原因和检查路径:
- 检查初始姿态:机器人是否以正常直立状态初始化。如果
qpos角度错误,初始就在倒。 - 检查 PD 增益:
kp太低,关节无法抵抗重力;kd太低,会产生振荡。 - 检查控制频率:如果控制周期大于物理步长太多,控制器响不过来。
- 检查模型材质:地面摩擦是否接近零。
推荐做法是先在 MuJoCo 的 viewer 里逐步观察质心高度和关节角度。如果质心高度从 0.95 米逐渐下降,先调kp;如果机器人像弹簧一样抖动,调大kd。
5.2 强化学习训练不收敛
现象:训练了几百万步,reward 没有上升,或者上升之后迅速掉回低位。
原因和检查路径:
- 奖励函数是否存在稀疏或冲突。如果速度奖励过大,策略很快学会原地快速摆腿而不是前进;如果高度惩罚过重,策略会“蹲下不动”来逃避惩罚。
- 状态空间是否缺失关键信息。比如没有基座速度,策略无法感知自己正在前进还是原地踏步。
- 动作尺度是否合理。如果动作空间范围远大于实际关节限位,策略只能在无效区域探索。
- 训练环境是否每次 reset 结果不一致导致方差过大。
排查方法:把 reward 拆开记录,分别看速度 reward、存活 reward、动作平滑 penalty 的曲线。哪一个曲线异常,就优先调哪一项。
5.3 真机部署后动作抖动和失控
现象:仿真里稳定奔跑的策略,到真机上开始高频抖动,或者迈出两步后突然摔倒。
原因和检查路径:
- 控制频率不一致。真机控制频率低于仿真,策略输出无法及时执行。
- 动作没有平滑。策略输出的目标角度直接发给电机,指令跳变产生抖动。
- 传感器噪声。真机 IMU 和关节编码器有噪声,仿真里可能没建模。
- 机械结构偏差。关节间隙、弹簧变形、零件对称性差异都会影响策略。
解决方向:先给动作加低通滤波,再增加传感器噪声建模和领域随机化。真机测试前,必须用手推动机器人测试安全性,并准备好急停。
5.4 全自主运动系统排查清单
以下清单可以直接复制到项目文档中:
部署前检查清单 [ ] 仿真步长和控制频率是否一致 [ ] 模型的自碰撞、关节限位是否符合实际硬件 [ ] 奖励函数每个项的量级是否合理 [ ] 训练时是否加入领域随机化 [ ] 策略输出是否经过平滑限制 [ ] 传感器噪声是否在仿真中建模 [ ] 真机实验是否有急停、安全绳或远程停止通道 [ ] 是否记录成功率、干预次数和稳定性指标6. 从运动会到生产落地:从 Demo 到稳定运行的工程要求
6.1 学习环境和生产环境的工程差异
能在运动会上展示的全自主机器人,和能在工厂连续运行 8 小时的机器人,中间还隔着大量工程工作。差异主要在硬件可靠性、软件架构、可观测性和安全机制上。
| 维度 | 学习/演示环境 | 生产环境 |
|---|---|---|
| 运行时长 | 几分钟 | 数小时以上 |
| 环境变量 | 固定或半固定 | 复杂、动态 |
| 失败处理 | 重新 reset | 必须诊断并恢复 |
| 日志 | 可有可无 | 必须完整可查 |
| 模型更新 | 离线训练后加载 | 需要灰度发布和回滚 |
| 安全 | 人和机器人隔开 | 人机可能共融 |
生产环境还需要增加配置外置化、日志采集、监控告警、远程诊断、模型版本管理。策略文件本身要放进模型仓库,不能靠本地文件覆盖。
6.2 工业场景的扩展路径:从奔跑速度到任务成功率
运动会里的奔跑、跳跃能力,本质上验证的是一套“高动态状态下维持稳定”的技术底座。这个底座可以迁移到多个工业场景:
- 复杂地形巡检:机器人自主判断台阶、斜坡、碎石路面。
- 仓储搬运:在狭窄通道中保持平衡并执行抓取或移载。
- 设备操作:需要双手和身体协调,例如操作阀门、按钮。
- 应急巡查:在光线不佳或复杂布局环境中执行任务。
扩展时,第一步不是继续调速度,而是把任务成功率提升到可接受阈值。例如巡检机器人要跑 100 次完整流程,至少 95 次不需要人工介入,才能进入小规模试点。
6.3 安全边界、数据合规与人机协同
全自主运动能力越强,安全边界越需要明确。机器人虽然能快速奔跑,但最终目标是可靠地服务于人,而不是在开放场景里展示极限能力。
落地时必须考虑:
- 碰撞检测和自动停机:感知到近距离障碍物时,策略应切换到安全姿态。
- 力矩和速度限制:默认运行参数不能越过硬件安全上限。
- 人工接管通道:现场人员应能通过急停按钮或远程指令让机器人停止。
- 数据合规:机器人携带相机在真实场景采集数据时,要遵守现场管理规定,涉及人员、隐私信息的图像必须做匿名化或过滤处理。
技术再强,也要设计“不做某些动作”的边界。全自主不等于无限制,而是在明确的约束条件下完成高难度任务。
回到开头提到的“打破 5 项人类纪录”,真正值得关注的不是数字本身,而是数字背后的系统能连续、可重复、低干预地完成高动态运动任务。对开发者来说,下一步最值得做的练习是:在仿真里实现“站立到匀速直线奔跑”的完整闭环,记录成功率和平均速度,再逐步加入随机地形和传感器噪声。跑通这个最小闭环后,再谈迁移到真机,会从容很多。