我一直觉得,弯道变道是智能车辆轨迹跟踪控制里最有代表性的工况。直线变道时,横向和纵向基本解耦,控制器调好一组PID就能跑得像模像样;但到了弯道上,曲率本身在变化,侧向加速度又和车速耦合在一起,之前那套参数基本就直接“原形毕露”。之前做某自动驾驶仿真验证项目时,我第一版Carsim与Simulink联合仿真模型在直线段一切正常,一进弯道前轮转角就开始高频小幅抖动,车辆慢慢朝弯道外侧滑出去,方向盘完全失去对轨迹的掌控。来回折腾了两三天,最后发现根因不在MPC的权重矩阵,而在参考路径里的航向角序列。
这篇文章就把Carsim与Simulink联合仿真实现弯道变道的完整链路拆开聊一聊:从仿真环境怎么搭、I/O接口怎么配,到弯道变道参考路径怎么生成、MPC轨迹跟踪控制器怎么设计,再到联合仿真调试时那些文档里不会写的坑。适合正在做自动驾驶控制仿真验证、或者在搞智能车辆轨迹跟踪课题的工程师和学生参考,思路可以直接照搬。
1. 为什么要用Carsim和Simulink做弯道变道仿真
1.1 弯道变道这个工况,为什么值得单独拿出来说
弯道变道和直道变道最大的区别在于:车辆状态方程里多了一个随时间变化的道路曲率项,而且这个曲率直接影响侧向加速度需求。直道上换道,横向位移跟着一个平滑多项式走就行,纵向速度基本可以用恒定值;弯道上换道,车辆本身就有一个朝向弯道内侧的横摆角速度,换道轨迹需要在这个基础横摆运动之上再叠加一次横向偏移,跟踪控制器必须同时处理好“跟随弯道”和“完成变道”两个任务。
从动力学角度看,弯道变道要求侧向加速度限制不能超出轮胎附着能力,车速越高、弯道半径越小,这个限制就越紧。仿真相比实车场地测试的优势非常明显:同一套控制器可以批量扫不同半径、不同车速、不同换道长度的工况,而且可以安全地试错。实测中如果在某组参数下车辆失控,后果可能是车损甚至是安全问题,但在仿真里最多是波形发散、数据异常,重跑一次就恢复,这是弯道变道控制研发放在仿真阶段进行的重要原因。
1.2 Carsim和Simulink在整个仿真链路里各自扮演什么角色
Carsim负责车辆动力学求解部分——包含悬架、轮胎、转向系统、制动系统、空气阻力等子模型,输出车辆在空间中的位置、速度、横摆角、横摆角速度、质心侧偏角等状态。Simulink则部署路径规划、速度规划、MPC控制器或者PID控制器等算法逻辑。两者的数据交互通过Carsim生成的S-Function完成,每个仿真步长内Simulink调用一次Carsim求解器,读取车辆状态并写回转向角、油门、制动等控制量。
这种分工非常自然:车辆动力学如果自己建模,模型标定非常浪费时间,而Carsim是成熟软件,改个参数、换辆车只需要在界面上操作;控制算法则需要频繁调整甚至换结构,放在Simulink里修改起来更方便。我之前也对比过几个平台,简单列一下:
| 工具 | 动力学精度 | 与Simulink接口便利性 | 开放程度 | 最适合场景 |
|---|---|---|---|---|
| Carsim | 高,百万级参数覆盖多种车型 | 高,直接生成S-Function | 较高,车辆参数开放 | 控制算法验证、硬件在环、整车级工况仿真 |
| CarMaker | 较高,场景管理强 | 中等,需要中间层脚本 | 中等 | 测试场景批量化、ADAS功能验证 |
| PreScan | 偏向传感器与场景 | 中等 | 中低 | 感知算法验证、V2X场景 |
| ADAMS | 极高,多体动力学 | 低,配置复杂 | 高 | 底盘悬架精细化分析,不适合控制快速迭代 |
控制算法验证这个环节,Carsim和Simulink组合在实用性和便利性之间平衡得最好,这也是它成为行业里做轨迹跟踪联合仿真首选方案的原因。
2. 联合仿真环境搭建:接口配置中最容易卡住的几个环节
2.1 版本兼容性和安装细节,先说最容易踩的坑
Carsim各版本与MATLAB/Simulink版本之间有明确的兼容关系,如果版本差异过大,生成S-Function之后在Simulink里会报编译错误,而且报错信息通常很隐晦。建议在项目开始前就确定好版本组合,并且整套环境只用这一组版本,避免多人协作时工程文件互相不兼容。
安装层面有几个细节容易被忽略:
- 安装路径绝对不能包含中文和空格,否则编译生成的VS工程文件路径不正确,S-Function加载会失败;
- Carsim生成的S-Function依赖C编译器,需要先确保MATLAB里能正常执行mex -setup,否则联合仿真模型无法编译;
- 打开Carsim模型时,注意选择与Simulink联合仿真对应的模式,而不是完全独立的Carsim试算模式,否则模型类型不对,无法生成正确的DLL。
提示:如果第一次运行联合仿真时Simulink报“Cannot find S-Function”或者加载dll失败,不要急着查Simulink模型里的问题,先回到Carsim工程里确认是否点过“Build/Generate”生成对应的动态库,再排查MATLAB工作区变量是否正确加载。
2.2 Carsim模型生成Simulink模型的操作流程
Carsim主界面中配置好车辆模型参数后,进入Simulink联合仿真配置页面,这里需要做三件事:配置输入通道、配置输出通道、生成模型。输入通道是Simulink写给Carsim的控制量,输出通道是Carsim反馈给Simulink的车辆状态。常见的通道配置我列成表:
| 类型 | 通道名 | 含义与单位 |
|---|---|---|
| 输入 | IMP_STEER_L1 | 前轮转向角,通常用rad |
| 输入 | IMP_THROTTLE_ENGINE_0 | 节气门开度或驱动扭矩 |
| 输入 | IMP_BRAKE_MASTER_CYLINDER | 主缸制动压力 |
| 输入 | IMP_VX | 可选的外部纵向速度输入 |
| 输出 | Vx | 纵向车速,m/s |
| 输出 | Vy | 侧向车速,m/s |
| 输出 | YawRate | 横摆角速度,rad/s |
| 输出 | Yaw | 横摆角,rad |
| 输出 | Xo, Zo, Yo | 惯性系坐标,m |
| 输出 | Beta | 质心侧偏角,rad |
| 输出 | Steer_L1 | 当前前轮转角反馈,rad |
通道配置好之后,点击“Send to Simulink”或者“Create Simulink Model”,Carsim会调用MATLAB生成或更新主模型文件,同时运行一个初始化脚本,把车辆参数写入MATLAB工作区。之后在Simulink中打开主模型,你会看到模型里有一个S-Function模块,里面是Carsim的接口封装,输入输出端口和刚才配置的通道一一对应。
初次拿到这个模型,我建议先不改动任何逻辑,直接双击S-Function模块,确认它的参数中包含车辆型号信息和初始化脚本路径。若路径错误或者脚本没运行,后面所有信号都会是空的。
2.3 仿真步长、信号记录和联调前的基本自检
联合仿真建议在Simulink求解器设置里选固定步长,常见做法是0.001s,控制周期的逻辑可以放在独立的子系统里用0.02s或0.05s的采样时间,通过Rate Transition模块连接。为什么强调固定步长?因为变步长求解器会在响应剧烈时自动缩小步长,但Carsim的S-Function通道在每个主步长内只同步一次,结果就是控制指令写入的时间点错乱,控制器性能会被莫名其妙的时序问题拖垮。
信号记录方面,用To Workspace模块把Vx、Vy、Yaw、Xo、Yo以及控制器的输出保存下来,变量命名建议带上前缀,例如veh.Vx、ref.X,这样在MATLAB工作区里不用纠结变量来源。数据记录至关重要,后期调参全靠这批数据复盘。
还有一个小技巧:接入正经控制器之前,先在Simulink里用常数输入模块给前轮转角一个固定值,比如0.02rad,跑1秒钟,看Carsim车辆有没有产生合理的横摆角速度响应。如果这步都不正常,说明接口通道方向或者单位有问题,此时去查MPC参数毫无意义。
3. 弯道变道参考路径生成:五次多项式与速度规划的配合
3.1 弯道场景定义与基础中心线
先定义一个可复现的场景:弯道半径取R=120m,车道宽度取3.5m,车辆初始在右侧车道中心线行驶,目标换入左侧车道中心线。弯道中心线可以用圆弧近似,曲率κ_ref=1/R,换道起点车辆航向角与弯道切向角一致。
为什么可以用圆弧近似而不需要做回旋线?因为验证控制算法时,道路曲率在换道段内基本保持不变,用圆弧做参考可以让控制器集中应对“变道轨迹叠加”这个核心难度。如果以后要测更真实的公路几何,再把中心线替换成回旋线,控制器部分不需要大改,变道叠加的逻辑是通用的。
3.2 用五次多项式生成横向偏移曲线
换道段的横向偏移d(s)是沿道路纵向距离s的函数。换道开始时偏移量为0,末端偏移量为3.5m;同时为了保证轨迹连续且曲率连续,两端的一阶导数和二阶导数也需要为0。一个三次多项式只能约束位置和斜率,端点上曲率会突变,车辆跟踪时转向角会出现跳变;解决这个问题的经典做法是用五次多项式。
归一化后的五次多项式只需一行公式,在工程里非常实用:
d(s) = d_final * [10 * (s / S)^3 - 15 * (s / S)^4 + 6 * (s / S)^5]
其中d_final是最终横向偏移距离,S是换道纵向长度。这个式子的好处是两端二阶导均为0,曲率连续,生成的轨迹让MPC控制器的工作难度显著降低。S的取值和车速强相关,低速小S,高速大S;工程上可以先按S = 0.6*v取,再根据实际仿真中的侧向加速度峰值调整。
MATLAB生成参考轨迹的代码可以这样写:
R = 120; % 弯道半径 v = 16.67; % 期望纵向车速 60km/h d_final = 3.5; % 换道横向偏移 S = 0.6 * v; % 换道纵向距离 s_vec = 0:0.1:2*pi*R/4; % 整个弯道行程 % 换道段的横向偏移曲线 s_lane = s_vec(s_vec <= S); d = d_final * (10*(s_lane/S).^3 - 15*(s_lane/S).^4 + 6*(s_lane/S).^5); % 弯道中心线坐标 theta = s_vec / R; xc = R * sin(theta); yc = R * (1 - cos(theta)); % 叠加换道偏移:法向方向是 (-sin(theta), cos(theta)) nlane = zeros(1, length(s_lane)); for i = 1:length(s_lane) t = s_lane(i) / R; nlane(i) = d(i); % 在弯道坐标系下,直接沿法向加偏移 end x_ref = xc(1:length(s_lane)) - nlane .* sin(s_lane/R); y_ref = yc(1:length(s_lane)) + nlane .* cos(s_lane/R);严格来说,弯道上的偏移叠加需要考虑几何失真,但在R≥80m的场景下,这种近似带来的位置误差远小于控制器跟踪误差,工程上可以直接用。生成后务必画出参考轨迹,肉眼检查曲线是否平滑。
3.3 速度规划:弯道上不能全程匀速
弯道变道过程中车速不能一直恒定,因为弯道本身需要向心加速度。如果侧向加速度限制为3m/s²,那么弯道半径120m对应的最大车速约为 sqrt(3 * 120) ≈ 18.97m/s,约68km/h。设计期望速度时,要先按整条参考轨迹的最大曲率计算允许车速,再在换道前后做梯形或者S形过渡。
速度规划最简单的实现方式:把整个轨迹按曲率分段,每段给一个限制速度,然后用平滑插值连接。参考代码片段如下:
kappa_ref = 1 / R; ay_lim = 3.0; % 侧向加速度限值 v_max_curve = sqrt(ay_lim / kappa_ref); v_target = min(v, v_max_curve); % 简单梯形速度曲线 t_total = S / v_target + 2.0; t = 0:0.01:t_total; v_profile = min(v_target, v * ones(size(t))); % 实际项目中可进一步加加速度限制,生成S形过渡有一点必须强调:参考轨迹的航向角序列不能只包含五次多项式的一阶导数,还必须叠加上弯道本身的切线角。也就是说,期望航向角psi_ref = atan2(dy_center, dx_center) + atan(d'(s))的近似。很多初学者生成参考轨迹时,换道终点航向角直接填0,车辆在弯道末端必定跑偏,这个问题我在调试时遇到不止一次。
4. 轨迹跟踪控制器设计:MPC横向控制与速度调节的配合
4.1 为什么选MPC,而不是纯跟踪或者PID
弯道变道控制里,纯跟踪算法的问题在于预瞄距离难调:预瞄太短,车辆振荡;预瞄太长,过弯时走线太靠内。PID横向控制则很难权衡超调量和稳态误差,而且很难显式处理转向角限位约束。MPC的价值在于把“未来一段轨迹的信息”和“当前状态偏差”统一放进一个优化问题里,同时能硬约束转向角范围和控制增量范围。弯道变道这个场景,曲率变化和横向偏差耦合明显,MPC的预测能力对抑制过弯时的超调非常关键。
4.2 简化车辆模型与离散化
工程上先用运动学自行车模型就足够把控制器逻辑调通,模型如下:
状态定义为横向偏差e_y、横向偏差变化率e_y_dot、航向偏差e_psi、航向偏差变化率e_psi_dot。连续状态方程可以写成:
e_y_dot = v_x * e_psi e_psi_dot = v_x / L * tan(delta) - v_x * kappa_ref
其中L是轴距,delta是前轮转角,kappa_ref是参考曲率。把状态空间离散化,采样时间取0.05s,得到离散状态方程x(k+1) = A_d * x(k) + B_d * u(k),控制量u建议选前轮转角变化率而不是转角本身,这样可以在优化里同时惩罚转角幅值和转角变化率,防止控制量剧烈抖动。
4.3 MPC代价函数、约束与初始参数
MPC每一帧求解一个有限时域优化问题,代价函数写成:
J = sum(k=1..Np) x(k)^T Q x(k) + sum(k=1..Nc) u(k)^T R u(k)
其中Q是对角矩阵,对应四个状态分量的惩罚权重;R是控制增量的惩罚权重。约束条件包括:|delta| <= δ_max,|Δdelta| <= Δδ_max,|e_y| <= 1.5m。
初始参数我建议这样设:
| 参数 | 初始值 | 说明 |
|---|---|---|
| 采样时间 dt | 0.05s | 与Carsim主步长解耦,控制器单独周期 |
| 预测时域 Np | 20 | 约1秒预测长度,弯道场景足够 |
| 控制时域 Nc | 8 | 控制自由度太多会导致求解慢且动作碎 |
| Q_ey | 30 | 横向偏差权重,偏大则跟踪更紧 |
| Q_edot | 10 | 横向偏差变化率权重 |
| Q_epsi | 20 | 航向偏差权重 |
| Q_edotpsi | 5 | 航向偏差变化率权重 |
| R | 10 | 控制增量权重,偏大则动作更平缓 |
调参顺序建议:先固定Q矩阵,从R=100开始往下减,直到转向角不再抖动;如果横向误差峰值偏大,再逐步增大Q_ey。不要一开始就把所有权重都调到很大,那样控制器会像神经质一样疯狂输出,仿真一定发散。
4.4 Simulink中的MPC实现骨架
在Simulink里实现MPC可以走两条路:用MPC Toolbox提供的模块,或者手写一个MATLAB Function做QP求解。Toolbox方便但不够灵活,手写框架则方便后面接入不同的求解器。下面是简化版MPC控制器的函数骨架,核心逻辑是每一帧构造QP问题后调用quadprog求解:
function u_out = mpc_controller(x_ref, y_ref, psi_ref, vx, state, params) % 简化的弯道变道MPC控制器 % 输入:参考轨迹、当前车速、当前状态、控制器参数 % 输出:前轮转角 delta Np = params.Np; dt = params.dt; L = params.L; R_q = params.R; Q = params.Q; % 计算参考曲率(由本次采样点估计) kappa_ref = curvatrue_from_trajectory(x_ref, y_ref); % 构造离散状态空间矩阵 A_d, B_d(简化运动学模型) [A_d, B_d] = vehicle_model_discrete(vx, L, kappa_ref, dt); % 构造QP问题: min 0.5*z'*H*z + f'*z % z = [u0, u1, ..., u_{Nc-1}] [H, f] = build_qp_matrix(Q, R_q, A_d, B_d, Np, Nc, state); % 线性不等式约束:转向角限位、控制增量限位 [Aineq, bineq] = build_qp_constraints(params.delta_max, params.ddelta_max, L, Nc); % 求解 z = quadprog(H, f, Aineq, bineq, [], [], [], [], [], optimoptions('quadprog','Display','off')); % 取第一个控制增量,累加到当前转向角 u_out = state.delta + z(1); end具体实现中还有很多细节,比如参考曲率要从最近参考点处计算、当前偏差点要从惯性系坐标投影到参考轨迹坐标系里、预测时域内曲率要按对应参考点更新而不是一直用当前点。这些地方是决定MPC实际表现的关键,用Toolbox模块时可以少操心一部分,手写时则要逐一确认。
速度控制与MPC配合,通常做法是在Simulink里用一个简单PID把期望速度转成油门/制动指令,输出给Carsim的油门和制动通道。建议把速度PID和MPC横向控制放在同一个子系统内,共用同一个采样步长,避免时间不同步产生额外问题。
5. 联合仿真调试实录:从发散到收敛的参数整定过程
5.1 第一次跑不通时的完整排查链路
我在实际项目中遇到过最典型的失败过程:参考轨迹看着平滑,MPC权重也调过很多轮,但一接上Carsim就横向误差越来越大,最后车辆冲出道路。排查链路值得记录一下,给后来者参考。
第一步先排除接口问题。在Simulink里给前轮转角一个很小的正弦激励,比如0.01rad、0.5Hz,观察Carsim输出的横摆角速度是否同频响应,如果响应方向反了或者没有响应,检查前轮转角输入通道的极性和单位。第二步检查参考轨迹生成脚本,把参考航向角ps_ref和实测航向角画在同一张图里,如果有固定偏差,大概率是航向角叠加逻辑写错了。第三步检查MPC求解结果,把每一步的期望转向角和实际输出转向角打印出来,如果两者差值一直很大,说明约束条件或者控制增量累加方式有问题。
这个流程走完,基本能把“接口问题”“参考轨迹问题”“控制器实现问题”三大类原因分开。直接改权重矩阵调试,很容易绕进死胡同。
5.2 常见故障现象与解决方法汇总
| 现象 | 可能的根因 | 处理方向 |
|---|---|---|
| 进入弯道后横向误差持续增大,方向盘小幅高频抖动 | 参考航向角未叠加弯道切线角;R权重太小 | 修正参考航向角生成逻辑;增大R,降低控制增量灵敏度 |
| 仿真开始车辆立刻冲出去,纵向速度瞬间飙到数百km/h | 油门或制动通道极性反了;初始速度状态与Carsim不一致 | 检查IMP_THROTTLE和IMP_BRAKE极性;确认车辆初始纵向速度设置 |
| 车辆对转向指令完全没有反应 | IMP_STEER_L1单位错误,比如给了rad但Carsim按deg解释 | 检查单位转换系数,统一使用弧度 |
| 跟踪效果还行但变道完成后车速过低 | 速度规划中曲率限速过于保守;ay_lim设置太小 | 适当提高侧向加速度限值,或改为变道末端平滑恢复目标车速 |
| 仿真卡顿导致波形不连续 | 固定步长设置过大或控制器步长和主步长比率不合理 | 主步长降到0.001s,控制器周期维持在0.02~0.05s之间 |
5.3 数据后处理:用什么指标判断调参是否到位
我习惯用三张曲线图判断联合仿真的质量:第一张是XY平面内参考轨迹与实车轨迹的叠加图,直观看整体贴合程度;第二张是横向误差随时间的变化曲线,重点看峰值的出现位置和稳态收敛时间;第三张是前轮转角时间曲线,观察是否出现明显的高频抖振。
工程上可以给自己定几个可量化的验收值:横向误差峰值不超过0.15m,变道完成后稳态误差不超过0.05m,前轮转角曲线没有持续的高频小幅振荡,侧向加速度峰值不超过3m/s²。达到这些指标之后,基本可以认为这一组参数在仿真工况内是有效的。此时再把参数表记录下来,后续扫不同半径和车速工况时,只需要按固定规律微调Q和R,不用从头开始试。
绘图脚本很简单,核心逻辑是从workspace拿数据画图:
figure; plot(ref_x, ref_y, 'b--', 'LineWidth', 1.5); hold on; plot(veh.Xo, veh.Yo, 'r-', 'LineWidth', 1.2); legend('参考轨迹', '实车轨迹'); xlabel('X/m'); ylabel('Y/m'); grid on; figure; plot(t_out, ey_array, 'k-'); xlabel('t/s'); ylabel('横向误差/m'); grid on;写在最后的经验
这个项目折腾下来,我最大的感触是:弯道变道联合仿真最花时间的往往不是控制器算法本身,而是参考轨迹的正确性和接口信号的一致性。控制器权重可以靠调试经验快速收敛,但一个没叠加弯道切线角的参考路径会让人怀疑整个MPC框架,最终发现是上游数据的问题,非常浪费时间。建议所有做联合仿真的朋友,第一步先画参考轨迹的图像检查,第二步做开环激励验证接口,第三步再接控制器闭环。顺序不要反,反过来就是没日没夜地加班。
另外一个小技巧:排查问题前先在纯Simulink里用简化车辆模型闭环跑一遍控制器,确认控制逻辑本身没问题,再切到Carsim联合仿真。这样几乎能把控制器实现问题全部提前消除。最后,无论用什么算法,先统一坐标系定义和角度单位,能帮你避开一半的坑。