上一篇整理:03:Observation 里面到底放什么
写在前面:Action 是 policy 的“输出接口”
上一篇看的是 observation,也就是 policy 每一步能看到什么。
这一篇继续往下看 action:policy 看完 observation 以后,到底输出了什么?
刚开始接触四足强化学习时,我很容易把 action 想象成很高层的命令,比如:
抬左前腿 |
但在大多数四足 locomotion 强化学习代码里,action 通常没有这么“语义化”。它更像是一串连续数值,最后会被转换成每个关节的控制目标。
所以这篇想先回答一个非常工程化的问题:
四足 RL 里的 action,为什么经常不是直接输出电机力矩,而是输出关节位置目标,或者关节位置目标附近的偏移量?
一句话结论
四足机器人强化学习里的 action 通常是 policy 的低层控制输出,最常见形式是每个关节的目标位置偏移:policy 输出归一化 action,经 action_scale 缩放后加到默认关节姿态上,再由底层 PD 控制器转换成电机力矩。
可以先记住这条链路:
observation → policy → action → target joint position → PD controller → torque → robot motion |
写成公式大概是:
其中:
- :当前 observation。
- :policy 输出的 action。
- :默认站立姿态下的关节角。
- :action scale,用来控制动作幅度。
- :当前真实关节角。
- :当前关节速度。
- :PD 控制器参数。
- :最终作用到关节上的力矩。
Action 不是“动作语义”,而是一组连续控制量
强化学习理论里经常写:
看起来 action 像是“智能体做了一个动作”。但落到四足机器人 locomotion 任务里,action 一般不是自然语言意义上的动作,而是一个固定维度向量。
以 12 自由度四足机器人为例,常见结构是:
每条腿 3 个关节 |
也就是:
action = [a0, a1, a2, ..., a11] |
每一个元素通常对应某个关节的控制目标或控制目标偏移。
它不是告诉机器人“走路”,而是每个控制周期都给 12 个关节一个新的低层目标。走路这种行为,是在长期训练中从这些连续控制量里“涌现”出来的。
常见的几种 action 设计
四足 RL 里的 action 设计大概可以分成几类。
1. 直接输出关节位置目标
一种很直观的设计是:policy 直接输出每个关节的目标角度。
q_target = action |
也就是说,网络输出什么,就把它当成目标关节角。
这种方式概念简单,但通常需要额外处理范围限制,因为神经网络输出本身没有物理常识:
- 可能输出超过关节极限的角度。
- 可能输出离当前姿态很远的目标。
- 可能导致动作跳变过大。
- 训练早期随机策略会让机器人姿态非常混乱。
所以实际工程里更常见的是下一种:输出默认姿态附近的偏移量。
2. 输出关节位置偏移
这是 legged locomotion 里非常常见的 action 形式:
q_target = q_default + action_scale * action |
这里的核心思想是:
不让 policy 从零开始发明一个完整姿态,而是让它围绕一个合理的默认站立姿态做小范围调整。
比如默认姿态是机器人自然站立时的关节角:
q_default = [ |
policy 输出的 action 通常会被限制在一个比较小的范围内,比如 。然后通过 action_scale 缩放:
action = 0.5 |
这样目标关节角不会乱飞,而是在默认姿态附近变化。
这对训练很重要。因为训练初期 policy 基本是随机的,如果动作空间太自由,机器人很容易一开始就疯狂抽搐、穿模、摔倒,学习信号也会变得很差。
3. 输出关节速度目标
也可以让 policy 输出目标关节速度:
dq_target = action_scale * action |
然后底层控制器根据速度误差产生力矩。
这种方式在某些机器人控制任务里也能用,但四足 locomotion 中不如位置目标常见。原因是单纯速度目标不直接约束姿态,训练时可能更难形成稳定的支撑结构。
4. 直接输出关节力矩
最自由的一种方式是 policy 直接输出 torque:
torque = action_scale * action |
它的好处是表达能力强,policy 可以直接学习如何用力,不受 PD 目标位置形式的限制。
但问题也很明显:
- 随机策略阶段很危险。
- torque 高频抖动会很严重。
- 对仿真精度更敏感。
- sim2real 难度更高。
- 真机上更容易过流、过热、撞限位。
所以对于很多四足机器人 locomotion 任务,直接 torque control 虽然理论上更自由,但工程上不一定更稳。
为什么常用“位置目标 + PD 控制”
我现在对这个选择的理解是:它在 policy 和电机之间加了一层稳定的低层控制器。
policy 不需要直接管每一瞬间应该输出多少力矩,而是给一个“我希望关节去哪里”的目标。真正的力矩由 PD 控制器生成。
公式是:
如果写完整一点,也可以是:
在很多四足 RL 实现里, 直接取 0,于是变成:
这里的直觉是:
- :如果当前位置离目标位置有误差,就产生一个往目标拉回去的力矩。
- :如果关节运动太快,就加阻尼,减少抖动。
所以 PD 层本身就带有一定稳定性。
policy 只要学会输出合适的目标位置偏移,就可以借助 PD 控制器完成比较平滑的运动。
这相当于给 policy 加了“动作先验”
位置目标 + PD 并不是单纯为了偷懒,而是在动作空间里加入了一种很有用的先验。
这个先验大概是:
机器人关节不要每一帧乱打力矩 |
这会让强化学习更容易。
因为 RL 的难点之一是探索。如果 action 直接是 torque,随机探索会非常粗暴;如果 action 是默认姿态附近的偏移,随机探索虽然仍然会乱,但乱的范围被控制住了。
也就是说:
torque action:policy 直接碰电机力矩 |
后者更像是给 policy 戴了一副“护栏”。
action_scale 为什么很关键
在代码里经常能看到类似参数:
self.action_scale = 0.25 |
或者配置文件中有:
action_scale: 0.25 |
它决定了 policy 输出的无量纲 action 最后会被放大成多大的关节角偏移。
例如:
action ∈ [-1, 1] |
那么每个关节目标相对默认角度的最大偏移大约是:
[-0.25 rad, 0.25 rad] |
换成角度大概是:
0.25 rad ≈ 14.3° |
这个量太小,机器人可能动不起来;太大,训练初期动作会很暴力,真机上也更危险。
可以这样理解:
action_scale 小:安全、保守、动作幅度不足 |
所以 action_scale 不是一个随便调的数字,它直接改变了策略实际控制机器人的“力度”。
action clip 和 torque clip
实际代码里除了 action_scale,还经常会有 clip。
action clip
policy 输出的 action 可能先被限制在某个范围内:
action = clip(action, -clip_actions, clip_actions) |
比如:
clip_actions = 1.0 |
这可以避免网络输出异常大值。
torque clip
PD 算出来的 torque 也可能被限制:
torque = clip(torque, -torque_limits, torque_limits) |
这是为了符合电机物理限制,也为了保护仿真和真机。
这两个 clip 不一样:
action clip:限制 policy 输出 |
训练时如果有这些限制,部署时也要对应上。否则策略在真机上的动作分布就变了。
decimation:policy 不一定每个仿真步都输出 action
四足 RL 代码里还经常出现一个参数:decimation。
它表示 policy action 的更新频率和物理仿真频率不一定一样。
比如:
仿真 dt = 0.005 s,也就是 200 Hz |
意思是:
物理仿真每 0.005 s 积分一次 |
在这 4 个仿真步内,通常保持同一个 action 或同一个 target position。
可以写成:
policy 输出 action |
这点很重要,因为部署时控制频率要对齐。
如果训练时 policy 是 50 Hz,真机部署时变成 100 Hz,动作效果就会变。不是说一定不能改频率,而是不能在没意识到的情况下改。
代码里通常怎么走
以常见 legged gym / Isaac Gym 风格为例,控制链路可以大概理解成下面这样。
第一步,policy 根据 observation 输出 action:
actions = policy(obs) |
第二步,对 action 做 clip:
actions = torch.clip(actions, -clip_actions, clip_actions) |
第三步,把 action 转成目标关节角:
target_q = default_dof_pos + action_scale * actions |
第四步,PD 控制器算 torque:
torques = p_gains * (target_q - dof_pos) - d_gains * dof_vel |
第五步,限制 torque:
torques = torch.clip(torques, -torque_limits, torque_limits) |
第六步,送进仿真器:
gym.set_dof_actuation_force_tensor(sim, torques) |
不同项目代码写法会不一样,但主线经常是这个逻辑。
为什么不让 policy 直接输出“足端位置”
还有一种直觉是:四足机器人不是靠脚走路吗?那 action 为什么不直接输出每个足端的位置?
比如:
front_left_foot_target = [x, y, z] |
这当然也可以设计,但它会引入额外问题:
- 足端目标要通过逆运动学转成关节角。
- 逆运动学可能有多解或不可达。
- 关节限制、碰撞、动力学响应还要额外处理。
- 足端轨迹和身体姿态之间的协调并不简单。
对于强化学习来说,直接在关节空间输出目标比较统一:
policy 输出 12 维关节相关 action |
也就是说,步态不是显式规划出来的,而是在优化奖励的过程中形成的。
Action 和 gait 的关系
如果 action 只是 12 个关节目标偏移,那 gait,也就是步态,是怎么出现的?
我的理解是:gait 不是 action 里的一个显式字段,而是 policy 在时间序列中输出 action 的规律。
例如 trot 步态大概会表现为:
左前腿 + 右后腿 协调 |
但网络不会直接输出:
gait = trot |
它只是每 20 ms 或类似频率输出一组关节目标。经过一段时间,这些目标序列形成了周期性的腿部运动,外部观察起来就像某种步态。
所以可以这样理解:
单步 action:一帧低层目标 |
Action 和 reward 的互相影响
Action 设计不是孤立的,它和 reward 强相关。
如果 action 是关节位置目标,reward 里通常会关心:
- 速度跟踪好不好。
- 身体姿态稳不稳。
- 关节动作是否过大。
- action 是否平滑。
- torque 是否过大。
- 关节速度是否过大。
- 足端接触是否合理。
其中 action 平滑项很常见:
它惩罚连续两帧 action 差太大。
为什么要惩罚这个?
因为即使用了 PD,如果 policy 每一帧目标位置跳来跳去,关节仍然会抖。action rate penalty 可以让策略输出更平滑。
还有 action magnitude penalty:
它鼓励 policy 不要总是输出过大的动作。
但这类惩罚也不能太大,否则机器人会变得过于保守,宁愿不动也不愿输出动作。
部署时最容易踩的坑
如果后面要把训练好的 policy 部署到真机,action 这一块至少要核对这些东西。
1. 关节顺序
policy 输出第 0 维,到底对应哪个关节?
训练中可能是:
FL_hip, FL_thigh, FL_calf, |
真机 SDK 里可能是另一种顺序。
如果顺序错了,policy 可能会把膝关节目标发给髋关节,把左腿动作发给右腿。模型本身没坏,但机器人会表现得非常离谱。
2. 关节正方向
仿真里某个关节正方向和真机编码器正方向可能相反。
如果符号错了,policy 想让关节抬腿,真机可能反方向压腿。
3. 默认关节角
训练时使用的 default_dof_pos 必须和部署时一致。
如果默认姿态不同,那么同一个 action 会变成不同的目标姿态。
4. action_scale
训练时:
action_scale = 0.25 |
部署时也必须一致。
如果部署时写成 0.5,动作幅度直接翻倍,真机表现会明显变激烈。
5. PD 参数
训练时用的 和真机低层控制器参数要尽量对应。
如果训练里关节很“硬”,真机上很“软”,策略可能跟不上目标。
如果训练里阻尼很大,真机上阻尼小,可能出现抖动。
6. 控制频率
训练时 policy 频率、PD 更新频率、仿真步长之间的关系要搞清楚。
部署时如果频率变了,等效控制行为也会变。
7. torque limit
真机电机力矩限制通常比仿真里更需要谨慎。
如果训练时经常依赖很大的 torque,但真机限制更低,部署后机器人可能无法完成同样动作。
一个简单例子:12 维 action 到 torque
假设有一个 12 自由度四足机器人。
policy 输出:
action shape = (12,) |
其中某个关节的 action 是:
a_i = 0.6 |
配置中:
action_scale = 0.25 rad |
那么目标角度是:
位置误差是:
PD torque 是:
所以 policy 并没有直接输出 3.8 这个 torque。它只是输出了一个归一化 action,后面经过 action_scale、默认姿态和 PD 控制,才变成最终力矩。
这个例子能帮助我把 action 的含义拆清楚。
我现在的理解小结
这篇先把 action 理成四足 RL 里的“输出接口”。
我现在会这样记:
observation 是 policy 的输入接口 |
这种设计的好处是:
动作空间更稳定 |
但也要注意:
action_scale、default_q、PD 参数、关节顺序、控制频率必须对齐 |
最重要的一句话是:
policy 输出的 action 通常不是最终电机力矩,而是经过缩放和低层控制器解释后的关节目标;理解这层转换,才能真正看懂四足 RL 代码里的 actions。
下一篇我准备继续整理 reward:四足机器人到底是怎么通过奖励函数被“塑造”出走路行为的。