做智能车辆方向,最常遇到也最容易被低估的仿真组合,就是MATLAB 2016跑模型预测控制(MPC)、Carsim 2018提供车辆动力学响应。紧急换道和避障这类场景里,控制算法要在一个很小的时域窗口内把车从原车道平稳挪到目标车道,同时不侧滑、不碰障碍物、不让乘员被横向加速度折腾得难受,这就不是单纯“给一个转角”能解决的问题。这篇文章会把整套方案从环境搭建、车辆模型降阶、参考轨迹生成到联调踩坑完整讲一遍,适合正在做毕设或课题复现的车辆控制方向学生,以及第一次接触Carsim联合仿真的工程师参考。
先说结论:这套方案最大的价值在于把“决策”“规划”“控制”和“高精度车辆模型”串成一条可调试的链路。上层判断该不该换道,轨迹生成器给出一条可执行的参考路径,MPC负责在每个采样周期滚动优化出前轮转角,Carsim则忠实地把轮胎力、侧偏、横摆这些动力学特性吐出来。任何一层出问题,最终都表现为车辆轨迹离谱,但根因往往隐藏得很深。
1. 紧急换道不是“方向盘一打就完了”:这套方案真正解决的控制矛盾
1.1 紧急换道和普通车道保持的差异到底在哪
车道保持问题里,横向偏差通常很小,车辆基本在线性区工作,用一个简单的预瞄控制器甚至PID就能处理。紧急换道完全不是这个量级。我们测试的典型工况是:车速80 km/h,前方出现静止障碍物,留给驾驶员的反应距离可能只有30到40米。车辆要在1.2秒左右完成约3.75米的横向位移,横摆角速度会冲到0.2 rad/s以上,前轮转角接近执行器极限,整个系统明显靠近轮胎附着的非线性边界。
这时候如果只用“跟踪某个前方目标点”的纯几何方法,会出现两个问题。第一,高速下车辆的横摆响应存在明显延迟,转角打了之后要过一会儿车头才真正转过来,简单几何关系算出来的角度天然落后一拍。第二,纯跟踪算法不会主动考虑执行器限制,也不会权衡“当前横向误差”和“未来趋势”之间的关系,容易在末端超调和振荡。
模型预测控制的思路是完全另一套:我不看当前这一个点,而是基于当前状态,把未来20到30个采样周期内的车辆运动都“模拟”一遍,在预测时域里同时评估跟踪误差、控制量大小、控制量变化率这些指标,最后把最优的那个转角输出给执行器。下一拍重新采样、重新预测,这就是滚动优化。
1.2 为什么MPC在换道场景里比PID和纯预瞄更合适
类比下棋的话,PID是只看当前棋盘的局部应对,纯预瞄是提前看了两步就出手,MPC等于把未来两百步的棋谱都在脑子里推演一遍,再用一组约束告诉自己哪些棋子不能丢。换道场景最怕的就是控制量饱和:想要更大的横向加速度去贴近参考轨迹,但前轮转角已经到极限,MPC能通过目标函数里的加权系数自动把“减小跟踪误差”和“不产生过大的控制量”变成同一个优化问题去平衡。
更重要的是约束表达。MPC天然支持把前轮转角限制、转角变化率限制、横向加速度限制写成优化问题的约束条件。避障时还可以进一步把“车辆位置不能进入障碍物边界”写成横向位置的不等式约束。这一点PID很难处理,PID只能靠调参数去间接避让,而MPC是在每个控制周期里直接对约束求解,安全性更直观。
这套架构整体上分三层:轨迹生成层输出期望路径,MPC控制层输出前轮转角,Carsim车辆层反馈速度、横摆角速度、位置和航向角。后面所有调试工作,都是围绕这三层之间的信号传递展开的。
2. 联合仿真环境搭建里最容易被低估的三层坑
2.1 版本配对逻辑:MATLAB 2016为什么配Carsim 2018反而舒服
很多人一上来就装最新版MATLAB,再配一个高版本Carsim,结果第一天就卡在S-Function导出上。我们的经验是:联合仿真领域,“稳定能跑”远比“版本新”重要。MATLAB 2016a的Simulink接口相对保守,对S-Function的封装方式、MPC Toolbox的调用方式都有大量现成资料可以参考。Carsim 2018刚好是接口支持比较开放的一代,导出的Simulink模型兼容性比后来的某些版本更好。
有一段时间我们在新版本MATLAB上试过同样的Carsim 2018模型,发现导出的S-Function在编译和总线信号对接上总有小问题,反而是回到MATLAB 2016之后一路顺利。这不是说新版本一定不行,而是对急着跑控制算法的项目来说,版本配对的核心逻辑是“选一个少出幺蛾子的组合”,不是“选最新”。
2.2 从Carsim导出Simulink模型的完整操作顺序
第一次接触Carsim的人最容易犯的错误是直接在主界面点“Send to Simulink”,生成模型之后就开始接线,完全没检查车辆参数和输出通道,结果后面所有仿真结果都是错的。正确的顺序应该是:
- 在Carsim主界面选择车辆参数页面,确认车辆型号、轮胎参数、悬架参数。我们常用的是一个接近中型轿车的默认参数集,道路摩擦系数设为0.85,模拟干燥柏油路面。
- 进入仿真工况设置,先做一个最简单的直线加速工况,确认车辆模型本身没跑偏。
- 配置输入和输出通道。输入侧至少要有油门、制动、前轮转角(左右前轮分别给角度的通道也要保留);输出侧把纵向速度Vx、横向速度Vy、横摆角速度Yaw_rate、大地坐标X和Y、横摆角Yaw全部加到导出列表。
- 设置Simulink模型生成路径和名称,这里建议路径不要带中文和空格,否则后面对接S-Function容易出问题。
- 点击Send to Simulink,在弹出来的模型里你会看到Carsim S-Function模块,外部还带一个输入端口和一个输出端口。
生成之后还有两步特别重要。一是Simulink求解器要改成定步长,我们实测用固定步长0.01秒比较稳;二是要检查Carsim的仿真结束时间,最好设得比Simulink仿真时间长2到3秒,否则仿真到后段Carsim输出会直接清零,看起来像控制器“突然失灵”。
2.3 信号单位与总线对象:控制器“发疯”的第一源头
Carsim导出的输出是一个总线对象,里面默认信号的单位和我们MATLAB控制算法里习惯用的单位经常不一致。这是联合仿真的第一大坑,我见过太多人在这一步浪费了整整一个下午。常见的默认配置里,车速可能是km/h,角度可能是deg,而MPC内部通常用m/s和rad。
| Carsim常用通道 | 常见默认单位 | 控制器常用单位 | 处理方式 |
|---|---|---|---|
| Vx / Vy | km/h | m/s | 除以3.6 |
| Yaw_rate | deg/s | rad/s | 乘以pi/180 |
| Yaw | deg | rad | 乘以pi/180 |
| SteerL1 / SteerR1 | deg | rad | 乘以pi/180 |
处理方式有两种。第一种是在Bus Selector之后逐个接Unit Conversion模块,适合习惯图形化操作的人;第二种是在MATLAB Function模块内部做单位换算,适合后面要频繁改控制算法的场景。无论哪种,都要先打开总线结构看清楚信号名称,不要凭印象接线。检查单位的最快办法是给这些信号接Scope,用已知的直线匀速工况对比数值量级。车速20 m/s,如果示波器上显示72,那基本可以确定是km/h没转换。
3. 把整车动力学装进MPC控制器:模型降阶与参数落地
3.1 从Carsim整车模型到MPC预测模型
Carsim提供的整车模型自由度很高,轮胎、悬架、转向几何都有详细表达,但这种模型没法直接拿去做MPC预测。道理很简单:MPC每个采样周期要往前推20到30步,如果每一步都用高自由度模型求解一次,计算量会让实时仿真直接卡死。所以必须做模型降阶。
常用做法是保留三自由度:纵向运动、横向运动和横摆运动。纵向速度可以在Carsim里用定速巡航控制保持住,MPC重点管横向位移和航向。预测模型的状态量一般取横向速度Vy、横摆角速度Yaw_rate、大地坐标Y、横摆角Yaw,有时把纵向速度Vx也纳入进来做速度协调。控制量用前轮转角。这个降阶模型在紧急换道工况里足够用,因为控制时域很短,车辆大多数时间还在轮胎线性区内,非线性部分由约束和Carsim的高保真反馈在闭环中兜住。
模型降阶时的核心参数是前轮侧偏刚度、后轮侧偏刚度、轴距和车辆质量。这些数值可以从Carsim车辆参数页里查到,也可以用一段简单的开环仿真反推校准。做法是给一个固定前轮转角斜坡输入,比对Carsim输出的横摆角速度响应曲线和线性二自由度模型的响应曲线,微调侧偏刚度直到两者接近。
3.2 状态方程、目标函数和约束的工程化写法
把连续车辆动力学模型离散化之后,得到标准的状态空间形式:
x(k+1) = A_d * x(k) + B_d * u(k)
其中u是前轮转角。MPC在每个采样时刻求解一个优化问题,目标函数大致是跟踪误差的二次型加上控制量、控制增量的惩罚,同时满足状态和控制约束。写成工程化表达就是:
min Σ (x_pred - x_ref)^T Q (x_pred - x_ref) + Σ u^T R u + Σ Δu^T S Δu
约束包括前轮转角绝对值限制、转角变化率限制,以及避障场景下横向位置的边界限制。状态量本身如果超出安全范围,例如横向加速度过大,可以用软约束加惩罚项处理,否则遇到机械式硬约束无解的情况,整个求解器会直接返回失败,仿真立刻崩掉。
参数给一个可以直接起步的推荐值:
| 参数 | 推荐范围 | 说明 |
|---|---|---|
| 采样周期Ts | 0.01到0.02秒 | 太小求解压力大,太大控制精度差 |
| 预测时域Np | 15到30 | 大约覆盖未来0.3到0.5秒 |
| 控制时域Nc | 2到5 | 控制量自由度不能太多 |
| 横向偏差权重 | 50到200 | 主导换道贴线精度 |
| 横摆角速度权重 | 1到10 | 抑制车头晃动 |
| 控制量权重R | 1e-4到1e-3 | 前轮转角数值本身小,权重要小 |
强调一点:权重不是算出来的,是调出来的。先用横向偏差权重占据主导把轨迹跟紧,再慢慢加横摆角速度权重去抑制末端振荡,直到换道曲线既贴线又不“甩头”。
3.3 在MATLAB 2016里选择求解路径:MPC Toolbox还是quadprog
实现路径有两条。一条是直接用MPC Toolbox,把离散状态空间模型封装成mpc对象,在Simulink里拖一个MPC Controller模块,配置好权重和约束就能跑。这条路的好处是快,适合验证整体方案;坏处是后续想加“避障边界约束”“动态障碍物约束”这类自定义线性不等式时,MPC Toolbox的表达不够灵活,2016版本尤其如此。
另一条是手写MPC,在每个控制周期调用quadprog求解二次规划。代码骨架大概是这样的:
function u = mpc_solve(Aq, Bq, x0, xref, H, f) % 用预测矩阵构造二次规划并求解,这里只展示核心结构 options = optimoptions('quadprog', 'Display', 'off', 'MaxIter', 100); u = quadprog(H, f, Aineq, bineq, [], [], -Umax, Umax, [], options);实际工程中,Aq、Bq、H这些矩阵要在初始化阶段一次性构造完,存成persistent变量,不要在MATLAB Function里每个控制周期重新算。我们手写MPC后的经验是:可读性虽然差一点,但想要改约束、改软惩罚、加调度逻辑,自由度比Toolbox高很多。最终还是建议先用Toolbox跑通全链路,确认车辆模型和单位都没问题,再切换到自定义QP版本做深入研究。
4. 期望轨迹生成和避障约束嵌入:换道不能只看一个点
4.1 参考轨迹生成:Sigmoid与五次多项式的实测差异
换道轨迹生成是所有上层逻辑里最不能说“随便给一条曲线”的部分。正常换道轨迹要保证起点和终点的横向速度、横向加速度都为零,否则车辆在切入瞬间会出现冲击感。两条常用曲线里,Sigmoid形式简单,但参数和物理量不直观;五次多项式更好用,因为它可以直接根据换道时间和横向位移写出。
五次多项式形式是:
y(t) = L * (10tau^3 - 15tau^4 + 6*tau^5)
其中tau = t / T,T是换道完成时间,L是横向位移。这个形式在tau从0到1的过程中,起终点位置、速度和加速度都满足零边界条件。实测下来,五次多项式的横向加速度峰值能解析估算,方便我们根据舒适性约束反推换道时间。
代码实现非常简单:
function yref = ref_traj_fifth(t, T_man, L) % t为当前时间,T_man为换道完成时间,L为横向位移 tau = t / T_man; yref = L * (10*tau^3 - 15*tau^4 + 6*tau^5); end在80 km/h工况下,换道时间取1.0到1.5秒都比较合理,换算成纵向预瞄距离大约30到40米。时间取太小,横向加速度会超过0.4g,乘员明显不适;时间取太大,避障距离不够,前方障碍物已经撞上。实际调参时可以先按最大横向加速度0.3g估算最短换道时间,再留20%余量。
4.2 障碍物目标边界怎么变成优化问题的硬约束
MPC避障最关键的一步,是把“不能碰到障碍物”转成数学约束。紧急换道中,决策层一般先确定换道方向,这里以向右换道为例。障碍物前方的横向位置边界可以表示成一条随纵向距离变化的下限曲线,MPC要求预测时域内的车辆横向位置y_pred满足:
y_pred(k) >= y_obs_lower(k)
其中y_obs_lower是一条考虑了车辆宽度和障碍物安全边距的参考线。离散到每个预测时刻,就构成一组线性不等式约束,写进quadprog的Aineq和bineq参数里。这样做的好处是约束物理含义非常清楚:时间越接近障碍物,边界要求越严格。
但固定方向的约束不是万能的。如果决策层判断失误,把换道方向定在障碍物同侧,优化问题很可能直接无解。所以工程上通常给横向约束加一个松弛变量,让求解器在无解时以很大的惩罚代价突破安全距离,而不是直接返回失败。这个细节不提前处理,联调时十有八九会遇到“仿真跑到一半突然NaN”的诡异问题。
4.3 触发策略:什么时候进入紧急换道模式
控制器不能无时无刻都在求解“贴着障碍物”的约束,否则正常行驶会被强制限制在很小的工作范围。实际方案里一般有一个独立的触发逻辑:当前方障碍物距离小于制动安全距离,且当前车道的碰撞时间TTC小于某个阈值时,才激活紧急换道模式。触发后轨迹生成器开始输出换道参考曲线,MPC同时切换到带避障约束的版本。
换道完成后要把控制模式切回普通车道保持。这里的切换一定要做平滑处理,最简单的做法是让参考轨迹在换道终点保持一小段时间,再逐步过渡到车道中心线。如果切换瞬间就把期望横向加速度从零变成一个小值,车辆会激灵一下,影响稳定性评价。
5. 调试全过程实录:三个让仿真当场崩溃的现场
5.1 横摆角速度方向反了:调了一个下午最后发现是单位
我先讲一个真实到让人心痛的案例。某次调试中,MPC输出的前轮转角持续增大,车头不断往一个方向偏,看起来像是控制器发散。我们先把MPC的横向偏差权重降下去,没用;把预测时域缩短,还是没用;把参考轨迹换成缓慢变化的斜坡,依然偏。最后把横摆角速度信号单独拉出来看,发现数值量级比预期大得多。正常0.1 rad/s的横摆角速度,示波器上显示接近6、7,这一看就是deg/s没有转换成rad/s。
排查单位问题有一个固定套路。先把车辆开环跑起来,给一个已知小转角,比如前轮转角恒定2度,车速稳定在20 m/s,理论上稳态横摆角速度大约在0.1到0.15 rad/s之间。如果示波器上看到6到9,就有理由怀疑是角度单位的问题。转换之后,同样的工况,控制器的输出和状态曲线一下就合理了。所以联调的第一步不是调MPC权重,而是把所有信号单位统一。
5.2 求解器超时:长预测时域与控制周期的拉扯
仿真跑到2秒左右开始一卡一卡,CPU占用接近100%,是联调中第二个高频问题。原因是每个控制周期都要解一个中等规模的二次规划,当预测时域Np取到30、Ts取0.01秒时,优化变量数量不小,同时MATLAB Function里如果还频繁做矩阵离散化,开销会翻倍。
解决思路是分两路走。第一路降计算量:Np从30降到20,采样周期从0.01放宽到0.02秒,控制频率从100 Hz降到50 Hz。紧急换道这个工况本身主要能量集中在10 Hz以内,50 Hz完全够用。第二路优化代码结构:把c2d离散化、预测矩阵构造都挪到初始化阶段,用persistent变量存起来,MATLAB Function里每步只做矩阵乘法和quadprog。实测同样参数下,每步求解时间能降低一半以上。
5.3 Carsim输出掉零与NaN:先检查时间和总线
还有一种现象更迷惑:控制器算法看起来完全没问题,但车辆跑着跑着突然开始直线滑行,好像前轮转角突然失去作用。排查发现是两个原因叠加。第一,Carsim的仿真结束时间设置得比Simulink短,当内部仿真结束后,S-Function输出端口直接置零,控制器拿到零状态当然无法正确输出。第二,控制输入回传Carsim时,Connector Bus里选中了错误的信号线,前轮转角通道实际一直给的是零,所以车辆才“不听使唤”。
排查路径也很直接。先检查Carsim Run Control页面里的仿真结束时间,把它设置成Simulink仿真时长加2秒;然后用几个Display模块直接看Carsim输入端的油门、制动、转角信号是否真的有非零值。如果输入端就已经是零,控制器再聪明也没用。
6. 从能跑到跑好:结果验证与复现清单
6.1 该看哪些曲线、怎么记录关键指标
很多项目把仿真跑通就以为结束了,结果写报告时发现没有记录关键指标,又得从头再跑一遍。建议从第一版闭环开始就养成记录曲线的习惯。最基础的三组曲线是:轨迹对比图(参考轨迹和实际轨迹)、状态响应图(横向速度、横摆角速度、横摆角)、控制量输出图(前轮转角随时间变化)。这三组图能覆盖控制精度、稳定性和执行器饱和度三个核心维度。
指标可以参考下面的基准:
| 指标 | 参考目标 | 说明 |
|---|---|---|
| 换道距离 | 30到40米 @80 km/h | 距障碍物距离决定的硬指标 |
| 最大横向加速度 | 小于0.4g | 舒适性与稳定性 |
| 最大横摆角速度 | 0.25到0.35 rad/s | 车辆是否可控 |
| 横向跟踪误差 | 小于0.15米 | 控制器精度 |
| 前轮转角峰值 | 小于8到10度 | 是否触达执行器极限 |
不要只看某一个时间点的截图。紧急换道的关键在于整个时间窗口内的连续曲线,尤其是换道结束后的稳定段,如果横向加速度还有明显振荡,说明权重和目标函数里的阻尼项还需要再调。
6.2 一份可以让后来者三天跑通的复现清单
在这套方案上反复折腾之后,我整理了一份复现清单,后来给一朋友复现时,照着做三天就跑通了第一版闭环。
- 环境层面:MATLAB 2016a先跑通mex -setup,确认C编译器配置正确;Carsim 2018切换到Simulink接口模式,不要用动画模式。
- 信号层面:导出的每个关键信号都接一个Display或Scope,逐一确认单位,统一做km/h转m/s、deg转rad。
- 车辆验证层面:先做开环测试,手动给斜坡前轮转角,确认Carsim响应符合预期,再进入闭环。
- 控制器层面:先用MPC Toolbox跑通,再换手写QP;权重从低到高逐步增加横向权重,每换一组参数都保留记录。
- 时间层面:Carsim仿真时间比Simulink长2秒,采样周期定步长0.01到0.02秒,预测时域20左右起步。
- 报告层面:每版仿真保存三组曲线和指标表格,方便回溯参数变化的影响。
这套清单最大的意义不是让每个人得到完全相同的仿真结果,而是把变量控制住了。先把单位、时间、总线这些“环境变量”全部固定下来,剩下的问题才真的有资格归因到算法参数上。我自己后来调任何联合仿真项目,都会先不碰MPC权重,只把信号链和数据流跑干净,确认从Carsim出来的每个数都符合物理直觉,再开始动控制器的参数。很多看起来像算法问题的事故,查到最后全是信号链上的小毛病。