3. 强化学习基础:马尔可夫决策过程(MDP)、状态空间、动作空间、奖励函数设计原则
好,我们正式开始强化学习的核心内容。说实话,很多初学者一上来就扎进各种算法里,结果越学越糊涂。我个人习惯是,先把MDP这个框架搞透。你想想看,整个强化学习,说白了就是在一个框架里做决策。这个框架,就是马尔可夫决策过程。
3.1 马尔可夫决策过程(MDP)——四足机器人的“游戏规则”
MDP是什么?它定义了一个智能体与环境交互的数学框架。对于我们的四足机器人来说,环境就是真实世界,智能体就是机器人的控制器。每一次交互,都是一次“观察-决策-行动-反馈”的循环。
一个标准的MDP可以用一个五元组来表示:(S, A, P, R, γ)。我刚开始接触时也觉得抽象,后来在项目中用多了,发现它其实非常直观。
| 符号 | 含义 | 四足机器人场景举例 |
|---|---|---|
| S | 状态空间(State Space) | 机身姿态、关节角度、足端触地信号 |
| A | 动作空间(Action Space) | 12个关节的目标角度或力矩 |
| P | 状态转移概率(Transition Probability) | 当前动作下,下一时刻状态的概率分布 |
| R | 奖励函数(Reward Function) | 行走速度、能耗、稳定性等指标的加权和 |
| γ | 折扣因子(Discount Factor) | 0.95~0.99,平衡短期与长期回报 |
这里有个关键点——马尔可夫性。意思是,下一时刻的状态只取决于当前状态和当前动作,跟历史无关。嗯,这在真实世界中当然不严格成立,但实践中我们通常假设它成立,或者通过设计状态空间来“近似”满足这个性质。
核心理解:MDP把“机器人如何行走”这个问题,转化为了一个数学优化问题。我们的目标,就是找到一个策略π(a|s),使得累积折扣奖励的期望最大化。
3.2 状态空间设计——你让机器人“看到”了什么?
状态空间的设计,直接决定了机器人能感知到什么信息。我在项目中遇到过一个问题:状态空间太简单,机器人学不会;太复杂,又学不动。这是个需要反复权衡的过程。
对于四足机器人稳定行走,我建议至少包含以下几类信息:
- 本体感知:机身IMU数据(roll, pitch, yaw角及角速度)、机身高度、线速度
- 关节状态:12个关节的当前角度和角速度
- 足端信息:4条腿的足端接触力或触地信号(二进制或连续值)
- 参考指令:期望的前向速度、侧向速度、转向角速度
举个例子,一个典型的状态向量可能是这样的:
# 状态空间维度示例(单位:rad, rad/s, m, m/s)
state = [
# 机身姿态 (6维)
roll, pitch, yaw, # 欧拉角
roll_rate, pitch_rate, yaw_rate, # 角速度
# 机身速度 (3维)
vx, vy, vz, # 线速度
# 关节状态 (24维)
q_1..q_12, # 12个关节角度
dq_1..dq_12, # 12个关节角速度
# 足端接触 (4维)
contact_FL, contact_FR, contact_RL, contact_RR, # 0或1
# 参考指令 (3维)
cmd_vx, cmd_vy, cmd_omega # 期望速度
]
# 总维度: 6 + 3 + 24 + 4 + 3 = 40维
我的经验:状态空间不是越大越好。我曾经把电机电流、温度都加进去,结果训练收敛极慢。后来发现,对于行走任务,关节角度和角速度已经包含了大部分动力学信息。记住一个原则:够用就好,冗余有害。
3.3 动作空间设计——机器人如何“行动”?
动作空间定义了机器人能做什么。对于四足机器人,常见的动作表示方式有两种:
| 动作类型 | 输出内容 | 优点 | 缺点 |
|---|---|---|---|
| 关节位置指令 | 12个关节的目标角度 | 训练稳定,易于与PD控制器结合 | 需要底层控制器配合,动作空间受限 |
| 关节力矩指令 | 12个关节的目标力矩 | 动作更灵活,能学习更精细的控制 | 训练难度大,容易产生不稳定行为 |
| 残差动作 | 在参考轨迹上的偏移量 | 结合了模型先验,训练效率高 | 依赖参考轨迹质量 |
我个人习惯在初期使用关节位置指令。为什么?因为四足机器人是个高自由度、高非线性的系统,直接输出力矩很容易让机器人“乱跳”。用位置指令加上一个固定的PD控制器,相当于给强化学习加了一个“安”。
# 动作空间示例:12维关节位置偏移
action = [
delta_q_1, delta_q_2, ..., delta_q_12 # 相对于默认姿态的偏移量
]
# 范围:通常限制在 [-0.5, 0.5] 弧度,防止动作过大
避坑指南:我曾经在动作空间里直接输出关节力矩,结果训练到一半,机器人的腿突然猛蹬,差点把测试台掀翻。后来我加了一个动作平滑项——让相邻两步的动作变化不能太大。这个技巧在四足机器人上非常实用。
3.4 奖励函数设计——你如何“告诉”机器人做对了?
奖励函数是强化学习的“指挥棒”。你设计什么样的奖励,机器人就学会什么样的行为。这可能是整个MDP设计中最艺术的部分。
对于四足机器人稳定行走,我通常把奖励函数拆成几个部分:
- 任务奖励:鼓励机器人达到目标速度。例如 r_task = exp(-|v_cmd - v_actual|)
- 稳定性奖励:惩罚机身过度倾斜或晃动。例如 r_stability = - (roll^2 + pitch^2)
- 能耗惩罚:鼓励节能行走。例如 r_energy = - Σ(τ_i * dq_i)
- 动作平滑惩罚:防止动作突变。例如 r_smooth = - Σ(a_t - a_{t-1})^2
- 存活奖励:每存活一步给予小正奖励,鼓励机器人保持不倒
一个典型的奖励函数设计如下:
def compute_reward(state, action, next_state):
# 任务奖励:跟踪速度指令
v_cmd = state['cmd_vx']
v_actual = next_state['vx']
r_task = torch.exp(-2.0 * (v_cmd - v_actual)**2)
# 稳定性惩罚:机身倾斜
roll, pitch = next_state['roll'], next_state['pitch']
r_stability = -0.1 * (roll**2 + pitch**2)
# 能耗惩罚
torques = action['torques']
joint_vel = state['dq']
r_energy = -0.01 * torch.sum(torques * joint_vel)
# 动作平滑惩罚
action_prev = state['prev_action']
r_smooth = -0.05 * torch.sum((action - action_prev)**2)
# 存活奖励
r_alive = 0.1 if not is_fallen(next_state) else -10.0
# 总奖励
total_reward = r_task + r_stability + r_energy + r_smooth + r_alive
return total_reward
设计原则总结:
- 稀疏 vs 密集:初期用密集奖励引导,后期可逐渐稀疏
- 奖励缩放:各分量量级要匹配,避免某个分量主导
- 避免“钻空子”:机器人会找到你奖励函数里的漏洞,比如原地抖动来获取存活奖励
- 逐步增加难度:先让机器人在平地上学会走,再增加地形复杂度
3.5 MDP框架下的四足行走——一张图看懂
说了这么多,我们用一张图把整个流程串起来。这张图是我在项目中经常用来跟团队成员沟通的框架图,它清晰地展示了MDP各要素在四足机器人上的对应关系。
你看,整个流程就是一个闭环:智能体根据当前状态输出动作,动作作用于环境,环境反馈新的状态和奖励,然后智能体再根据新状态做出决策。这个循环不断重复,直到训练结束。
3.6 实践中的几个关键问题
最后,我想分享几个在项目中反复踩过的坑:
- 状态归一化:不同物理量的量级差异很大(角度0.1 rad vs 速度1 m/s),一定要做归一化处理。我一般用running mean和std在线归一化。
- 动作限幅:不要指望强化学习自己学会“温柔”地控制。在动作输出后加一个tanh或clip,把动作限制在合理范围内。
- 奖励函数调参:这是个体力活。我建议先让机器人在仿真里学会“不倒”,再逐步加入速度跟踪等任务目标。
- 仿真到现实的迁移:MDP在仿真中训练时,记得加入随机化(domain randomization),否则到真实机器人上会“水土不服”。
一个小技巧:在训练初期,可以把奖励函数的各个分量打印出来,看看机器人到底在“学什么”。如果某个分量一直为零或一直很大,说明设计有问题。我曾经发现机器人在原地转圈也能获得很高的存活奖励,后来加了“前进惩罚”才解决。
好了,MDP的基础就讲到这里。记住,这个框架是你理解后续所有强化学习算法的基石。把状态空间、动作空间、奖励函数这三个要素想清楚,后面的算法实现就会顺畅很多。