基于深度强化学习的跟车策略学习方法

一、强化学习基本原理

1.1 什么是强化学习

强化学习旨在学习如何做,即如何根据情况采取动作,从而实现数值奖励信号最大化。学习者不会接到动作指令,而是必须自行尝试发现回报最高的动作方案。

                                                                          —Sutton and Barto,强化学习导论

1.2 强化学习与传统控制的关系

强化学习 (RL) 已成功地训练计算机程序在游戏中击败全球最厉害的人类玩家。在状态和动作空间较大、环境信息不完善并且短期动作的长期回报不确定的游戏中,这些程序可以找出最佳动作。

图1-1 人机大战

在为真实系统设计控制器的过程中,工程师面临同样的挑战,如控制机器人走路或自动驾驶汽车。

图1-2 机器人走路

图1-3 自动驾驶

1.2.1 控制目标

从广义上而言,控制系统的目标是确定正确的系统输入(动作),从而生成期望的系统行为。在反馈控制系统中,控制器使用状态观测提高性能并修正随机干扰。工程师描述被控对象和环境模型,运用反馈信号,设计控制器,从而满足系统需求。以上概念表述十分简单;然而,倘若系统难以建模、高度非线性或者状态和动作空间较大,则很难实现控制目标。

图1-4 传统的控制器设计示意图

1.2.2 控制问题

为了理解此类难题对控制设计问题造成的进一步后果,不妨设想一下开发步行机器人控制系统的场景。要控制机器人(即系统),可能需要命令数十台电机操控四肢的各个关节。每一项命令是一个可执行的动作。系统状态观测量有多种来源,包括摄像机视觉传感器、加速度计、陀螺仪及各电机的编码器。

图1-5 行走机器人的控制问题

控制器必须满足多项要求:

• 确定适当的电机扭矩组合,确保机器人正常步行并保持躯体平衡。

• 需要在避开多种随机障碍物的环境下操作。

• 抗干扰,如阵风。

控制系统设计不仅要满足上述要求,还需满足其他附加条件,比如在陡峭的山坡或冰块上行走时保持平衡。

图1-6 行走机器人控制器需要考虑的问题

1.2.3 传统的控制方案

通常,解决此类问题的最佳方法是将问题分解成为若干部分,逐个击破。

如图1-7所示,我们可以构建一个提取摄像机图像特征的流程。例如,障碍物的位置和类型,或者机器人在全局参照系中所处的位置。综合运用这些状态与其他传感器传回的处理后的观测值,完成全状态估测。 估算的状态值和参考值将反馈给控制器,其中很可能包含多个嵌套控制回路。外部环路负责管理高级机器人行为(如保持平衡),内部环路用于管理低级行为和各个作动器。

图1-7 行走机器人的控制器设计方案

这种控制方案要求各环路之间相互交互,使得设计和调优变得异常困难。同时,确定最佳的环路构造和问题分解也并不轻松。

1.2.4 强化学习的控制方案

如果不是尝试单独设计每一个组件,而是设想一下将其全部塞进一个函数里, 由该函数负责接收所有观察结果并直接输出低级动作。 毋庸置疑,这可以简化系统方块图,但这个函数会是怎样的结构?我们该如何设计这个函数呢?创建一个单一的大函数比构建由分段子组件构成的控制系统,看起来难度要大;不过,强化学习可以帮助我们达成目标。

图1-8 基于强化学习的行走机器人控制器设计方案

1.3 强化学习的基本原理与流程

1.3.1 强化学习的基本原理

与传统监督学习与无监督学习不同,强化学习采用动态环境数据,其目标并不是对数据进行分类或标注,而是确定生成最优结果的最佳动作序列,通过一个所谓的智能体(agent)来探索环境、与环境交互并从环境中学习。

智能体中有一个函数可接收状态观测量(输入),并将其映射到动作集(输出)。也就是前面提到过的单一函数,它将取代控制系统的所有独立子组件。在RL 命名法中,此函数称之为策略。策略根据一组给定的观测量决定要采取的动作。

以步行机器人为例,观察结果是指每个关节的角度、机器人躯干的加速度和角速度,以及视觉传感器采集的成千上万个像素点。策略将根据所有这些观测量,输出电机指令,使机器人移动四肢。随后,环境将生成奖励,向代理反映特定作动器指令组合的效果。如果机器人能够保持直立并继续行走,对应的奖励则将高于机器人摔倒时的奖励。

如果可以设计出一项完美的策略,针对观察到的每一种状态向适当的作动器发出适当的指令,那么目标就达成了。因此,强化学习算法应运而生。它可以根据已采取的动作、环境状态观测量以及获得的奖励值来改变策略。智能体的目标是使用强化学习算法学习最佳环境交互策略;这样一来,无论在任何状态下,智能体都能始终采取最优动作——即长期奖励最丰厚的动作。

强化学习的目标与控制问题相似,只不过方法不同,使用不同的术语表示相同的概念。 通过这两种方法,我们希望确定正确的系统输入,从而让系统产生期望的行为。 我们的目的在于判断如何设计策略(或控制器),从而将环境(或被控对象)的状态观测量映射到最佳动作(作动器指令)。 状态反馈信号是指环境观察结果,参考信号则内置到奖励函数和环境观测量中。

1.3.2 强化学习的基本流程

如图1-9所示,强化学习一般涉及到五个步骤,分别是建立环境,设计奖励信号,研究策略表示方法、设计训练算法,验证部署。其中,我们需要一个环境,供智能体开展学习,需要选择环境里应该有什么,是虚拟的仿真环境还是真实的物理环境; 需要考虑最终想要智能体做什么工作,并设计奖励函数, 激励智能体实现目标;需要选择一种表示策略的方法,思考如何构造参数和逻辑,由此构成智能体的决策部分;需要选择一种算法来训练智能体,争取找到最优的策略参数;需要在实地部署该策略并验证结果, 从而利用该策略。

图1-9 强化学习的基本流程

二、跟车问题描述

跟车问题是指通过控制车辆自身的转向角度和速度,与领头车保持一定的安全距离,跟随领头车行驶。跟车问题在学术领域被称为路径跟随问题,目前该问题有很多解决方法,如模型预测控制方法,感兴趣的朋友可以参考软件Matlab提供的路径跟随控制系统。在这里,我们采用当前人工智能最为流行的深度强化学习方法来解决该问题。

2.1 马尔科夫决策过程

针对路径跟随问题,可以采用马尔科夫决策过程进行描述,即用四元组$E=<S,A,P,R>$,$E$为当前的路径跟随环境,$S$为路径跟随小车观测外部环境的状态空间,$A$为路径跟随小车的动作空间,$P$为状态转移函数,$R$为奖励函数。在某一时刻$t$,路径跟随小车agent观测外部环境的状态记为$s_t$,根据所学习的跟车策略$\pi(s_t)$输出动作$a_t$,并从外部环境获取即时奖励$r_t=R(s_t,a_t)$。

如图2-1所示,给出了路径跟随示意图。其中,领头车为蓝色小车,跟随车为黄色小车,$e_1$、$e_2$为领头车与跟随车的横向偏移距离和纵向偏移距离,$d_{rel}$为领头车与跟随车的偏移距离,$\theta_{yaw1}$、$\theta_{yaw2}$为领头车和跟随车的绝对航向角(与$x$轴的夹角,按逆时针旋转为正方向)。跟随车需要与领头车保持一定的纵向偏移距离和速度,同时横向偏移距离不能过大,才能保证较好的路径跟随效果。

图2-1 路径跟随示意图

2.2 状态空间设计

在状态空间设计中,我们认为领头车与跟随车的相对位置$(\Delta{x},\Delta{y})$、相对航向角$\Delta{\theta}$、横向偏移距离$e_1$、纵向偏移距离$e_2$、跟随车的线速度$V_{ego}$以及参考速度$V_{ref}$与线速度之差$e_V$、横向偏移距离变化率$\dot{e}1$和相对航向角变化率${\Delta}\dot{\theta}$,这些因素都会对路径跟随效果有一定影响。其中,跟随车的参考速度$V{ref}$主要根据领头车的线速度$V_{lead}$、预设的跟随车线速度$V_{set}$以及领头车与跟随车的安全距离$d_{safe}$进行计算,而将安全距离$d_{safe}$定义成与跟随车线速度有关的线性函数,即$d_{safe}=t_{gap}*V_{ego}+d_{default}$,$t_{gap}$、$d_{default}$分别为常量。当$d_{safe}-d_{rel}>0$时,取领头车线速度和跟随车预设线速度的最小值,反之,取随车预设得线速度,故跟随车的参考速度$V_{ref}$可以看成是领头车与跟随车的安全距离$d_{safe}$的映射函数。 $$ V_{ref}=\left{\begin{array}{}min(V_{lead},V_{set}) & d_{safe}-d_{rel}>0\V_{set}, & d_{safe}-d_{rel}{\leq}0\end{array}\right. $$

由此,跟随车的状态空间可采用九元组进行表示,即$<\Delta{x},\Delta{y},\Delta{\theta},V_{ego},e_V,e_1,e_2,\dot{e}_1,{\Delta}\dot{\theta}>$

2.3 奖励函数设计

在强化学习算法中,奖励函数设计是极其重要的一环,它可以将任务目标具体化、数值化,奖励函数设计的好坏直接体现了其对任务逻辑的理解深度,决定了智能体最终能否学到期望的行为策略,从而影响算法的收敛速度和最终性能。

为了保证较好的路径跟随效果,需要对以下几个方面进行考虑:

  • 跟随车的参考速度$V_{ref}$与线速度$V_ego$之差$e_v$不能太大;
  • 领头车与跟随车的横向漂移距离$e_1$不能太大;
  • 跟随车上一次角速度$w_{t-1}$和线速度$v_{t-1}$控制量幅度不能太大;
  • 领头车与跟随车的横向和纵向漂移距离较大时,将受到惩罚。

根据上述情况,路径跟随的奖励函数设计为:

$r_t=-0.1e_v^2-0.1e_1^2-0.1\omega_{t-1}^2-0.5v_{t-1}-10F_t+M_t+2H_t$

其中,$e_v$为前车与后车的纵向速度差(m/s),$e_v=V_{ref}-V_{ego}$;当领头车与跟随车的横向和纵向漂移距离较大时,$F_t=1$,否则,$F_t=0$;当$e_v^2<1$时,$M_t=1$,否则,$M_t=0$;当$e_1^2<0.01$时,$H_t=1$,否则,$H_t=0$;

2.4 动作空间设计

在路径跟随环境中,车辆采用的是双轮差速模型,其控制变量主要有线速度和角速度。为保证车辆控制的平滑性和稳定性,将线速度和角速度按照连续动作进行设计。

三、跟车仿真环境搭建

详见 仿真系统搭建

3.2 基于Simulink的跟车仿真环境

3.2.1 车辆模型

在路径跟随环境中,领头车和跟随车的运动模型将采用Matlab提供的双轮差速模型,该模型主要模拟位置矢量$q=[x,y,\theta]^T$、运动速度矢量$u=[v,w]^T$($w$为瞬时角速度、$v$为瞬时线速度),两个矢量的关系如下式所示。 $$ \dot{q}=S\cdot{u} $$ 其中,$S=\left[ \begin{array}{l} cos\theta & 0 \ sin\theta & 0 \ 0 & 1 \end{array} \right]$。该模型的输入为运动速度矢量(线速度与角速度),输出为位置矢量,可以根据速度的变化得到小车模型下一时刻的位置。

图3-23 双轮差速模型

关于车辆模型,Matlab提供了基本参数:

  • 车轮半径wheel radius:车轮的半径将影响运动时的速度与位置关系,默认为0.05;
  • 车轮速度范围wheel speed range:线速度和角速度的范围,默认为负无穷到正无穷;
  • 轮距track width:两轮之间的距离,默认为0.2;
  • 控制方式vehicle inputs:matlab提供了两种,一是Wheel Speeds,即输入左右轮的速度;二是Vehicle Speed Heading Rate,即输入线速度和角速度。

3.2.2 领头车机动控制模型

Matlab除了提供3.2.1节所述的双轮差速模型,还提供了controller Pure Pursuit控制器,该控制器能够实现车辆按照预设的航路点运动。如图3-24所示,根据路径跟随环境的需要,在指定区域设置四个航路点,领头车将沿着四个航路点循环往复运动,跟随车将按照领头车的运动轨迹跟随。

图3-24 领头车的四个航路点

3.2.3 跟车的状态输入、奖励计算与动作输出

3.2.3.1 状态输入

根据3.2节的状态空间设计,提供强化学习算法所需要的状态输入,示例代码如下:

function observation = get_obs(x1,x2,y1,y2,lateral_rel_dist, longitudinal_rel_dist, 
    yaw1, yaw2, leader_linear_vel, ego_linear_vel, ...
    yaw_delta_former, lateral_rel_dist_former)  
    % 状态空间
    % 输入
    % longitudinal_rel_dist 领头车与跟随车的纵向相对距离
    % lateral_rel_dist 领头车与跟随车的横向相对距离
    % yaw_delta 领头车与跟随车的航向角偏差
    % ego_linear_vel 跟随车的线速度
    % e_v 跟随车的参考速度与线速度之差
    % lateral_rel_dist_dot 领头车与跟随车的横向相对距离变化率
    % yaw_delta_dot 领头车与跟随车的航向角偏差变化率

    % 计算领头车与跟随车的相对位置
    x_delta = x1 - x2;
    y_delta = y1 - y2;

    % 计算领头车与跟随车的绝对航向角之差
    yaw_delta = yaw1-yaw2;
    
    % 计算跟随车的横向偏移距离变化率和相对航向角变化率
    lateral_rel_dist_dot = lateral_rel_dist - lateral_rel_dist_former;
    yaw_delta_dot = yaw_delta - yaw_delta_former;

    % 计算跟随车的参考速度与线速度之差
    v_ref = get_ref_velocity(leader_linear_vel, ...
    ego_linear_vel, longitudinal_rel_dist);
    e_v = v_ref - ego_linear_vel;

    observation = [x_delta, y_delta, longitudinal_rel_dist,e_v, ego_linear_vel, ...
        lateral_rel_dist, lateral_rel_dist_dot, yaw_delta, yaw_delta_dot];
end
3.2.3.2 奖励计算

根据3.3节的奖励函数设计,计算跟随车在跟随过程中的即时奖励,示例代码如下:

function reward = reward_calc(e_v, lateral_rel_dist, ego_linear_vel,... 			     ego_omega_vel, last_ego_omega_vel, isDone)
	% e_v 跟随车参考速度与线速度之差
	% lateral_rel_dist 领头车与跟随车的横向相对距离
	% ego_linear_vel 跟随车的线速度
	% ego_omega_vel 跟随车的角速度
	% last_ego_omega_vel 跟随车上一次的角速度
	% isDone 判断仿真是否终止 0表示仿真继续 1表示仿真终止
    F_t = 0.0;
    M_t = 0.0;
    H_t = 0.0;
    delta_omega = 0.0;
    
    e_v_square = e_v * e_v;
    lateral_rel_dist_square = lateral_rel_dist * lateral_rel_dist;

    if isDone==1
        F_t = 1.0;
    end

    if e_v_square < 1.0
        M_t = 1.0;
    end

    if lateral_rel_dist_square<0.01
        H_t = 1.0;
    end

    if sim_step >= 2
        delta_omega = ego_omega_vel - last_ego_omega_vel;
    end

    reward = -0.1*e_v_square - 0.1*ego_linear_vel*ego_linear_vel- ...                  			0.1*lateral_rel_dist_square - 0.5*ego_omega_vel*ego_omega_vel -...
         0.4*delta_omega*delta_omega - 10.0*F_t + M_t + + 2.0*H_t;

end
3.2.3.3 动作输出

在路径跟随环境中,跟随车的线速度控制在0.15-0.25m/s范围内,角速度控制在-1.0~1.0rad/s范围内,强化学习算法采用连续类型的控制量控制跟随车运动。如图所示,给出了跟随车的线速度和角速度的限幅范围。

3.2.4 仿真环境终止条件

当出现以下三种情况时,仿真将终止:

  • 跟随车与领头车发生碰撞

  • 跟随车与领头车的横向偏移距离超出阈值

  • 跟随车与领头车的纵向偏移距离超出阈值

其示例代码如下:

function flag = EnvisDone(rel_dist, lateral_rel_dist, longitudinal_rel_dist, 			ego_linear_vel)
	% avoid_dist 领头车与跟随车的碰撞距离
	% max_lateral_rel_dist 跟车的横向最大偏移距离
	% max_longitudinal_rel_dist 跟车的纵向最大偏移距离
    flag = 0;
    avoid_dist = 0.3;
    min_ego_linear_vel = 0.12;
    max_lateral_rel_dist= 0.7;
    max_longitudinal_rel_dist = 1.0;

    if rel_dist <= avoid_dist
        flag = 1;
    end

    if longitudinal_rel_dist >= max_longitudinal_rel_dist
        flag = 1;
    end
    
    if abs(lateral_rel_dist) >= max_lateral_rel_dist
        flag = 1;
    end
end

如图3-25所示,我们在Simulink仿真环境中创建了领头车、跟随车、信号处理模块。其中,领头车的信号处理模块主要负责其运动控制,即绕四个航路点循环往复运动;跟随车的信号处理模块主要负责为强化学习算法提供输入,如状态值、奖励值和仿真结束条件等。

图3-25 基于Simulink的路径跟随环境

四、跟车控制器训练与部署

在Matlab软件的Reinforcement Learning Toolbox™中, 提供了DQN、PPO、SAC 、DDPG和TD3 等经典的强化学习算法,结合这些算法进行策略训练,可以为复杂应用(如资源分配、机器人和自主系统)实现控制器和决策算法。其中,TD3算法是解决连续动作空间的一类强化学习算法,在路径跟随问题中,车辆的控制量(线速度和角速度)也是连续值,符合TD3算法应用的条件。

4.1 TD3训练算法

TD3算法被称为双重延迟深度确定性策略梯度(Twin Delayed Deep Deterministic Policy Gradient,TD3),它是在深度确定性策略梯度(DDPG)算法发展而来的。

4.1.1 网络结构

如图4-1所示,TD3算法的网络结构有6个。其中,Actor网络输入是状态,输出是动作。Critic网络输入是状态和动作,输出是对应的$Q$值。Actor网络的目的是根据状态 $s_t$ ,能够输出使得$Q(s_t,a_t)$最大的动作$a_t$,这个$a_t$越能使$Q(s_t,a_t)$大,就说明网络训练地越好。Critic网络的目的是根据状态动作对$(s_t,a_t)$能够输出其动作值$Q(s_t,a_t)$ ,这个$Q$值越精确,就说明网络训练地越好。

图4-1 TD3算法的网络结构

Actor网络和Target Actor网络的区别是,Actor网络是每步都会在经验池中更新,而Target Actor网络是隔一段时间将Actor的网络参数拷贝到Target Actor网络中,实现Target Actor网络的更新。这种“滞后”更新是为了保证在训练Actor网络时训练的稳定性。同样,Critic网络和Target Critic网络也是为了保证在训练Actor网络时训练的稳定性。

4.1.2 产生经验数据的过程

已知一个状态$s_0$,通过 actor网络得到动作$a'_0$ ,然后再加噪声$N$得到动作$a_0=a'_0+N$ (噪声是为了保证一定的探索,普通的高斯噪声即可),然后将$a_0$输入到环境中,得到$s_1$和$r_1$,这样就得到一个经验数据$(s_0,a_0,s_1,r_1)$ ,然后将经验数据放入经验池中。

图4-2 Actor网络产生经验数据的过程

经验池 存在的意义是为了消除经验数据的相关性,因为强化学习中前后动作通常是强相关的,将其打散放入经验池中,在训练神经网络时,随机地从经验池中选出一批经验数据,这样能够使神经网络训练地更好。

4.1.3 Actor网络的更新过程

如图4-3所示,Actor网络的损失函数就是$-Q$,$-Q$越小越好,这个$-Q$则由Critic0网络得到。以经验数据$(s_0,a_0,s_1,r_1)$ 为例,将$s_0$输入到Actor网络中,得到预测的动作$a_{0_predict}$,直接将$s_0$和$a_{0_predict}$输入到Critic0网络中,得到$Q$值,然后将$-Q$作为损失函数,修正Actor网络。

图4-3 Actor网络的更新过程

值得注意的是,Actor网络是最重要的,因为它直接决定了智能体采取策略的好坏。想要训练出一个好的Actor网络,需要一个准确的Critic网络来评价它,因此 TD3的剩下5个网络 都是 为了创造 出一个 尽可能精确的Critic网络。

4.1.4 Critic网络的更新过程

Target Actor网络的目的是为了让Critic网络更容易稳定收敛,如果用频繁更新的Actor网络做下一步动作的预测,会导致Critic网络很难收敛, Target Critic网络的目的 与Target Actor网络的目的相同,也是想用一个更新不频繁的网络让Critic网络稳定收敛。

图4-4 Critic网络的更新过程

考虑到在实际的应用中,Critic网络总是过高的估计$Q$值,TD3算法采用两个网络对Q值进行估计,然后选择较小的那个,这样尽可能地 避免过高地估计$Q$值。为此,频繁更新的Critic网络也需要采用两个,用$r_{1}+\gamma * \min \left{Q_{0}\left(s_{1}, a_{1 N}\right), Q_{1}\left(s_{1}, a_{1 N}\right)\right}$来更新两个Critic网络,即用$r_{1}+\gamma * \min \left{Q_{0}\left(s_{1}, a_{1 N}\right), Q_{1}\left(s_{1}, a_{1 N}\right)\right}$分别与$Q_0(s_0,a_0)$和$Q_1(s_0,a_0)$做均方差,然后作为损失值对Critic网络进行梯度下降。

此外,为了鼓励智能体探索, 给Target Actor网络的预测动作**$a_{1_predict}$ **加了一个噪声$N$ ,变为动作$a_{1N}$ 之后,才作为两个Target Critic网络的输入,使得下一步的$Q$值更精确。

4.1.5 TD3算法的优点

一是 将一个Target Critic网络变为两个Target Critic网络,取两者较小的作为下一状态的Q值,从而避免Q值过高地被估计。

二是 对Target Actor 网络的输出进行了加噪声处理,从而使得Target Critic网络的预测输出Q值尽可能精确。

三是 采用了延迟软更新的方式去更新一个Target Actor 网络、两个Target Critic网络,以及采用延迟更新的方式更新Actor网络。

4.2 创建智能体

根据4.1节TD3算法的网络结构,在matlab脚本文件中创建相关的智能体,示例代码如下:

function agent = createPFCAgent(obsInfo,actInfo)
	% obsInfo 状态空间信息
	% actInfor 动作空间信息
	
	% 获取状态空间和动作空间的维数
    numObs = obsInfo.Dimension(1);
    numAct = 2;
	
	% 定义Critic网络
    statePath1 = [
        featureInputLayer(numObs,'Normalization','none','Name','observation')
        fullyConnectedLayer(400,'Name','CriticStateFC1')
        reluLayer('Name','CriticStateRelu1')
        fullyConnectedLayer(300,'Name','CriticStateFC2')
        ];
    actionPath1 = [
        featureInputLayer(numAct,'Normalization','none','Name','action')
        fullyConnectedLayer(300,'Name','CriticActionFC1')
        ];
    commonPath1 = [
        additionLayer(2,'Name','add')
        reluLayer('Name','CriticCommonRelu1')
        fullyConnectedLayer(1,'Name','CriticOutput')
        ];

    criticNet = layerGraph(statePath1);
    criticNet = addLayers(criticNet,actionPath1);
    criticNet = addLayers(criticNet,commonPath1);
    criticNet = connectLayers(criticNet,'CriticStateFC2','add/in1');
    criticNet = connectLayers(criticNet,'CriticActionFC1','add/in2');

    criticOptions = rlRepresentationOptions('Optimizer','adam','LearnRate',1e-3,... 
              		'GradientThreshold',1,'L2RegularizationFactor',2e-4);
    critic1 = rlQValueRepresentation(criticNet,obsInfo,actInfo,...
    			    'Observation',{'observation'},'Action',{'action'},criticOptions);
    critic2 = rlQValueRepresentation(criticNet,obsInfo,actInfo,...
        			'Observation',{'observation'},'Action',{'action'},criticOptions);

	% 定义Actor网络
    actorNet = [
        featureInputLayer(numObs,'Normalization','none','Name','observation')
        fullyConnectedLayer(400,'Name','ActorFC1')
        reluLayer('Name','ActorRelu1')
        fullyConnectedLayer(300,'Name','ActorFC2')
        reluLayer('Name','ActorRelu2')
        fullyConnectedLayer(numAct,'Name','ActorFC3')                       
        tanhLayer('Name','ActorTanh1')
        ];
    actorOptions = rlRepresentationOptions('Optimizer','adam','LearnRate',1e-3,...
                                'GradientThreshold',1,'L2RegularizationFactor',1e-5);
    actor  = rlDeterministicActorRepresentation(actorNet,obsInfo,actInfo,...
        'Observation',{'observation'},'Action',{'ActorTanh1'},actorOptions);

	% 设置TD3算法参数
    agentOptions = rlTD3AgentOptions;
    agentOptions.DiscountFactor = 0.99;
    agentOptions.TargetSmoothFactor = 5e-3;
    agentOptions.TargetPolicySmoothModel.Variance = 0.2;
    agentOptions.TargetPolicySmoothModel.LowerLimit = -0.5;
    agentOptions.TargetPolicySmoothModel.UpperLimit = 0.5;

	% 创建内置TD3算法的智能体
    agent = rlTD3Agent(actor,[critic1 critic2],agentOptions);
end

如图4-5所示,智能体定义完成后,在Simulink主菜单点击“库浏览器”按钮,在弹出窗口搜索栏输入“reinforcement learning”,找到"RL Agent"模块,通过鼠标将其拖入到Simulink的路径跟随环境中,双击"RL Agent"模块,在XXX文本框中填入智能体的名称,例如"agent"。

图4-5 RL Agent设置窗口

4.3 训练智能体

智能体创建完成,需要对智能体的训练环境进行配置,如随机种子、环境重置参数初始化、采样速率、仿真结束时间、最大训练回合数、单个回合的最大步长等。具体示例代码如下:

% 打开Simulink环境
mdl = "m2carRL";
open_system(mdl);

% 设置状态空间和动作空间信息
obsInfo = rlNumericSpec([9 1]);
actInfo = rlNumericSpec([2 1],'LowerLimit',[0.1; 1.0],'UpperLimit',[0.5; 1.0]);

% 创建内置强化学习算法的Simulink环境
blks = mdl + ["/RL Agent"];
env = rlSimulinkEnv(mdl,blks,obsInfo,actInfo);

% 设置环境重置时的初始化函数
env.ResetFcn = @onecarpfcResetFcn;

% 固定随机种子
rng(0);

% 创建智能体
agent = createPFCAgent(obsInfo,actInfo);

% 设置采样速率、仿真时间、最大回合数、单个回合最大步长数
Ts = 0.1;
Tf = 120; 
maxepisodes = 30000;
maxsteps = ceil(Tf/Ts);

% 设置训练参数, 如并行训练模式、并行采集线程数、采集数据间隔等
trainingOpts = rlTrainingOptions(...
    'MaxEpisodes',maxepisodes,...
    'MaxStepsPerEpisode',maxsteps,...
    'Verbose',false,...
    'Plots','training-progress',...
    'ScoreAveragingWindowLength',10); 
trainingOpts.UseParallel = true;
trainingOpts.ParallelizationOptions.Mode = "async";
trainingOpts.ParallelizationOptions.DataToSendFromWorkers = "Experiences";
trainingOpts.ParallelizationOptions.StepsUntilDataIsSent = 32;


doTraining = true;
if doTraining    
    trainingStats = train(agent,env,trainingOpts);   
else
	load('one_car_pfc__td3_parallel.mat','agent');
end

% 保存训练结果
save('one_car_pfc__td3_parallel.mat', 'agent');

训练智能体的脚本文件编写完成后,点击脚本文件的菜单项“运行”按钮,即可开始训练智能体。如图4-6所示,训练过程中,matlab会自动启动情节管理器,在情节管理器窗口可以查看智能体的训练过程,如当前回合的奖励、近10个回合的平均奖励等。

图4-6 情节管理器窗口

训练完成后,可对训练好的智能体进行效果检验,验证的示例代码如下:

% 加载智能体
load('one_car_pfc__td3_parallel.mat','agent');

% 创建验证的Simulink仿真环境
simOptions = rlSimulationOptions('MaxSteps',1200);
experience = sim(env,agent,simOptions);
totalReward = sum(experience.Reward)

4.4 部署智能体

将训练好的智能体部署到实际的虚拟环境或真实环境中,还需要利用matlab的代码生成器生成通用C++代码,供各类系统调用。具体示例代码如下:

load('one_car_pfc__td3_parallel.mat','agent');
generatePolicyFunction(agent, 'FunctionName', "PFC_Policy",'MATFileName',"PFC_Agent.mat");

如图4-7所示,matlab会自动生成PFC_Policy.m和PFC_Agent.mat两个文件,打开PFC_Policy.m文件,其代码内容如下:

function action1 = PFC_Policy(observation1)
%#codegen

% Reinforcement Learning Toolbox
% Generated on: 08-Mar-2022 21:07:29

action1 = localEvaluate(observation1);


end
%% Local Functions
function action1 = localEvaluate(observation1)

persistent policy

if isempty(policy)
	policy = coder.loadDeepLearningNetwork('PFC_Agent.mat','policy');
end
observation1 = observation1(:)';
action1 = predict(policy, observation1);
end

图4-7 matlab自动生成的策略文件

可以按照正常脚本文件中的m函数调用即可

4.4.1 基于ROS的虚拟跟车环境

基于第3节所建立的Simulink跟车环境,可以建立类似的基于ROS虚拟跟车环境,利用ROS提供的发布/订阅通信机制实现车辆的控制。如图4-8所示,将matlab自动生成的策略脚本文件封装成控制器模块,供虚拟跟车环境使用,示例代码如下:

function action = one_car_pfc(obs)

action = PFC_Policy(obs);
action = double(action);

图4-8 基于ROS的路径跟随环境

若需要实现多车跟车效果,相邻的两辆车以前面为领头车、后面为跟随车。例如,环境中有4辆车时,2号车跟随1号车,3号车跟随2号车,4号车跟随3号车,依次类推。如图4-9所示,我们构建了基于ROS的4车路径跟随环境。

图4-9 基于ROS的多车路径跟随环境

4.4.2 基于ROS的真实跟车环境

若基于ROS的虚拟跟车环境完成后,即可使用真车测试跟车效果。matlab提供了sim2real的实现机制,可将Simulink的代码自动生成支持ROS的执行代码,具体操作步骤是:XXX

五、填坑指南

1. 神经网络输出变量的类型转换问题

matlab编译生成的神经网络模型,其输出类型是single,需要转换成doule类型,否则会报“This assignment writes a 'single' value into a 'double' type. Code generation does not support changing types through assignment. Check preceding assignments or input type specifications for type mismatches.”错误。解决办法详见4.4.1节代码第3行。

六、参考文献