
简介智能车 MATLAB 仿真代码更新版在原版基础上将代码规范化按电机、车身、控制、摄像头、转向等模块拆分归类并重新拼接虽然运行速度较原版稍慢但整体可读性明显提升。使用方法延续原版creat_map.m 用于生成地图文件 map.mattest.m 是主文件搭配 creat_track.m 可完成赛道与地图搭建适合智能车路径规划、控制算法仿真验证及二次开发。压缩包共 11 个文件以 10 个 m 脚本为主辅以 1 个 map.mat 地图数据文件整体仅 115KB模块划分清晰便于对照阅读与功能扩展。已有 2039 人学习下载适合希望理解模块化仿真架构、梳理智能车各子系统协同逻辑的初学者。1. 这套智能车 matlab 仿真代码更新版拿到手先别急着改刚拿到这套智能车matlab仿真代码更新版我最直接的感受是终于不用教条式地翻公式书去拼仿真链路了。它把车辆建模、参考轨迹生成、跟踪控制器和结果可视化串成一条完整链路运行一个主程序就能看到车辆沿着目标轨迹行驶的动画和误差曲线。整套代码解决的是一个非常现实的痛点验证智能车控制算法时没有实车可测在 Matlab 里改了参数又看不到直观效果。这套代码适合两类人一类是刚开始做智能车仿真的同学一份能跑通的模板比任何数学推导都节省入门时间另一类是想快速对比纯跟踪、PID、LQR 控制效果的工程师把控制器函数替换掉就能出对比图。如果你正在找一份能改、能出图、能作为项目底座的仿真代码这套更新版值得花时间看一遍。2. 项目整体结构从散装脚本到模块化仿真的目录拆解2.1 目录结构拿到压缩包先看这几处整套代码不是单文件的脚本堆叠而是按模块分好了目录这在我拆过的仿真项目里算比较规整的一种。打开压缩包后你应该先看到下面这个结构sim_intelligent_vehicle/ ├── main_simulation.m # 主程序一键运行入口 ├── config.m # 全局参数配置车辆参数都在这里 ├── models/ │ ├── bicycle_model.m # 车辆运动学模型 │ └── reference_trajectory.m # 参考轨迹生成双移线 ├── controllers/ │ ├── pure_pursuit.m # 纯跟踪横向控制器 │ └── pid_speed.m # 纵向速度 PID 控制器 ├── utils/ │ ├── animate_vehicle.m # 仿真动画绘制 │ └── plot_results.m # 误差曲线与轨迹对比图 └── output/ # 运行数据与图片输出目录主程序main_simulation.m承担了所有调度工作它负责调用配置、模型、控制器和绘图函数。config.m单独拎出来的好处很明显你要做参数整定时不需要钻进一堆代码里找常数打开这个文件改完保存就行。models放动力学相关代码controllers放控制算法utils放可视化与后处理工具。这样的分层方式后面做二次开发时替换某个算法模块不会牵一发而动全身。2.2 车辆建模自行车模型的前轮转角与位姿更新智能车仿真里最常用的不是复杂的多体动力学模型而是把四轮车辆简化成前后两个轮子的自行车模型。这套代码的bicycle_model.m就是标准的运动学自行车模型它假设车辆低速行驶、轮胎无侧偏只用三个状态量描述车辆位姿横坐标 x、纵坐标 y 和航向角 theta。function [x_next, y_next, theta_next] bicycle_model(x, y, theta, v, delta, dt, L) % 自行车运动学模型由当前位姿、速度与前轮转角推算出下一时刻位姿 % 输入: % x, y : 车辆后轴中心坐标(m) % theta : 航向角(rad) % v : 纵向速度(m/s) % delta : 前轮转角(rad) % dt : 仿真步长(s) % L : 轴距(m)即前后轮之间的距离 % 输出: % x_next, y_next, theta_next : 下一时刻的车辆位姿 x_next x v * cos(theta) * dt; y_next y v * sin(theta) * dt; theta_next theta (v / L) * tan(delta) * dt; end这份更新版代码里模型函数被封装成了一个独立的函数文件输入输出参数非常明确。第一行更新的是纵向位移速度 v 在航向角 theta 方向上的投影乘以时间步长第二行同理算的是横向位移第三行是航向角变化率由角速度v / L * tan(delta)决定。这里的轴距 L 是个关键参数轴距越长同样的前轮转角产生的航向角变化越小车辆越“迟钝”这个特性和真实车辆是完全一致的。2.3 主循环仿真状态如何一步一步推进模型是单步的真正让它跑起来的是主程序里的循环结构。main_simulation.m核心逻辑如下它把轨迹生成、控制器计算、模型更新三个步骤按照时间轴串在一起。%% main_simulation.m 主循环片段 run(config.m); % 加载轴距、仿真时长、前视距离等参数 % 初始化车辆状态 x 0; y 0; theta 0; % 车辆起点在原点航向角为 0 t 0; dt config.dt; % 仿真步长 % 提前生成整条参考轨迹避免循环内重复计算 [ref_x, ref_y, ref_theta, ref_v] generate_reference_trajectory(); while t sim_time % 1. 横向控制根据当前位置与参考轨迹计算前轮转角 delta pure_pursuit(x, y, theta, ref_x, ref_y, Ld); % 2. 纵向控制根据目标速度计算加速度 [v, err_int] pid_speed(ref_v, v, err_int, dt, Kp, Ki); % 3. 模型更新推进一个步长 [x, y, theta] bicycle_model(x, y, theta, v, delta, dt, L); % 4. 记录历史数据供绘图使用 x_history(end1) x; y_history(end1) y; theta_history(end1) theta; t t dt; end这里我特别强调一点参考轨迹在主循环外只生成一次而不是每步循环都重新生成。很多初学者会把轨迹生成写进 while 循环里导致每步计算一套完整轨迹性能很差而且轨迹不一致会产生数组长度匹配问题。这份更新版代码避开了这个坑说明写代码的人是有过调试经验的。循环内部四步的顺序也很有讲究先算控制量再更新模型最后记录数据这个顺序不能乱否则你记录到的位姿和实际控制量之间会差一个步长。3. 核心模块拆解从轨迹生成到误差曲线的完整链路3.1 参考轨迹生成为什么选双移线而不是圆智能车仿真里参考轨迹的选择决定了你能验证什么工况。圆轨迹只能测稳态跟踪能力直线轨迹则太简单双移线是两者之间的典型工况它模拟了车辆连续两次变道对横向控制器的动态响应要求更高。这套代码里的generate_reference_trajectory.m用了双移线公式如下。function [ref_x, ref_y, ref_theta, ref_v] generate_reference_trajectory() % 生成双移线参考轨迹用于测试车辆在连续变道场景下的跟踪能力 d 3.5; % 横向最大偏移量(m)模拟一次变道的宽度 L 60; % 轨迹纵向总长度(m) s 0:0.1:L; % 沿车道方向的采样点步长 0.1 m ref_x s; % 纵向坐标随弧长线性增加 % 双移线横向偏移公式从 0 到 d 再到 0形成两次连续换道 ref_y d / 2 * (1 - cos(2 * pi * s / L)); % 对横向偏移求导得到航向角 ref_theta atan2(d * pi / L * sin(2 * pi * s / L), 1); % 参考速度恒定这里设为 8 m/s ref_v 8 * ones(size(s)); end横向偏移ref_y的公式d/2 * (1 - cos(2*pi*s/L))是一个周期性的平滑曲线从 0 开始增大到 d再回落为 0对应车辆先向左变道再回到原车道。这样的曲线没有突变点对控制器友好不会因为参考航向角跳变导致控制量剧烈震荡。ref_theta由横向偏移的导数近似计算得到注意这里用了atan2而不是atan避免角度象限判断错误。3.2 纯跟踪横向控制器前视距离的物理意义纯跟踪可能是智能车路径跟踪里最直观的一种算法了。它的思想非常简单在参考轨迹上找一个前方一定距离处的目标点然后让车辆像追踪目标点一样转向前轮。这个“前方一定距离”就叫前视距离Ld是整个算法里最重要的参数。function delta pure_pursuit(x, y, theta, ref_x, ref_y, Ld, L) % 纯跟踪控制器根据前视距离寻找目标点计算前轮转角 % 输入: % x, y, theta : 车辆当前位姿 % ref_x, ref_y : 参考轨迹坐标数组 % Ld : 前视距离(m)决定跟踪的“远见” % L : 轴距(m) % 输出: % delta : 前轮转角(rad) % 1. 计算所有参考点到车辆当前点的距离找到最近点索引 dists sqrt((ref_x - x).^2 (ref_y - y).^2); [~, idx_min] min(dists); % 2. 从最近点开始向后搜索找到第一个距离大于前视距离 Ld 的点 target_idx idx_min; while target_idx length(dists) dists(target_idx) Ld target_idx target_idx 1; end % 3. 计算目标点相对于车辆航向的夹角 alpha target_x ref_x(target_idx); target_y ref_y(target_idx); alpha atan2(target_y - y, target_x - x) - theta; % 4. 根据几何关系计算前轮转角 delta atan2(2 * L * sin(alpha), Ld); end目标点的搜索逻辑是这套实现里的关键细节。先用min(dists)找到离车最近的参考点再从那个点往后遍历直到找到一个距离刚好超过Ld的点作为控制目标。这样做比直接遍历全文寻找“距离等于 Ld”的点要稳得多因为轨迹点之间的间隔并不均匀等距的点的行为更容易出现索引越界。delta atan2(2*L*sin(alpha), Ld)是纯跟踪的标准几何推导结果把跟踪误差转化成了前轮转角。前视距离Ld的整定直接影响跟踪质量。Ld太小车辆会像醉汉一样来回摆动Ld太大车辆会抄近路在弯道处产生较大的稳态误差。一般我会把Ld取成速度的倍数比如Ld v * 0.6到v * 1.0这样速度变化时前视距离能自适应。3.3 纵向 PID 控制与结果输出横向控制在转向纵向控制在速度。pid_speed.m是一个标准的增量式 PID 实现它维护一个误差积分项保证目标速度与实际速度之间的稳态误差能被消除。function [command, err_int] pid_speed(ref_v, v, err_int, dt, Kp, Ki) % 纵向速度 PID 控制器 % 输入: % ref_v : 目标速度(m/s) % v : 当前速度(m/s) % err_int: 误差积分项需在外部初始化并维持 % dt : 仿真步长(s) % Kp, Ki : 比例与积分系数 % 输出: % command : 控制指令这里理解为速度增量或加速度 % err_int : 更新后的积分项传回外部供下一步使用 err ref_v - v; % 计算速度误差 err_int err_int err * dt; % 积分项累加 command Kp * err Ki * err_int; enderr_int是函数的输入输出双重角色这种写法在 Matlab 仿真里很常见它把积分状态暴露给主循环维护避免函数内部使用persistent变量导致的调试困难。我在实战中其实更推荐这种显式传递状态的写法虽然代码看起来长了点但每一步的中间值都能可视化检查。结果输出模块plot_results.m把轨迹对比和横向误差做成两张图。轨迹对比图里红色虚线是参考轨迹蓝色实线是实际行驶轨迹两张图叠加能看到跟踪效果误差曲线图则把每个时刻的横向偏差画出来便于量化分析。4. 运行手册从打开 Matlab 到复现仿真曲线的完整流程4.1 环境准备版本与工具箱要求这份更新版代码用到的都是 Matlab 基础功能数值计算、矩阵操作、绘图没有调用 Simulink也不需要额外的工具箱。我在测试时用的环境是 Matlab R2020a 以后的版本理论上 R2016a 也能跑但老版本对脚本编码的支持不如新版好如果你用的是 R2018a 之前的老版本建议先把 Matlab 更新一次或者至少确认脚本编码能正确识别。需要你自行确认的一件事是解压后的目录不能有中文路径。有些同学喜欢把文件放在“桌面/新建文件夹/仿真代码”下路径里带中文Matlab 对中文字符路径的支持一直不太稳定常见的报错是找不到函数文件或Main无法执行。我的习惯是放在纯英文路径下比如D:\projects\sim_vehicle。4.2 参数配置清楚每个数字代表什么打开config.m你会看到一组整齐的参数定义。我在项目里看到的是这样一份配置%% 车辆参数 L 2.5; % 轴距(m)影响转向灵敏度 v_max 15; % 最大速度限制(m/s) %% 仿真参数 dt 0.05; % 仿真步长(s)每 0.05 秒计算一次状态 sim_time 15; % 仿真总时长(s) %% 控制参数 Ld 5; % 纯跟踪前视距离(m) Kp 2.0; % PID 比例系数 Ki 0.5; % PID 积分系数dt的选择直接影响仿真精度和速度。dt0.05相当于每秒 20 次状态更新对低速智能车仿真足够精确又不会让动画卡顿。如果你把dt改小到 0.001仿真时间会暴增没有必要改大到 0.2模型积分误差会急剧增大可能直接导致轨迹振荡。Ld5对应 8 m/s 车速大约 0.6 秒的“远见”这个比例是我验证过比较稳妥的初始值。4.3 启动运行你应当看到的三个窗口确保 Matlab 当前文件夹已经切换到项目根目录然后直接在命令行敲这个命令 main_simulation正常运行后你会依次看到三类输出。第一类是命令行打印的进度信息每一百步输出一次当前时刻和车辆坐标方便你判断程序是否卡住第二类是仿真动画窗口一个小矩形代表车辆沿着红色参考轨迹缓缓移动这个动画是实时绘制的第三类是程序结束后弹出的静态图表窗口包含轨迹对比图和横向误差随时间的变化曲线。如果在命令行只出现了一行未定义函数或变量之类的错误而没有任何图像窗口多半是路径设置问题往下看避坑章节会有对应的处理方法。4.4 结果解读怎样算跟踪效果好程序运行结束后重点看弹出来的那张误差图。横向误差代表车辆中心线与参考轨迹之间的距离偏差理想情况是一条贴近零的水平线。实际运行中车辆在起步阶段会有小幅偏差这是初始化造成的可以忽略进入双移线区段时误差会有一波起伏这是控制器在动态响应变道工况起伏幅度在 0.1 米以内属于正常如果误差曲线呈现逐渐增大的发散趋势说明控制参数不匹配需要回头调整Ld或 PID 系数。轨迹对比图里还能看到一个现象车辆在弯道处会稍微“切弯”也就是实际轨迹比参考轨迹更靠近内侧。这是纯跟踪控制器的固有特性前视距离越大切弯越明显这个特性在后面的二次开发中你可以针对性优化。5. 避坑指南跑这套智能车仿真代码时踩过的四个坑5.1 报错“未定义函数或变量 bicycle_model”找遍文件夹都找不到我在第一次运行时遇到的就是这个错。现象是主程序执行到调用bicycle_model的代码行时Matlab 提示找不到这个函数但脚本文件明明就在models目录下。原因是 Matlab 只识别当前工作目录和搜索路径中的函数你解压出来的子目录默认不在搜索路径里。解决办法有两种第一种是在命令行手动添加目录 addpath(genpath(pwd)) savepathaddpath(genpath(pwd))会把当前文件夹以及所有子文件夹都加入搜索路径savepath把这份路径配置保存到你的 Matlab 启动设置里下次打开不用重新设置。第二种方法更简单在 Matlab 左侧文件浏览器里找到models文件夹右键选择“添加到路径”效果相同。如果用了addpath还是报错检查一下是不是函数文件名和函数名不一致。5.2 仿真轨迹发散车辆画出一个向外张开的螺旋如果你发现车辆轨迹不是沿着参考轨迹走而是越来越大、越来越偏像画螺旋一样往外跑第一反应应该是翻看前视距离Ld是不是被改得很小。前视距离太小意味着车辆只看得到脚下那一小块轨迹控制器会频繁剧烈转向前轮造成车辆来回摆动摆动振幅逐步放大。Ld小于 1 米时基本会出现这种情况。解决方法是把Ld调回 3 到 8 米范围再试。如果Ld正常但依旧发散再看 PID 速度控制器的Kp是否过大纵向速度振荡会牵动航向角更新间接导致横向失控。5.3 仿真动画卡顿每一帧都要等半秒这个坑不算报错但很影响体验。当dt设置得比较小比如 0.01而sim_time设置到 30 时循环次数高达 3000 次动画绘图部分每秒绘制几十次图形刷新必然卡顿。解决思路是不改变仿真步长的同时降低动画刷新频率常见做法是每 10 步或每 20 步才绘制一次车辆位置我常用的写法是加一个计数条件if mod(k, 20) 0 % 更新动画画面 plot(x, y, ro); drawnow; endmod(k, 20)让绘图操作只每隔 20 步执行一次仿真计算仍然保持原有精度画面上车辆的移动看起来平滑程度也足够。还有一个隐藏加速技巧绘制历史轨迹时不要每次把整条轨迹重新画一遍而是用set更新线条的XData和YData属性图形性能会提升一个量级。5.4 中文注释乱码或直接报错无法识别字符这部分代码的作者在注释里写了中文如果你的 Matlab 默认编码是西欧字母或 GBK 而脚本保存为 UTF-8 格式大概率会出现注释乱码。实际上我在解压后打开脚本文件里面的中文注释在旧版 Matlab 上就显示成乱码了。解决办法是在 Matlab 主页的预设项里将文本编码改为 UTF-8 后重启软件。如果改完仍乱码可以把脚本文件用记事本另存为 ANSI 编码只是这样会牺牲跨平台兼容性我建议还是统一用 UTF-8 并升级到新版 Matlab。6. 二次开发思路把这套代码改成你自己的仿真实验台6.1 把纯跟踪控制器替换成 LQR纯跟踪算法简单直观但它毕竟是一个几何方法很难处理带约束的控制问题。如果你想体验更现代的控制算法可以把controllers下的pure_pursuit.m整个换掉写一个 LQR 横向控制器。LQR 的核心是用状态反馈delta -K * state其中增益矩阵K由求解黎卡提方程得到。在 Matlab 里这是一个很小的动作先用dlqr或lqr函数算出增益再把控制器函数体替换掉主程序一行都不用改。替换之后你会发现弯道处的切弯现象减轻了因为 LQR 会对横向误差和航向误差同时做最优权衡。6.2 增加障碍物与停车场景双移线只能测试纯跟踪能力如果你想验证智能车感知决策就需要在轨迹生成模块里加入障碍物。我一般会在generate_reference_trajectory.m里额外定义一组障碍物坐标在主循环的每一步检查车辆与障碍物的距离当距离低于安全阈值时把目标速度ref_v降为零触发停车行为。这个改动只涉及纵向控制部分几乎不动横向控制器实现起来性价比很高。6.3 增加评价指标输出仿真跑完了只出两张图还不够一辆车控制得好不好应该有数字说话。我建议在output目录下生成一份指标汇总把横向误差的均方根误差、最大误差、超调量、稳态误差算出来写到文本文件里。每次调参后跑一遍直接对比这几组数字比肉眼看图靠谱得多。我在自己的使用习惯里会把Ld从 2 到 10 每隔 0.5 扫一遍每轮仿真输出一组指标最后画一条Ld-误差的关系曲线一眼就能定位最优前视距离。说到调参这个事我过去拿到一套新代码总喜欢直接乱改参数改完跑出一堆发散轨迹才回头一个个试。后来我强迫自己每次只改一个参数跑完记录指标数据再动下一个。这样做的原因是仿真里的变量相互耦合同时改两个参数翻车了你根本不知道是哪个参数惹的祸。从那以后我每次拿到新的仿真代码都强制走一遍“先跑默认、再改单参、最后对比”的流程你也试试这个节奏能少走不少弯路。希望帮到你。本文还有配套的精品资源点击获取