简介这是一份智能车MATLAB仿真代码更新版资源包面向智能车竞赛参赛者与仿真建模初学者帮助读者通过模块化代码快速搭建车辆运动学与控制算法仿真环境。相比旧版代码按车身、电机、转向、摄像头、控制等部件分类封装可读性明显提升便于按需修改和二次开发。资源共11个文件以10个MATLAB脚本.m为核心涵盖地图生成、主程序、运动控制与感知模块另含1个.mat地图文件整体仅115KB轻量易用。目前已吸引2039人学习浏览。读者拿到后可先运行creat_map生成地图文件再通过主程序test.m启动仿真观察各模块协同工作脚本中已将Motor、Chip、Body、Steer、Camera、control等部件单独拆分为函数文件方便调试单个环节或替换算法策略适用于课程设计、赛前验证和算法对比。1. 智能车仿真更新版一次从模型到控制器的全面重构做智能车巡检或竞速项目时最让人头疼的不是写在车上的控制代码而是你根本不敢在真车上试第一次转向。某同学把一辆小车翻到路沿之后我们整个团队就把验证工作全部挪进了MATLAB仿真环境。这份智能车MATLAB仿真代码更新版不是简单改了几个参数而是把车辆运动学模型、路径跟踪控制器和传感器模拟三大部分全部重写了一轮。它的价值在于你可以在没有实体车的情况下完成从建模、仿真到控制参数调优的完整闭环。适合正在做智能车课程设计、准备竞赛算法验证或者想在硬件上费钱之前先看算法效果的开发者。2. 项目结构与技术选型MATLAB脚本和Simulink模型是怎么分工的拿到代码包第一件事不是双击某个文件而是先看它整体的模块划分。这个更新版按模型-控制-环境-工具四层组织和大多数临时拼凑的仿真代码不一样它把车辆模型、控制器、传感器三部分解耦每一块都能单独替换。2.1 目录结构每个文件是干什么的解压后典型目录如下smart_car_sim/ ├── main.m % 主入口负责初始化参数并运行仿真 ├── model/ │ ├── vehicle_kinematics.m % 车辆运动学模型Ackermann转向 │ ├── vehicle_dynamics.m % 车辆动力学模型可选含轮胎滑移 │ └── sensor_model.m % 超声波/视觉传感器模拟 ├── control/ │ ├── pid_controller.m % PID路径跟踪控制器 │ ├── stanley_controller.m % Stanley跟踪控制器 │ └── path_planner.m % 路径生成直线圆弧多项式 ├── environment/ │ ├── obstacle_map.m % 障碍物地图绘图和碰撞检测 │ └── trajectory_plot.m % 轨迹与误差可视化 ├── config/ │ └── sim_params.m % 仿真参数配置 └── output/ └── (保存的仿真结果)这里main.m是总入口它先调用sim_params拿到参数然后初始化车辆状态、生成参考路径再进入主循环。每个仿真步内先让控制器计算转向角再喂给车辆模型更新位姿最后通过传感器模型探测环境。这种单向依赖关系让二次开发变得很直接你想换控制器只要保证新控制器输入输出格式一样其他文件不用动。文件之间的依赖关系可以概括为一张静态调用表文件被谁调用调用谁主要职责main.m用户直接运行所有其他文件装配整个仿真流程sim_params.mmain.m无集中管理所有仿真参数vehicle_kinematics.mmain.m无实现车辆运动学递推pid_controller.mmain.m无计算PID转向角path_planner.mmain.m无生成参考路径点sensor_model.mmain.m无计算障碍物距离和碰撞标志trajectory_plot.mmain.m无绘制轨迹、误差、控制量每个文件都保持独立参数靠struct传递不会出现全局变量污染。这是老版本代码没有的改进——旧版里直接把参数写死在各个函数里改一个轴距要同步改五个文件改到后面自己都忘了哪处没更新。2.2 为什么选MATLAB而不直接用C或Python这个问题几乎每次分享都会被问。对于智能车仿真来说MATLAB有两个难以替代的优势一是矩阵运算和绘图原生集成你可以零额外代码画出车辆轨迹、误差曲线和动态过程二是它可以无缝切换到Simulink把控制算法用状态流和框图重新表达方便后续做代码生成部署到嵌入式平台。如果你只是要做算法验证、参数扫参或者交课程报告MATLAB脚本方式比搭建复杂Simulink模型更直接。这个更新版就保留了纯脚本流程但同时在control目录里预留了*.slx的Simulink版本入口常见做法是给一个run_simulink.m来加载模型并配置仿真器。如果你以后要产出C代码可以拿Simulink模型配合嵌入式编码器比纯手写C移植要快很多。对比Python生态MATLAB的数值积分器更成熟而且ode45、ode4这些求解器都经过严格验证不像自己写的欧拉法在高动态工况下容易发散。更重要的是MATLAB里调试矩阵维度非常直观工作区直接显示每个变量的尺寸对初学者友好。缺点是正版授权比较贵不过对于高校课题和竞赛项目学校机房大多都装了这倒不是大问题。2.3 核心仿真流程一个仿真步里发生了什么整个仿真主循环可以浓缩为下面这段伪代码思路% main.m 核心循环伪代码 for k 1:N_steps % 1. 获取当前参考路径点 ref get_reference_point(path, x, y, lookahead); % 2. 控制器计算转向角 delta controller.calc(ref, state); % 3. 车辆模型更新位姿 state vehicle_model.step(state, delta, dt); % 4. 检测碰撞和越界 [collision, dist] sensor_model.check(state, obstacles); % 5. 记录数据用于绘图 history(:, k1) [state; delta; dist]; end每一步的逻辑顺序是固定的先看参考点再算控制量再推进车辆模型最后做环境检测。这里有几个关键参数lookahead是前视距离决定控制器看多远dt是仿真步长决定数值积分精度N_steps是总步数。我一般会把/config/sim_params.m里的参数单独提出来每次跑不同工况就改这个文件不让数字散落在主程序里。这个更新版也遵循了这个习惯比如初始位置、目标速度、最大转向角、传感器量程都在同一个文件中配置。仿真的时间轴是这样推进的物理时间从0开始每个循环推进dt秒车辆状态按运动学方程更新控制器的计算频率也是每dt一次。如果你要模拟控制器更新频率低于传感器频率可以在循环里加一个计数器例如每5个仿真步才执行一次控制器让车辆模型以更细的步长积分这样能更贴近真实控制器的离散周期。3. 核心模块拆解车辆模型、控制器和传感器到底怎么写的这一章是代码包的重点。我拆开每个核心文件挑最关键的几段代码讲清楚实现思路方便你改造成自己的版本。3.1 车辆运动学模型Ackermann转向的离散递推智能车在低速场景下运动学模型比动力学模型更实用。Ackermann转向模型把车辆简化成一个带转向角的前轮和一个固定后轴的自行车模型状态量是位置[x, y]和偏航角theta。这个更新版的model/vehicle_kinematics.m核心代码如下function state vehicle_kinematics_step(state, delta, v, dt, L) % state: [x; y; theta] % delta: 前轮转向角 (rad) % v: 纵向速度 (m/s) % dt: 仿真步长 (s) % L: 轴距 (m) x state(1); y state(2); theta state(3); % 离散递推采用一阶欧拉法 x_new x v * cos(theta) * dt; y_new y v * sin(theta) * dt; theta_new theta (v / L) * tan(delta) * dt; state [x_new; y_new; theta_new]; end这段代码的物理含义很清晰车辆前进距离是v*dt把它投影到x和y方向偏航角的变化率等于v/L * tan(delta)这正是自行车模型的核心关系。注意这里delta是前轮转角不是方向盘转角两者之间通常还有一个转向传动比你可以在sim_params里加一个参数来模拟。如果你用的是差速转向的小车比如两轮自平衡或履带式公式要改成以左右轮速度差驱动常见做法是% 差速模型v_left, v_right 分别为左右轮线速度t_width 为轮距 omega (v_right - v_left) / t_width; x_new x (v_right v_left) / 2 * cos(theta) * dt; y_new y (v_right v_left) / 2 * sin(theta) * dt; theta_new theta omega * dt;差速模型适合麦克纳姆轮或履带车转弯半径可以为零但前轮转向的Ackermann模型更适合标准四轮车。这个更新版默认Ackermann但在同目录下也留了一个vehicle_dynamics.m里面额外加入了侧偏刚度适合高速工况。动态模型比运动学模型多两个状态量——横摆角速度和侧向速度公式里多了轮胎侧偏刚度系数如果想模拟甩尾现象那就得用这个版本。3.2 路径跟踪控制器PID和Stanley两种实现对比控制部分是智能车仿真的灵魂。这个代码包同时实现了经典PID和更适合几何跟踪的Stanley控制器。control/pid_controller.m里的核心计算如下function delta pid_controller(ctrl_state, ref_pos, state) % ctrl_state: [integral_error; prev_error] % ref_pos: 参考点 [x_ref; y_ref] % state: 当前车辆状态 [x; y; theta] % 横向误差参考点到车辆位置的垂直距离 err -sin(state(3)) * (state(1) - ref_pos(1)) ... cos(state(3)) * (state(2) - ref_pos(2)); kp ctrl_state.kp; ki ctrl_state.ki; kd ctrl_state.kd; % 积分和微分 ctrl_state.integral_error ctrl_state.integral_error err * dt; deriv_error (err - ctrl_state.prev_error) / dt; ctrl_state.prev_error err; % 输出转角并限幅 delta kp * err ki * ctrl_state.integral_error kd * deriv_error; delta max(-max_steer, min(max_steer, delta)); end这里的核心是把横向误差投影到车身坐标系下用sin和cos做坐标变换。很多新手直接拿ref_pos.y - state.y当误差那样在车转弯时会得到错误的方向导致控制量反号。参数kp、ki、kd分别对应比例、积分、微分三个系数实际调参时先只给kp逐步增大直到出现等幅振荡再取一半作为最终值然后加kd抑制超调。PID调参有一个固定流程把ki和kd先设为零增大kp观察轨迹直到车辆在直线路径上出现轻微来回摆振然后记录这时的kp值取它的50%作为工作点接着加kd让摆动快速衰减最后加一点ki消除稳态误差。注意ki不宜过大否则在转弯处会出现明显的过冲和积分饱和。Stanley控制器是更现代的选择它基于参考路径上的最近点计算前轮转角function delta stanley_controller(ref_pos, ref_yaw, state, v, k_stanley) % ref_pos: 最近参考点 [x_ref; y_ref] % ref_yaw: 参考点的航向角 % state: 当前车辆状态 [x; y; theta] % v: 当前车速 % k_stanley: Stanley增益 % 车辆前轴中心到参考路径的横向误差在参考路径坐标系下 cross_err -sin(ref_yaw) * (state(1) - ref_pos(1)) ... cos(ref_yaw) * (state(2) - ref_pos(2)); % 航向角误差 theta_err state(3) - ref_yaw; % 附加一个速度相关的补偿项v很小时直接输出纯航向修正 delta theta_err atan2(k_stanley * cross_err, max(v, 0.1)); delta max(-max_steer, min(max_steer, delta)); endStanley算法有个特点当横向误差为零时转角等于航向角误差车辆会迅速贴回路径而误差大时atan2会产生一个大角度修正。它会让人明显感觉比PID更“聪明”因为它是几何方法天然考虑路径曲率。你可以改k_stanley来调节追踪敏感度一般从1.0开始试值越大收敛越猛但过大容易振荡。对比两种控制器的适用场景PID适合参考路径比较平滑、曲率变化不剧烈的工况调好参数后很稳定Stanley在急弯和曲率突变路径上表现更好因为它直接计算最近点误差。但如果参考路径本身噪声很大Stanley会因为最近点跳动而输出抖舵这时候PID反而更平滑。更新版里在main.m中让你通过params.controller_type切换方便做对比实验。3.3 传感器与环境建模碰撞检测和虚拟量程传感器仿真既要做物理装车前的验证也要避免让控制算法读到“理想化”数据。model/sensor_model.m中实现了一个简化的测距传感器和碰撞检测function [distance, collision] sensor_check(state, obstacles, sensor_range) % state: [x; y; theta] % obstacles: Nx2矩阵每行一个障碍物 [ox; oy] % sensor_range: 传感器最大量程 collision false; min_dist sensor_range; for i 1:size(obstacles, 1) dx obstacles(i, 1) - state(1); dy obstacles(i, 2) - state(2); dist sqrt(dx^2 dy^2); if dist min_dist min_dist dist; end % 若障碍物距车辆质心小于阈值视为碰撞 if dist 0.3 % 阈值与车身尺寸相关 collision true; end end % 如果传感器有角度限制这里还可以加入方位角判断 distance min_dist; end这个模型把所有障碍物当作点来处理对大多数路径规划验证来说够了。如果你想模拟超声波束的扇形覆盖常见做法是生成多个方向上的射线逐一做线段求交这块更新版没有展开但接口已经预留在sensor_model.m里——你只需要补一个raycast()函数即可。碰撞阈值0.3米对应一个车身宽度约0.6米的小车如果你的车尺寸不同记住去改这个值。射线检测的参考实现大概是% 把距离传感器模拟为多个方向的射线 angles theta linspace(-pi/6, pi/6, 5); % 5条射线覆盖60度扇形 min_dist sensor_range; for i 1:length(angles) % 选代射线方向上的障碍物交点计算距离 for j 1:size(obstacles, 1) % 用向量叉积求交这里省略几何细节 dist ray_intersect(state(1), state(2), angles(i), obstacles(j,:)); if dist 0 dist min_dist min_dist dist; end end end这样的传感器模型会引入近距离检测盲区更真实。但要注意射线数量越多仿真越慢作为平衡5条射线在普通PC上每秒可以跑几百个仿真步完全够用。3.4 主程序串联从参数读取到结果导出最后把主循环看一遍。main.m里除了循环之外还有参数初始化和结果保存两部分。核心代码如下% 初始化 params sim_params(); state [params.x0; params.y0; params.theta0]; ref_path path_planner.generate(params.path_type, params.path_length); history zeros(5, params.N_steps 1); history(:, 1) [state; 0; 0]; % 选择控制器 if params.controller_type 0 controller pid_controller; else controller stanley_controller; end % 主循环 for k 1:params.N_steps ref get_reference_point(ref_path, state, params.lookahead); delta controller(ref, state, params); state vehicle_kinematics_step(state, delta, params.v, params.dt, params.L); [dist, collision] sensor_check(state, params.obstacles, params.sensor_range); history(:, k1) [state; delta; dist]; if collision fprintf(仿真停止第%d步发生碰撞\n, k); break; end end % 绘制 trajectory_plot(ref_path, history, params.obstacles);这里用函数句柄pid_controller/stanley_controller切换控制器不用改循环体。history矩阵的行分别存x, y, theta, delta, distance后面绘图和误差分析都用它。get_reference_point会从参考路径里找一个离车辆最近且大于前视距离的点这样控制器不会看到身后的路径点。我强烈建议你自己在循环里加一句history(:, k1) ...之前先打印delta的数值范围看看驱动器会不会出现饱和。这个更新版里已经加了限幅但如果你后续把max_steer设置太大限幅就不会生效车会原地打转。主循环里还有一个细节参考路径生成用的是path_planner.generate()输入的是path_type和path_length。path_type支持straight直线、curve二次曲线和polygon闭合多边形。polygon模式适合做赛道仿真参考路径首尾相连可以测试车辆跑圈的稳态误差。4. 运行与调试从配置参数到可视化输出的完整流程把代码克隆到本地后跑通的第一个关键点是环境是否干净。这个项目不依赖额外工具箱但至少需要2018年以后的MATLAB版本关于版本问题常见做法是检查sin、atan2等函数是否被引入太老的版本可能存在deg2rad缺失。4.1 环境准备与参数初始化双击打开config/sim_params.m你会看到类似这样的内容params.v 1.5; % 巡航速度 (m/s) params.dt 0.02; % 仿真步长 (s) params.N_steps 3000; % 总步数 params.L 0.4; % 轴距 (m) params.max_steer pi / 6; % 最大转向角 (rad) params.lookahead 0.8; % 前视距离 (m) params.x0 0; params.y0 0; params.theta0 0; params.controller_type 1; % 0为PID1为Stanley params.kp 1.8; params.ki 0.05; params.kd 0.2; params.k_stanley 1.2; params.obstacles [1.2, 0.8; 2.5, 1.5; 3.0, -0.5]; params.path_type curve; % straight, curve, polygon这些参数直接决定了仿真的行为。比如dt0.02也就是50Hz如果使用欧拉法离散车速1.5m/s时每一帧车前进0.03米对于0.6米长的车来说是合理的。N_steps3000对应60秒仿真跑完一次只要几十秒因为不涉及图形实时绘制而是最后统一画图。跑之前建议先运行一行检查代码run(config/sim_params.m); disp(params);确认所有字段都定义好了尤其是params.obstacles是否为空矩阵。如果为空必须把sensor_check里空障碍物的分支处理写对否则会报索引越界。我就在这个坑上花过半小时。还需要确认当前工作目录是否正确。如果你直接双击main.m运行MATLAB会以当前脚本所在目录为工作目录但如果你先把文件夹加入路径再用main命令调用就可能出现找不到sim_params.m的问题。稳妥做法是在项目根目录右键设置为当前文件夹或者用cd切过去。4.2 运行仿真并查看结果在MATLAB命令窗口输入main程序会进行计算结束后弹出figure窗口。默认画法会显示三张子图车辆轨迹跟踪对比、转向角随时间变化、侧向误差变化曲线。预期结果因参数而不同。以默认参数跑你会看到车辆从原点出发沿着参考路径稍微偏移后迅速收敛误差最终稳定在1厘米以内。如果出现轨迹振荡说明kp太大或者前视距离太小如果出现转向角持续饱和说明速度太快或路径曲率太大需要降低params.v。结果保存功能很实用。output/目录下会多出一个history.mat文件你可以事后加载它做误差分析load(output/history.mat); err sqrt((history(1,:)-ref_path.x).^2 (history(2,:)-ref_path.y).^2); plot(err);这段代码计算每一帧车辆到整个参考路径的欧氏距离虽然不是最短距离但足够观察收敛趋势。真正的横向误差应该用向量投影计算不过那通常留给正式调参阶段再做。运行结束后建议查看history矩阵的第四行转向角和第五行传感器最小距离。第四行如果出现频繁的max_steer极限值说明控制器在做剧烈修正可能是前视距离太小或速度太快第五行如果一直等于sensor_range说明障碍物离得太远碰撞检测等于没有生效你可以主动调整障碍物坐标让它们落在路径附近。4.3 Simulink版本的切换可选如果你需要看控制算法在Simulink里的状态流表现代码包里的run_simulink.m会帮你启动smart_car_model.slx。打开模型后汽车运动学用积分器模块控制器用一个MATLAB Function块写相同公式其他部分用信号线连接。这里要注意Simulink与纯脚本的仿真步长不一致问题——Simulink默认变步长求解器可能在车辆模型上产生非预期误差建议把Solver Type改为固定步长ode4步长设成0.01秒。我一般用纯脚本把参数调通然后才打开Simulink做时序验证。两者之间容易出现的差异是数值积分精度所以不要期待完全相同的轨迹只要趋势一致即可。Simulink模型里的控制器模块是一个MATLAB Function内部代码和纯脚本版几乎一致但变量需要通过port连接。如果你在脚本里把params整个结构体传给控制器在Simulink里就要改成一维信号否则总线对象配置会非常麻烦。常见做法是在Simulink版中把kp、ki、kd三个常数抽出来放到基础工作区用常量模块直接引用。5. 避坑记录智能车仿真中常见的五个翻车现场代码更新版虽然把大部分结构都理顺了但实际跑起来仍然会有各种超出预期的玄学问题。这里把最常见的五个踩坑点整理成“现象-原因-解决”你在复现时碰到可以直接对照。5.1 车辆直接跑成直线完全不跟随曲线路径现象设置path_typecurve但输出轨迹是一条直线路径跟踪完全失效。原因参考路径生成函数返回的是一条二次多项式但主程序里没有正确调用path_planner.generate导致ref_path其实是一串零向量。或者前视距离太大get_reference_point一直取到路径终点控制器认为自己已经到达。解决先检查ref_path的前几行数值确认坐标在变然后把lookahead缩小到0.5以下并打印当前参考点与车辆位置的距离看看是不是匹配。常见做法是临时在循环里画一个点可视化参考点是否合理。还有一种隐蔽情况是路径生成函数里用了polyval但当多项式系数向量长度不对时polyval会返回全零。检查path_planner.generate输入参数长度确保path_length是正数。5.2 仿真报错“Index exceeds matrix dimensions”现象主循环跑到第20步就报错说矩阵索引越界。原因history矩阵只有N_steps1列但在碰撞后提前break退出后续绘制函数试图访问history的最后几列时列数不足。解决绘制前先记录实际运行的步数actual_steps然后只绘制前actual_steps1列。或者干脆为history预留最大列数不用的列留空绘制时截断。这个更新版代码里我建议自行加一个actual_steps变量养成这种习惯。具体修改方案很简单在循环开始前定义actual_steps N_steps在碰撞分支里改为actual_steps k - 1; break;然后在绘制函数里传入actual_steps只绘制history(:, 1:actual_steps1)。这样即使提前停止绘图也不会越界。5.3 转向角一直为零控制器似乎没有生效现象小车沿着直线走delta恒为0但路径明明是曲线。原因控制器输入参数顺序不匹配。比如pid_controller内部用的是ctrl_state.kp而你在sim_params里写的是params.kp并且传入时没有把params拆解成ctrl_state。解决在调用控制器之前统一做一次转换ctrl_state struct(kp, params.kp, ki, params.ki, ... kd, params.kd, integral_error, 0, ... prev_error, 0);然后让所有分支都传这个ctrl_state。不要在控制器内部猜测字段名直接以struct形式传入避免隐性错误。还有一种情况是控制器返回结果被覆盖。比如在main.m里先调用controller随后又执行了一个赋值语句把delta重新赋值为0。检查主循环里是否有其他代码不小心覆盖了delta。5.4 画出来的车辆轨迹像锯齿非常不光滑现象轨迹线明显有锯齿状抖动每几步跳一下。原因仿真步长dt与控制器更新频率不匹配。比如控制器每隔5个仿真步才更新一次转向角而车辆模型每一步都在积分导致转向角阶跃变化。解决要么让控制器每个步长都更新要么在控制器内部做零阶保持保留上一次输出。更稳定的是让车辆模型采用更小的积分步长而控制器以较慢频率运行这是多速率仿真的经典做法用两个for嵌套即可实现。参考实现for k 1 : N_steps if mod(k, ctrl_period) 0 || k 1 % 每隔 ctrl_period 步更新一次控制量 delta controller(ref, state, params); end % 车辆模型每步都积分delta 保持不变 state vehicle_kinematics_step(state, delta, params.v, params.dt, params.L); end这样能模拟真实控制器的零阶保持效果轨迹也会更平滑。5.5 想要保存仿真数据但output目录为空现象运行结束后output文件夹里没有history.mat。原因多数情况下是代码里save命令被放在碰撞跳出的break之前或者代码路径用了相对路径但当前工作目录不在项目根目录。解决在main.m最后强制保存并且用绝对路径拼接save(fullfile(fileparts(mfilename(fullpath)), output, history.mat), history);这样可以确保无论从哪个目录启动都会保存到项目下的output文件夹。从那以后我每次跑仿真都强制把save放在main函数的最末尾不管是不是提前退出。6. 二次开发思路把这份仿真代码改造成你自己的实验平台这份更新版代码的价值在于它是一副“骨架”你可以按自己的需求换血。下面的改造思路都基于实际场景并且改动范围都控制在一个文件内不会牵一发动全身。6.1 更换车辆参数从竞赛小车到工程样车最通用的改造是替换sim_params.m中的车辆物理参数。比如你要仿真一个轴距为1.2米的无人快递车只需要修改params.L1.2同时调整最大转向角。但要注意轴距变大后相同前视距离下车辆响应会变慢建议同步把lookahead调整为轴距的1.5~2倍。如果你要模拟的是舵机响应延迟常见做法是在控制器输出delta后加一个一阶惯性环节delta_smoothed delta_smoothed (delta - delta_smoothed) * (dt / tau);其中tau是舵机时间常数单位秒。这行代码可以插在history记录之前能让你的仿真更接近真实。6.2 换成纯跟踪算法并对比效果我想分享一个非常有趣的改造方向把控制器替换成纯跟踪Pure Pursuit算法然后和现有PID、Stanley一起跑对比实验。纯跟踪的实现核心是计算车辆到前视点之间的圆弧曲率function delta pure_pursuit(ref_pos, state, L, lookahead) % 计算前视点与车辆之间的夹角 alpha atan2(ref_pos(2) - state(2), ref_pos(1) - state(1)) - state(3); % 曲率半径 kappa 2 * sin(alpha) / lookahead; % 转角和曲率之间的关系L为轴距 delta atan(L * kappa); end你只需要在main.m里增加一个params.controller_type 2并把这个函数放在control目录下。然后跑三种控制器的误差曲线你会发现纯跟踪在低速大曲率路径上表现得比PID更稳定但前视距离需要根据速度自适应调整。这是一个做课程设计时很容易拿高分的小实验把前视距离写成速度的线性函数让车辆动态调整视野。另外如果你想把仿真结果迁移到实车上我建议在output里增加一个control_log.csv导出模块把每一步的delta和state写进去。这样你拿到硬件上可以离线回放先用这份MATLAB仿真代码做前期的算法验证再用回放数据驱动开发板调试整个过程会顺畅很多。我的习惯是每次改完代码强制跑一遍main 打开三张图 检查save是否成功从那次碰过空目录的坑之后这套流程就再也没让我返工过。希望这些拆解能帮你在智能车仿真的路上少走几步弯路。本文还有配套的精品资源点击获取