首页 / 资讯中心 / 文章详情

Dubins曲线MATLAB实现详解:路径规划最短路径与避坑指南

Dubins曲线MATLAB实现详解:路径规划最短路径与避坑指南 ★ FEATURED ARTICLE
简介这套基于MATLAB的Dubins曲线路径规划实现面向机器人、自动驾驶等领域的开发者解决任意给定起点终点坐标和初始最终朝向角时生成满足最小转弯半径约束的最短可行路径问题。压缩包体积仅9KB共含6个文件其中两个核心m文件dubins_core.m、dubins_curve.m分别完成曲线计算与绘制另有README说明、许可证及Git配置文档。已有679人学习下载。用户可直接传入起点终点坐标及heading角度运行得到参数化曲线数据与平滑插值图便于快速验证算法效果代码简洁、结构清晰适合作为路径规划模块嵌入无人车或无人机系统也适合教学演示和算法研究。1. Dubins curve for MATLAB路径规划里绕不开的经典最短路径实现做无人车、无人机或移动机器人路径规划的人迟早会遇到一个问题全局路径规划算出来的是一条折线而执行机构需要一条带最小转弯半径约束的光滑曲线。Dubins curve for MATLAB 解决的就是这个痛点——它给出在起终点位置和朝向都给定的前提下由直线段和定半径圆弧组成的最短可行路径。为什么值得花时间看这套代码因为它把路径规划里最麻烦的运动学约束用一个解析函数就解决了不需要迭代优化几毫秒出结果。适合做局部轨迹生成、航路拼接、以及给 A* 或 RRT 结果做后处理平滑的开发者。下面按我实际拆解这套资源时走过的路径把几何、实现和坑一次讲清楚。2. 六种路径族与解析几何选对转向序列算准每个圆心2.1 LSL、RSR、LSR、RSL、RLR、LRL从候选空间理解 Dubins 解的结构先有个全局印象。Dubins 曲线的最优路径只会是以下六种形态中的一种LSL、RSR、LSR、RSL、RLR、LRL。其中 L 表示左转圆弧R 表示右转圆弧S 表示直线段。也就是说要么是「圆弧-直线-圆弧」CSC要么是「圆弧-圆弧-圆弧」CCC。为什么不会有「圆弧-圆弧-直线」这种组合因为如果前两段是圆弧后一段是直线那么第二段圆弧的终点必然和直线相切等价于可以用一个更短的 CCC 或 CSC 表示这在几何上可以证明不会成为最短候选。这六种候选的空间意义很直观。LSL 和 RSR 适合起点和终点朝向大致平行的情况车辆先朝一个方向偏转走一段直线再偏转回目标朝向。LSR 和 RSL 适合起点和终点朝向差异较大的情况比如先向左转直线穿插后向右转用一个「S 形」完成朝向调整。RLR 和 LRL 则出现在起终点朝向差接近 π 的时候车辆几乎调了个头中间那个圆弧起到了过渡作用。工程上多数实现会直接把这六种全部算一遍然后取路径长度最短的那条。原因有两个第一几何解析计算非常快六次求解的总耗时在 MATLAB 里也只有毫秒级没有性能压力第二按场景预判只选两三种容易在边界条件下漏掉真正的全局最优。所以与其在选型上做启发式判断不如老老实实全算。路径族转向序列典型适用场景出现频率LSL左转-直线-左转起终点朝向近似平行且同侧偏移高RSR右转-直线-右转与 LSL 对称偏移方向相反高LSR左转-直线-右转起终点朝向差异较大S 形穿越高RSL右转-直线-左转与 LSR 对称高RLR右转-左转-右转朝向差接近 π掉头场景中LRL左转-右转-左转与 RLR 对称中表里说的「出现频率」是一个经验值。实际编码时不要依赖这个频率做剪枝因为一旦跳过某个候选边界场景就会直接失败。六种全算取最小长度是 Dubins 实现里最稳的策略。2.2 圆心的解析式把转向序列翻译成坐标计算任何一条 Dubins 路径的第一步都是把起点和终点各自对应的转向圆求出来。以起点为例左转圆和右转圆的圆心分别是% 起点左转圆圆心 c_left [x1 r*sin(theta1), y1 - r*cos(theta1)]; % 起点右转圆圆心 c_right [x1 - r*sin(theta1), y1 r*cos(theta1)];终点的圆心计算同理把 x1、y1、theta1 换成 x2、y2、theta2 即可。这里 theta1 是弧度制的全局朝向角必须是相对全局 x 轴正方向的角。左转对应的圆心在起点位姿的左侧右转在右侧这个符号约定必须和后面的圆弧方向判断保持一致。切入点的推导是重点。以 LSL 为例两个圆心分别是起点左转圆 c1 和终点左转圆 c2。连接两个圆心的向量 v c2 - c1其长度记为 d。当 d 大于 2r 时两个圆之间存在一条外公切线直线段方向与 v 同向。切点的坐标是v c2 - c1; d norm(v); u v / d; % 单位方向向量 p1 c1 r * u; % 起点圆切点 p2 c2 - r * u; % 终点圆切点注意 p2 用的是减号因为终点圆的切点在沿直线方向的另一侧。这段代码的成立条件是 d 2r。如果 d 恰好等于 2r直线段长度为 0p1 和 p2 重合LSL 退化成两段相切圆弧。实际计算时 d 非常接近 2r 会导致切点微小的数值抖动需要用阈值判定跳过。RSR 的计算与 LSL 完全对称只要把圆心换成右转圆切点公式里 r 的符号反一下即可。LSR 和 RSL 则不同它们连接的是内公切线直线段的方向不再是两个圆心的连线方向而是需要先求两个圆心连线的角度再根据角度与辅助角的关系旋转得到切向量。2.3 内公切线求解LSR 和 RSL 的核心差异内公切线可以这样理解起点圆和终点圆分别位于直线的两侧。设两圆心连线与 x 轴的夹角为 phi atan2(c2(2)-c1(2), c2(1)-c1(1))两圆心距离为 d。对于 LSR辅助角度 beta acos(2*r/d)。那么直线段的方向角 psi phi beta具体正负号取决于转向组合。切点坐标phi atan2(c2(2)-c1(2), c2(1)-c1(1)); beta acos(2*r/d); psi phi beta; % LSR 取加RSL 取减 u [cos(psi), sin(psi)]; p1 c1 r * u; % 起点圆切点起点是左转圆 p2 c2 - r * u; % 终点圆切点终点是右转圆参数说明acos(2*r/d) 成立的条件是 d 2r否则 acos 在实数域无定义。这个条件就是内公切线存在的几何约束。当 d 恰好为 2r 时beta 0内公切线退化为外公切线。这个边界在代码里要单独处理否则 acos 输入大于 1 会返回 NaN。实际项目中如果 d 小于 2r说明进入 CCC 族的解空间。顺带提一个我在代码里常做的防御所有 acos 调用前先 clamp 一下把输入限制在 [-1, 1] 区间。这不是在掩盖问题而是因为浮点计算里 d 的值可能刚好在阈值附近产生 1.0000000002 这种值直接让 acos 返回 NaN 导致整条路径生成失败。clamp 掉之后由后续长度比较逻辑自然处理边界代码更健壮。2.4 CCC 族中间圆半径与圆心的几何关系当 d 2r 时无论怎么选 CSC 组合都无法找到可行路径最短路径必然属于 CCC 族。CCC 的几何本质是找一个半径 ρ 大于等于 r 的中间圆让它与起点圆和终点圆分别相切。以 RLR 为例起点圆是右转圆终点圆是右转圆中间圆是左转圆。中间圆的圆心位于起点圆心和终点圆心连线的垂直平分线附近具体位置通过三角形余弦定理确定。设起点圆心 ca终点圆心 cb中间圆圆心 cc。两段外切关系给出 |cc - ca| ρ - r外切内切关系给出 |cc - cb| ρ r。这里的方向和符号要特别小心RLR 中起点圆和中间圆是外切终点圆和中间圆是内切。把这两个约束写成方程利用已知的 d 和夹角可以解出 ρ 和 cc 的位置。一个稳定的实现是枚举 ρ用二分法迭代求最小可行半径但既然是解析资源推荐直接解一元方程。% RLR 中间圆半径求解示意 d norm(cb - ca); phi atan2(cb(2)-ca(2), cb(1)-ca(1)); % 由余弦定理中间圆心到起圆的距离 l1 rho - r % 解二次方程得到 rho取大于 r 的正根 rho d/2 r;这组关系我一般在实现里用一个单独函数处理不收进主流程因为 CCC 在常规路径规划里出现频率远低于 CSC。但判断逻辑必须留好否则起终点朝向差接近 π 时函数会直接报错。如果你只需要处理汽车、轮船这类最小转弯半径明显的载体CCC 还会经常出现在窄通道掉头场景不能忽略。3. MATLAB 核心实现从位姿输入到离散路径点序列3.1 输入定义与参数校验单位、归一化、角度预处理写实现的第一步不是摆公式而是把输入规范定死。我一般把位姿定义为 [x, y, theta] 的行向量通通是双精度。theta 单位统一用弧度不接受角度。函数入口设计成function path dubins_curve(x1, y1, theta1, x2, y2, theta2, r, step_size) % 输入起点 x1,y1,theta1终点 x2,y2,theta2最小转弯半径 r采样步长 step_size % 输出path 是 N×3 矩阵每行是 [x, y, theta]这里的 step_size 是路径点的间隔长度单位与坐标一致。它直接决定输出的路径点数。实际应用里step_size 通常取 0.1 到 0.5 倍的转弯半径。太小了点数爆炸后续做轨迹跟踪时计算冗余太大了路径不光滑控制器会看到明显的折线。参数校验环节至少要检查半径 r 是否大于 0step_size 是否在合理范围。如果起点和终点完全重合且朝向相同应该直接返回一个整圆路径否则会导致圆弧段角度计算除以零。这类边界情况我放在避坑专场细说。参数含义单位典型值x1, y1起点位置与 r 同单位地图坐标theta1起点朝向角弧度-π 到 πx2, y2终点位置与 r 同单位地图坐标theta2终点朝向角弧度-π 到 πr最小转弯半径长度单位由车辆实测step_size路径点步长长度单位0.1r 到 0.5r推荐在入口处把所有角度做一次统一预处理function a wrap_angle(a) a mod(a pi, 2*pi) - pi; end逻辑说明mod 加偏移量再减回去把任意角度映射到 [-π, π)。这个函数会在切点计算、圆弧角度累积、朝向差判断等多个环节反复用到。注意 mod 的基数取 2*pi不要取 pi否则正负半轴会错位。3.2 圆弧段离散化起点极角、角度增量与采样圆弧段是所有 Dubins 路径里最容易出错的地方。原因在于圆弧方向的两套定义全局逆时针和机体自身转向。L 表示逆时针R 表示顺时针——这里要统一成全局坐标下的旋转方向而不是相对车体的方向。很多人写代码第一步就在这里翻车。一个标准的圆弧段离散化流程是这样的% 以 LSL 起点圆弧段为例 c1 [x1 r*sin(theta1), y1 - r*cos(theta1)]; % 起点在圆上的极角 start_angle atan2(y1 - c1(2), x1 - c1(1)); % 切点 p1 在圆上的极角 end_angle atan2(p1(2) - c1(2), p1(1) - c1(1)); % 角度增量圆弧是逆时针保证增量为正 delta end_angle - start_angle; delta wrap_angle(delta); if delta 0 delta delta 2*pi; end % 按角度间隔采样 arc_length r * delta; n_points round(arc_length / step_size); arc_angles linspace(start_angle, start_angle delta, n_points); arc_points [c1(1) r*cos(arc_angles); c1(2) r*sin(arc_angles)];逻辑说明先求出起点和切点的极角做一个 wrap 处理后左转圆弧的角度增量被强制到 [0, 2π)然后等角度间隔采样。采样点的航向角在圆弧段上应该是起始角加上已经转过的角度这个角度要随时 wrap否则输出到轨迹跟踪模块时会出现角度突变。参数说明linspace 生成的 arc_angles 是等角度采样对应到弧长上每段的间隔并不完全等于 step_size。越靠近圆心位置实际间隔越接近 r * delta / n_points。要严格的等弧长采样应该用弧长比例映射角度这个进阶做法放到最后一章展开。3.3 直线段与拼接去重与长度分配直线段相对简单直接两点连线。但要注意直线段的起点是上一段圆弧的切点而不是起点位姿本身。比如 LSL 里直线段的起点是起点圆弧的切点 p1终点是终点圆弧的切点 p2。如果把直线段直接放在起点和终点之间转弯约束就被丢掉了。% 直线段从 p1 到 p2 dist norm(p2 - p1); n_points round(dist / step_size); t linspace(0, 1, n_points); line_points p1 t * (p2 - p1); % 拼接前去掉首尾重复点这里的 p1 和 p2 都是前一步计算好的切点。t 是列向量保证和 p1 的行向量维度能正确广播。linspace 生成从 0 到 1 的等比序列乘上向量差就得到直线上的采样点。注意是 linspace(0, 1, n_points) 而不是 (0, dist, n_points)因为后者会导致实际步长变成 dist/(n_points-1)而不是 step_size。拼接时最隐蔽的问题是重复点。圆弧段的最后一个点和直线段的第一个点通常是同一个点如果直接合并会留下一个零长度分段后续做数值微分时会出现 NaN。合并操作要显式去掉中间段的第一个点function full_path merge_segments(varargin) full_path []; for i 1:nargin seg varargin{i}; if ~isempty(full_path) seg seg(2:end, :); % 去掉与上一段末尾重合的首点 end full_path [full_path; seg]; end end这个函数接收任意数量的路径段自动去掉段间重复点。注意它也处理了第一段为空的情况避免索引越界。3.4 六候选取最短避免阈值切换毛刺选最短路径的做法是分别计算六种候选的总长度取最小值。长度计算方式有两种一种是用各段几何长度直接相加速度快另一种是离散化之后用路径点累计长度更贴近输出结果。我一般用前者做候选选择用后者做最终输出的路径长度避免离散化误差影响选型。% 六种候选统一求解示意 families {LSL, RSR, LSR, RSL, RLR, LRL}; best_len inf; best_path []; for i 1:6 family families{i}; segments build_segments(family, x1, y1, theta1, x2, y2, theta2, r); if isempty(segments) continue; % 该族在当前位姿下无解 end L sum_segment_length(segments); if L best_len best_len L; best_path discretize_segments(segments, step_size); end end逻辑说明build_segments 返回该路径族的三段参数包括每段的类型、圆心、切点、起止角度。sum_segment_length 按几何公式累加长度不依赖离散化。discretize_segments 在确认最优后才生成路径点序列避免对非最优候选做无效计算。这里的性能技巧是把长度计算和离散化分开。六种候选都算长度但只有最优的那条才做离散化。路径点生成涉及 linspace 和 atan2 调用耗时远大于几行几何长度公式。这种先后分离的结构在需要反复生成数百条 Dubins 路径的批处理场景里能明显节省时间。4. 可视化与质量验证把计算结果摊开来看4.1 绘制路径与起点终点朝向axis equal 是关键MATLAB 里可视化很简单plot 就能完成。但要把朝向和曲率一起显示出来才能看出路径质量。我常用的绘图模板figure; plot(path(:,1), path(:,2), b-, LineWidth, 1.5); hold on; quiver(x1, y1, cos(theta1), sin(theta1), 0.5, r); quiver(x2, y2, cos(theta2), sin(theta2), 0.5, r); axis equal; grid on;axis equal 是必须的不然 x 轴和 y 轴比例不一致圆弧会被拉伸成椭圆看起来像路径出错。quiver 的第四个参数 0.5 是箭头长度比例可以根据坐标系范围调整。另外把圆心的位置也画出来一般我会用 o 标记加小圆圈方便检查圆弧段是否与直线相切。如果圆弧和直线段的连接处出现明显折角那就是切点计算有误。判断切点是否正确还有一个土办法在连接处分别在左右取两个路径点算三个点的连线的夹角。如果夹角接近 180°说明连接平滑如果出现明显转折就是切点偏移了。这个检查在写代码时可以做成断言自动测试用。4.2 曲率与转向半径的核验数值微分不要直接裸奔Dubins 路径的一个核心约束就是曲率不能超标也就是路径上每个点的转弯半径不能小于设定的 r。可视化可以直观看到但更好的方式是直接计算曲率。对离散路径点做数值微分dtheta diff(wrap_angle(path(:,3))); ds sqrt(sum(diff(path(:,1:2)).^2, 2)); curvature abs(dtheta ./ ds); if any(curvature 1/r 1e-6) warning(路径存在曲率超限); end逻辑说明曲率等于航向角变化除以弧长。直线段上 dtheta 为 0曲率为 0圆弧段上 dtheta 恒定曲率等于 1/r。如果某个点的曲率超过 1/r说明路径在离散化或拼接时产生了不该有的折角。1e-6 是容差避免浮点误差误报。注意 diff(wrap_angle(path(:,3))) 里的 wrap_angle 是必须的。路径点航向角在跨越 ±π 边界时数值上会从 3.14 跳到 -3.14直接 diff 会得到 -6.28 的假变化量曲率计算彻底失真。这个坑我在第一次写验证脚本时就踩过输出的曲率图像在路径过 x 轴负半轴时出现一条竖直刺线排查了很久才发现是角度没 wrap。4.3 与占据栅格地图的碰撞检测衔接Dubins 曲线经常被用到全局路径规划的后端前端 A* 或 RRT 先生成一条拓扑路径Dubins 再把折线变成满足运动学约束的曲线。这时要注意Dubins 曲线输出的是连续路径不感知障碍物碰撞检测必须在生成后单独做。我一般会用 robotics toolbox 的 occupancyMap 对象做栅格查询occ getOccupancy(map, path(:,1:2)); if any(occ 0.5) error(路径穿过障碍物); endgetOccupancy 接收的是 N×2 的坐标矩阵返回对应栅格的占用概率。阈值的 0.5 是常用默认值实际项目里要根据建图时的置信度调整。如果路径点之间间隔比较大可能漏检两栅格之间的障碍物。稳妥做法是在每两个相邻路径点之间做线性插值取插值点再做碰撞检测。插值密度怎么定我一般取 step_size 的一半作为插值间隔这样即使路径点本身稀疏碰撞检测的粒度也不会差太多。还有一种更省事的做法直接把 step_size 调小到 0.1r让路径点密到可以跳过插值。但这样会显著增加路径点数后续轨迹跟踪的实时性会受影响。实际操作中我倾向保持 step_size 适中用单独的碰撞检测点集来做障碍物判断路径点只负责表示几何形态。5. Dubins 路径避坑从数值边界到工程习惯的五个雷区5.1 最小转弯半径与坐标单位的无量纲化现象同一组位姿把坐标单位从米换成千米后生成的路径不同甚至计算出 NaN。原因Dubins 路径的几何关系对 r 与坐标数量级很敏感。如果坐标值在 10⁴ 量级而 r 只有 0.5圆心坐标的计算会出现严重的浮点舍入误差。圆心距离 d 和半径 r 的相对精度差距过大切点会漂移。解决调用函数之前把所有坐标归一化到以 r 为单位的坐标系。具体做法是把 x、y 除以 r令 r1算出路径后再把坐标乘回去% 归一化到 r1 的几何空间 x1n x1 / r; y1n y1 / r; x2n x2 / r; y2n y2 / r; % 后续所有几何计算在归一化空间进行 % 输出前恢复 path(:,1) path(:,1) * r; path(:,2) path(:,2) * r;归一化后r1所有圆半径和切点坐标都在同一个量级浮点误差大幅下降。这个操作是我拿到任何 Dubins 代码后第一个加的。5.2 朝向角度的参考系混用全局角与机体角现象生成的路径在起点处就出现一段异常圆弧看起来像车辆先原地打了个转。原因大部分车辆模型的朝向角定义是相对全局 x 轴的正方向但有些传感器输出的航向角是相对磁北或者相对车体坐标系的。如果在换算时没有统一theta1 的符号和参考系就错了左转和右转的判断也会跟着错。解决除了在文件头部加注释块明确输入角度定义入口处还要做一次强制 wrap。我在 3.1 里写的 wrap_angle 函数就是为了挡住这类问题。一旦 theta 被映射到 [-π, π)参考系错乱导致的角度漂移会立刻在可视化里暴露出一个 2π 的跳变而不是悄悄混进圆弧计算。5.3 圆弧段角度跨零点的 wrap 问题现象路径在某一段圆弧上突然出现一个毛刺曲线跳到了圆心的另一侧。原因切点的 atan2 角度差计算没有处理 ±π 跳变。比如起始角是 2.9终止角是 -2.9直接相减是 -5.8再取模后得到的是 1.48而不是正确的 0.38。解决用 wrap_angle 或者 atan2(sin(delta), cos(delta)) 做角度差映射然后判断方向delta end_angle - start_angle; delta atan2(sin(delta), cos(delta)); if delta 0 delta delta 2*pi; end这个方法是我每次写圆弧离散化时必加的防线比单纯 if 判断要稳得多因为 atan2(sin, cos) 天然处理了所有跨零点情况。从那以后凡是涉及角度差的地方我都不再直接减一律过一遍这个映射。5.4 起终点重合或近似重合的退化情况现象起点等于终点时函数直接返回长度为 0 的路径但调用方后续的路径跟踪模块却崩了。原因当 d 非常小或为 0 时两个圆几乎重合切点求解公式里的分母趋近于 0产生 NaN 或 inf。这在语义上是合法的——假设车辆绕了一圈回到原地路径长度应该是 2πr 而不是 0。解决在函数开头判断两点的位置差和朝向差if norm([x2-x1, y2-y1]) 1e-10 * r ... abs(wrap_angle(theta2 - theta1)) 1e-6 % 起点终点同位置且同朝向返回完整圆路径 theta_line linspace(theta1, theta1 2*pi, round(2*pi*r / step_size)); path [x1 r*cos(theta_line); y1 r*sin(theta_line); theta_line]; return; end这里的位置差阈值要乘以 r保证不同尺度下的一致性。绕一整圈的圆路径在很多巡检任务里就是标准的单点原地掉头动作处理好了反而能补全功能。5.5 六种候选之间的切换抖动现象LSL 和 RSR 的长度基本一样但切换时路径出现跳变。原因当两个圆心距离 d 刚好越过 2r 的阈值时解从「可以画直线」切换成「必须走 CCC」。这个切换点在数学上连续但不可微数值上前后两个候选的路径长度差很小但路径形态差别大导致输出的路径点序列不完全连续。解决在工程实现里给阈值加一个滞后区间。比如 d 2r * (1 1e-6) 时才允许使用 CSC否则强制走 CCC。还有一种更省事的方案不单独判断阈值直接计算六种候选的实际长度让长度比较自动处理切换。因为 CCC 族在 d 略大于 2r 时长度必然大于 CSCmin 比较天然会回到 CSC。这个方案实现简单也避免了在代码里维护两个阈值分支。6. 进阶用法等弧长离散化与轨迹跟踪接口6.1 等弧长采样替代 linspacelinspace 做圆弧采样时弧长间隔并不严格等于 step_size。当路径点要喂给轨迹跟踪控制器时时间间隔是基于路程而不是角度来计算的。这时候需要改成等弧长采样。已知圆弧总长度 L_arc目标步长 step_size先计算 n round(L_arc / step_size)再生成弧长序列 s linspace(0, L_arc, n)通过 s / r 得到角度增量序列L_arc r * delta; n max(round(L_arc / step_size), 2); s linspace(0, L_arc, n); angles start_angle s / r; arc_points [c1(1) r*cos(angles); c1(2) r*sin(angles)];s / r 实际上是弧度增量因为弧长 l rθ。注意 n 最小为 2防止极短圆弧段计算出单点路径。直线段部分用 0:step_size:dist 补终点的方式生成s_line 0:step_size:dist; if s_line(end) ~ dist s_line(end1) dist; end line_points p1 s_line * (p2 - p1) / dist;直线段这样写比 linspace 更严格每个间隔都等于 step_size只有最后一小段不足步长。实际使用中这个末段残留是正常的控制器通常会在抵达终点附近时切换停止逻辑。6.2 把路径转成时间参数化的轨迹路径点只是几何信息最终进控制器还需要时间和速度信息。一般做法是给每个路径点累加弧长得到 s_accum再按期望速度 v 分配时间戳 t s_accum / v。需要加减速时用 S 形速度剖面重新映射时间轴seg_len sqrt(sum(diff(path(:,1:2)).^2, 2)); s_accum [0; cumsum(seg_len)]; t s_accum / cruise_speed; trajectory [t, path(:,1), path(:,2), wrap_angle(path(:,3))];注意这样生成的是匀速轨迹不适合起停场景。更稳妥的做法是先规划 s(t) 的速度剖面再用样条插值把 s_accum 映射到新的累计弧长。对 Dubins 这种路径直接用三次样条在路径点之间的 s 上插值就能得到一个平滑的速度曲线再反解时间和位置。样条插值后的路径点需要重新做曲率校验因为样条可能压低曲率峰值的同时引入振荡反而破坏 Dubins 最优性。6.3 与 Simulink 模型对接的单位一致性从 MATLAB 函数输出到 Simulink 模块最常见的问题是单位不匹配。路径点的坐标单位是米而车辆模型里如果用的是 pixel 或者 cm转弯半径 r 也要跟着改。我在做某跨平台系统的仿真验证时就踩过这个直接把 r 写成 2但地图坐标单位是像素结果车辆在图上画的圆半径和实际物理尺度的圆对不上。解决方式在 Simulink 的 MATLAB Function 模块里把 r 做成可调参数从主脚本或参数表统一传入。这样在调仿真时只要改一处所有路径生成和车辆模型同步更新。另外Simulink 的 Solver 类型记得选固定步长离散求解器变步长会在圆弧段采样点稀疏的地方出现数值抖动轨迹跟踪曲线会看到不自然的锯齿。6.4 一点收尾习惯从那以后我每次拿到一个和 Dubins curve 相关的 MATLAB 代码第一件事不是跑 demo而是先做三件事第一步把坐标单位改成与 r 一致并归一化第二步用 wrap_angle 把输入角度和路径输出角度都包一次第三步跑两组数据校验——一组是 LSL 跨坐标轴的场景一组是起终点重合绕一整圈的退化场景。这三步走完函数基本不会出幺蛾子。Dubins 曲线本身不难难的是那些藏在角度边界和数值阈值里的细节。希望这份拆解能帮你把踩坑的弯路一次走完。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站