[应用方案] 单片机样条插值算法

[复制链接]
 楼主| jonas222 发表于 2025-1-25 22:57 | 显示全部楼层 |阅读模式
本帖最后由 jonas222 于 2025-1-25 22:58 编辑

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


  2. static arm_spline_instance_f32  S;                                //样条插值结构体
  3. static arm_spline_type type=ARM_SPLINE_NATURAL;        //自然样条插值
  4. static float32_t  x[32];                                                //原始数据x
  5. static float32_t  y[32];                                                //原始数据y
  6. static uint32_t n=32;                                                        //原始数据个数
  7. static float32_t  coeffs[3*(32-1)];                                //稀疏矩阵
  8. static float32_t  tempBuffer[2*(32-1)];                        //内部计算缓冲数组



  9. static float32_t  xq[128];
  10. static float32_t  pDst[128];
  11. static uint32_t blockSize=128;

  12. #define num_tab 128/32

  13. void interp_test(u8 mode)
  14. {
  15.         if(mode)
  16.         {
  17.                 type=ARM_SPLINE_NATURAL;//自然样条插值
  18.         }
  19.         else
  20.         {
  21.                 type=ARM_SPLINE_PARABOLIC_RUNOUT;//抛物样条插值
  22.         }

  23.         u8 i=0;
  24.         for(i=0;i<32;i++)
  25.         {
  26.                 x[i]=i*num_tab;
  27.                 y[i]=1.f+arm_sin_f32(100.f*PI*i/256.f+PI/3.f);
  28.         }

  29.         for(i=0;i<128;i++)
  30.         {
  31.                 xq[i]=i;
  32.         }

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

  35.         printf("*****x********\r\n");
  36.         for(i=0;i<32;i++)
  37.         {
  38.                 printf("%f\r\n",x[i]);
  39.         }
  40.         printf("*****y********\r\n");
  41.         for(i=0;i<32;i++)
  42.         {
  43.                 printf("%f\r\n",y[i]);
  44.         }


  45.         printf("*****x1********\r\n");
  46.         for(i=0;i<128;i++)
  47.         {
  48.                 printf("%f\r\n",xq[i]);
  49.         }

  50.         printf("*****y1********\r\n");
  51.         for(i=0;i<128;i++)
  52.         {
  53.                 printf("%f\r\n",pDst[i]);
  54.         }
  55. }

测试结果
[backcolor=var(--bg3)]

样条插值测试结果
可以看出插值后,正弦曲线变得很平滑,两者插值在细节上
无明显的区别,,只是最后几个点和趋势上抛物样条插值变缓
自然样条插值保持跟原始数据一样
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[n], b[n], c[n], d[n]; // 存储三次样条系数
float h[n-1], alpha[n-1]; // h是步长,alpha用于构建三对角矩阵

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

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

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

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

// 计算插值结果
float spline_interpolate(float x) {
    // 找到 x 所在的区间
    int i = ...; // 通过二分查找或其他方法确定 i
    float dx = x - x[i];
    return a[i] + b[i]*dx + c[i]*dx*dx + d[i]*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 | 显示全部楼层
单片机通常具有有限的存储空间,因此需要优化数据存储和算法实现。
可以考虑使用动态内存分配(如果单片机支持)或固定大小的数组来存储样条系数。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

44

主题

1654

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部