样条插值曲线类型及其优缺点说明

本文深入解析了Spline的不同类型,包括Natural Spline、Catmull-Rom Spline、Cubic B-Spline等,提供了如何使用它们的方法,并详细阐述了各自的优缺点,帮助开发者根据需求选择合适的Spline类型。

Spline Types

This page gives a breakdown of each spline type, how to use each one, and the advantages/disadvantages of each type.

Tl;dr

If you need the spline to pass through the input points, start with a Catmull-Rom Spline. If you don't, start with a Cubic B-Spline.

Simple Types

If you're not sure which one to use, start with these three.

Natural Spline

The Natural Spline computes the curvature for each point, using a formula that involves every point in the input, then interpolates the spline based on the list of points and the corresponding list of curvatures.

To use, import the appropriate header: #include "spline_library/natural/natural_spline.h"

Create a Natural Spline by passing a std::vector to the constructor, containing a list of points to interpolate through:std::shared_ptr<Spline> mySpline(new NaturalSpline(myPointList));

Advantages
  • Curvature is continuous (?)
Disadvantages
  • No local control (?)

Catmull-Rom Spline

A Catmull-Rom Spline computes the tangent for a point from the positions of the two closest points, then interpolates based on both the position and the tangent.

To use, import the appropriate header: #include "spline_library/hermite/cubic/cr_spline.h"

Create a catmull-rom spline by passing a std::vector to the constructor, containing a list of points to interpolate through:std::shared_ptr<Spline> mySpline(new CRSpline(myPointList));

Advantages
  • Local control (?)
Disadvantages
  • Curvature isn't continuous (?). For some use cases this isn't a problem, so I wouldn't worry about it unless you know you need it to be continuous.
  • There must be a nonzero distance between each adjacent set of points
  • Non-looping variation requires an "extra" point on either end of the data set which will not be interpolated

Cubic B-Spline

The B-Spline (Basis Spline) is very similar in concept to the Bezier Curve, and the cubic B-Spline is a specific type of B-Spline.

It is possible to create B-Splines with arbitrary powers (as opposed to enforcing cubic) but enforcing cubic allows for much simpler formulas and better performance.

To use, import the appropriate header: #include "spline_library/basis/cubic_b_spline.h"

Create a Cubic B-Spline by passing a std::vector to the constructor, containing a list of control points:std::shared_ptr<Spline> mySpline(new CubicBSpline(myPointList));

Advantages
  • Local control (?)
  • Curvature is continuous (?)
Disadvantages
  • The interpolated line does not necessarily pass through the specified points
  • Non-looping variation requires an "extra" point on either end of the data set which will not be interpolated

Advanced Types

If one of the simple types above doesn't meet your needs, the following types are also available. These spline types are more powerful, but also more difficult to use correctly, and/or carry important caveats that may not be immediately obvious.

Centripetal Catmull-Rom Spline

The Centripetal CR Spline is a variation of the Catmull-Rom Spline formula. Instead of spacing each point exactly one T apart, the distance in T between any two points will be proportional to the square root of distance between the two points. Thus, points that are very far apart will be further apart in T than points that are close together.

To use it, provide a value for the optional alpha parameter in the CRSpline constructor. A value of 0.5 will produce a centripetal Catmull-Rom Spline, while a value of 0.0 (default) will revert to the standard formula. Other values are allowed too - a value of 1.0 will result in a "chordal" variation, and the formula will work with any number, negative or positive. Values other than 0.0 or 0.5 should be very rare, however.

It has been proven mathematically that the centripetal variation avoids certain types of self-intersections, cusps, and overshoots, producing a more aesthetically pleasing spline.

Advantages (compared to CRSpline)
  • Proven to avoid self-intersections and overshoots when there are large variations in distance between adjacent points.
Disadvantages (compared to CRSpline)
  • Modifies T values of points - points that are close together will have a smaller T distance and vice versa. This may be a problem if the points are keyframes for an animation, for example, or any other data series where the T values have some external meaning

Cubic Hermite Spline

The Cubic Hermite Spline takes a list of points, and a corresponding list of tangents for each point. The Catmull-Rom Spline is a subclass of the Cubic Hermite Spline which automatically computes the tangents, rather than expecting the user to supply them.

An example use case for this spline type is for physical simulation time series data, where spline->getPosition(t) returns the object's position at time T. If you know the object's velocity in addition to its position, you can make the interpolation more accurate by providing that velocity as the tangent.

To use, import the appropriate header: #include "spline_library/hermite/cubic/cubic_hermite_spline.h"

Create a Cubic Hermite Spline by passing two equal-length std::vector to the constructor, one containing a list of points to interpolate through, and the other containing the corresponding tangent for each point: std::shared_ptr<Spline> mySpline(new CubicHermiteSpline(myPointList, myTangentList));

Advantages
  • Local control (?)
  • Easily control the tangent at each point
Disadvantages
  • Curvature isn't continuous (?). For some use cases this isn't a problem, so I wouldn't worry about it unless you know you need it to be continuous.
  • You cannot create a spline where there is zero distance between two adjacent points
  • Cannot be used if you don't know the desired tangent for each point

Quintic Hermite Spline

The Quintic Hermite Spline takes a list of points, a corresponding list of tangents for each point, and a corresponding list of curvatures for each point.

An example use case for this spline type is for physical simulation time series data, where spline->getPosition(t) returns the object's position at time T. If you know the object's acceleration in addition to its velocity and position, you can make the interpolation more accurate by providing that acceleration for the curvature.

To use, import the appropriate header: #include "spline_library/hermite/quintic/quintic_hermite_spline.h"

Create a Quintic Hermite Spline by passing three equal-length std::vector to the constructor, one containing a list of points to interpolate through, another containing the corresponding tangent for each point, and a third containing the corresponding curvature for each point: std::shared_ptr<Spline> mySpline(new QuinticHermiteSpline(myPointList, myTangentList, myCurvatureList));

Advantages
  • Local control (?)
  • Easily control the tangent and curvature at each point
  • Curvature is continuous (?)
Disadvantages
  • You cannot create a spline where there is zero distance between two adjacent points
  • Cannot be used if you don't know the desired tangent and curvature for each point
  • More computationally intensive than the cubic version

  • More "wiggly" than the cubic version. This sounds vague, but it's actually quantifiable: For the cubic version, the derivative of curvature is constant, but for the quintic version, the derivative of curvature is a quadractic function.


本文转自:
https://github.com/ejmahler/SplineLibrary/blob/master/docs/SplineTypes.md,代码也在这里哦

### 三次样条插值的C语言实现及详细说明 在处理一维数据插值问题时,**三次样条插值(Cubic Spline Interpolation)**是一种广泛应用的插值方法,它通过构建分段的三次多项式函数,使得插值结果不仅在节点上连续,还具有连续的一阶和二阶导数,从而保证了插值曲线的平滑性[^1]。该方法适用于对精度和平滑性要求较高的数据拟合任务,例如机器人路径规划、金融数据插值等场景。 #### 插值原理 三次样条插值的基本思想是:在每个区间 $[x_i, x_{i+1}]$ 上构造一个三次多项式 $S_i(x)$,使得插值函数 $S(x)$ 满足以下条件: 1. $S(x_i) = y_i$,即插值函数在已知点上的值与原始数据一致; 2. $S(x)$ 在整个区间上具有连续的一阶导数和二阶导数; 3. 边界条件通常采用自然边界条件(二阶导数为0)或周期边界条件。 设插值节点为 $x_0, x_1, \dots, x_n$,对应的函数值为 $y_0, y_1, \dots, y_n$,定义 $h_i = x_{i+1} - x_i$,则三次样条插值函数可表示为: $$ S_i(x) = a_i(x - x_i)^3 + b_i(x - x_i)^2 + c_i(x - x_i) + d_i $$ 其中,$d_i = y_i$,其他系数 $a_i, b_i, c_i$ 通过求解三对角方程组得到。 #### 算法步骤 1. 构造三对角矩阵并求解二阶导数 $M_i$; 2. 根据 $M_i$ 计算各区间上的插值系数; 3. 对任意 $x$ 找到其所在的区间 $[x_i, x_{i+1}]$; 4. 利用插值公式计算该点的插值结果。 #### C语言实现 以下是一个完整的C语言实现示例,用于计算给定节点上的三次样条插值函数,并对任意点进行插值: ```c #include <stdio.h> #include <stdlib.h> #define N 4 // 数据点个数 // 三对角矩阵求解函数(追赶法) void tridiagonal_solve(double a[], double b[], double c[], double d[], double x[], int n) { double *cp = (double *)malloc(n * sizeof(double)); double *dp = (double *)malloc(n * sizeof(double)); cp[0] = c[0] / b[0]; dp[0] = d[0] / b[0]; for (int i = 1; i < n; i++) { double denom = b[i] - a[i] * cp[i - 1]; cp[i] = c[i] / denom; dp[i] = (d[i] - a[i] * dp[i - 1]) / denom; } x[n - 1] = dp[n - 1]; for (int i = n - 2; i >= 0; i--) { x[i] = dp[i] - cp[i] * x[i + 1]; } free(cp); free(dp); } // 三次样条插值函数 void cubic_spline_interpolate(double x[], double y[], int n, double xx, double *yy) { double *h = (double *)malloc((n - 1) * sizeof(double)); double *a = (double *)malloc((n - 1) * sizeof(double)); double *b = (double *)malloc(n * sizeof(double)); double *c = (double *)malloc((n - 1) * sizeof(double)); double *d = (double *)malloc(n * sizeof(double)); double *M = (double *)malloc(n * sizeof(double)); // 计算步长 for (int i = 0; i < n - 1; i++) { h[i] = x[i + 1] - x[i]; } // 构造三对角矩阵的系数 for (int i = 1; i < n - 1; i++) { a[i] = h[i - 1]; b[i] = 2 * (h[i - 1] + h[i]); c[i] = h[i]; d[i] = 6 * ((y[i + 1] - y[i]) / h[i] - (y[i] - y[i - 1]) / h[i - 1]); } // 自然边界条件 M[0] = M[n-1] = 0 b[0] = 1; b[n - 1] = 1; // 求解 M tridiagonal_solve(a, b, c, d, M, n); // 查找区间 int k = 0; while (xx > x[k + 1] && k < n - 2) { k++; } // 插值计算 double t = (xx - x[k]) / h[k]; *yy = (1 - 3 * t * t + 2 * t * t * t) * y[k] + (3 * t * t - 2 * t * t * t) * y[k + 1] + (t - 2 * t * t + t * t * t) * h[k] * M[k] + (-t * t + t * t * t) * h[k] * M[k + 1]; } int main() { double x[N] = {0.0, 1.0, 2.0, 3.0}; double y[N] = {0.0, 0.8415, 0.9093, 0.1411}; // sin(x)值 double xx = 1.5; double yy; cubic_spline_interpolate(x, y, N, xx, &yy); printf("插值点 x = %f 的值为 y = %f\n", xx, yy); return 0; } ``` #### 算法说明 - **三对角矩阵求解**:采用追赶法(Thomas算法)求解三对角方程组,效率高且适用于样条插值问题。 - **自然边界条件**:设两端点的二阶导数为0,适用于大多数非周期性插值问题。 - **插值计算**:根据Hermite插值公式,利用已知节点值和二阶导数计算任意点的插值结果。 #### 适用场景 - 机器人路径规划:平滑路径生成,避免突变。 - 数据拟合与信号处理:填补缺失数据,提高数据质量。 - 图形学与动画:生成平滑曲线,提高视觉效果。 #### 优缺点 | 特性 | 优点 | 缺点 | |--------------|------------------------------|----------------------------------| | 插值精度 | 高 | 实现较复杂,计算量较大 | | 平滑性 | 曲线连续且光滑 | 对边界条件敏感 | | 应用场景 | 工程计算、路径生成、信号处理 | 不适用于实时性要求极高的系统 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值