路径平滑优化算法–贝塞尔曲线算法
路径平滑是机器人路径规划中的关键技术,旨在使机器人能够以平滑且连续的方式移动。贝塞尔曲线(Bézier Curves)因其平滑性、灵活性和易于实现的数学性质,成为路径平滑中广泛采用的工具。以下将详细介绍贝塞尔曲线在路径平滑中的应用,包括曲线的连续性概念、贝塞尔曲线的数学基础及其具体实现方法
文章目录
1.贝塞尔曲线原理
贝塞尔曲线是一种在计算机图形学、设计和工程领域广泛使用的参数曲线,以其创建平滑路径的能力而著称。它由法国工程师Pierre Bézier在1960年代为雷诺汽车的车身设计而推广,因其直观性和灵活性成为现代设计中的核心工具。贝塞尔曲线的本质是通过一组控制点定义曲线的形状,曲线并不一定经过所有控制点,但这些点决定了曲线的走向和曲率。它的生成动画过程可以查看这个网址。
1.1 贝塞尔曲线的基本概念
贝塞尔曲线的形状由控制点的数量和位置决定。根据控制点的数量,贝塞尔曲线可以分为不同的阶数,以下贝塞尔是详细分类和说明:
1.1.1 一阶贝塞尔曲线
一阶贝塞尔曲线需要2个控制点,假设分别为 P 0 , P 1 P_0, P_1 P0,P1,则贝塞尔曲线上的任意点 p 1 ( t ) p_1(t) p1(t)可以表述为:
- p 1 ( t ) = ( 1 − t ) P 0 + t P 1 p_1(t) = (1 - t) P_0 + t P_1 p1(t)=(1−t)P0+tP1
t t t 的取值范围为 [ 0 , 1 ] [0,1] [0,1],下面不再重复说明。
一阶曲线其实就是线性插值,就是一个线段。
1.1.2 二次贝塞尔曲线
二阶贝塞尔曲线需要3个控制点,假设分别为 P 0 , P 1 , P 2 P_0, P_1, P_2 P0,P1,P2。 P 0 P_0 P0 和 P 1 P_1 P1 和 P 2 P_2 P2 分别形成一阶曲线:
- P 0 P_0 P0 和 P 1 P_1 P1 形成的线段上的任意点 p 1 , 1 ( t ) = ( 1 − t ) P 0 + t P 1 p_{1,1}(t) = (1 - t) P_0 + t P_1 p1,1(t)=(1−t)P0+tP1
- P 1 P_1 P1 和 P 2 P_2 P2 形成的线段上的任意点 p 1 , 2 ( t ) = ( 1 − t ) P 1 + t P 2 p_{1,2}(t) = (1 - t) P_1 + t P_2 p1,2(t)=(1−t)P1+tP2
在形成的两个一阶线上,可以生成二阶贝塞尔点:
- p 2 ( t ) = ( 1 − t ) p 1 , 1 + t p 1 , 2 p_2(t) = (1 - t) p_{1,1} + t p_{1,2} p2(t)=(1−t)p1,1+tp1,2
展开公式,即可得到二次贝塞尔曲线的参数方程:
- p 2 ( t ) = ( 1 − t ) 2 P 0 + 2 t ( 1 − t ) P 1 + t 2 P 2 p_2(t) = (1 - t)^2 P_0 + 2t(1 - t) P_1 + t^2 P_2 p2(t)=(1−t)2P0+2t(1−t)P1+t2P2
1.1.3 三阶贝塞尔曲线
三阶贝塞尔曲线需要4个控制点,假设分别为 P 0 , P 1 , P 2 , P 3 P_0, P_1, P_2, P_3 P0,P1,P2,P3。 P 0 P_0 P0 和 P 1 P_1 P1 和 P 2 P_2 P2 和 P 3 P_3 P3 分别形成一阶曲线:
- P 0 P_0 P0 和 P 1 P_1 P1 形成的线段上的任意点 p 1 , 1 ( t ) = ( 1 − t ) P 0 + t P 1 p_{1,1}(t) = (1 - t) P_0 + t P_1 p1,1(t)=(1−t)P0+tP1
- P 1 P_1 P1 和 P 2 P_2 P2 形成的线段上的任意点 p 1 , 2 ( t ) = ( 1 − t ) P 1 + t P 2 p_{1,2}(t) = (1 - t) P_1 + t P_2 p1,2(t)=(1−t)P1+tP2
- P 2 P_2 P2 和 P 3 P_3 P3 形成的线段上的任意点 p 1 , 3 ( t ) = ( 1 − t ) P 2 + t P 3 p_{1,3}(t) = (1 - t) P_2 + t P_3 p1,3(t)=(1−t)P2+tP3
在形成的三个一阶线上,可以生成两个二阶贝塞尔点:
- p 2 , 1 ( t ) = ( 1 − t ) p 1 , 1 + t p 1 , 2 p_{2,1}(t) = (1 - t) p_{1,1} + t p_{1,2} p2,1(t)=(1−t)p1,1+tp1,2, p 2 , 2 ( t ) = ( 1 − t ) p 1 , 2 + t p 1 , 3 p_{2,2}(t) = (1 - t) p_{1,2} + t p_{1,3} p2,2(t)=(1−t)p1,2+tp1,3
最后,再形成的两个二阶点上,可以生成三阶贝塞尔点:
- p 3 ( t ) = ( 1 − t ) p 2 , 1 + t p 2 , 2 p_3(t) = (1 - t) p_{2,1} + t p_{2,2} p3(t)=(1−t)p2,1+tp2,2
展开公式,即可得到三阶贝塞尔曲线的参数方程:
- p 3 ( t ) = ( 1 − t ) 3 P 0 + 3 t ( 1 − t ) 2 P 1 + 3 t 2 ( 1 − t ) P 2 + t 3 P 3 p_3(t) = (1 - t)^3 P_0 + 3t(1 - t)^2 P_1 + 3t^2(1 - t) P_2 + t^3 P_3 p3(t)=(1−t)3P0+3t(1−t)2P1+3t2(1−t)P2+t3P3
1.1.4 n阶贝塞尔曲线
通过上面一阶、二阶、三阶的推导,我们可以很容易推广到 n阶贝塞尔曲线。 n n n阶贝塞尔曲线需要 n + 1 n+1 n+1 个控制点,假设分别为 $P_0, P_1, P_2, \dots, P_n $,共有 n + 1 n+1 n+1 个控制点。 P 0 P_0 P0 和 P 1 P_1 P1 和 P 2 P_2 P2, …, P n − 1 P_{n-1} Pn−1 和 P n P_n Pn 分别形成 n n n 个一阶曲线:
- P 0 P_0 P0 和 P 1 P_1 P1 形成的线段上的任意点 p 1 , 0 ( t ) = ( 1 − t ) P 0 + t P 1 p_{1,0}(t) = (1 - t) P_0 + t P_1 p1,0(t)=(1−t)P0+tP1
- P n − 1 P_{n-1} Pn−1和 P n P_n Pn形成的线段上的任意点 p 1 , n − 1 ( t ) = ( 1 − t ) P n − 1 + t P n p_{1,n-1}(t) = (1 - t) P_{n-1} + t P_n p1,n−1(t)=(1−t)Pn−1+tPn
在形成的 n n n 个一阶线上,可以生成 n − 1 n-1 n−1 个二阶贝塞尔点:
p 2 , 0 ( t ) = ( 1 − t ) p 1 , 0 + t p 1 , 1 p_{2,0}(t) = (1 - t) p_{1,0} + t p_{1,1} p2,0(t)=(1−t)p1,0+tp1,1
. . . ... ...
p 2 , n − 2 ( t ) = ( 1 − t ) p 1 , n − 2 + t p 1 , n − 1 p_{2,n-2}(t) = (1 - t) p_{1,n-2} + t p_{1,n-1} p2,n−2(t)=(1−t)p1,n−2+tp1,n−1
以此类推,最后在形成的两个 ( n − 1 ) (n-1) (n−1)阶点上,可以生成一个 n n n阶贝塞尔点:
- p n ( t ) = ( 1 − t ) p n − 1 , 0 + t p n − 1 , 1 p_n(t) = (1 - t) p_{n-1,0} + t p_{n-1,1} pn(t)=(1−t)pn−1,0+tpn−1,1
展开公式,即可得到n阶贝塞尔曲线的参数方程:
- p n ( t ) = ∑ i = 0 n C n i ( 1 − t ) n − i t i P i p_n(t) = \sum_{i=0}^n C_n^i (1 - t)^{n-i} t^i P_i pn(t)=∑i=0nCni(1−t)n−itiPi
其中 C n i = n ! i ! ( n − i ) ! C_n^i = \frac{n!}{i! (n-i)!} Cni=i!(n−i)!n!
- 参考公式可以看出 n阶贝塞尔曲线生成的过程其实是一个递归过程。
1.1.5 贝塞尔曲线在路径平滑中的应用
贝塞尔曲线可用于实现 G 1 G^1 G1 连续和 G 2 G^2 G2 连续的路径平滑。
2. 连续性条件:几何 vs 参数连续
路径平滑强调几何连续( G n G^n Gn),因为它基于曲线内在性质(如弧长),独立于参数化。参数连续( C n C^n Cn)依赖t的选择,可能因重参数化而变。
- G 0 G^0 G0连续:位置匹配, P ( 1 ) = Q ( 0 ) P(1) = Q(0) P(1)=Q(0)( Q Q Q为下一段)。
- G 1 G^1 G1连续:切线方向相同。起点切线 ∝ P 1 − P 0 ∝ P_1 - P_0 ∝P1−P0,终点 ∝ P 3 − P 2 ∝ P_3 - P_2 ∝P3−P2;拼接时确保 P 2 , P 3 , Q 1 P_2, P_3, Q_1 P2,P3,Q1共线且同向。
- G 2 G^2 G2连续:曲率匹配。曲率 κ ( t ) = ∣ P ′ ( t ) × P ′ ′ ( t ) ∣ / ∣ P ′ ( t ) ∣ 3 κ(t) = |P'(t) × P''(t)| / |P'(t)|^3 κ(t)=∣P′(t)×P′′(t)∣/∣P′(t)∣3(2D叉积)。端点曲率需相等,避免加速度突变。
为什么 G 2 G^2 G2重要?机器人有曲率上限 κ m a x = 1 / ρ m i n ( ρ m i n 为最小转弯半径) κ_max = 1/ρ_min(ρ_min为最小转弯半径) κmax=1/ρmin(ρmin为最小转弯半径)。 G 2 G^2 G2确保路径可跟踪,无振动或滑动。
3. 基于二次贝塞尔曲线的高保真 G₂ 连续路径平滑方法
此方法出自于2024IEEE TRANSACTIONS ON INTELLIGENT VEHICLES计算机一区的High-Fidelity and Curvature-Continuous Path Smoothing With Quadratic Bézier Curve,该方法针对非完整约束车辆(如无人车、无人机)生成高保真、曲率连续的轨迹,适用于自动驾驶和机器人导航场景。
3.1 方法介绍
路径平滑是车辆导航的后端处理环节,用于将前端规划器(如A*或RRT)生成的折线路径转换为平滑轨迹,以满足非完整动态约束(如曲率连续性)。传统方法(如三次贝塞尔或样条曲线)往往计算复杂或保真度低,而本方法创新性地采用二次贝塞尔曲线(理论上最低阶曲线),生成 G 2 G^2 G2连续轨迹(除拐点外),并确保局部曲率最大值精确位于原路径点上,从而保持轨迹与原路径的高保真度(偏差<10cm),无需额外碰撞检测。
核心优势:
- 高保真:轨迹通过所有路径点,曲率峰值对齐路径点,保留原路径的碰撞自由和温和转弯特性。
- 连续性:实现 G 0 G^0 G0(位置)和 G 1 G^1 G1(切线)连续; G 2 G^2 G2(曲率)几乎处处连续(拐点处幅度连续)。
- 高效优化:通过两步迭代(保真和连续控制),每步解析求解,收敛快(~20迭代,毫秒级),比高阶贝塞尔方法快50%。
- 实用性:实验验证在模拟(森林/实验室)和真实车辆(空中/地面)场景中表现优异,提升车辆稳定性和执行效率。
方法输入:有序自由路径点 P = { p 0 , p 1 , … , p n + 1 } \mathcal{P} = \{p_0, p_1, \dots, p_{n+1}\} P={p0,p1,…,pn+1};输出:n段二次贝塞尔曲线组成的平滑轨迹 T \mathcal{T} T。以下上图为二阶下图为G2CBS
3.2 理论基础
二次贝塞尔曲线由三个控制点 c 0 , c 1 , c 2 c_0, c_1, c_2 c0,c1,c2定义,参数方程为:
- b ( t ) = ( 1 − t ) 2 c 0 + 2 t ( 1 − t ) c 1 + t 2 c 2 , t ∈ [ 0 , 1 ] b(t) = (1-t)^2 c_0 + 2t(1-t) c_1 + t^2 c_2, \quad t \in [0,1] b(t)=(1−t)2c0+2t(1−t)c1+t2c2,t∈[0,1]
一阶导数:
- b ′ ( t ) = 2 ( 1 − t ) ( c 1 − c 0 ) + 2 t ( c 2 − c 1 ) b'(t) = 2(1-t)(c_1 - c_0) + 2t(c_2 - c_1) b′(t)=2(1−t)(c1−c0)+2t(c2−c1)
二阶导数:
- b ′ ′ ( t ) = 2 ( c 2 − 2 c 1 + c 0 ) b''(t) = 2(c_2 - 2c_1 + c_0) b′′(t)=2(c2−2c1+c0)
曲率:
- k ( t ) = ∣ b ′ ( t ) × b ′ ′ ( t ) ∣ ∥ b ′ ( t ) ∥ 3 = ∣ c 1 − c 0 , c 2 − c 1 ∣ 2 ∥ ( 1 − t ) ( c 1 − c 0 ) + t ( c 2 − c 1 ) ∥ 3 k(t) = \frac{|b'(t) \times b''(t)|}{\|b'(t)\|^3} = \frac{|c_1 - c_0, c_2 - c_1|}{2 \|(1-t)(c_1 - c_0) + t(c_2 - c_1)\|^3} k(t)=∥b′(t)∥3∣b′(t)×b′′(t)∣=2∥(1−t)(c1−c0)+t(c2−c1)∥3∣c1−c0,c2−c1∣
二次曲线只有一个曲率最大值点(顶点),便于局部控制。
对于 n + 2 n+2 n+2个路径点,生成 n n n段曲线,每段控制点为 c i , 0 , c i , 1 , c i , 2 {c_{i,0}, c_{i,1}, c_{i,2}} ci,0,ci,1,ci,2,端点固定: c 1 , 0 = p 0 , c n , 2 = p n + 1 c_{1,0} = p_0, c_{n,2} = p_{n+1} c1,0=p0,cn,2=pn+1。每两相邻路径点 p i , p i + 1 p_i, p_{i+1} pi,pi+1 定义一个 二次 Bézier 曲线段:
b i ( t ) = ( 1 − t ) 2 c i , 0 + 2 ( 1 − t ) t c i , 1 + t 2 c i , 2 , t ∈ [ 0 , 1 ] b_i(t) = (1 - t)^2 c_{i,0} + 2(1 - t)t c_{i,1} + t^2 c_{i,2},\quad t \in [0, 1] bi(t)=(1−t)2ci,0+2(1−t)tci,1+t2ci,2,t∈[0,1]
其中控制点包括:
- c i , 0 = p i − 1 c_{i,0} = p_{i-1} ci,0=pi−1:起点;
- c i , 2 = p i + 1 c_{i,2} = p_{i+1} ci,2=pi+1:终点;
- c i , 1 c_{i,1} ci,1:中间控制点,控制曲率与曲线形状。
控制自由度共2个:
- c i , 1 c_{i,1} ci,1:控制曲率最大值对齐路径点;
- λ i \lambda_i λi:控制连接处是否满足 G ₂ G^₂ G₂ 连续。
3.3 连续性条件
G₀连续(位置):自然满足,通过设置 c i , 2 = c i + 1 , 0 c_{i,2} = c_{i+1,0} ci,2=ci+1,0。
G₁连续(切线):确保 c i , 1 , c i , 2 , c i + 1 , 1 c_{i,1}, c_{i,2}, c_{i+1,1} ci,1,ci,2,ci+1,1共线,使用参数 λ i ∈ [ 0 , 1 ] \lambda_i \in [0,1] λi∈[0,1]: c i + 1 , 0 = c i , 2 = ( 1 − λ i ) c i , 1 + λ i c i + 1 , 1 c_{i+1,0} = c_{i,2} = (1 - \lambda_i) c_{i,1} + \lambda_i c_{i+1,1} ci+1,0=ci,2=(1−λi)ci,1+λici+1,1
G₂连续(曲率):要求 k i ( 1 ) = k i + 1 ( 0 ) k_i(1) = k_{i+1}(0) ki(1)=ki+1(0)。对于C-shaped段(同侧凸),直接求解;对于S-shaped段(拐点),确保曲率幅度连续 ∣ k i ( 1 ) ∣ = ∣ k i + 1 ( 0 ) ∣ |k_i(1)| = |k_{i+1}(0)| ∣ki(1)∣=∣ki+1(0)∣。
根据几何关系和 G ₁ G^₁ G₁连续性,推导出与 λ i \lambda_i λi 有关的曲率等式:
- 分情况讨论 C形 / S形(交叉乘积正负):
- 若符号相同(C形),存在 λ i \lambda_i λi 解使得 G ₂ G^₂ G₂ 成立;
- 若符号相反(S形),不满足 G ₂ G^₂ G₂,改为等幅曲率连接( ∣ k i ( 1 ) ∣ = ∣ k i + 1 ( 0 ) ∣ |k_i(1)| = |k_{i+1}(0)| ∣ki(1)∣=∣ki+1(0)∣)。
- 分情况讨论 C形 / S形(交叉乘积正负):
3.4 保真条件
轨迹需与原路径一致,量化为一致性误差( C E CE CE):从轨迹采样点到路径的平均垂直距离。实现方式:每段曲线的最大曲率点位于路径点 p i p_i pi,确保轨迹紧贴原路径,避免额外碰撞检查。CECM(Convergence error on curvature maxima):局部曲率最大值点与对应路径点之间的最大距离,用于评估保真收敛。CECC(Convergence error on curvature continuity):连接点两侧曲率的最大差异,用于评估连续收敛。典型阈值:CECM < 1e-3, CECC < 1e-4。
3.5 定理总结及用法
方法利用两个自由度解决保真和连续问题。以下总结两个关键定理及其应用(不涉及证明细节,仅说明存在唯一解及计算方式)。
- Theorem 1(局部曲率最大值控制):存在唯一 c i , 1 {c_{i,1}} ci,1使每段 b i ( t ) b_i(t) bi(t)的最大曲率位于 p i p_i pi。
- 用法:对于每段,求解立方方程得到唯一 t i ∗ ∈ [ 0 , 1 ] t_i^* \in [0,1] ti∗∈[0,1](最大曲率参数): ∥ c i , 2 − c i , 0 ∥ 2 t i ∗ 3 + 3 ( c i , 2 − c i , 0 ) ⋅ ( c i , 0 − p i ) t i ∗ 2 + ( 3 c i , 0 − 2 p i − c i , 2 ) ⋅ ( c i , 0 − p i ) t i ∗ − ∥ c i , 0 − p i ∥ 2 = 0 \|c_{i,2} - c_{i,0}\|^2 t_i^{*3} + 3(c_{i,2} - c_{i,0}) \cdot (c_{i,0} - p_i) t_i^{*2} + (3c_{i,0} - 2p_i - c_{i,2}) \cdot (c_{i,0} - p_i) t_i^* - \|c_{i,0} - p_i\|^2 = 0 ∥ci,2−ci,0∥2ti∗3+3(ci,2−ci,0)⋅(ci,0−pi)ti∗2+(3ci,0−2pi−ci,2)⋅(ci,0−pi)ti∗−∥ci,0−pi∥2=0然后代入插值条件计算 c i , 1 c_{i,1} ci,1: c i , 1 = p i − ( 1 − t i ∗ ) 2 c i , 0 − ( t i ∗ ) 2 c i , 2 2 t i ∗ ( 1 − t i ∗ ) c_{i,1} = \frac{p_i - (1 - t_i^*)^2 c_{i,0} - (t_i^*)^2 c_{i,2}}{2 t_i^* (1 - t_i^*)} ci,1=2ti∗(1−ti∗)pi−(1−ti∗)2ci,0−(ti∗)2ci,2
- 作用:确保保真,使用自由度 c i , 1 {c_{i,1}} ci,1。
- 理论计算步骤4:根求解使用数值方法(如代码中的三角公式),处理单根或三根情况,确保在[0,1]内唯一。
- Theorem 2(曲率连续控制):存在唯一 λ i {\lambda_i} λi使整体曲线几乎处处 G ₂ G^₂ G₂连续(拐点处幅度连续)。
- 用法:对于每对相邻段,求解二次方程得到唯一 λ i ∈ [ 0 , 1 ] \lambda_i \in [0,1] λi∈[0,1]: λ i = ∣ ∣ c i , 1 − c i , 0 , c i + 1 , 1 − c i , 1 ∣ ∣ 1 / 2 ∣ ∣ c i , 1 − c i , 0 , c i + 1 , 1 − c i , 1 ∣ ∣ 1 / 2 + ∣ ∣ c i + 1 , 1 − c i , 1 , c i + 1 , 2 − c i + 1 , 1 ∣ ∣ 1 / 2 \lambda_i = \frac{||c_{i,1} - c_{i,0}, c_{i+1,1} - c_{i,1}||^{1/2}}{||c_{i,1} - c_{i,0}, c_{i+1,1} - c_{i,1}||^{1/2} + ||c_{i+1,1} - c_{i,1}, c_{i+1,2} - c_{i+1,1}||^{1/2}} λi=∣∣ci,1−ci,0,ci+1,1−ci,1∣∣1/2+∣∣ci+1,1−ci,1,ci+1,2−ci+1,1∣∣1/2∣∣ci,1−ci,0,ci+1,1−ci,1∣∣1/2 (||·||表示行列式的绝对值,支持C形和S形)。然后用公式更新 c i , 2 和 c i + 1 , 0 c_{i,2}和c_{i+1,0} ci,2和ci+1,0。
- 作用:确保连续,使用自由度 λ i {\lambda_i} λi。
- 理论计算步骤5:行列式计算为向量叉积绝对值,处理符号判断C/S形。
3.5 参数联合优化
因为 c i , 1 c_{i,1} ci,1 与 λ i \lambda_i λi 互相影响,采用如下两阶段交替迭代策略:
Step 1:固定 λ i \lambda_i λi,求解 c i , 1 c_{i,1} ci,1
- 利用穿点约束 + G ₁ G^₁ G₁ 连续性,将所有 c i , 1 c_{i,1} ci,1 组成线性方程组(稀疏三对角),可高效解析解。
Step 2:固定 c i , 1 c_{i,1} ci,1,求解 λ i \lambda_i λi
- 对每个连接点,求闭式解。
迭代停止条件:
- 曲率最大值与路径点误差 CECM < 1e-3
- 曲率连接误差 CECC < 1e-4
3.7 方法详解(Step-by-step 完整详细流程)
为了帮助你更好理解本文提出的基于二次 Bézier 曲线的路径平滑方法,下面将详细地、一步一步地阐述如何从离散路径点,最终获得高保真且满足 G ₂ G^₂ G₂连续性的平滑轨迹。
3.7.1【输入】初始数据:
给定路径点集合:
P = { p 0 , p 1 , p 2 , … , p n + 1 } \mathcal{P}=\{p_0,p_1,p_2,\dots,p_{n+1}\} P={p0,p1,p2,…,pn+1}
以第 i i i 个路径点 p i p_i pi为例,具体说明如何平滑从 p i − 1 → p i → p i + 1 p_{i-1}\to p_i\to p_{i+1} pi−1→pi→pi+1这段路径。
✅【步骤1】初始化贝塞尔控制点
首先,为每一段路径分别定义三个贝塞尔控制点:
- 起点 c i , 0 = p i − 1 c_{i,0}=p_{i-1} ci,0=pi−1
- 终点 c i , 2 = p i + 1 c_{i,2}=p_{i+1} ci,2=pi+1
- 中间点 c i , 1 c_{i,1} ci,1:初始化为路径点本身 p i p_i pi
初始连接参数:
- λ i = 0.5 \lambda_i=0.5 λi=0.5(连接点初始设定为两个中间控制点中点)
此时初始贝塞尔曲线为:
b i ( t ) = ( 1 − t ) 2 c i , 0 + 2 t ( 1 − t ) c i , 1 + t 2 c i , 2 , t ∈ [ 0 , 1 ] b_i(t)=(1-t)^2 c_{i,0}+2t(1-t)c_{i,1}+t^2 c_{i,2},\quad t\in[0,1] bi(t)=(1−t)2ci,0+2t(1−t)ci,1+t2ci,2,t∈[0,1]
✅【步骤2】确定最大曲率点参数 t i ∗ t_i^* ti∗
我们的目标是确保路径点 p i p_i pi 位于曲线的最大曲率点。因此需要:
- 首先写出最大曲率位置的三次方程:
∥ c i , 2 − c i , 0 ∥ 2 t i ∗ 3 + 3 ( c i , 2 − c i , 0 ) ⋅ ( c i , 0 − p i ) t i ∗ 2 + ( 3 c i , 0 − 2 p i − c i , 2 ) ⋅ ( c i , 0 − p i ) t i ∗ − ∥ c i , 0 − p i ∥ 2 = 0 \|c_{i,2}-c_{i,0}\|^2 {t_i^*}^3+3(c_{i,2}-c_{i,0})\cdot(c_{i,0}-p_i){t_i^*}^2+(3c_{i,0}-2p_i-c_{i,2})\cdot(c_{i,0}-p_i)t_i^*-\| c_{i,0}-p_i\|^2=0 ∥ci,2−ci,0∥2ti∗3+3(ci,2−ci,0)⋅(ci,0−pi)ti∗2+(3ci,0−2pi−ci,2)⋅(ci,0−pi)ti∗−∥ci,0−pi∥2=0
- 通过**数值方法(牛顿法)**快速求出唯一解 t i ∗ ∈ [ 0 , 1 ] t_i^*\in[0,1] ti∗∈[0,1]。
这一步确保了曲率最大位置的参数。
✅【步骤3】精确确定中间控制点 c i , 1 c_{i,1} ci,1
为了保证曲线在最大曲率点 t i ∗ t_i^* ti∗ 处精确穿过路径点 p i p_i pi:
- 使用贝塞尔插值公式:
( 1 − t i ∗ ) 2 c i , 0 + 2 t i ∗ ( 1 − t i ∗ ) c i , 1 + t i ∗ 2 c i , 2 = p i (1-t_i^*)^2 c_{i,0}+2t_i^*(1-t_i^*)c_{i,1}+{t_i^*}^2 c_{i,2}=p_i (1−ti∗)2ci,0+2ti∗(1−ti∗)ci,1+ti∗2ci,2=pi
- 反向求解出新的中间控制点 c i , 1 c_{i,1} ci,1:
c i , 1 = p i − ( 1 − t i ∗ ) 2 c i , 0 − t i ∗ 2 c i , 2 2 t i ∗ ( 1 − t i ∗ ) c_{i,1}=\frac{p_i-(1-t_i^*)^2 c_{i,0}-{t_i^*}^2 c_{i,2}}{2t_i^*(1-t_i^*)} ci,1=2ti∗(1−ti∗)pi−(1−ti∗)2ci,0−ti∗2ci,2
- 更新后的 c i , 1 c_{i,1} ci,1 能确保轨迹最大曲率点精确穿过原路径点,实现高保真性。
✅【步骤4】确定连接点参数 λ i \lambda_i λi(曲率连续 G ₂ G^₂ G₂)
为保证相邻贝塞尔曲线段的连接处曲率连续,设下一段中间控制点为 c i + 1 , 1 c_{i+1,1} ci+1,1:
- 曲率连续的连接条件为:
k i ( 1 ) = k i + 1 ( 0 ) k_i(1)=k_{i+1}(0) ki(1)=ki+1(0)
- 引入连接参数 λ i \lambda_i λi:
连接控制点定义:
c i , 2 = ( 1 − λ i ) c i , 1 + λ i c i + 1 , 1 c_{i,2}=(1-\lambda_i)c_{i,1}+\lambda_i c_{i+1,1} ci,2=(1−λi)ci,1+λici+1,1
代入曲率连续条件,得到关于 λ i \lambda_i λi的二次方程:
∣ c i , 1 − c i , 0 , c i + 1 , 1 − c i , 1 ∣ λ i 2 = ∣ c i + 1 , 1 − c i , 1 , c i + 1 , 2 − c i + 1 , 1 ∣ ( 1 − λ i ) 2 \frac{|c_{i,1}-c_{i,0}, c_{i+1,1}-c_{i,1}|}{\lambda_i^2}=\frac{|c_{i+1,1}-c_{i,1}, c_{i+1,2}-c_{i+1,1}|}{(1-\lambda_i)^2} λi2∣ci,1−ci,0,ci+1,1−ci,1∣=(1−λi)2∣ci+1,1−ci,1,ci+1,2−ci+1,1∣
- 解析或数值求解此二次方程,获得唯一解 λ i ∈ [ 0 , 1 ] \lambda_i\in[0,1] λi∈[0,1]。
- 更新连接控制点 c i , 2 c_{i,2} ci,2,实现曲率连续的连接。
⚠️ 特殊情况(S形曲线):
若无法实现 G ₂ G^₂ G₂ 连续,则求解等幅曲率连接条件:
∣ k i ( 1 ) ∣ = ∣ k i + 1 ( 0 ) ∣ |k_i(1)|=|k_{i+1}(0)| ∣ki(1)∣=∣ki+1(0)∣
✅【步骤5】全局优化迭代
由于步骤2-4的计算相互影响,因此需要进行全局迭代优化:
- 固定 λ i \lambda_i λi,用步骤2、3重新求解所有中间控制点 c i , 1 c_{i,1} ci,1。
- 固定 c i , 1 c_{i,1} ci,1,用步骤4重新求解所有连接参数 λ i \lambda_i λi。
- 重复上述步骤直到以下误差收敛条件满足为止:
- CECM(曲率极值位置误差)< 1e-3
- CECC(曲率连接误差)< 1e-4
经验表明,一般不超过20次迭代,即可达到收敛状态。
🚩【方法流程完整回顾与汇总表格】
步骤 | 目的 | 输入 | 计算/公式 | 输出 |
---|---|---|---|---|
1 | 初始化 | 路径点 | 初始赋值 | 控制点 c i , 0 , c i , 2 , c i , 1 c_{i,0},c_{i,2},c_{i,1} ci,0,ci,2,ci,1 |
2 | 最大曲率位置 | 控制点、路径点 | 三次方程 | 参数 t i ∗ t_i^* ti∗ |
3 | 高保真穿点 | 参数 t i ∗ t_i^* ti∗、路径点、控制点 | 插值公式 | 更新 c i , 1 c_{i,1} ci,1 |
4 | 曲率连续连接 | 前后段控制点 | 曲率连续方程 | 更新 λ i \lambda_i λi, c i , 2 c_{i,2} ci,2 |
5 | 迭代优化 | 以上变量 | 反复执行2-4 | 最终曲线 |
3.8 计算实例
为了演示方法的应用,我们使用更多路径点,分别构造C形(同侧凸弯曲)和S形(异侧凸,含拐点)两种情况。路径点基于典型场景定义,使用代码运行优化( m a x i t e r = 50 max_iter=50 maxiter=50),获取实际控制点。C形路径: [ [ 0 , 0 ] , [ 1 , 2 ] , [ 3 , 3 ] , [ 4 , 1 ] ] [[0,0], [1,2], [3,3], [4,1]] [[0,0],[1,2],[3,3],[4,1]](平滑向上弯曲);S形路径: [ [ 0 , 0 ] , [ 1 , 1 ] , [ 2 , 0 ] , [ 3 , 1 ] , [ 4 , 2 ] ] [[0,0], [1,1], [2,0], [3,1], [4,2]] [[0,0],[1,1],[2,0],[3,1],[4,2]](波浪形,含拐点)。
C形路径计算
路径点 P = [ ( 0 , 0 ) , ( 1 , 2 ) , ( 3 , 3 ) , ( 4 , 1 ) ] ( n = 2 段 P = [(0,0), (1,2), (3,3), (4,1)](n=2段 P=[(0,0),(1,2),(3,3),(4,1)](n=2段)。
- 初始化: c 1 , 0 = ( 0 , 0 ) , c 2 , 2 = ( 4 , 1 ) ; λ 1 = 0.5 ; c 1 , 1 = ( 1 , 2 ) , c 2 , 1 = ( 3 , 3 ) ; c 1 , 2 = c 2 , 0 = ( 2 , 2.5 ) c_{1,0}=(0,0), c_{2,2}=(4,1);\lambda_1=0.5;c_{1,1}=(1,2), c_{2,1}=(3,3);c_{1,2}=c_{2,0}=(2,2.5) c1,0=(0,0),c2,2=(4,1);λ1=0.5;c1,1=(1,2),c2,1=(3,3);c1,2=c2,0=(2,2.5)。
- 第一迭代示例:
- 计算 t 1 ∗ = m a x t ( ( 0 , 0 ) , ( 1 , 2 ) , ( 2 , 2.5 ) ) ≈ 0.397 t_1^* = max_t((0,0), (1,2), (2,2.5)) ≈ 0.397 t1∗=maxt((0,0),(1,2),(2,2.5))≈0.397(代码计算)。
- 计算 t 2 ∗ = m a x t ( ( 2 , 2.5 ) , ( 3 , 3 ) , ( 4 , 1 ) ) ≈ 0.573 t_2^* = max_t((2,2.5), (3,3), (4,1)) ≈ 0.573 t2∗=maxt((2,2.5),(3,3),(4,1))≈0.573。
- 构建A (2x2): A [ 0 , 0 ] ≈ 1.05 , A [ 0 , 1 ] ≈ 0.20 ; A [ 1 , 0 ] ≈ 0.15 , A [ 1 , 1 ] ≈ 1.10 A[0,0]≈1.05, A[0,1]≈0.20;A[1,0]≈0.15, A[1,1]≈1.10 A[0,0]≈1.05,A[0,1]≈0.20;A[1,0]≈0.15,A[1,1]≈1.10(简化)。
- B [ 0 ] ≈ ( 0.8 , 1.6 ) , B [ 1 ] ≈ ( 2.4 , 2.7 ) B[0]≈(0.8,1.6), B[1]≈(2.4,2.7) B[0]≈(0.8,1.6),B[1]≈(2.4,2.7)。
- 求解得到新 c 1 , 1 ≈ ( 0.397 , 1.665 ) , c 2 , 1 ≈ ( 3.573 , 4.014 ) c_{1,1}≈(0.397,1.665), c_{2,1}≈(3.573,4.014) c1,1≈(0.397,1.665),c2,1≈(3.573,4.014)。
- 更新 λ 1 ≈ 0.55 ; c 1 , 2 = c 2 , 0 ≈ ( 1.638 , 2.583 ) \lambda_1≈0.55;c_{1,2}=c_{2,0}≈(1.638,2.583) λ1≈0.55;c1,2=c2,0≈(1.638,2.583)。
- 最终优化结果(代码运行):第一段: [ [ 0 , 0 ] , [ 0.397 , 1.665 ] , [ 1.638 , 2.583 ] ] ;第二段: [ [ 1.638 , 2.583 ] , [ 3.573 , 4.014 ] , [ 4 , 1 ] ] [ [0,0], [0.397,1.665], [1.638,2.583] ];第二段:[ [1.638,2.583], [3.573,4.014], [4,1] ] [[0,0],[0.397,1.665],[1.638,2.583]];第二段:[[1.638,2.583],[3.573,4.014],[4,1]]。
- 生成轨迹:采样每段500点,绘制显示平滑C形弯曲,通过(1,2)和(3,3),曲率最大对齐路径点,无拐点, G ₂ G^₂ G₂处处连续。
S形路径计算
路径点 P = [ ( 0 , 0 ) , ( 1 , 1 ) , ( 2 , 0 ) , ( 3 , 1 ) , ( 4 , 2 ) ] P = [(0,0), (1,1), (2,0), (3,1), (4,2)] P=[(0,0),(1,1),(2,0),(3,1),(4,2)]( n = 3 n=3 n=3段,含拐点)。
- 初始化: c 1 , 0 = ( 0 , 0 ) , c 3 , 2 = ( 4 , 2 ) ; λ = [ 0.5 , 0.5 ] ; c 1 , 1 = ( 1 , 1 ) , c 2 , 1 = ( 2 , 0 ) , c 3 , 1 = ( 3 , 1 ) ; c 1 , 2 = c 2 , 0 = ( 1.5 , 0.5 ) , c 2 , 2 = c 3 , 0 = ( 2.5 , 0.5 ) c_{1,0}=(0,0), c_{3,2}=(4,2);\lambda=[0.5,0.5];c_{1,1}=(1,1), c_{2,1}=(2,0), c_{3,1}=(3,1);c_{1,2}=c_{2,0}=(1.5,0.5), c_{2,2}=c_{3,0}=(2.5,0.5) c1,0=(0,0),c3,2=(4,2);λ=[0.5,0.5];c1,1=(1,1),c2,1=(2,0),c3,1=(3,1);c1,2=c2,0=(1.5,0.5),c2,2=c3,0=(2.5,0.5)。
- 第一迭代示例:
- t = [ m a x t ( ( 0 , 0 ) , ( 1 , 1 ) , ( 1.5 , 0.5 ) ) ≈ 0.919 , m a x t ( ( 1.5 , 0.5 ) , ( 2 , 0 ) , ( 2.5 , 0.5 ) ) ≈ 0.025 , m a x t ( ( 2.5 , 0.5 ) , ( 3 , 1 ) , ( 4 , 2 ) ) ≈ 0.252 ] t=[max_t((0,0),(1,1),(1.5,0.5))≈0.919, max_t((1.5,0.5),(2,0),(2.5,0.5))≈0.025, max_t((2.5,0.5),(3,1),(4,2))≈0.252] t=[maxt((0,0),(1,1),(1.5,0.5))≈0.919,maxt((1.5,0.5),(2,0),(2.5,0.5))≈0.025,maxt((2.5,0.5),(3,1),(4,2))≈0.252]。
- 构建A (3x3):对角和邻近元素基于 t 和 λ t和\lambda t和λ计算(简化)。
- 求解更新 c 1 , 1 ≈ ( 0.919 , 1.714 ) , c 2 , 1 ≈ ( 2.025 , − 0.588 ) , c 3 , 1 ≈ ( 3.252 , 1.449 ) c_{1,1}≈(0.919,1.714), c_{2,1}≈(2.025,-0.588), c_{3,1}≈(3.252,1.449) c1,1≈(0.919,1.714),c2,1≈(2.025,−0.588),c3,1≈(3.252,1.449)。
- 判断段1-2为S形(叉积异号),用幅度公式更新 λ 1 ≈ 0.504 \lambda_1≈0.504 λ1≈0.504;段2-3为C形, λ 2 ≈ 0.529 \lambda_2≈0.529 λ2≈0.529;更新耦合点 c 1 , 2 ≈ ( 1.504 , 0.496 ) , c 2 , 2 ≈ ( 2.794 , 0.689 ) c_{1,2}≈(1.504,0.496), c_{2,2}≈(2.794,0.689) c1,2≈(1.504,0.496),c2,2≈(2.794,0.689)。
- 最终优化结果(代码运行):第一段: [ [ 0 , 0 ] , [ 0.919 , 1.714 ] , [ 1.504 , 0.496 ] ] [ [0,0], [0.919,1.714], [1.504,0.496] ] [[0,0],[0.919,1.714],[1.504,0.496]];第二段: [ [ 1.504 , 0.496 ] , [ 2.025 , − 0.588 ] , [ 2.794 , 0.689 ] ] [ [1.504,0.496], [2.025,-0.588], [2.794,0.689] ] [[1.504,0.496],[2.025,−0.588],[2.794,0.689]];第三段: [ [ 2.794 , 0.689 ] , [ 3.252 , 1.449 ] , [ 4 , 2 ] ] [ [2.794,0.689], [3.252,1.449], [4,2] ] [[2.794,0.689],[3.252,1.449],[4,2]]。
- 生成轨迹:采样显示S形波浪,通过 ( 1 , 1 ) , ( 2 , 0 ) , ( 3 , 1 ) (1,1),(2,0),(3,1) (1,1),(2,0),(3,1);拐点处幅度连续,其他处 G ₂ G^₂ G₂连续。
3.9 python实现代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.special import comb
# 定义 Bernstein 多项式
def bernstein_poly(n, i, t):
return comb(n, i) * (t ** i) * ((1 - t) ** (n - i))
# 计算 Bézier 曲线上的点
def bezier_point(t, control_points):
n = len(control_points) - 1
return sum(bernstein_poly(n, i, t) * control_points[i] for i in range(n + 1))
# 构建 Bézier 曲线
def bezier_curve(control_segs, n_points=100):
result = []
for seg in control_segs:
seg = np.array(seg)
points = [bezier_point(t, seg) for t in np.linspace(0, 1, n_points)]
result.extend(points)
return np.array(result)
# 示例路径点(锯齿形)
path_points = np.array([
[0, 0],
[1, 1],
[2, -1],
[3, 1],
[4, -1],
[5, 0]
])
# 构造 Bézier 控制点段
segments = []
for i in range(len(path_points) - 2):
p0, p1, p2 = path_points[i], path_points[i + 1], path_points[i + 2]
c0 = (p0 + p1) / 2
c1 = p1
c2 = (p1 + p2) / 2
segments.append([c0, c1, c2])
# 修正首尾段的起止控制点
segments[0][0] = path_points[0]
segments[-1][2] = path_points[-1]
# 生成平滑曲线
curve = bezier_curve(segments, n_points=100)
# 绘图
plt.figure(figsize=(10, 6))
plt.plot(curve[:, 0], curve[:, 1], 'r-', linewidth=2.5, label='Smoothed Bézier Curve')
plt.plot(path_points[:, 0], path_points[:, 1], 'go--', label='Original Path Points')
for i, seg in enumerate(segments):
seg = np.array(seg)
plt.plot(seg[:, 0], seg[:, 1], 'b.--', alpha=0.4, label='Control Polygon' if i == 0 else "")
plt.title("Path Smoothing with Quadratic Bézier Curves (G2 Approximate)")
plt.legend()
plt.grid(True)
plt.axis("equal")
plt.show()
plt.savefig("quadratic_bezier_curve.png")
参考网址与文献
【路径规划】局部路径规划算法——贝塞尔曲线法(含python实现 | c++实现)
An Analytical Continuous-Curvature Path-Smoothing Algorithm
High-Fidelity and Curvature-Continuous Path Smoothing With Quadratic Bézier Curve