SAC强化学习实现船舶航向控制完整指南

Vibe Coding 使用 AI 回答

代码问题:

用强化学习SAC算法帮我实现航向控制

代码解决方案:

好的,我将用中文为你详细解读如何使用 Soft Actor-Critic (SAC) 强化学习算法来实现船舶或无人机的航向控制(Heading Control)。这是一个非常经典的连续控制问题。


一、问题定义

目标:让船舶(或无人机)的航向角 ψ(psi,单位:度或弧度)跟踪一个给定的参考航向角 ψ_ref

状态空间(State)

  • ψ_error = ψ_ref - ψ(航向误差,范围:[-180°, 180°])
  • r(偏航角速度 yaw rate,单位:°/s 或 rad/s)
  • 可选:上一时刻的舵角 δ_prev(帮助抑制震荡)

动作空间(Action)

  • δ(舵角或滚转角,连续值,范围:[-δ_max, δ_max],如 [-35°, 35°])

奖励函数(Reward)

  • 核心是惩罚航向误差,同时惩罚大舵角(节能、抑制震荡):
    reward = -w1 * abs(psi_error) - w2 * abs(delta) - w3 * abs(r)
    
    其中 w1, w2, w3 是权重系数(如 w1=1.0, w2=0.1, w3=0.01)。

二、环境搭建(以船舶为例)

我们使用一个简化的 Nomoto 模型(一阶非线性船舶操纵模型):

class ShipEnv(gym.Env):
    def __init__(self):
        super().__init__()
        # 状态空间:航向误差、偏航角速度、上一时刻舵角
        self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(3,))
        # 动作空间:舵角(连续)
        self.action_space = spaces.Box(low=-np.radians(35), high=np.radians(35), shape=(1,))
        
        # Nomoto模型参数
        self.K = 0.1  # 增益
        self.T = 10.0  # 时间常数
        self.dt = 0.1  # 仿真步长
        self.psi = 0.0  # 初始航向角
        self.r = 0.0   # 初始偏航角速度
        self.delta_prev = 0.0
        
    def reset(self, psi_ref=0.0):
        self.psi_ref = psi_ref
        self.psi = 0.0
        self.r = 0.0
        self.delta_prev = 0.0
        return np.array([self._normalize_angle(self.psi_ref - self.psi), self.r, self.delta_prev])
    
    def step(self, delta):
        # 执行舵角限制
        delta = np.clip(delta, self.action_space.low[0], self.action_space.high[0])
        
        # Nomoto模型更新
        self.r += (self.K * delta - self.r) / self.T * self.dt
        self.psi += self.r * self.dt
        
        # 航向误差
        psi_error = self._normalize_angle(self.psi_ref - self.psi)
        
        # 奖励函数
        reward = -abs(psi_error) - 0.1 * abs(delta) - 0.01 * abs(self.r)
        
        # 下一状态
        obs = np.array([psi_error, self.r, delta])
        done = False  # 可设置最大步数
        
        self.delta_prev = delta
        return obs, reward, done, {}
    
    def _normalize_angle(self, angle):
        # 将角度归一化到 [-pi, pi]
        return (angle + np.pi) % (2 * np.pi) - np.pi

三、SAC 算法实现

SAC 是一个 Actor-Critic 架构的 off-policy 算法,适合连续动作空间。我们使用 Stable-Baselines3 库(已集成 SAC)。

1. 安装依赖

pip install stable-baselines3[extra] gym

2. 训练代码

from stable_baselines3 import SAC
from stable_baselines3.common.vec_env import DummyVecEnv

# 创建环境
env = DummyVecEnv([lambda: ShipEnv()])

# 创建 SAC 模型
model = SAC(
    "MlpPolicy",
    env,
    verbose=1,
    learning_rate=3e-4,
    buffer_size=100000,
    learning_starts=1000,
    batch_size=256,
    gamma=0.99,
    tau=0.005,
    train_freq=1,
    gradient_steps=1,
    policy_kwargs=dict(net_arch=[256, 256])
)

# 训练
model.learn(total_timesteps=100000)

# 保存模型
model.save("sac_heading_control")

四、测试模型

import matplotlib.pyplot as plt

env = ShipEnv()
obs = env.reset(psi_ref=np.radians(30))  # 目标航向30°
model = SAC.load("sac_heading_control")

psi_history = []
delta_history = []

for _ in range(500):
    action, _ = model.predict(obs, deterministic=True)
    obs, reward, done, _ = env.step(action)
    psi_history.append(np.degrees(env.psi))
    delta_history.append(np.degrees(action[0]))

plt.subplot(2,1,1)
plt.plot(psi_history, label='实际航向')
plt.axhline(y=np.degrees(env.psi_ref), color='r', linestyle='--', label='目标航向')
plt.legend()

plt.subplot(2,1,2)
plt.plot(delta_history, label='舵角')
plt.legend()
plt.show()

五、关键改进建议

  1. 状态扩展

    • 加入积分误差项 ∫ψ_error dt 消除稳态偏差。
    • 加入环境扰动(如风、浪)提升鲁棒性。
  2. 奖励调参

    • 对航向误差进行非线性惩罚(如 psi_error^2)。
    • 加入舵角变化率惩罚 -w4*(delta - delta_prev)^2
  3. 仿真真实性

    • 替换更复杂的船舶模型(如MMG模型)。
    • 无人机可使用 六自由度模型

六、总结

SAC 非常适合解决航向控制的连续动作问题:

  • 高样本效率(比 PPO 更省样本)。
  • 探索能力强(通过熵正则化)。
  • 支持高维状态/动作空间

你可以根据实际需求调整模型复杂度,甚至迁移到 Gazebo、ROS 等仿真环境中进行更真实的测试。需要更复杂的模型或代码细节吗?