ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于MPC的汽车路径跟踪控制:Carsim与Simulink联合仿真实战

基于MPC的汽车路径跟踪控制:Carsim与Simulink联合仿真实战

简介:面向智能驾驶与车辆控制方向学习者,这套资料围绕Carsim动力学仿真与MATLAB/Simulink下的模型预测控制(MPC)路径跟踪联合仿真展开,并给出动态视频生成方案。资源共13个文件,压缩包约214KB,核心文件包括Simulink模型(Chap5.mdl)、Carsim仿真参数(.cpar)、MPC线性化及雅可比计算脚本(.m)、参考轨迹数据(.mat)和README说明文档,另有zbak备份及授权文件,覆盖整车建模、控制器设计、离线仿真与结果导出全流程。内容从Carsim与Simulink双向接口搭建,到MPC滚动优化与轨迹跟踪控制均有完整实现,可直接加载运行;借助参考轨迹数据和线性化脚本,可快速复现控制效果,并针对自定义路径重新整定控制器参数。资源已有61人学习,适合正在开展车辆横向控制课题、需要快速搭建联合仿真环境的研究生或工程师参考复用。

1. 基于MPC的汽车路径跟踪控制:从Carsim联合仿真到视频生成,这条链路到底卡在哪

做路径跟踪控制的人大概都有过这种体验:MPC的公式推得头头是道,仿真里误差收敛得干净利落,一旦要把控制器接上一辆“真实”的车辆模型,就开始各种翻车。基于MPC的汽车路径跟踪控制,落到工程上是一条确定的链路:用Carsim输出车辆动力学状态,在Simulink里跑MPC求解器算出前轮转角,再把转角送回Carsim闭合回路,最后把动画画面和误差曲线录成视频用于评审与答辩。这套方案的受众很明确:正在做横向控制课题的硕博生、自动驾驶规控方向的新手工程师,以及想快速验证MPC算法的团队。它的价值在于不造车也能让控制算法在高保真动力学环境里验证,还能产出直观的演示素材。卡住新手的从来不是MPC理论本身,而是联合仿真的接口配置、参数匹配和视频生成这三个工程坑。

2. MPC路径跟踪控制器的模型怎么定:横向误差状态方程与预测时域选择

2.1 运动学模型还是动力学模型:低速路径跟踪的工程取舍

路径跟踪MPC的第一步不是调权重,而是选模型。低速场景——车速10m/s以下、路面附着良好的泊车或园区接驳工况,运动学自行车模型就够用。状态取横向偏差e和航向角偏差Δψ,控制量取前轮转角δ,模型写成:

e_dot = v·Δψ
Δψ_dot = v/L·δ − v·κ_ref

其中v是纵向车速,L是轴距,κ_ref是参考路径的曲率。运动学模型假设车轮没有侧偏,结构简单,但已经抓住了路径跟踪最核心的两个量:车偏离参考线多少、车头指向偏了多少。

为什么这个控制问题偏偏适合用MPC?因为横向控制本质上是带约束的优化问题。MPC的核心思路是“预测+约束+滚动优化”:把未来Np步的横向偏差都写成当前状态与控制序列的线性组合,在一个代价函数里同时压制跟踪误差和控制增量,并且显式处理前轮转角幅值约束。对比纯跟踪或者PID,MPC能在过弯前就把方向打提前量,因为它的预测项看到了前方的曲率变化。这个“看得见未来”的特性,是它成为横向控制主流方案的根本原因。

如果车速超过15m/s,或者要做紧急变道、低附着路面工况,运动学模型的精度就不够了。这时需要切到动力学模型,状态里加横向速度Vy和横摆角速度r,轮胎力用侧偏刚度线性化表达。Carsim里轮胎的侧偏刚度可以从轮胎配置文件里导出来,换算成前轴等效侧偏刚度和后轴等效侧偏刚度。工程上的通行做法是先拿运动学模型把整个联合仿真的链路打通,跑出低误差的跟踪结果,再逐步升级模型复杂度。一上来就用七自由度动力学模型,只会让联合仿真的排错变得无处下手。

2.2 状态方程离散化与预测时域参数的起点设置

模型定完之后要做的第一件事是离散化。Carsim内部积分步长通常取0.001秒到0.01秒,而MPC的控制周期Ts一般取0.02到0.05秒。这里有一个新手最容易忽略的概念:Carsim里有一个“通信间隔”(communication interval),它决定了Carsim向Simulink输出状态、接收控制量的频率,这个值必须和MPC的Ts保持一致。如果两边不一致,控制器看到的车辆状态是过期的,转角指令到达车辆端也晚半拍,整个回路的相位裕度会被吃掉。

离散化方法用前向欧拉就够了,Ts在0.05秒以内不会引入明显误差。把上面的运动学模型写成矩阵形式:

x(k+1) = Ad·x(k) + Bd·u(k) + Dd·κ_ref(k)

其中Ad = [1, Ts·v; 0, 1],Bd = [0; Ts·v/L],Dd = [0; −Ts·v],状态x = [e; Δψ]。下面的代码给出这三个矩阵的构造方式:

% 离散化参数 Ts = 0.05; % MPC控制周期,单位秒 v = 8.0; % 纵向车速,单位m/s(匀速假设) L = 2.7; % 轴距,单位m % 运动学模型离散矩阵 Ad = [1, Ts*v; 0, 1]; Bd = [0; Ts*v/L]; Dd = [0; -Ts*v]; % 打印检查 disp('Ad ='); disp(Ad); disp('Bd ='); disp(Bd); disp('Dd ='); disp(Dd);

代码逻辑不复杂:第一行是横向偏差e的递推,当前时刻的航向角偏差会按速度累积产生新的横向偏差;第二行是航向角偏差的递推,前轮转角通过v/L这个系数改变航向角变化率,曲率项则作为已知扰动前馈进去。这里把曲率当扰动而不是状态,是因为参考路径的曲率在规划阶段就是已知序列,MPC预测未来时可以直接把这个扰动效果叠加上去。

接下来是预测时域Np和控制时域Nc。用时间跨度Tp = Np·Ts来衡量预瞄距离,Tp取1秒到1.5秒是常见起点。车速8m/s时,Tp=1.2秒意味着预瞄距离差不多10米,这个距离足够覆盖常规园区工况的弯道曲率变化。按Ts=0.05秒算下来,Np取20到30之间。Nc的取法一般是Np的1/3到1/2,5到8步的余量既能保证控制自由度,又不至于让优化变量太多拖慢求解速度。下表是不同车速下我个人习惯的起点配置:

车速(m/s)Ts(s)NpNc预瞄距离(m)
50.052055
80.0525610
100.0525812.5
150.02401012

注意一个容易翻车的点:Np并不是越大越好。预瞄距离过长,控制器会把远处的弯道信息提前引入当前决策,结果就是进弯前就开始打方向,走出来的轨迹会明显切弯心。反过来Np太小,MPC就退化成带约束的LQR,预测优势发挥不出来。这个参数需要根据参考路径的最大曲率做一轮扫描式调试。

3. Carsim与Simulink联合仿真:接口配置和信号通道对接

3.1 Carsim端设置:Simulink接口模式与车辆模型编译

联合仿真的原理先讲清楚:Carsim把整车动力学模型编译成S-Function(本质是一个DLL文件),Simulink作为主控软件调用这个S-Function,两边在每个通信间隔交换一次信号。所以联合仿真不是“两个软件都在跑”,而是Simulink在跑,Carsim是被Simulink调用的被控对象库。

Carsim端的配置在Run Control页面。常见操作流程是:先在Carsim里把车辆参数、路面附着、仿真工况设置好,然后在运行配置里选择Simulink接口——不同版本的Carsim叫法略有差异,有的叫“Simulink model”,有的直接列出对应MATLAB版本号。选择之后,Carsim会生成一个S-Function名称,同时生成一份用于Simulink的示例模型文件。我一般会到Carsim安装目录下确认一下DLL文件确实生成出来了,然后把DLL所在路径加到MATLAB的搜索路径里。这一步不做,后面Simulink运行时会直接报“Cannot load”错误。

输入输出通道的配置是这章的重点。Carsim作为被控对象,默认向Simulink输出的信号包括纵向车速Vx、横向车速Vy、横摆角、横摆角速度、车辆位置X/Y等;Simulink需要回传给Carsim的信号主要是前轮转角。初次搭建时推荐用前轮转角而不是方向盘转角作为控制量,因为方向盘转角到前轮转角之间还有一个转向系减速比,用前轮转角可以少标定一个参数。典型通道配置如下表:

通道方向Carsim变量Simulink信号名说明
Carsim→SimulinkVxvx纵向车速,用于MPC模型
Carsim→SimulinkYawyaw横摆角,用于计算航向角偏差
Carsim→SimulinkYaw rateyaw_rate横摆角速度,可选
Carsim→SimulinkX coordinatepos_x车辆全局位置X
Carsim→SimulinkY coordinatepos_y车辆全局位置Y
Simulink→CarsimFront wheel steeringdelta_f前轮转角(rad)

通道顺序极其重要。Carsim生成示例模型时,输入输出端口的顺序就是上表的顺序。你在Simulink里接线时,端口的物理顺序必须和这个表严格一致,不然会出现“给转角车不走、状态信号接错告警”的诡异现象。

3.2 Simulink端对接:S-Function配置与外部模式选择

在Simulink里新建模型,从“User-Defined Functions”库拖入S-Function块,函数名填Carsim生成的S-Function名称。模型图的信号流是:S-Function输出车辆状态 → MPC控制器计算前轮转角 → 转角信号送回S-Function输入。为了快速验证通道顺序没接反,我习惯先把控制量改成常数:给一个固定0.02rad的前轮转角,跑几秒钟仿真,看车是否走出一个规则的圆弧。如果车走了圆弧但方向和预期相反,说明转角通道正负号反了;如果车完全不动,大概率是输入输出通道接错。这一步十分钟能做完,能省掉后面几个小时的排错时间。

求解器配置有一个硬性要求:Simulink求解器必须设置为固定步长离散(fixed-step discrete),步长取上面确定的Ts值。Carsim的S-Function内部有它自己的积分步长,外部求解器只要保证信号交换频率正确即可。如果选了变步长连续求解器,会出现两种麻烦:一是代数是环错误,二是仿真步长被MPC的离散更新打断,结果曲线出现锯齿状振荡。

外部模式(External mode)这里单独说一句。外部模式意味着Simulink把模型编译成C代码独立运行,再通过TCP/IP和Carsim通讯,这适合硬件在环或实车代码生成场景。如果你的目的是离线验证MPC算法,用Normal模式就够了。外部模式还要求本机装了C编译器(MinGW或MSVC),很多新手卡在这里两三天,最后发现压根不需要外部模式。记住一个原则:先Normal模式把算法调通,再考虑外部模式做实时性测试。

4. 在Simulink里把MPC控制器跑通:代价函数、约束与MATLAB Function实现

4.1 用MATLAB Function实现MPC求解的核心代码

MPC在Simulink里的实现有两条路:一条是用Model Predictive Control Toolbox里的MPC Controller块,填状态空间模型和权重就能跑,省事但依赖工具箱授权;另一条是自己写MATLAB Function,可以看清控制器的每一个角落,也方便做定制化修改。这里给出教学版的代码骨架,展示从预测矩阵构造到二次规划求解的完整过程。

function delta_f = mpc_path_tracking(x, kappa_seq, u_prev, p) % 输入: % x - [e; delta_psi] 当前横向偏差(m)和航向角偏差(rad) % kappa_seq - 未来Np个参考路径曲率序列 % u_prev - 上一时刻前轮转角(rad) % p - 结构体参数 % 输出: % delta_f - 当前时刻前轮转角(rad) Np = p.Np; Nc = p.Nc; Ts = p.Ts; v = p.v; L = p.L; x0 = x; % 离散模型矩阵 Ad = [1, Ts*v; 0, 1]; Bd = [0; Ts*v/L]; Dd = [0; -Ts*v]; % 预分配预测矩阵 Phi = zeros(2*Np, 2); % 状态对初始状态的映射 Psi = zeros(2*Np, Nc); % 状态对控制序列的映射 Tau = zeros(2*Np, 1); % 曲率扰动序列 % 堆叠预测方程: x(k+i) = Phi_i*x0 + Psi_i*U + Tau_i % 先处理i=0的项 for i = 1:Np row = (i-1)*2 + 1; Phi(row:row+1, :) = Ad^i; for j = 1:min(i, Nc) Psi(row:row+1, j) = Ad^(i-j) * Bd; end Tau(row:row+1, 1) = Ad^(i-1) * Dd * kappa_seq(i); end % 提取预测的横向偏差e: 从状态向量中取奇数行 E_pred = Phi(1:2:end, :) * x0 + Psi(1:2:end, :) * zeros(Nc,1) + Tau(1:2:end, 1); % 构造二次规划代价: J = E'*Q*E + dU'*R*dU Qe = p.Q; % 横向偏差权重 Rdu = p.R; % 控制增量权重 H = Psi(1:2:end,:)' * Qe * Psi(1:2:end,:) + Rdu * eye(Nc); f = Psi(1:2:end,:)' * Qe * (Phi(1:2:end,:)*x0 + Tau(1:2:end,1)); % 控制量限幅与增量限幅 lb = -p.delta_max * ones(Nc,1); ub = p.delta_max * ones(Nc,1); % 增量约束: |u(i) - u(i-1)| <= ddelta_max, 这里用u_prev作为起点 Ain = tril(ones(Nc)); % 累积和矩阵, 构造u_i = u_prev + cumsum(du) bin = u_prev + p.ddelta_max - lb; % 为简化,这里只做幅值约束; 完整增量约束需要额外处理 % 求解二次规划 options = optimoptions('quadprog', 'Display', 'off', 'Algorithm', 'active-set'); du = quadprog(H, f, [], [], [], [], lb, ub, [], options); % 取第一个控制增量并输出 delta_f = u_prev + du(1); end

代码的逻辑顺序是:先用离散状态方程把未来Np步的横向偏差写成当前状态的线性函数,再把这个预测偏差写成关于控制增量序列的二次型,最后用quadprog求解出最优控制增量序列。代码中的Phi、Psi、Tau三个矩阵是MPC预测的核心——Phi负责把当前状态推演到未来,Psi负责把未来控制量的作用叠加进去,Tau负责把曲率前馈补偿进预测。

这段代码有两个明显的简化:一是增量约束没有完全写进QP的约束矩阵,二是预测偏差只取了横向偏差e这一项,没有把航向角偏差也写进代价函数。完整工程实现时,把Q改成2×2对角矩阵,同时让E_pred变成2Np维的完整状态预测,再把增量约束通过不等式矩阵拼进Ain,代码量会翻一倍,但结构完全一致。不想手推这些矩阵堆叠的话,直接用MPC Controller块——构造一个状态空间模型,设置PredictionHorizon和ControlHorizon,权重面板里填Q和R,控制效果和自己写QP几乎一样,代价则是少了一层对求解过程的掌控感。

4.2 权重怎么调:Q/R比例、转角限幅与增量约束的配合

调参顺序比调参数值本身更重要。固定Np、Nc、Ts不动,只调Q和R。Q是横向偏差权重,Q大意味着控制器把“贴线”放在第一位,代价是前轮转角变化频繁,方向盘抖得厉害;R是控制增量权重,R大意味着转向平顺,但过弯时跟踪滞后明显,横向误差峰值变大。Q和R的比值是真正决定行为的东西,常见起点是Q/R比值在10到100之间。我的习惯是先用Q=10、R=0.1起步,即比值100,看跟踪误差是否能控制在0.1米以内,如果误差收敛但转角振荡,就加大R到0.3~0.5。

约束的设置有两条。幅值约束对应前轮转角的机械极限,实车一般在±35度即±0.61rad,仿真里取±0.35rad就够,留出安全裕量。增量约束限制每个控制周期转角的跳变幅度,这个约束对乘坐舒适性影响极大——每步0.02rad是一个不经意的快速变道都会触发限制的数值,正常路径跟踪几乎不会碰到它,但一旦碰到,方向盘输出会明显变“肉”。约束和权重是强耦合的:增量约束给得太紧,MPC求解器会让横向误差变大去换取转向平顺;Q给得太大,又会让转角频繁顶到幅值约束上。出现“求解器报无解”或者转角在约束边界来回振荡时,先放宽增量约束,再降Q,不要同时动两个参数。

另外一个绕不开的实操点是曲率前馈序列kappa_seq的取值。参考路径如果是一堆离散点,需要根据当前车辆位置向前取Np个点的曲率,做线性插值。这里常见的问题是索引错位:车辆已经过了某个路径点,但取的还是那个点的曲率,等于让控制器看着后视镜开弯道。后面的避坑章节会具体展开这个现象。

5. 联合仿真避坑排查:版本不匹配、代数环与信号错位的三类高频问题

5.1 Carsim与Simulink版本不匹配导致DLL加载失败

现象:Simulink模型一运行就弹红色报错,提示“Cannot load carsim S-Function”或者“Invalid MEX file”。有时候上午还好好的,下午换了台电脑就再也跑不起来。

原因:Carsim生成的S-Function DLL是针对特定MATLAB版本编译的。Carsim版本和MATLAB版本差得太多,DLL里的接口结构和MATLAB预期的不一致,直接加载失败。很多实验室的电脑上装着多个MATLAB版本,默认启动的是新版本,而Carsim只适配了旧版本。

解决:先确认Carsim官方支持矩阵里列出的MATLAB版本列表,我一般用“Carsim版本号 + MATLAB支持版本”作为关键词去查。然后打开MATLAB的“Set Path”,把Carsim安装目录下的接口文件夹——通常是Carsim安装路径里的某个dll文件所在目录——手动添加进去,同时清空Simulink缓存:在MATLAB命令窗口执行rehash toolboxcache。如果还是失败,大概率是版本不匹配而不是路径问题,换装Carsim对应的MATLAB版本,一劳永逸。

5.2 求解器配置不当导致代数环振荡

现象:联合仿真能跑,但MPC输出的转角以极高的频率来回跳,Scope里看到的转角曲线像一条锯齿带,车辆实际轨迹也跟着S形扭动。

原因:Simulink求解器用的是变步长连续求解器,Carsim的S-Function和MPC的离散更新之间形成了代数环。连续求解器为了保证精度会把步长缩小到极小,但这个极小步长和Carsim的通信间隔对不上,信号在同一个仿真步里来回循环,振荡就发生了。

解决:把Simulink求解器改成固定步长离散求解器,步长设成和MPC的Ts一致。另外检查有没有在模型里混用了连续模块——比如给某个信号加了一个Continuous PID Controller,这会让整个模型重新退化成连续系统。MPC控制器和Carsim接口都必须是离散的,整个链路上不要出现连续状态。改完求解器配置后,把“Treat as atomic unit”勾到S-Function块上,可以进一步隔离代数环。

5.3 预瞄点曲率序列错位:车辆位置和参考路径对不上

现象:直线路段上跟踪误差正常,一到弯道,MPC表现得像是提前一个弯道就开始打方向,车身明显切弯,横向误差峰值出现在弯道顶点之后。

原因:这是曲率前馈序列kappa_seq的取点位置和车辆实际位置不同步。常见错误是用一个固定的数组索引,比如把参考路径的第1到第Np个点当作“前方路径”,而没有根据车辆当前位置动态计算最近路径点索引。车辆位置前移之后,索引还停留在原来的位置附近,相当于控制器看到的“前方”其实是“侧方”甚至“后方”的路径信息。

解决:每一控制周期都根据车辆位置计算最近路径点索引,然后以该索引为起点向后取Np个点的曲率。索引计算用最近邻搜索就够,路径点密度足够高时,线性插值即可。这里值得在仿真里加一个校验信号:把当前使用的kappa_seq第一个值和车辆当前所在位置的路径曲率打印出来,两者应该几乎相等。如果差的量级超过一个弯道的曲率变化,说明索引逻辑有bug。

5.4 外部模式实时性不足导致视频录制掉帧

现象:开了Simulink外部模式跑联合仿真,Carsim动画窗口里的车速明显比真实情况慢半拍,用录屏软件录出来的视频卡顿严重,Simulink曲线和动画画面怎么都对不上时间轴。

原因:外部模式下Simulink把模型编译成独立进程和Carsim通讯,但普通PC上没有实时系统支撑,Windows的调度延迟加上Carsim动力学积分步长太短,整个仿真只能以低于实时的速度运行。视频录制时,录屏软件按实际时间戳记录画面,而仿真时间在按自己的节奏推进,两边对不齐。

解决:不在外部模式下录视频。先正常跑完一次仿真,Carsim会把车辆运动数据保存下来,然后进入Carsim的后处理(Post Processing)模式,用保存的数据离线回放动画并生成视频。Simulink那条数据曲线则直接用在仿真过程中保存的工作区变量重绘。两条视频各自生成后再合成,时间轴由仿真时间统一控制,就不存在实时性问题了。

6. 仿真视频生成技巧:Carsim动画画面与Simulink数据曲线的同步录制

视频生成的第一个原则是先跑仿真、再回放、最后录屏,不要边仿真边录。Carsim跑完一次仿真后会自动保存车辆轨迹数据,在后处理界面里可以重新播放动画,而且播放倍率可以自由控制。动画视角选跟随视角(chase view)最容易展示路径跟踪效果,车在画面中间,参考线清晰可见;俯视视角适合展示整体轨迹贴合度;驾驶员视角则适合展示方向盘操作是否自然。帧率统一设30fps,Carsim的Video Generation功能可以直接导出MP4格式的视频文件。

Simulink这边的数据曲线要做到和动画同步,最省事的方法是在Scope里放一个阶跃特征信号。做法是在参考路径的第一个弯道入口处人为叠加一个0.1m的路面横摆激励,MPC控制器对这个激励的响应会在横向误差曲线上留下一个明显的特征尖峰。动画画面里同一个位置也会出现车辆轻微摆动。录完两路素材后,在剪辑工具里以这个特征尖峰为对齐点,误差最多差一帧。

更规范的做法是用MATLAB脚本重绘曲线再用VideoWriter写帧,这样时间轴由仿真数据的每个采样点严格控制,精确到毫秒。脚本的核心逻辑是:读入保存的横向误差序列,plot一帧保存一帧,帧率与动画文件保持一致,最后用VideoWriter合并成完整曲线视频。工作量不大,但对齐效果远好于手动剪辑。第一次录视频我翻过车:画面里车明显向左偏,数据曲线却显示误差接近零。后来发现是后处理回放时选错了仿真结果文件,车的数据和一个空跑工况的数据混在了一起。从那以后我养成了一个习惯:每次跑完仿真,立刻在工作区保存一个带时间戳的.mat文件,文件名里写明工况和参数,回放动画前先核对数据文件的时间范围。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表