人形机器人感知跑酷框架深度评测
先明确几个关键判断:人形机器人在野外非结构化地形中实现高速运动,长期被视为足式机器人领域的终极难题。核心难点在于,双足系统与轮式底盘存在本质差异。轮式平台遇到沟壑只能停滞,而双足理论上可跨越复杂障碍——但前提是控制系统必须精确决策“落脚点”与“落脚姿态”。
近期,“Hiking in the Wild”感知跑酷框架提供了极具说服力的解决方案。其核心逻辑简洁高效:采用端到端训练,机器人直接从深度图像中学习关节动作,无需中间建图或外部状态估计,即可在野外以每秒2.5米的速度稳健行进。本文将从问题背景、关键技术到代码实现,逐层解析该方案。
1. 问题背景与研究动机
足式运动的核心挑战:从“盲走”到“视觉引导”
足式机器人运动控制本质上需解决两大问题:感知与控制如何耦合,以及如何从被动响应转向主动预判。
传统“盲走”方法依赖本体感知(如关节力矩、IMU)适应地形。面对草地、碎石等轻度不平整,接触力反馈能提供一定鲁棒性。但这种方式是被动的——等到脚掌接触台阶边缘才做出反应,此时姿态已失控,跌倒几乎不可避免。
因此,将外部感知引入控制回路势在必行。但现有方案存在明显瓶颈:
LiDAR建图方案受限于更新频率低(10-20Hz),高动态场景易产生运动畸变,且对状态估计精度极度敏感——定位偏移数厘米即可能导致踩空。
深度图像重建方案泛化能力有限,在陌生野外环境中表现欠佳,通常仅支持低速运动。更棘手的是,这类方案高度依赖定制传感器配置,代码复现困难。
本研究提供的解决方案
“Hiking in the Wild”针对上述痛点,提出系统性四件套:
第一,端到端感知控制。原始深度图像直接映射至关节动作,完全消除中间地图表示。第二,安全落足机制,通过地形边缘检测与体积点惩罚,使机器人自主识别“危险区域”。第三,抗奖励欺骗策略,基于平坦斑块采样生成可达导航目标,避免训练中的投机行为。第四,零样本迁移部署,采用双向深度对齐,使仿真策略直接适配真实机器人。
2. 系统架构概览
整体框架采用单阶段强化学习方案。训练阶段通过丰富的地形课程与安全约束学习鲁棒策略,部署阶段借助轻量化ONNX推理实现实时控制。架构虽不复杂,但各层设计均针对性强。
3. 核心技术详解
3.1 问题建模:部分可观测马尔可夫决策过程
感知人形运动控制被建模为POMDP——机器人仅能通过传感器获取部分观测,无法获得环境完整状态。训练采用PPO算法,这是一种稳健的Actor-Critic框架,通过限制策略更新幅度保障训练稳定性。对于含大量自由度的人形机器人,PPO在高维连续动作空间中表现优于其他方法。
观测空间设计是关键环节。Actor的观测包含本体感知与外部感知两部分。本体感知反映机器人自身状态(关节位置、速度、姿态等),外部感知提供地形信息——
为捕获时序依赖关系,系统采用滑动窗口机制,保存历史h步观测数据。训练采用非对称Actor-Critic架构——Critic额外获取无噪声的完整状态信息与基座线速度等特权信息,从而更准确评估状态价值;Actor则专注于处理有限观测。
3.2 自中心深度图合成
深度图像的高保真合成是实现零样本Sim-to-Real迁移的前提。系统采用NVIDIA Warp框架实现GPU加速光线投射,确保合成效率。
3.3 双向深度对齐机制
仿真与真实世界之间的域差距是Sim-to-Real迁移的核心难题。本方案思路直接:定义两条变换管线,将仿真与真实的原始深度观测映射至统一的感知空间。
仿真管线 Fsim,核心策略为“退化”——对理想深度图注入多种缺陷,模拟物理传感器的常见问题:
裁剪缩放 → 距离相关高斯噪声 → 视差伪影合成 → 高斯模糊 → 归一化 → OOD扰动
各步骤功能明确:裁剪缩放聚焦图像中心关键特征;距离相关噪声模拟深度传感器随距离增加的精度衰减;视差伪影模拟双目匹配失效产生的无效“白区”;高斯模糊模拟光学运动模糊;OOD扰动以一定概率用随机噪声替换整帧,增强对瞬时感知故障的鲁棒性。
真实管线 Freal 则执行相反操作——修复物理传感器缺陷:
原始深度 → 裁剪缩放 → 深度修复(Inpainting) → 高斯模糊
深度修复算法专门恢复因遮挡或视差阴影产生的“黑区”(零值像素)。
3.4 跨步时序采样策略
高速运动时,单帧深度图提供的信息有限。系统采用跨步采样策略构建深度历史缓冲区:
$$ H_t = left{ I_{t-kcdot ell} mid k = 0,1,dots,m-1 right} $$
其中 m 为帧数,l 为时间步长。这种稀疏采样的优势在于:不增加计算负担,同时有效扩展时间感受野至 (m-1) · l 步,使策略能捕捉地形变化趋势及机器人相对速度信息。
4. 安全落足机制
4.1 问题分析
人形机器人的脚掌面积远大于四足机器人,这使落足位置精确性变得极为关键。四足机器人采用点接触,轻微偏移影响不大;人形机器人需要面接触才能维持平衡——当脚掌中心踩在楼梯边缘时,支撑面积不足,打滑或翻倒几乎必然发生。
传统基于模型的规划器虽能生成精确落足点,但对地图误差极度敏感。高程图若存在厘米级偏差,规划的落足点可能实际位于悬空区域。此外,传统方法通常需预先定义离散落足点候选集,在连续变化的野外地形中根本不适用。
4.2 地形边缘检测算法
系统通过比较相邻三角面片的二面角来识别尖锐地形边缘。这是项目中实际的边缘检测实现:
class EdgeCylinder(VirtualObstacleBase):
"""基于二面角的边缘检测器基类"""
def __init__(self, cfg: EdgeCylinderCfg):
self.cfg: EdgeCylinderCfg = cfg
self.angle_threshold = cfg.angle_threshold
def generate(self, mesh: trimesh.Trimesh, device="cpu") -> None:
"""检测网格中的尖锐边缘并存储为虚拟障碍物"""
# 1. 获取所有相邻面片的二面角
angles = mesh.face_adjacency_angles
threshold = np.deg2rad(self.angle_threshold)
# 2. 筛选超过阈值的尖锐边缘
sharp_mask = angles > threshold
if not np.any(sharp_mask):
edge_end_points = np.empty((0, 6), dtype=np.float32)
print("[WARNING] No sharp edges detected.")
else:
# 获取尖锐边缘的顶点索引
sharp_edges = mesh.face_adjacency_edges[sharp_mask]
v = mesh.vertices
# 构建边缘端点坐标数组 (num_edges, 6): [x0,y0,z0, x1,y1,z1]
edge_coords = np.hstack([v[sharp_edges[:, 0]], v[sharp_edges[:, 1]]])
edge_end_points = self.process_edges(edge_coords)
# 3. 创建圆柱体空间网格用于穿透检测
if edge_end_points.size > 0:
self.cylinders = CylinderSpatialGrid(
cylinders=np.concatenate([
edge_end_points,
np.ones_like(edge_end_points[:, :1]) * self.cfg.cylinder_radius,
], axis=1),
num_grid_cells=self.cfg.num_grid_cells,
device=self.device,
)
项目提供多种边缘处理策略,包括基于Plücker坐标的共线边缘合并、RANSAC线段拟合、贪婪拼接策略,可根据地形复杂程度选择。
4.3 体积点穿透惩罚
在机器人脚掌碰撞体内部分布一组体积点 P,利用NVIDIA Warp进行大规模并行距离查询,计算每个点相对于空间碰撞网格的穿透深度。
惩罚奖励的计算公式为:
$$ r_{mathrm{vol}} = -sum left| d_i right| cdot left( left| v_i right| + varepsilon right) $$
其中 d_i 为点 i 的穿透偏移量,v_i 为该点在世界坐标系下的线速度,$ varepsilon = 10^{-3} $ 为数值稳定常数。
该设计的关键洞察:高速撞击或刮擦边缘的行为会受到更重惩罚,从而引导策略学习选择稳定的落足位置。
4.4 代码实现:体积点传感器配置
以下来自项目中的实际配置:
# 体积点传感器配置
leg_volume_points = VolumePointsCfg(
prim_path="{ENV_REGEX_NS}/Robot/.*_ankle_roll_link",
points_generator=Grid3dPointsGeneratorCfg(
x_min=-0.025, # 脚掌后端
x_max=0.12, # 脚掌前端
x_num=10, # X方向采样点数
y_min=-0.03, # 脚掌左侧
y_max=0.03, # 脚掌右侧
y_num=5, # Y方向采样点数
z_min=-0.04, # 脚底
z_max=0.0, # 脚面
z_num=2, # Z方向采样点数
),
debug_vis=False,
)
每只脚掌内生成10×5×2=100个体积点,覆盖主要接触区域。
5. 抗奖励欺骗的速度指令生成
5.1 奖励欺骗问题
强化学习训练中有一个经典问题:当使用均匀采样的随机速度指令时,智能体往往会学会“原地转圈”之类的投机行为,而非真正穿越障碍物。数学上,这种策略能最大化累积奖励——因为绕圈在统计意义上也能满足速度跟踪奖励要求,且无需冒险攀爬困难地形。但这完全违背训练初衷。更棘手的是,一旦这种投机策略固化,很难通过简单调整奖励权重来纠正。
5.2 平坦斑块采样算法
解决方案是:在地形网格上预先识别“平坦斑块”作为可达的导航目标。通过IsaacLab框架的FlatPatchSamplingCfg配置类实现,以下是实际配置:
from isaaclab.terrains import FlatPatchSamplingCfg, TerrainGeneratorCfg
# 在地形配置中定义平坦斑块采样
"pyramid_stairs": terrain_gen.PerlinPyramidStairsTerrainCfg(
proportion=0.15,
step_height_range=(0.05, 0.23),
step_width=0.3,
platform_width=2.5,
# ... 其他地形参数 ...
flat_patch_sampling={
"target": FlatPatchSamplingCfg(
num_patches=50, # 采样50个平坦斑块
patch_radius=[0.05, 0.10, 0.15, 0.20], # 多尺度半径检测
max_height_diff=0.05, # 最大高度差5cm
x_range=(3.7, 3.7), # X方向采样范围
y_range=(-0.0, 0.0), # Y方向采样范围
),
},
),
核心参数:num_patches控制每个地形块的斑块数量;patch_radius是多尺度半径列表,系统在不同半径下验证平坦性;max_height_diff是斑块内允许的最大高度差;x_range和y_range限制采样空间范围,引导目标点分布。
这种配置确保目标点落在稳定的平坦地面上,而非陡坡或不可达区域。
5.3 基于位置的速度指令生成
选定目标点后,根据目标相对位置动态生成速度指令:
# 速度指令计算
def compute_velocity_command(target_pos_base_frame, k_v, k_omega, v_max, omega_max):
"""
输入: 目标在机体坐标系下的位置(x_g, y_g)
输出: 线速度v_x, 角速度ω_z
"""
x_g, y_g = target_pos_base_frame
# 线速度:与目标距离成正比
v_x = clip(k_v * x_g, 0, v_max)
# 角速度:与目标方位角成正比
heading_error = atan2(y_g, x_g)
omega_z = clip(k_omega * heading_error, -omega_max, omega_max)
return v_x, omega_z
由于使用前向相机,系统只关注前向运动和航向对齐,侧向速度指令设为零。
5.4 代码实现:速度指令生成器
以下来自项目中的核心实现:
def _update_command(self):
"""根据目标位置更新速度指令"""
# 计算目标相对位置
target_vec = self.pos_command_w - self.robot.data.root_pos_w[:, :3]
target_dist = torch.norm(target_vec[:, :2], dim=1)
# 转换到机体坐标系
self.pos_command_b[:] = quat_rotate_inverse(
yaw_quat(self.robot.data.root_quat_w), target_vec
)
# 计算线速度指令(与距离成正比)
self.vel_command_b[:, :2] = (
self.pos_command_b[:, :2] * self.cfg.velocity_control_stiffness
)
# 计算航向误差
target_direction = torch.atan2(target_vec[:, 1], target_vec[:, 0])
self.heading_command_w = wrap_to_pi(
target_direction - self.robot.data.heading_w
)
# 计算角速度指令
self.vel_command_b[:, 2] = (
self.heading_command_w * self.cfg.heading_control_stiffness
)
# 速度限幅
self.vel_command_b[:, 0] = torch.clamp(
self.vel_command_b[:, 0], min=0, max=self.max_command_b[:, 0]
)
# 距离阈值判断:目标过近时停止
self.vel_command_b[:] *= (
target_dist > self.cfg.target_dis_threshold
).unsqueeze(-1)
6. 对抗运动先验(AMP)
6.1 动机与原理
为使机器人运动更自然流畅,系统引入对抗运动先验(AMP)框架。核心思路是训练一个判别器,让其区分策略生成的运动与参考运动数据。传统强化学习往往产出机械、僵硬的运动模式——能完成任务,但动作生硬。AMP借鉴生成对抗网络思想,将参考运动数据视为“真实样本”,策略生成的运动视为“生成样本”,通过对抗训练使策略逐步模仿参考运动的风格特征。
这种方法的好处在于:无需手工设计复杂的步态奖励函数。判别器会自动学习区分自然与非自然运动的特征。
6.2 参考数据集构成
参考数据集以50Hz频率采集,来源有三个:
| 数据来源 | 内容 | 时长 | 用途 |
|---|---|---|---|
| MPC合成数据 | 稳定行走模式 | - | 提供基础稳定性 |
| 人体动捕数据 | 上下台阶、攀爬平台 | 379.62s | 复杂动作参考 |
| LAFAN数据集 | 高速奔跑动作 | 1.54s | 高动态运动参考 |
为避免“模式坍塌”问题,行走与奔跑策略使用不同数据集分别训练。
6.3 判别器训练
判别器采用最小二乘损失训练:
$$ L_D = mathbb{E}_Mleft[left(D(S)-1right)^2right] + mathbb{E}_Pleft[left(D(S)+1right)^2right] $$
风格奖励计算为:
$$ r_t = maxleft(0,, 1 - 0.25left(D(S_t)-1right)^2right) $$
相比二元交叉熵损失,MSE损失与二次奖励的组合能提供更平滑的梯度,避免梯度消失问题。
7. 奖励函数设计
7.1 奖励函数分类
总奖励由四个主要部分组成:
$$ R = r_{mathrm{task}} + r_{mathrm{reg}} + r_{mathrm{safe}} + r_{mathrm{amp}} $$
| 类别 | 奖励项 | 权重 | 作用 |
|---|---|---|---|
| 任务奖励 | track_lin_vel_xy_exp | 2.0 | 线速度跟踪 |
| track_ang_vel_z_exp | 2.0 | 角速度跟踪 | |
| is_alive | 3.0 | 存活奖励 | |
| 正则化 | volume_points_penetration | -4.0 | 边缘穿透惩罚 |
| feet_air_time | 0.5 | 鼓励抬腿 | |
| feet_slide | -0.4 | 惩罚脚滑 | |
| action_rate_l2 | -0.005 | 动作平滑性 | |
| 安全奖励 | dof_pos_limits | -1.0 | 关节位置限制 |
| undesired_contacts | -1.0 | 非法接触惩罚 | |
| AMP奖励 | style_reward | - | 运动自然性 |
7.2 关键奖励函数实现
这是项目中 feet_air_time 的实际实现:
def feet_air_time(env, command_name, vel_threshold, sensor_cfg):
"""奖励双足机器人的长步态"""
contact_sensor = env.scene.sensors[sensor_cfg.name]
# 获取空中时间和接触时间
air_time = contact_sensor.data.current_air_time[:, sensor_cfg.body_ids]
contact_time = contact_sensor.data.current_contact_time[:, sensor_cfg.body_ids]
# 判断是否处于单腿支撑状态
in_contact = contact_time > 0.0
in_mode_time = torch.where(in_contact, contact_time, air_time)
single_stance = torch.sum(in_contact.int(), dim=1) == 1
# 计算奖励:仅在单腿支撑时给予奖励
reward = torch.min(
torch.where(single_stance.unsqueeze(-1), in_mode_time, 0.0), dim=1
)[0]
# 零速度指令时不给奖励
reward *= torch.norm(
env.command_manager.get_command(command_name)[:, :2], dim=1
) > vel_threshold
return reward
8. 结语
“Hiking in the Wild”框架代表了人形机器人感知运动控制领域的重要突破。端到端的学习范式、创新的安全机制以及高效的部署架构,使机器人在野外复杂地形中实现高速稳健行进成为现实。相比传统分层控制架构,该方案避免了感知、规划、控制各模块间的信息损失与延迟累积,让机器人能以更快的速度响应环境变化。
从搜救到巡检,从物流配送到工业运维,这一方向的技术基础正逐步夯实。当然,距离大规模落地应用仍面临诸多挑战——例如更复杂的动态障碍物避让、更长距离的自主导航、更恶劣天气下的感知鲁棒性。但至少在这一步,方向明确,路径可行。


