如何用Python(优先SciPy)生成指定曲率与首尾切线的三次样条?
实现带切线约束与曲率限制的三次样条曲线(SciPy版)
嘿,我来帮你搞定这个带约束的三次样条需求!首先得提个关键注意点:你给的路点里x坐标有重复的情况(比如前两个点x都是-18),直接用x作为自变量插值会出问题,所以咱们得用参数化样条——用一个参数t遍历每个路点,分别对x(t)和y(t)做插值,这样就能完美处理非单调的路径了。
下面是完整的可运行代码,包含切线约束、曲率计算和可视化:
import numpy as np from scipy.interpolate import CubicSpline import matplotlib.pyplot as plt # 给定的路点数组 waypoints = np.array([ [-18., -20.], [-18., 18.], [-14., 18.], [-14., -18.], [-10., -18.], [-10., 18.], [ -6., 18.], [ -6., -18.], [ -2., -18.], [ -2., 18.], [ 2., 18.], [ 2., -18.], [ 6., -18.], [ 6., 18.], [ 10., 18.], [ 10., -18.], [ 14., -18.], [ 14., 18.], [ 18., 18.], [ 18., -20.] ]) # 1. 设置参数化变量t,每个路点对应一个t值(从0到19) t = np.arange(len(waypoints)) x_coords = waypoints[:, 0] y_coords = waypoints[:, 1] # 2. 定义起点和终点的切线约束(一阶导数) # 起点切线:沿第一个点到第二个点的方向 dx_start = x_coords[1] - x_coords[0] dy_start = y_coords[1] - y_coords[0] # 终点切线:沿倒数第二个点到最后一个点的方向 dx_end = x_coords[-1] - x_coords[-2] dy_end = y_coords[-1] - y_coords[-2] # 3. 创建三次样条,指定边界导数约束 # 对x(t)和y(t)分别构建样条 cs_x = CubicSpline(t, x_coords, bc_type=((1, dx_start), (1, dx_end))) cs_y = CubicSpline(t, y_coords, bc_type=((1, dy_start), (1, dy_end))) # 4. 生成密集采样点用于绘制曲线 t_dense = np.linspace(0, t[-1], 1000) x_spline = cs_x(t_dense) y_spline = cs_y(t_dense) # 5. 计算并检查曲率 # 求一阶、二阶导数 x1 = cs_x(t_dense, 1) # x'(t) x2 = cs_x(t_dense, 2) # x''(t) y1 = cs_y(t_dense, 1) # y'(t) y2 = cs_y(t_dense, 2) # y''(t) # 参数化曲线的曲率公式 curvature = np.abs(x1 * y2 - x2 * y1) / (x1**2 + y1**2)**(1.5) # 设置你的曲率阈值,这里示例用0.1,可按需修改 curvature_threshold = 0.1 max_curv = np.max(curvature) print(f"曲线最大曲率: {max_curv:.4f}") if max_curv > curvature_threshold: print(f"⚠️ 警告:最大曲率超过设定阈值 {curvature_threshold}") else: print(f"✅ 曲率符合要求,未超过阈值 {curvature_threshold}") # 6. 可视化结果 plt.figure(figsize=(10, 8)) plt.scatter(x_coords, y_coords, color='#ff4444', s=60, label='原始路点') plt.plot(x_spline, y_spline, color='#2196f3', linewidth=2, label='三次样条曲线') plt.xlabel('X 坐标') plt.ylabel('Y 坐标') plt.title('带切线约束的参数化三次样条曲线') plt.legend() plt.grid(True, linestyle='--', alpha=0.7) plt.axis('equal') # 保持坐标轴比例,避免曲线变形 plt.show()
关键部分解释
参数化样条:
因为你的路点x坐标不是严格单调的(比如前两个点x都是-18),直接用x作为自变量插值y会触发SciPy的错误。用参数t来分别拟合x(t)和y(t),就能轻松处理这种上下往复的路径。切线约束实现:
通过CubicSpline的bc_type参数,我们指定了起点和终点的一阶导数(切线方向)。这里直接用相邻路点的差值作为导数,完全符合你要求的“起点切线经过起点和下一个点,终点切线经过前一个点和终点”的约束。曲率计算与检查:
用参数化曲线的曲率公式计算每一点的曲率,然后和你设定的阈值对比。如果曲率超标,你可以考虑:- 在曲率过大的路段增加过渡路点,让曲线更平滑;
- 调整边界导数的大小(如果允许的话);
- 改用B样条并调整节点间距,但三次样条已经足够满足大多数平滑需求。
内容的提问来源于stack exchange,提问作者cisprague
相关产品推荐
相关产品推荐

