jonas222 发表于 2025-1-25 22:57

单片机样条插值算法

本帖最后由 jonas222 于 2025-1-25 22:58 编辑

插值算法是一种很常用的数据处理手段,插值包括了最邻近插值、线性插值(单线性和双线性)、样条插值(自然样条、抛物样条),本文以抛物样条和自然样条两种方法进行对比,基于ARM单片机GD32F103进行测试。测试函数 #include "dsp_test.h"


static arm_spline_instance_f32S;                                //样条插值结构体
static arm_spline_type type=ARM_SPLINE_NATURAL;        //自然样条插值
static float32_tx;                                                //原始数据x
static float32_ty;                                                //原始数据y
static uint32_t n=32;                                                        //原始数据个数
static float32_tcoeffs;                                //稀疏矩阵
static float32_ttempBuffer;                        //内部计算缓冲数组



static float32_txq;
static float32_tpDst;
static uint32_t blockSize=128;

#define num_tab 128/32

void interp_test(u8 mode)
{
      if(mode)
      {
                type=ARM_SPLINE_NATURAL;//自然样条插值
      }
      else
      {
                type=ARM_SPLINE_PARABOLIC_RUNOUT;//抛物样条插值
      }

      u8 i=0;
      for(i=0;i<32;i++)
      {
                x=i*num_tab;
                y=1.f+arm_sin_f32(100.f*PI*i/256.f+PI/3.f);
      }

      for(i=0;i<128;i++)
      {
                xq=i;
      }

      arm_spline_init_f32(&S,type,x,y,n,coeffs,tempBuffer);
      arm_spline_f32(&S,xq,pDst,blockSize);

      printf("*****x********\r\n");
      for(i=0;i<32;i++)
      {
                printf("%f\r\n",x);
      }
      printf("*****y********\r\n");
      for(i=0;i<32;i++)
      {
                printf("%f\r\n",y);
      }


      printf("*****x1********\r\n");
      for(i=0;i<128;i++)
      {
                printf("%f\r\n",xq);
      }

      printf("*****y1********\r\n");
      for(i=0;i<128;i++)
      {
                printf("%f\r\n",pDst);
      }
}
测试结果https://i1.hdslb.com/bfs/article/83a8e53a6afb0a90d4db40c27a5bf2f1e0fe2708.jpg@1122w_840h.webp

样条插值测试结果可以看出插值后,正弦曲线变得很平滑,两者插值在细节上无明显的区别,,只是最后几个点和趋势上抛物样条插值变缓自然样条插值保持跟原始数据一样

mnynt121 发表于 2025-2-4 20:08

每个区间内使用线性函数(直线)进行插值。简单,但通常不够平滑。

uptown 发表于 2025-2-5 09:01

将需要进行插值的数据点的坐标和对应的数值输入到单片机中,通常可以通过外部传感器采集、通信接口传输或预先存储在程序代码中等方式获取这些数据。

adolphcocker 发表于 2025-2-5 11:09

在求解线性方程组时,注意数值稳定性,避免由于舍入误差导致的插值结果不准确。
可使用分解方法(如LU分解)提高计算的稳定性和效率。

lzbf 发表于 2025-2-5 12:42

样条插值通过在已知数据点之间插入多项式片段来生成平滑的曲线。常用的样条插值方法包括线性样条、二次样条和三次样条。其中,三次样条插值是最常用的方法,因为它可以生成非常平滑的曲线。

jkl21 发表于 2025-2-5 14:10

单片机的内存通常有限,因此在实现样条插值时需要优化算法,减少内存占用。

pixhw 发表于 2025-2-5 17:50

为了减少单片机的计算负担,可以预先计算并存储一些重复计算的部分

claretttt 发表于 2025-2-6 16:32

相比线性样条,可以提供更平滑的曲线,但可能在某些点处导数不连续

hudi008 发表于 2025-2-6 17:15

正确选择和应用边界条件,确保插值曲线在边界处的光滑性和准确性。

lzbf 发表于 2025-2-6 19:15

样条插值通过将数据点分割成若干段,每段使用一个低阶多项式(通常是三次多项式)来逼近,从而在整个数据范围内生成一条平滑曲线。相比于简单的线性插值,样条插值能够更好地保留曲线的形状和趋势,减少振荡和失真。

sanfuzi 发表于 2025-2-13 20:10

样条插值中最常用的是三次样条插值,因为它能够提供平滑的二阶导数

lzbf 发表于 2025-2-14 17:53

插值曲线在节点处仅保证连续,不保证光滑,即导数不连续,可能会出现折线效果。

bestwell 发表于 2025-2-14 19:52

编写高效的C/C++代码,避免不必要的循环和函数调用。
使用内联函数或汇编语言优化关键路径上的代码。

pmp 发表于 2025-2-17 10:38

单片机的内存有限,因此在实现算法时需要注意内存的使用,避免内存溢出。

averyleigh 发表于 2025-2-17 13:05

// 假设已有插值点数组 x[] 和 y[],以及插值点数量 n
float a, b, c, d; // 存储三次样条系数
float h, alpha; // h是步长,alpha用于构建三对角矩阵

// 计算步长
for (int i = 0; i < n-1; i++) {
    h = x - x;
}

// 构建三对角矩阵并求解
for (int i = 1; i < n-1; i++) {
    alpha = (3/h) * (y - y) - (3/h) * (y - y);
}

// 使用追赶法求解三对角线性方程组
// ...(此处省略追赶法的具体实现)

// 构造三次样条函数的系数
for (int i = 0; i < n-1; i++) {
    a = y;
    b = (y - y) / h - h * (2*d + d) / 3;
    c = d;
    d = (d - d) / (3*h);
}

// 计算插值结果
float spline_interpolate(float x) {
    // 找到 x 所在的区间
    int i = ...; // 通过二分查找或其他方法确定 i
    float dx = x - x;
    return a + b*dx + c*dx*dx + d*dx*dx*dx;
}

qiufengsd 发表于 2025-2-17 14:48

在计算机图形学中,使用样条插值来绘制平滑的曲线和曲面。

linfelix 发表于 2025-2-17 16:32

对于实时性要求高的应用,可适当降低精度以提高计算速度。

lihuami 发表于 2025-2-17 18:58

样条插值是一种常用的数值分析方法,用于在已知数据点之间生成平滑的曲线。在单片机应用中,样条插值常用于控制电机运动、生成平滑的运动轨迹、图像处理等领域。

yeates333 发表于 2025-2-17 21:24

对于一个新的查询点,确定它所在的区间,并使用该区间的样条函数计算插值结果。

uptown 发表于 2025-2-17 23:08

单片机通常具有有限的存储空间,因此需要优化数据存储和算法实现。
可以考虑使用动态内存分配(如果单片机支持)或固定大小的数组来存储样条系数。
页: [1] 2
查看完整版本: 单片机样条插值算法