首页 / 资讯中心 / 文章详情

分层强化学习训四足机器人步态:高层选步态低层控关节实战

分层强化学习训四足机器人步态:高层选步态低层控关节实战 ★ FEATURED ARTICLE
简介这份资源面向机器人运动控制方向的研究者与开发者聚焦用分层强化学习训练四足机器人掌握多种步态解决复杂运动策略学习中状态与动作空间过大、训练效率偏低的问题。压缩包共50个文件约3.77MB以Python脚本为主配合C与hpp头文件搭建Raisim仿真环境另有yaml参数配置、gif步态演示及说明文档覆盖从环境构建到算法实现的完整链路。项目将决策拆分为高层方向速度规划与低层腿足协调控制结合PPO与stable_baselines3实现训练流程并给出trot、bound、pace等多种步态的演示素材便于对照复现与调参。已有230人学习适合具备强化学习基础、希望深入四足机器人步态识别与运动控制实战的读者参考。1. 分层强化学习训四足机器人步态为什么“一个策略打天下”总是翻车四足机器人步态学习这件事很多人第一次上手都会掉进同一个坑用一个 PPO 或者 SAC 策略把前进、转向、小跑、爬坡、抗扰动全部塞进同一个网络里训练。结果就是——平地上走得还行一上斜坡就趴窝慢走稳如老狗一加速就原地抽搐。这不是调参不够努力而是分层强化学习Hierarchical Reinforcement Learning, HRL要解决的核心问题把“什么时候用什么步态”和“这个步态具体怎么迈腿”拆成两层来学。这个方向适合两类人一类是做足式机器人控制、已经跑通过单策略训练但被多步态切换折磨的工程师另一类是想找一个结构清晰、能复现的 HRL 落地项目来理解“高层决策 低层控制”到底怎么接起来的学习者。本文围绕四足机器人多种步态学习这个具体任务把分层结构怎么设计、低层策略怎么训、高层怎么调度、奖励怎么给、坑在哪一层层拆开讲。你照着做能在仿真里跑出一只至少会三种步态、能根据指令切换的四足机器人。2. 分层强化学习拆解四足步态高层选步态低层控关节2.1 为什么四足多步态天然适合分层结构四足机器人的步态本质上是一组周期性的足端轨迹模式。小跑trot是对角腿成对摆动踱步walk是四条腿依次抬起跳跃bound是前后腿分组摆动。每种步态都有自己的相位关系、抬腿高度、身体高度和期望速度区间。如果让一个扁平策略同时学这些网络要隐式地在不同模式之间切换梯度会互相干扰训练极不稳定。分层的好处在于解耦。高层策略Manager/Controller只负责一件事根据当前速度指令、地形坡度和机身姿态决定“现在该用哪种步态”输出是一个离散的步态选择或者一个连续的步态参数向量。低层策略Worker只负责一件事在给定步态模式下输出十二个关节的目标角度或力矩让机器人稳定地执行这个步态。两层的时间尺度也不一样——高层可以每 0.5 秒决策一次低层每 0.02 秒输出一次动作。这种时间抽象本身就是 HRL 的经典动机。从工程角度看分层还带来一个实际好处低层策略可以单独预训练。你可以先用单一步态把低层 worker 训到能稳定走再冻结或半冻结低层去训高层做步态切换。这比端到端从零训要快得多也更容易 debug。2.2 两层策略的网络结构与观测空间设计低层 worker 的观测空间一般包含机身角速度3 维、重力方向投影3 维、速度指令3 维前后、左右、转向、关节位置12 维、关节速度12 维、上一帧动作12 维再加上步态相位信息2 维用 sin/cos 表示当前步态周期中的位置。总共大约 47 维。动作空间是 12 维连续值对应十二个关节的目标角度偏移量再通过 PD 控制器转成力矩。高层 manager 的观测空间要更“粗”一些机身线速度3 维、角速度3 维、重力投影3 维、高度1 维、地形坡度估计2 维、当前步态 one-hot 编码假设 4 种步态就是 4 维。总共约 16 维。动作空间有两种常见设计离散的步态选择4 维 one-hot或者连续的步态参数比如步频、抬腿高度、占空比共 3 到 5 维。离散设计更简单连续设计更灵活但训练更难。我一般建议先从离散开始。下面是一个用 PyTorch 定义两层策略网络的示例代码结构不复杂关键是观测和动作的维度要对上。import torch import torch.nn as nn class LowLevelWorker(nn.Module): 低层策略输入观测输出12维关节目标角度偏移 def __init__(self, obs_dim47, act_dim12, hidden256): super().__init__() self.net nn.Sequential( nn.Linear(obs_dim, hidden), nn.ELU(), nn.Linear(hidden, hidden), nn.ELU(), nn.Linear(hidden, hidden), nn.ELU(), ) # 输出均值和对数标准差用于连续动作采样 self.mu nn.Linear(hidden, act_dim) self.log_std nn.Linear(hidden, act_dim) def forward(self, obs): h self.net(obs) mu self.mu(h) log_std torch.clamp(self.log_std(h), -4, 0.5) return mu, log_std class HighLevelManager(nn.Module): 高层策略输入粗粒度观测输出步态选择logits def __init__(self, obs_dim16, num_gaits4, hidden128): super().__init__() self.net nn.Sequential( nn.Linear(obs_dim, hidden), nn.ELU(), nn.Linear(hidden, hidden), nn.ELU(), nn.Linear(hidden, num_gaits), # 离散步态选择 ) def forward(self, obs): return self.net(obs) # 返回logits配合Categorical分布使用这段代码里低层 worker 用了三个隐层、每层 256 个单元ELU 激活。输出分两支mu 是动作均值log_std 是对数标准差训练时从正态分布采样推理时直接用 mu。log_std 被 clamp 到 [-4, 0.5]防止方差过大或过小导致训练崩溃。高层 manager 更轻量两个隐层 128 单元输出 4 个步态的 logits配合torch.distributions.Categorical做离散采样。参数方面obs_dim 必须和你的仿真环境实际输出的观测维度严格一致。如果你用的仿真器输出的观测里没有步态相位需要自己根据仿真时间步和步态周期算一个相位出来加进去。act_dim 固定是 12对应四足十二个关节。num_gaits 根据你想学的步态数量定一般 3 到 5 种够用。2.3 训练流程先训低层再训高层最后联合微调训练流程分三个阶段这是我在多个项目里验证过最稳的路径。第一阶段固定一种步态比如 trot用 PPO 训低层 worker。奖励函数包含速度跟踪项、姿态保持项、关节力矩惩罚项、足端滑动惩罚项。训练到机器人能稳定跟踪 0.5 到 1.5 米每秒的速度指令机身高度波动小于 3 厘米。这个阶段一般需要 2000 到 5000 个 episode取决于仿真速度和奖励设计。第二阶段冻结低层 worker 的参数训高层 manager。高层每 25 个仿真步0.5 秒决策一次输出步态选择。低层在高层选定的步态模式下执行动作。高层的奖励是低层累积奖励的加权和再加上步态切换的平滑惩罚。这个阶段收敛很快因为低层已经能稳定执行各种步态高层只需要学“什么情况下选什么步态”。第三阶段解冻低层用较小的学习率联合微调两层。这一步是为了让低层适应高层调度带来的分布变化。联合训练时高层学习率设为低层的十分之一左右避免高层策略剧烈变化导致低层崩溃。# 训练主循环伪代码PPO 分层调度 for episode in range(total_episodes): obs_low env.reset() obs_high extract_high_obs(obs_low) gait_onehot torch.zeros(4); gait_onehot[0] 1.0 # 初始步态 for step in range(max_steps): # 高层每25步决策一次 if step % 25 0: with torch.no_grad(): logits manager(obs_high) gait_idx torch.distributions.Categorical(logitslogits).sample() gait_onehot torch.nn.functional.one_hot(gait_idx, 4).float() # 低层每步输出动作 obs_low_aug torch.cat([obs_low, gait_onehot, phase_encoding(step)], dim-1) mu, log_std worker(obs_low_aug) dist torch.distributions.Normal(mu, log_std.exp()) action dist.sample() next_obs, reward, done, info env.step(action.numpy()) # ... 存储transition计算GAE更新网络 ... obs_low next_obs obs_high extract_high_obs(next_obs)这段伪代码展示了分层调度的核心逻辑高层每 25 步决策一次步态低层每步输出关节动作。phase_encoding函数根据当前步数和步态周期计算相位 sin/cos 值。实际训练时高层的 transition 存储频率是低层的 1/25计算 GAE 时要分别处理两层的时间尺度。注意gait_onehot在高层不决策的步里保持不变这是时间抽象的关键。3. 奖励函数与步态切换让机器人自己学会“什么时候该换腿”3.1 低层奖励的五个组成部分与权重设置低层奖励设计直接决定机器人走得像不像样。我一般用五项加权和第一项是速度跟踪奖励r_vel exp(-2.0 * ||v_cmd - v_actual||^2)范围 0 到 1。这一项让机器人跟上指令速度。第二项是姿态奖励r_ori exp(-5.0 * ||gravity_proj_xy||^2)惩罚机身倾斜。第三项是高度奖励r_height exp(-10.0 * (h - h_target)^2)保持机身高度稳定。第四项是动作平滑奖励r_smooth -0.01 * ||a_t - a_{t-1}||^2抑制关节抖动。第五项是足端滑动惩罚r_slip -0.05 * ||v_foot_contact||^2接触地面时足端水平速度越小越好。权重方面速度跟踪 1.0姿态 0.5高度 0.3平滑 0.1滑动 0.2。这些权重不是绝对的但如果你发现机器人原地踏步不走先加大速度跟踪权重如果机身晃得厉害加大姿态权重如果关节抖得像帕金森加大平滑权重。3.2 高层奖励怎么设计才能避免频繁切换高层奖励如果直接等于低层累积奖励会出现一个问题机器人会在两种步态之间反复横跳因为切换瞬间可能恰好获得较高奖励。解决办法是在高层奖励里加一个切换惩罚项r_switch -0.5 * I(gait_t ! gait_{t-1})。这个惩罚不能太大否则机器人会一直不切换也不能太小否则切换太频繁。0.3 到 0.8 之间比较合适具体值取决于你的低层奖励量级。另一个技巧是给高层加一个“步态一致性”奖励如果连续 N 次决策都选了同一个步态给一个小额正奖励。这能鼓励高层在稳定场景下保持步态不变只在必要时切换。3.3 步态相位编码与周期参数怎么调步态相位编码是低层策略能走出周期性步态的关键。常见做法是用两个标量sin(2πt/T)和cos(2πt/T)表示当前在步态周期中的位置其中 T 是步态周期。trot 的 T 一般设 0.3 到 0.5 秒walk 的 T 设 0.6 到 1.0 秒bound 的 T 设 0.25 到 0.4 秒。相位编码要作为观测的一部分喂给低层网络而不是让网络自己从时间步里隐式学习。显式给相位信息能大幅加速训练因为网络不需要自己推断周期。如果你发现机器人走起来节奏混乱先检查相位编码的频率和步态周期是否匹配。import numpy as np def phase_encoding(step, gait_period_steps): 根据当前步数和步态周期计算相位编码 phase (step % gait_period_steps) / gait_period_steps return np.array([np.sin(2 * np.pi * phase), np.cos(2 * np.pi * phase)]) # 不同步态的周期仿真步数假设仿真频率50Hz GAIT_PERIODS { trot: 20, # 0.4秒 walk: 40, # 0.8秒 bound: 15, # 0.3秒 pace: 25, # 0.5秒 }这段代码里gait_period_steps是步态周期对应的仿真步数。假设仿真频率 50Hztrot 周期 0.4 秒就是 20 步。相位编码返回 sin 和 cos 两个值拼到低层观测里。注意不同步态的周期不同高层切换步态时相位编码的周期参数也要跟着切换否则低层会收到错误的相位信息。4. 避坑与排查分层强化学习训四足步态最常见的五个翻车现场4.1 低层策略在高层切换步态后直接摔倒现象高层从 trot 切到 walk 的瞬间机器人前腿抬起时序错乱机身重心偏移过大直接侧翻。原因低层 worker 在训练时只见过单一固定周期的相位编码切换步态后相位周期突变低层观测分布发生剧烈偏移策略输出超出训练分布范围。解决在低层预训练阶段就随机化步态周期让低层见过不同周期的相位编码。具体做法是在每个 episode 开始时从[15, 20, 25, 30, 40]里随机选一个周期而不是固定用 trot 的 20。这样低层对周期变化有鲁棒性高层切换时不会崩。4.2 高层策略退化成“永远选同一种步态”现象训练日志里高层输出的步态选择概率始终偏向 trot其他步态概率接近零机器人不会根据地形切换步态。原因高层奖励里切换惩罚过大或者低层在其他步态上的表现明显差于 trot导致高层学到“选其他步态拿不到好奖励”。解决先检查低层在每种步态下的单独表现。如果 walk 或 bound 的低层策略本身就走不稳高层当然不会选。确保每种步态的低层策略都单独训到能稳定行走。然后降低切换惩罚从 0.5 降到 0.2给高层更多探索空间。还可以在训练初期给每种步态加一个小的探索奖励鼓励高层尝试不同选择。4.3 联合微调时低层策略崩溃机器人开始原地抽搐现象第三阶段解冻低层后训练损失突然飙升机器人动作变得剧烈抖动速度跟踪完全失效。原因高层策略在第二阶段已经收敛到某个步态选择分布联合训练时高层梯度传回低层导致低层参数被推离预训练的最优区域。如果高层学习率没有调低这种偏移会非常剧烈。解决联合训练时高层学习率设为低层的 1/10低层学习率也从预训练的 3e-4 降到 1e-4。另外可以加一个 KL 约束限制联合训练时低层策略输出分布与预训练策略的 KL 散度不超过阈值。如果还是崩就先冻结低层的前几层只微调最后几层。4.4 仿真里走得好迁移到实物上步态完全乱套现象仿真里 trot 稳定在 1.2 米每秒实物上同样指令下机器人要么走不动要么突然加速然后摔倒。原因仿真里的关节动力学、地面摩擦、电机响应延迟和实物差距太大。分层策略对低层观测里的关节速度和力矩估计非常敏感实物上的噪声和延迟会直接破坏相位编码的时序。解决在仿真训练时加入域随机化——随机化地面摩擦系数0.4 到 1.2、电机力矩延迟0 到 20 毫秒、机身质量±15%、关节阻尼±20%。另外在观测里加入高斯噪声标准差设为观测范围的 2% 到 5%。这些随机化能显著提升迁移鲁棒性。如果实物上还是不行先降低速度指令从 0.3 米每秒开始测确认低层能稳定跟踪再逐步加速。4.5 训练前期奖励上升但机器人实际步态越来越奇怪现象奖励曲线一直在涨但可视化一看机器人学会了用三条腿跳或者拖着一条腿走来骗取速度跟踪奖励。原因奖励函数设计有漏洞。速度跟踪奖励只看机身速度不看足端接触模式机器人可以用非步态的方式达到目标速度。姿态奖励和高度奖励也可能被绕过。解决加入足端接触模式奖励。对于 trot期望的接触模式是对角腿同时接触对于 walk期望四条腿依次接触。可以用一个简单的接触序列匹配奖励r_contact 1.0 if contact_pattern matches expected else -0.5。另外加入关节限位惩罚防止机器人用极端关节角度作弊。奖励设计永远是一个对抗过程你堵一个漏洞策略就找下一个多轮迭代才能收敛到自然步态。5. 从仿真到实物分层步态策略的验证方法与一个调参习惯分层强化学习训四足步态最终要回答的问题是这套策略到底能不能用。验证方法分三层。第一层是仿真内定量测试在平地上测速度跟踪误差在斜坡上测姿态保持能力在随机推力下测恢复时间。每种步态单独测再测切换场景。第二层是鲁棒性测试把地面摩擦系数从 0.4 调到 1.2把机身质量加 15%把电机延迟加到 20 毫秒看策略是否还能稳定行走。第三层才是实物测试从低速、平地、无扰动开始逐步加难度。一个具体的验证脚本可以这样写固定速度指令序列让机器人依次执行 0.5、1.0、1.5 米每秒的前进指令每个速度持续 5 秒记录实际速度、机身高度、姿态角。然后给一个 0.3 米每秒的侧向指令看机器人是否能切换到侧向步态。最后给一个 0.5 弧度每秒的转向指令看转向时的步态选择。# 仿真验证脚本示例 test_commands [ {vx: 0.5, vy: 0.0, wz: 0.0, duration: 5.0}, {vx: 1.0, vy: 0.0, wz: 0.0, duration: 5.0}, {vx: 1.5, vy: 0.0, wz: 0.0, duration: 5.0}, {vx: 0.0, vy: 0.3, wz: 0.0, duration: 3.0}, {vx: 0.5, vy: 0.0, wz: 0.5, duration: 3.0}, ] results [] for cmd in test_commands: obs env.reset() cmd_vec np.array([cmd[vx], cmd[vy], cmd[wz]]) steps int(cmd[duration] / env.dt) vel_errors, heights, gait_switches [], [], 0 prev_gait None for step in range(steps): obs_high extract_high_obs(obs) gait_logits manager(torch.FloatTensor(obs_high)) gait_idx torch.argmax(gait_logits).item() if prev_gait is not None and gait_idx ! prev_gait: gait_switches 1 prev_gait gait_idx obs_low_aug build_low_obs(obs, gait_idx, step) action worker(torch.FloatTensor(obs_low_aug))[0].detach().numpy() obs, _, done, info env.step(action, cmd_vec) vel_errors.append(np.linalg.norm(info[vel_actual] - cmd_vec)) heights.append(info[body_height]) if done: break results.append({ cmd: cmd, mean_vel_error: np.mean(vel_errors), height_std: np.std(heights), gait_switches: gait_switches, })这个脚本遍历五组速度指令每组记录平均速度跟踪误差、机身高度标准差和步态切换次数。mean_vel_error低于 0.15 米每秒算合格height_std低于 0.03 米算稳定gait_switches在 5 秒内不超过 3 次算合理。如果某个指令下误差突然变大回去检查低层在该速度区间的训练数据是否充分。调参这件事我自己的习惯是每次只改一个参数改完跑三个随机种子取平均。分层强化学习的随机性比单层更大因为两层策略的交互会放大方差。三个种子如果方差超过 20%说明这个参数对结果影响不稳定需要重新审视。另外训练日志里一定要记录每种步态被选中的频率和切换次数这两个指标比总奖励更能反映策略是否学到了有意义的步态调度。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站