3. 强化学习基础

好,我们正式开始强化学习的核心内容。说实话,很多初学者一上来就扎进各种算法里,结果越学越糊涂。我个人习惯是,先把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

设计原则总结:

  1. 稀疏 vs 密集:初期用密集奖励引导,后期可逐渐稀疏
  2. 奖励缩放:各分量量级要匹配,避免某个分量主导
  3. 避免“钻空子”:机器人会找到你奖励函数里的漏洞,比如原地抖动来获取存活奖励
  4. 逐步增加难度:先让机器人在平地上学会走,再增加地形复杂度

3.5 MDP框架下的四足行走——一张图看懂

说了这么多,我们用一张图把整个流程串起来。这张图是我在项目中经常用来跟团队成员沟通的框架图,它清晰地展示了MDP各要素在四足机器人上的对应关系。

四足机器人MDP框架图 智能体(策略网络) 动作 A:12维关节指令 环境(四足机器人+物理世界) 状态 S:40维观测向量 奖励 R:多目标加权和 输出 执行 观测 反馈 输入 折扣因子 γ γ=0.99 平衡短期与长期回报 状态转移 P(s'|s,a) 由物理引擎/真实世界 隐式定义

你看,整个流程就是一个闭环:智能体根据当前状态输出动作,动作作用于环境,环境反馈新的状态和奖励,然后智能体再根据新状态做出决策。这个循环不断重复,直到训练结束。

3.6 实践中的几个关键问题

最后,我想分享几个在项目中反复踩过的坑:

  • 状态归一化:不同物理量的量级差异很大(角度0.1 rad vs 速度1 m/s),一定要做归一化处理。我一般用running mean和std在线归一化。
  • 动作限幅:不要指望强化学习自己学会“温柔”地控制。在动作输出后加一个tanh或clip,把动作限制在合理范围内。
  • 奖励函数调参:这是个体力活。我建议先让机器人在仿真里学会“不倒”,再逐步加入速度跟踪等任务目标。
  • 仿真到现实的迁移:MDP在仿真中训练时,记得加入随机化(domain randomization),否则到真实机器人上会“水土不服”。

一个小技巧:在训练初期,可以把奖励函数的各个分量打印出来,看看机器人到底在“学什么”。如果某个分量一直为零或一直很大,说明设计有问题。我曾经发现机器人在原地转圈也能获得很高的存活奖励,后来加了“前进惩罚”才解决。

好了,MDP的基础就讲到这里。记住,这个框架是你理解后续所有强化学习算法的基石。把状态空间、动作空间、奖励函数这三个要素想清楚,后面的算法实现就会顺畅很多。