ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Simulink智能车辆局部路径规划与DWA避障仿真实战解析

Simulink智能车辆局部路径规划与DWA避障仿真实战解析 如果你最近也在用Simulink做智能车辆方向的课程作业或者比赛项目应该和我有同感Simulink上手不难难的是把一个“完整场景”跑通——尤其是局部路径规划这种既要建模又要控制还要可视化的活儿。我上个月刚完成了一个基于Simulink的智能车辆局部路径规划避障仿真从车辆模型搭建、DWA规划算法实现到调试可视化全部走了一遍这中间踩的坑确实不少但收获也很大。这篇文章就把我完整的实现过程和思考写下来适合正在学Simulink但不知道从哪里下手、或者想用仿真验证路径规划算法的同学参考。1. 为什么我把“局部路径规划”当作Simulink的第一个实战项目很多初学者学Simulink都是从PID控制、信号处理这些例子入门的但我个人觉得智能车辆方向的第一炮打“局部路径规划”最合适。原因很朴素局部路径规划能同时逼着你搞清楚三件事——车辆模型怎么建、规划算法怎么算、仿真系统怎么联调而这三点恰恰是Simulink工程应用的核心。1.1 局部路径规划和全局路径规划的分工差异先说概念。全局路径规划是上帝视角地图已知一次性找出一条从起点到终点的长距离路线A*、RRT这类算法干的就是这个事。局部路径规划则是把车开到眼前根据实时感知到的障碍物在很小的时间窗口内算出一条能绕过去的短途轨迹再把这条轨迹交给控制层去跟踪。智能车辆系统里两者是配合使用的全局规划负责“走哪条路”局部规划负责“别撞上去”。我这次做的避障场景就是典型的局部路径规划问题——只知道当前位姿、目标点和周围障碍物要在几个周期内输出一条可执行的速度和转角指令。这类问题非常适合用Simulink做因为它本质上是连续时间系统加离散决策模型的混合仿真。1.2 纯Simulink还是Carsim联合仿真两条路线的取舍经常在论坛里看到有人问“局部路径规划仿真用Simulink还是Carsim联合仿真”也看到很多热词集中在carsim和simulink联合仿真上。我的建议是先分清你的目标是“验证算法”还是“验证整车”。如果你主要想验证规划算法的效果比如DWA、纯跟踪这些那纯Simulink完全够用。车辆用一个简化的运动学模型就行不用上精确的动力学。如果你的重点是底盘响应、轮胎力、横向稳定性这些那就需要Carsim提供高保真车辆模型Simulink负责跑规划控制算法两者通过Carsim S-Function接口交互。我做这个项目的阶段目标是跑通算法链路所以选了纯Simulink方案。Carsim联仿的优势是真实但前期的标定和接口配置工作量会明显增加不适合作为第一个项目。建议你先用简化模型把算法链路跑通后续需要精度再慢慢加Carsim这是个非常务实的路线。1.3 工具箱选型别一上来就装自动驾驶工具包我查过很多资料发现很多人都推荐直接上MATLAB的自动驾驶工具箱Automated Driving Toolbox但实际上做这个避障仿真根本不需要那么多重型工具。我最终用的只有组件用途Simulink主仿真环境MATLAB Function编写DWA规划算法和纯跟踪控制算法Simulink Control Design辅助信号线性化分析可选基础MATLAB环境绘制栅格地图、XY图像可视化和结果分析这里想强调一下Simulink最强大的地方是信号流和模块化算法部分用MATLAB Function写反而比直接拖刀模块更清晰、更容易调试。刚开始学的时候不要陷入“必须用某个高级工具箱”的误区先把手头的Simulink基础环境用熟比什么都强。2. 场景模型搭建车辆运动学模型与避障空间定义把仿真跑起来的第一个基础工作是把“车”这个对象用一种可以在Simulink里表达的数学模型描述出来再把周围环境定义好。这一环看着简单但后面很多奇奇怪怪的仿真发散问题都出在这儿。2.1 我的首选运动学自行车模型自动驾驶仿真里最常用的简化模型就是自行车模型也叫单轨模型它假设车辆左右轮合并成一个虚拟中心轮然后车辆状态用四个量描述x,y车辆在大地坐标系下的位置θ航向角车头朝向与x轴的夹角δ前轮转角虚拟前轮的转角状态方程非常直观dx/dt v * cos(θ) dy/dt v * sin(θ) dθ/dt v * tan(δ) / L其中L是轴距v是后轴中心的纵向速度。这个模型在Simulink里实现起来就三个积分器和几个三角函数模块的组合。也许有人会问“这个模型是不是太简单了会不会和真车差异很大”确实它没考虑轮胎侧偏、质心侧偏角、横摆阻尼所以高速紧急避障时精度不足。但如果车速控制在30km/h以下的中低速避障场景它的精度已经完全够用了。我在很多比赛项目的初版仿真里用的都是这个模型因为规划算法本身就需要足够快的迭代周期模型太复杂反而拖慢整个仿真链路。在模型实现上我直接在Simulink里搭了连续积分模块状态变量用三个积分器的初始值来设定。这里有个小经验初始化时不要全设成0。比如车辆从(0,0)出发朝向x轴正方向可以设但如果起始位姿带角度就在积分器初始条件里直接填初始θ这个初始条件对后面的场景对齐非常关键。2.2 环境地图和障碍物怎么定义避障场景里除了车还需要目标点和障碍物。我的做法是用一个MATLAB脚本在仿真开始前把场景数据加载到基础工作区然后在Simulink模块的参数里引用这些变量% 场景初始化脚本 init_scene.m % 起点与目标点 start_pose [0, 0, pi/6]; % x, y, yaw goal [25, 15]; % 目标点 % 静态障碍物列表每行一个 [x, y, 半径] obstacles [8, 6, 0.8; 12, 12, 1.2; 18, 7, 1.0; 20, 14, 0.9];然后在模型里的Constant模块或MATLAB Function输入端口引用obstacles这个变量。用脚本初始化的好处是改场景只需要改脚本不需要动模型结构。后来我为了对比不同障碍物布局对规划结果的影响批量跑了几十组不同场景全靠这个脚本化的场景定义方式。如果想可视化场景可以在MATLAB侧画一个静态图把障碍物画成圆形然后叠加仿真轨迹一起输出。这一步不是必须的但对理解算法行为帮助极大。2.3 坐标系的坑全局坐标统一操作这是新手特别容易翻车的地方。局部路径规划中你可能会拿到雷达坐标系下的障碍物信息、全局坐标系下的目标点信息、车身坐标系下的位姿信息如果不在一个坐标系里做规划输出就会乱套。我的原则是所有规划运算统一在全局坐标系大地坐标系下进行。也就是说仿真开始前就把障碍物坐标、目标点坐标、车辆状态全部换算到同一坐标系。现在很多感知模块输出的是车身坐标系下的相对位置如果直接拿来规划需要提前用齐次变换矩阵把它转到世界系。在Simulink里可以通过Fcn模块实现转换但第一次做的话建议直接在MATLAB Function内部做坐标转换逻辑更好查。这儿的另一个重点是要区分“路径”和“轨迹”。路径是空间中的一条几何曲线只管(x,y)轨迹是带时间信息的路径每个点都对应速度和时间。DWA算法输出的严格来说是轨迹因为它包含了速度信息。在搭建模块时接口设计要能同时输出路径点序列和控制量后面做可视化时路径和轨迹都能画出来会非常直观。3. 规划算法落地在Simulink里从零实现DWA局部规划算法是局部路径规划仿真的灵魂。我选的算法是动态窗口法DWA, Dynamic Window Approach这也是智能车辆局部避障场景里应用最广的算法之一网上很多“动态避障小车路径规划”的教程用的都是它。3.1 为什么选DWA而不是A*或RRT先说说为什么不选别的。A*和RRT本质是几何搜索算法它们输出的是无碰撞路径但不直接包含“速度怎么变”的信息后面的速度规划还得单独做。而DWA直接在当前速度空间中采样每个候选速度都能推算出对应的轨迹然后根据目标方向、障碍物距离、速度大小三个指标打分选优。它把“路径”和“速度”一次性规划出来了非常适合实时性要求高的局部避障场景。DWA的另一个好处是直观——速度空间窗口动态窗口的物理意义很清晰你当前速度是v0受加速度限制下一时刻能达到的速度只可能落在v0±a*dt这个区间内同样角速度也有一个可行区间。在这个窗口内采样若干个(v,w)组合每个组合向前模拟一小段时间就能得到若干条候选轨迹选得分最高的那条执行这就是DWA的全部逻辑。3.2 用MATLAB Function写DWA核心逻辑在Simulink里我建了一个DWA_Planner的MATLAB Function模块输入是当前位姿、当前速度、目标点坐标和障碍物列表输出是线速度指令v_cmd、角速度指令w_cmd和当前时刻的规划轨迹。核心代码骨架如下function [v_cmd, w_cmd, trajectory] dwa_planner(x, y, theta, v, w, goal, obstacles) % 速度空间采样参数 v_max 1.0; v_min -0.2; % 线速度范围 w_max 0.6; w_min -0.6; % 角速度范围 v_step 0.1; w_step 0.15; % 采样分辨率 predict_time 2.0; % 轨迹预测时长 % 动态窗口受加速度限制 a_v_max 0.5; a_w_max 0.8; % 线加速度和角加速度限制 v_dw max(v_min, v - a_v_max * 0.1):v_step:min(v_max, v a_v_max * 0.1); w_dw max(w_min, w - a_w_max * 0.1):w_step:min(w_max, w a_w_max * 0.1); best_score -inf; trajectory [x, y, theta]; for vi v_dw for wi w_dw [traj, valid] simulate_trajectory(x, y, theta, vi, wi, predict_time, 0.1, obstacles); if ~valid continue; end score calc_score(traj, goal, vi, wi, obstacles); if score best_score best_score score; v_cmd vi; w_cmd wi; trajectory traj; end end endsimulate_trajectory就是按照上一节的运动学模型用给定的vi和wi向前推算一段轨迹每推一步检查轨迹点是否和障碍物圆发生碰撞一旦距离小于障碍半径车辆半径就标记为无效轨迹。这里有一点要注意MATLAB Function模块里写循环嵌套没问题但不要在里面调用fmincon这类优化函数Simulink代码生成时会遇到各种限制。DWA本身就是暴力采样循环展开是最合理的实现方式。3.3 评价函数权重调参决定成败DWA的得分公式是三个子项加权和score alpha * heading_score beta * v_score gamma * obs_scoreheading_score轨迹终点朝向与目标方向的夹角越小得分越高鼓励朝向目标走v_score速度越接近最大速度越高鼓励走得快obs_score轨迹与最近障碍物的距离越远越高鼓励远离障碍物我第一版用的是alpha0.1、beta0.2、gamma0.7结果车辆极其保守稍微看到障碍物就停在原地不动甚至绕大圈。后来把gamma降到0.3beta提上去后车辆会选择从障碍物旁边平滑绕过去而不是原地发呆。这里有一个重要经验权重不是一成不变的场景一变就得重调。障碍物密集的狭窄通道里安全权重必须高空旷场地里速度权重应该占主导。如果你有精力可以把三个权重参数也作为模型输入用Scope或GUI方式动态调整调试效率会高很多。DWA还有一个经典问题局部极小值。车辆在U形障碍物前会反复横摆却一直找不到出路因为它只看得到局部信息。解决办法是引入“逃逸”机制比如检测到长时间横向位移没有进展时临时提高转向偏置。这个我在后面调试阶段还会提到。4. 仿真链路搭建与可视化调试的关键设置算法写好了车辆模型也有了接下来这一步是把它们连成一个闭环仿真系统。看起来只是连线但很多“仿真跑不起来”“结果完全不对”的问题都出在这一环节的系统性设置上。4.1 整体信号流和模块布局我的仿真模型模块结构大致是环境初始化模块输出障碍物列表和目标点→ DWA规划模块 → 纯跟踪控制模块 → 车辆运动学模型模块 → 状态反馈回路。信号流中要特别注意的是DWA输入不仅有车辆状态还有障碍物列表这属于一维矩阵信号。Simulink里矩阵信号在MATLAB Function模块间传递时要注意维度信息最好在MATLAB Function编辑界面里手动声明输入的端口尺寸免得出现隐式维度不匹配的问题。我建议在DWA和车辆模型之间加一个纯跟踪控制环。因为DWA输出的是期望线速度和期望角速度但车辆模型的真实控制量是前轮转角δ。这两者之间需要一个换算关系δ atan(w * L / v)如果速度v接近零时计算会有奇异点必须做保护处理。我的做法是当v小于0.01时直接把δ置为0或者直接跳过转向计算这在低速场景里表现得很稳定。4.2 求解器和步长仿真稳定性第一道关卡最开始我把求解器设置为变步长自适应求解器VariableStepAuto结果仿真到一半就报错或者速度曲线剧烈抖动。原因很简单变步长求解器在碰到离散采样逻辑或者状态突变时会自动把步长调得很小而DWA的预测逻辑是基于固定时间步长的两者不匹配就会产生数值振荡。我的最终配置是求解器选择固定步长Fixed-step步长设0.02秒用ode4四阶龙格库塔算法。为什么要0.02秒因为DWA的控制周期我是按10Hz设的每个控制周期内有5个仿真步足够车辆模型在两次决策之间产生明显的状态变化。如果你把控制频率提到20Hz那仿真步长可以相应设到0.01。固定步长还能保证仿真结果的可重复性。变步长仿真在步长自适应过程中会有微小的数值误差累积不同机器上跑出来的轨迹可能对不上这在做对比实验时很让人头疼。就地选步长就是牺牲一点计算精度换稳定性和可复现性值。4.3 可视化调试Scope、XY Graph和MATLAB脚本三管齐下有了正确闭环之后可视化就成了调试人员最好的朋友。我用三种方式同时观察信号Scope看速度和角速度指令随时间的变化。重点看有没有周期性突变、有没有输出饱和。XY Graph实时画车辆位置和轨迹。这是判断避障效果最直接的窗口能不能绕开障碍物一目了然。MATLAB脚本后处理仿真结束后把记录的轨迹数据和障碍物列表画在同一张图上便于生成报告和保存结果。在记录数据方面我习惯在模型里加To Workspace模块把时间戳、状态量、控制量都保存成结构体变量。我的经验是字段命名要有统一前缀比如longi_vel、yaw_rate_cmd这种格式因为后面数据分析脚本很多命名乱了整个复盘过程会非常痛苦。5. 我踩过的坑从系统仿真发散到车辆原地打转这部分是全文最想分享的因为我踩过的每一个坑都花了一个下午甚至一天才排查出来。这里的经验不是什么教学书里会系统讲的全是实战中熬出来的。5.1 仿真第一次发散速度突变我盯了半小时信号图项目第一版模型一跑Scope里速度信号就像过山车一样冲上冲下最后仿真直接中断。一开始我还以为是求解器步长不够但调小步长也没用。最后逐段看信号才发现我的运动学模型里速度是直接积分出来的但DWA输出的v_cmd在某个采样点从1.0直接跳到-0.2这个阶跃信号喂给积分器后直接导致积分值大幅跳变加上模型本身非线性就爆了。排查思路值得分享一下第一看输入信号是否有限幅第二看积分器初值是否合理第三看反馈回路有没有正反馈。我的问题就出在“没给速度指令加限幅”上。解决方案是在VIP输出到车辆模型之前加Saturation模块把线速度限制在[-0.2, 1.0]之内角速度限制在[-0.8, 0.8]之内。加完限幅仿真立刻稳定了。5.2 代数环一个Memory模块解决隐性问题第一次跑通闭环后我发现仿真速度特别慢查了下提示日志发现模型里存在代数环Algebraic Loop。原因是DWA模块的输入之一是当前速度而它输出的速度指令又直接决定当前速度更新形成了瞬时依赖关系。仿真器被迫在每个步长内迭代求解这个隐式方程速度自然慢很多。解决方法有两种一是把DWA的输出经过一个单位延迟模块Unit Delay再反馈给DWA输入但这会让算法落后一个周期二是用Memory模块打破代数环让反馈信号在步长边界上更新。我用的是后者既能保留实时性又能消除代数环。这个坑特别隐蔽如果你不加检查根本不知道模型里有代数环。我的建议是搭建完毕一定到分析-诊断查看器里看一眼有没有代数环警告这类问题越早发现越容易解决。5.3 车辆原地打转DWA权重配比和局部极小问题项目中段我把场景换成两个靠得很近的障碍物测试车辆开始在中间一个小区域内反复横摆像原地打转一样始终无法朝目标前进。这就是我前面提到的DWA局部极小值问题。排查后确认是代价函数权重配比不合适安全距离项占比重太大车辆宁可在原地保持距离也不愿意冒一点风险穿过狭窄通道。我把gamma从0.7降到0.5之后情况有所改善但还不够后来又在heading_score里加入了“历史轨迹记忆”惩罚如果连续时长超过一定值横向位移增量始终小于阈值就在评价函数里临时提高heading权重强迫车辆朝目标点方向“突进”一下。这个方法不是完美的但在特定的竞赛场景下实测有效车辆能较快走出死区。5.4 初始状态全部为零带来的起跑困难有一次我重新搭了个模型忘了设置车辆初始状态为[0,0,pi/6]而全留了0。结果车辆速度从0起步DWA输出了一个很小的速度指令但纯跟踪控制器在低速下因为前轮转角计算出现奇异点直接把转向指令给饱和了车辆完全无法转向一直直行。排查时发现速度从未超过0.05前轮转角一直在最大值。后来加了低速转向保护逻辑当v小于0.1时不计算纯跟踪的δ而是直接沿用上一个周期转向角。这个逻辑对低速停车入库场景同样有效各位可以借鉴。6. 仿真之外的延伸C代码生成、Carsim联仿和实车部署思路跑通了仿真探索才算刚开始。局部路径规划仿真真正的价值是验证算法逻辑验证通过后下一步总会想到这玩意能不能部署到实物上6.1 从Simulink模型到C代码提前做好离散化我试过用Simulink自带的Embedded Coder生成C代码这是最顺的一条路。但代码生成有一个前提模型必须是离散系统。连续积分模块在有代码生成需求时通通要换成离散积分器例如离散状态空间、单位延迟组合。我一开始没注意模型里保留了好几个连续积分块生成代码时会报“不支持连续状态”之类的错误。正确的流程是先在Simulink里把连续运动学模型改成离散模型步长0.01秒然后依次把MATLAB Function里的逻辑全部改成纯离散算法比如把积分改成累加最后再尝试代码生成。这个过程差不多要额外花一个晚上但换来的是一套可以直接跑到单片机或者工控机上的算法原型性价比很高。另外要留意MATLAB Function在代码生成时对数据类型有严格约束输入输出必须是固定尺寸、固定类型的信号不能像仿真时那样随便传结构体。我在DWA函数里用coder相关的优化指令处理了几个动态维度变量但这方面没人指导时容易卡住建议先跑最简结构成功一条链路后再加复杂度。6.2 和ROS自主导航仿真的取舍现在机器人领域经常用ROSgazebo仿真做路径规划很多人会问“Simulink是不是多余了”。我的理解是两者解决的不是一个层面的问题。Gazebo仿真更适合验证软件系统级的功能比如多传感器融合、消息通信、导航栈的调度而Simulink更适合验证控制系统级的算法逻辑比如规划指令到车辆执行指令的换算、底盘响应、模型输入输出接口设计。做这个项目之后我的体会是如果未来你的目标是走向实车最好两个环境都玩一玩Simulink里快速验证核心算法ROS环境里做系统级联调两者数据结果可以互相印证。6.3 后续扩展方向动态障碍物和传感器仿真目前我做的是静态障碍物场景。如果想往动态场景扩展有两个方向可以走。一种是在场景里加入以恒定速度移动的障碍物然后在DWA的轨迹预测里加入动态障碍物的位置预测这相当于在碰撞检测环节做“未来时刻障碍物位置”的判断。更有挑战一些的做法是把感知延时加进仿真链路因为规划频率和感知频率不匹配会导致障碍物信息滞后车辆会一头冲进障碍物里。我开始考虑这个方向时才发现仿真与真实系统最大的差距就在信息同步上这个坑建议大家早点体验。如果想接入更真实的传感器也可以用Simulink的车辆信号仿真模块做激光雷达点云输出再经过障碍物聚类后送入规划器这样整个闭环就和真实系统几乎一致了。最后分享一个我个人在做这个项目时的体会学Simulink最忌讳的是不停看教程而不动手。教程看一百遍也不如自己从头搭一个避障仿真来得深刻。我首版模型里各种错误百出但经历了这些错误之后我对运动学模型、DWA参数、反馈链路和数值稳定性的理解完全上了一个台阶。如果你的时间允许建议你也像我一样给自己设定一个具体场景目标比如“车辆绕开三个不同位置的障碍物并到达目标点”然后一步步把链路打通。整个过程很磨人但跑通那一刻的成就感是刷多少教程都换不来的。如果在搭建过程中遇到什么奇怪的问题欢迎按我上面的排查思路试试看很多坑确实是可以提前避开的。
返回列表