scipy 2D轨迹点的鲁棒样条拟合

d5vmydt9  于 2022-11-10  发布在  其他
关注(0)|答案(2)|浏览(185)

我有一些2D轨迹的数据。我想用一条插值曲线来拟合这些数据,并且我认为在每组三个连续点之间使用三次样条曲线会很好。但是,我对scipy中的插值函数有点困惑。以下是我目前拥有的数据:

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

xs = [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 = [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]

tck, u = interpolate.splprep([xs, ys])
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()

结果如下:x1c 0d1x
现在我明白了一切都像广告上说的那样工作,但我想知道是否有某种方法可以对生成的样条曲线的导数或平滑度设置一个限制,以消除像那个丑陋的尖峰这样的伪像。
非常感谢!

0yg35tkg

0yg35tkg1#

问题是你的最后五个点(下面标记为绿色)的顺序“不正确”,样条需要按照这个顺序排列。如果你按照ys值排列你的最后五个点,并稍微调整一下你的s值,你会得到以下结果。

idx = np.arange(ys.shape[0])
idx[-5:] = np.flip(idx[-5:][np.argsort(ys[-5:])])

tck, u = interpolate.splprep([xs[idx], ys[idx]], s=40)
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.scatter(xs[-5:], ys[-5:], c='g')

所以你是说我必须手动删除/排序异常值?

不。问题是样条插值要遵守顺序。所以它不是最接近你的点的线,而是它首先接近你的第一个点,然后接近你的第二个点,等等。一个解决方案是像我上面做的那样对你的点排序。或者手动做,你可以使用启发式。由于我们从一个非常合理的排序开始,局部优化应该给予一个更好的可能最优的排序。我所说的局部优化是指,如果两个点之间的距离缩短,我就交换两个点,直到不再有两个点这样做为止。

def combinations(arr):
    n = arr.shape[0]
    upper = np.tri(n,n,-1,dtype='bool').T
    a,b = np.meshgrid(arr,arr)
    return b[upper].reshape(-1), a[upper].reshape(-1)

idx = np.arange(xs.shape[0])
grid = np.meshgrid(idx,idx)
distances = ((xs[grid[0]]-xs[grid[1]])**2+(ys[grid[0]]-ys[grid[1]])**2)**(1/2)

idx = np.arange(distances.shape[0])

def local_opt(idx, dist):
    current_distance = calc_dist(idx, dist)
    for i,j in itertools.combinations(idx, 2):
            idx[i], idx[j] = idx[j], idx[i]
            if calc_dist(idx, dist) < current_distance:
                return local_opt(idx,dist)
            idx[i], idx[j] = idx[j], idx[i]
    return idx

def calc_dist(idx, dist):
    return dist[idx[:-1],np.roll(idx,-1)[:-1]].sum()

idx = local_opt(idx,distances)
z9smfwbn

z9smfwbn2#

尝试单调插值,例如Pchip或Akima 1DInterpolator。第一步可能是对曲线进行参数化(例如通过笛卡尔距离确定线长---但你需要使用各种参数化,因为它们会产生不同的曲线),然后使用单调插值。它们支持多维y值,因此你可以对坐标和弧长进行插值。
或者,您可以使用splreps参数来控制平滑量。

相关问题