You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二维轨迹点鲁棒样条拟合 如何消除scipy插值的异常尖峰

你碰到的突兀尖峰是scipy.interpolate.splprep默认参数下强制插值过所有点导致的:默认参数s=0时,曲线必须严格经过每一个输入坐标点,当输入点存在局部噪声、相邻点间距突变时,三次样条会为了满足过点条件产生不必要的振荡,也就是你看到的异常尖峰。以下是几种可落地的解决方法:

方案1:调整平滑参数s

直接在splprep调用时增加s平滑因子参数,取值可根据需求调整,取值越大曲线越平滑,允许偏离原始点的幅度越大:

import numpy as np
import scipy.interpolate as interpolate
import matplotlib.pyplot as plt

xs = np.array([398.01543948, 400.99034244, 402.36995272, 401.05813953, 398.65277778,
 395.97260274, 393.08474576, 390.325, 387.42105263, 384.17073171,
 380.42028986, 377.20754717, 373.80769231, 370.04545455, 366.796875,
 363.33823529, 359.63636364, 356.22033898, 352.95555556, 349.41176471,
 345.87878788, 341.89189189, 337.91666667, 334.84482759, 331.60273973,
 328, 296.51515152, 293.91176471, 290.31111111, 287.16666667,
 283.97222222, 281.56, 278.79591837, 276.32631579, 273.65195849,
 271.53191489, 270.25503356, 279.75497404, 276.09359445, 270.42035064,
 298.7761194, 298.74285714])
ys = np.array([172.76204179, 176.63910967, 179.49095377, 180.34710744, 180.82075472,
 181.04255319, 181.07368421, 181.06382979, 181.1875, 181.21875,
 181.15909091, 181.06410256, 181, 181.01923077, 180.84615385,
 180.8125, 180.69135802, 180.69565217, 180.68, 180.68571429,
 180.69117647, 180.62264151, 180.87323944, 180.71666667, 180.63333333,
 180.64788732, 181.4137931, 180.94871795, 181.31372549, 181.25,
 181.02, 180.53030303, 180.63541667, 180.73529412, 180.72631579,
 180.00884956, 179.03915758, 172.17751105, 171.69548635, 173.73376084,
 156.93617021, 156.52671756])

# 调整s参数,这里设置为10,可根据实际效果调整
tck, u = interpolate.splprep([xs, ys], s=10)
x_i, y_i = interpolate.splev(np.linspace(0, 1, 100), tck)

plt.plot(x_i, y_i)
plt.scatter(xs, ys, c='r')
plt.show()

s参数本质是控制拟合误差的权重:s=0时误差为0必须过所有点,s>0时允许拟合误差总和不超过s,以此换取更平滑的曲线,对带噪声的轨迹数据鲁棒性更好。

方案2:显式控制导数/曲率限制

如果需要精细控制样条的一阶、二阶导数连续性(对应限制曲率突变),可以用make_interp_spline配合弦长参数化实现:

# 先计算弦长参数t,比默认均匀参数化更适配轨迹数据特征
t = np.zeros(len(xs))
t[1:] = np.cumsum(np.sqrt(np.diff(xs)**2 + np.diff(ys)**2))
t /= t[-1] # 归一化到0-1区间

# 构造自然三次样条,bc_type='natural'代表边界二阶导数为0,曲率更平滑
spl = interpolate.make_interp_spline(t, np.c_[xs, ys], k=3, bc_type='natural')
t_new = np.linspace(0,1,100)
pts = spl(t_new)
x_i, y_i = pts[:,0], pts[:,1]

bc_type还支持自定义端点导数取值,可根据需求限制曲线变化速率、曲率的上下限。

方案3:分段局部插值避免全局振荡

如果你的需求是每三个连续点之间做局部三次插值,不想全局插值带来的远距离点影响当前段形状,可以用分段处理逻辑:

x_i = []
y_i = []
step = 20 # 每段插值的点数
for i in range(len(xs)-2):
    x_seg = xs[i:i+3]
    y_seg = ys[i:i+3]
    t_seg = np.linspace(0,1,3)
    # 二次样条适配三点插值
    spl_seg = interpolate.make_interp_spline(t_seg, np.c_[x_seg, y_seg], k=2)
    # 最后一段保留尾点
    t_new = np.linspace(0,1,step) if i != len(xs)-3 else np.linspace(0,1,step)
    seg_pts = spl_seg(t_new)
    x_i.extend(seg_pts[:,0])
    y_i.extend(seg_pts[:,1])
# 处理拼接处的重复点
x_i = np.array(x_i)
y_i = np.array(y_i)

这种局部插值的方式不会因为远处的点影响当前段的曲线形状,不会出现全局振荡的突兀尖峰。

内容的提问来源于stack exchange,提问作者Mr Squid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 19:39:02