如何在三点间绘制平滑曲线?Scipy Spline插值峰值偏移问题
嘿,我完全懂你遇到的问题——用Scipy的普通样条插值时,曲线的最高点居然跑到(3,5)前面去了,曲线形态直接失衡,这确实挺让人头疼的。其实问题出在普通样条插值的默认行为上,它会尽量贴合数据点,但如果只有这三个控制点,很容易出现偏移。下面给你两种靠谱的解决方案,保证曲线完美符合你的需求:
方案1:带约束的三次样条插值(适合后续扩展控制点)
如果之后你可能需要添加更多的控制点,或者想要更灵活的平滑曲线,推荐用Scipy的make_interp_spline函数,它支持设置导数约束,能强制让(3,5)成为曲线的最高点(极值点)。
具体代码如下:
import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import make_interp_spline # 你的原始控制点 x_points = np.array([1, 3, 5]) y_points = np.array([0, 5, 0]) # 创建带约束的三次样条:指定起点和终点的一阶导数为0(让曲线两端平缓) # 中间点(3,5)会自动成为极值点,因为三次样条在对称控制点+两端导数为0的约束下,必然在中间达到峰值 spline = make_interp_spline(x_points, y_points, k=3, bc_type=((1, 0), (1, 0))) # 生成密集的x值来绘制平滑曲线 x_smooth = np.linspace(1, 5, 100) y_smooth = spline(x_smooth) # 绘图展示 plt.plot(x_smooth, y_smooth, label='Constrained Cubic Spline', linewidth=2) plt.scatter(x_points, y_points, color='red', s=50, label='Control Points') plt.xlabel('X Axis') plt.ylabel('Y Axis') plt.legend() plt.show()
这段代码里,bc_type=((1, 0), (1, 0))表示给起点(x=1)和终点(x=5)设置一阶导数为0,这样曲线在两端会平缓过渡,同时因为控制点是对称的,三次样条会自然在(3,5)处达到最高点,不会出现偏移。
方案2:二次曲线拟合/插值(最适合当前三个点的场景)
你的三个点刚好是对称的,完全可以用二次抛物线来拟合——这是最直接、最准确的方案,曲线天然经过所有点,且最高点严格在(3,5)。
代码示例:
import numpy as np import matplotlib.pyplot as plt # 原始控制点 x_points = np.array([1, 3, 5]) y_points = np.array([0, 5, 0]) # 定义二次曲线方程:根据三个点推导出来的,y = -5/4*(x-3)² +5 def quadratic_curve(x): return -5/4 * (x - 3)**2 + 5 # 生成密集x值 x_smooth = np.linspace(1, 5, 100) y_smooth = quadratic_curve(x_smooth) # 绘图 plt.plot(x_smooth, y_smooth, label='Quadratic Curve', linewidth=2) plt.scatter(x_points, y_points, color='red', s=50, label='Control Points') plt.xlabel('X Axis') plt.ylabel('Y Axis') plt.legend() plt.show()
如果你想用Scipy的插值函数来实现同样的效果,也可以用interp1d指定kind='quadratic',因为三个点的二次插值就是准确的抛物线:
from scipy.interpolate import interp1d # 创建二次插值函数 f = interp1d(x_points, y_points, kind='quadratic') y_smooth = f(x_smooth)
总结
- 如果只是处理当前这三个对称点,二次曲线方案是最优选择,简单准确,没有任何偏移问题;
- 如果后续需要添加更多控制点,或者需要更复杂的平滑曲线,就用带约束的三次样条插值,通过设置导数条件来保证关键节点的形态。
内容的提问来源于stack exchange,提问作者Claudiu Creanga
相关产品推荐
相关产品推荐

