在嵌入式设备中用多项式快速计算三角函数和方根

这篇具有很好参考价值的文章主要介绍了在嵌入式设备中用多项式快速计算三角函数和方根。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

惯性传感器的倾角计算要用到三角函数.

在 MCS-51, Cortex M0, M3 之类的芯片上编程时, 能使用的资源是非常有限, 通常只有两位数KB的Flash, 个位数KB的RAM. 如果要使用三角函数和开方就要引入 math.h, 会消耗掉10KB以上的Flash空间. 在很多情况下受硬件资源限制无法使用 math.h, 这时候使用简化的方法进行三角函数和开方运算就非常有意义, OlliW's Bastelseiten在2014年的一篇文章里, 提供了几个实用的计算方法. 下面介绍其计算方法和代码实现.

快速正弦余弦(Sin, Cos)计算

将角度 \(x \in [0, \frac{\pi}{2}]\)通过下面的式子转换到 $ \alpha \in [-\frac{1}{2}, \frac{1}{2}]$ 区间

\[\alpha = \frac{2}{\pi} x - \frac{1}{2} \]

于是, 对应 \(\alpha\) 的多项式近似计算为

\[\sin\alpha = a_0 - b_1\alpha + a_2\alpha^2 - b_3\alpha^3 + a_4\alpha^4 - b_5\alpha^5 + a_6\alpha^6 \\ \cos\alpha = a_0 + b_1\alpha + a_2\alpha^2 + b_3\alpha^3 + a_4\alpha^4 + b_5\alpha^5 + a_6\alpha^6 \]

如果将上面的符号固定项和变化项分成\(A\)\(B\)两部分

\[A = a_0 + a_2\alpha^2 + a_4\alpha^4 + a_6\alpha^6 \\ B = b_1\alpha + b_3\alpha^3 + b_5\alpha^5 \]

\(\sin\alpha\)\(\cos\alpha\) 可以通过 A 和 B 的值表达

\[\sin\alpha = A - B \\ \cos\alpha = A + B \]

对应的各项系数值

\(a_0 = 0.707106781187 \\ a_2 = -0.872348075361 \\ a_4 = 0.179251759526 \\ a_6 = -0.0142718282624 \\ \\ b_1 = -1.110670322264 \\ b_3 = 0.4561589075945 \\ b_5 = -0.0539104694791\)

使用上面的计算方式, 结果绝对误差小于\(6.5 \times 10^{-6}\), 并且 \(\cos^2 x + \sin^2 x\) 不会超过 1. 计算过程只需要7次乘法和7次加法.

C语言实现

const   float coeff[7] = {
  // a0 ~ a6           b1 ~ b5
   0.707106781187,  -1.110670322264,
  -0.872348075361,   0.4561589075945,
   0.179251759526,  -0.0539104694791,
  -0.0142718282624
};

/**
 * @param alpha: value between 0 and 0.5
*/
void sincos_normalized(float alpha, float *sin, float *cos)
{
  int i;
  float alpha_exp = 1.0, part_a = 0, part_b = 0;

  for (i = 0; i < 7; i++)
  {
     if (i % 2 == 0)
     {
        part_a = part_a + (coeff[i] * alpha_exp);
     }
     else
     {
        part_b = part_b + (coeff[i] * alpha_exp);
     }
     alpha_exp = alpha_exp * alpha;
  }
  *sin = part_a - part_b;
  *cos = part_a + part_b;
}

float calculate(float degree_in)
{
  int quadrant, multi;
  float degree = degree_in, alpha, cos, sin, c, s;

  multi = (int)(degree / 90.0);
  degree = degree - (multi * 90.0);
  alpha = (degree / 90) - 0.5;
  sincos_normalized(alpha, &s, &c);
  multi = multi % 4;
  if (multi == 0)
  {
    sin = s;
    cos = c;
  }
  else if (multi == 1)
  {
    sin = c;
    cos = -s;
  }
  else if (multi == 2)
  {
    sin = -s;
    cos = -c;
  }
  else if (multi == 3)
  {
    sin = -c;
    cos = s;
  }
  printf("d_in:%5.0f d:%5.0f a:%10.5f  sin:%10.5f  cos:%10.5f\r\n", degree_in, degree, alpha, sin, cos);
}

计算的结果和 math.h 的 sin cos 函数对比, 数值几乎一样, 仅在个别数值的小数点后第五位会有\(\pm1\)的差异.

平方根倒数计算

对于1附近的数值, 平方根倒数可以使用牛顿迭代法计算, 实际上非常简单,因为它只涉及加法和乘法,而不涉及除法, 对于 \(x \in [0.6, 1.4]\), 计算式为

\[y_0 = 1 \\ y_{n+1} = y_n (1.5 - 0.5 x {y_n}^2) \\ \]

计算两次牛顿迭代需要3次乘法, 而二阶泰勒级数只需要2次, 但是牛顿迭代法精度更高, 甚至比三阶泰勒级数的精度更高. 如果执行三次牛顿迭代则需要6次乘法, 在\(0.6 < x < 1.4\)的范围内结果精度优于\(1 \times 10^{-4}\), 注意\(x\)的取值范围, 因为近似是以1为中心展开的, 所以离1越远差异越大, 在这个范围之外例如\(x = 0.5\)的时候, 三次迭代就达不到这个精度. 在实际应用中, 可以将要计算的数值提一个系数转换到 \(x \in [0.6, 1.4]\)区间进行计算.

C语言实现

float inverse_sqrt(int interates, float x)
{
  float y;

  y = 1.5 - (x / 2);
  while (interates--)
  {
    y = y * (1.5 - 0.5 * x * y * y);
  }
  return y;
}

// 使用 0.5 ~ 2.1 之间的数字测试, 分别迭代5次
int main(int argc, char *const argv[])
{
  int i, j;
  for (i = 0; i < 17; i++)
  {
    printf("%4.1f ", i*0.1 + 0.5);
    for (j = 0; j < 5; j++)
    {
      printf("%11.9f ", inverse_sqrt(j, i*0.1 + 0.5));
    }
    printf("\r\n");
  }
  return 0;
}

快速反正弦(Arcsin)计算

原文最终选择的是多项式近似, 避免了取绝对值等复杂处理, 只是在 \(x = \pm 1\) 附近的绝对精度较差, 输出值规范化为 \(\pi\),即定义 \(\arcsin(x) = \pi \times asin(x)\). 计算式为

\[asin(x) = \frac{x}{2} \times \frac{a_0 + a_2x^2 + a_4x^4 + a_6x^6}{b_0 + b_2x^2 + b_4x^4 + b_6x^6} \]

对应的系数数值为
\(a_0 = 0.318309886 \\ a_2 = -0.5182875 \\ a_4 = 0.222375 \\ a_6 = -0.016850156 \\ \\ b_0 = 0.5 \\ b_2 = -0.89745875 \\ b_4 = 0.46138125 \\ b_6 = -0.058377188\)

\(|x|<0.75\)时, 绝对误差小于 \(1 \times 10^{-5}\), 当 \(|x|<0.91\)时, 绝对误差小于 \(4.2 \times 10^{-5}\), 在 \(x \approx 0.997\)时, 最大误差为 \(0.011\).

C语言实现

const float coeffa[4] = {
  // a0 ~ a6
   0.318309886,
  -0.5182875,
   0.222375,
  -0.016850156
};

const float coeffb[4] = {
  0.5,
  -0.89745875,
  0.46138125,
  -0.058377188
};

const float pi = 3.14159265358979;

float arcsin(float x)
{
  int i;
  float x2 = 1, a = 0, b = 0;

  for (i = 0; i < 4; i ++)
  {
    a = a + coeffa[i] * x2;
    b = b + coeffb[i] * x2;
    x2 = x2 * x * x;
  }
  return (x * pi / 2) * (a / b);
}

int main(int argc, char *const argv[])
{
  int i;
  float x, alpha, expect;
  for (i = 0; i < 20; i++)
  {
    x = 0.01 + (i * 0.05);
    alpha = arcsin(x);
    expect= asin(x);
    printf("x:%4.2f  a:%9.6f e:%9.6f\r\n", x, alpha, expect);
  }
}

计算结果, 最右侧一列为 math.h 的 asin() 函数, 用于对比

x:0.01  a: 0.010000 e: 0.010000
x:0.06  a: 0.060036 e: 0.060036
x:0.11  a: 0.110223 e: 0.110223
x:0.16  a: 0.160691 e: 0.160691
x:0.21  a: 0.211575 e: 0.211575
x:0.26  a: 0.263022 e: 0.263022
x:0.31  a: 0.315193 e: 0.315193
x:0.36  a: 0.368268 e: 0.368268
x:0.41  a: 0.422454 e: 0.422454
x:0.46  a: 0.477996 e: 0.477995
x:0.51  a: 0.535185 e: 0.535185
x:0.56  a: 0.594386 e: 0.594386
x:0.61  a: 0.656060 e: 0.656061
x:0.66  a: 0.720815 e: 0.720819
x:0.71  a: 0.789485 e: 0.789498
x:0.76  a: 0.863278 e: 0.863313
x:0.81  a: 0.944073 e: 0.944152
x:0.86  a: 1.035139 e: 1.035270
x:0.91  a: 1.143404 e: 1.143284
x:0.96  a: 1.291451 e: 1.287002

将0.9附近细分一下

x:0.90  a: 1.119760 e: 1.119769
x:0.91  a: 1.143404 e: 1.143284
x:0.92  a: 1.168431 e: 1.168081
x:0.93  a: 1.195150 e: 1.194413
x:0.94  a: 1.224027 e: 1.222630
x:0.95  a: 1.255752 e: 1.253236
x:0.96  a: 1.291451 e: 1.287002
x:0.97  a: 1.333107 e: 1.325231
x:0.98  a: 1.384628 e: 1.370462
x:0.99  a: 1.455034 e: 1.429257

\(0 < x < 0.6\)区间的精度最高, 在\(0.6 < x < 0.9\)区间结果数值偏小, 在\(0.9 < x < 1\)区间结果数值偏大. 越接近1精度越差, 实际使用时在大于\(0.97\)时需要做一些补偿.文章来源地址https://www.toymoban.com/news/detail-837757.html

参考

  • 用多项式快速计算三角函数等 https://www.olliw.eu/2014/fast-functions/

到了这里,关于在嵌入式设备中用多项式快速计算三角函数和方根的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C 数据结构】 用单链表存储一元多项式,并实现两个多项式相加运算。

    本次代码纯c语言,可以支持输入两个多项式的项式、系数、指数。 实验目的: 1 掌握单链表的基本工作原理; 2 实现链式存储下的两个多项式的相加。 实验步骤 1 定义链式存储的数据结构 2 完成多项式的初始化,即给多项式赋初值 3 完成多项式的输出 4 实现多项式的相加及结

    2024年02月06日
    浏览(46)
  • 牛顿插值法、拉格朗日插值法、三次插值、牛顿插值多项式、拉格朗日插值多项式

    两点式线性插值 调用Matlab库函数 拉格朗日二次插值: 牛顿二次插值 结果分析:通过对比不同插值方法,可以看到在一定范围内(高次会出现龙格现象),插值次数越高,截断误差越小(插值结果越接近于真实函数值);同时,对于相同次数的插值,由于不同的插值方法它们

    2024年02月11日
    浏览(45)
  • Jacobi正交多项式

    注:本文的内容主要根据文末中的参考文档[1,2,3]中的内容进行整理完成。 设 I = [ − 1 , 1 ] I=[-1,1] I = [ − 1 , 1 ] 是实轴上的标准区间,定义在 I I I 上的正函数: ω α , β ( x ) = ( 1 − x ) α ( 1 + x ) β , α − 1 , β − 1 omega_{alpha,beta}(x)=(1-x)^{alpha}(1+x)^{beta}, alpha-1,beta-1 ω α , β

    2024年02月13日
    浏览(43)
  • 多项式承诺:KZG

    参考文献: Merkle, R. ”Protocols for Public Key Cryptosystems.” Proc. 1980 Symp. on Security and Privacy, IEEE Computer Society (April 1980), 122-133. Benaloh J, Mare M. One-way accumulators: A decentralized alternative to digital signatures[C]//Workshop on the Theory and Application of of Cryptographic Techniques. Springer, Berlin, Heidelberg, 1993

    2024年02月04日
    浏览(55)
  • 多项式拟合

    文章内容部分参考: 建模算法入门笔记-多项式拟合(附源码) - 哔哩哔哩 (bilibili.com) (9条消息) 数学建模——人口预测模型 公有木兮木恋白的博客-CSDN博客 数学建模人口预测模型 多项式拟合是数据拟合的一种,与插值有一定区别(插值要求曲线经过给定的点,拟合不一定经

    2024年02月04日
    浏览(50)
  • 多项式混沌展开法

    多项式混沌采用多项式基组合成随机空间,来描述和传播随机变量的不确定性。本质是利用正交多项式的优异性能,通过随机变量的输入到响应的映射过程建立代理模型。该方法收敛性好,使用方便,能较好的适用于复杂的系统。但是该方法理论难度高,多元情况下正交多项

    2023年04月15日
    浏览(43)
  • 多项式乘法逆

    前置知识:NTT学习笔记(快速数论变换) 情景代入 洛谷P4238 【模板】多项式乘法逆 给定一个多项式 f ( x ) f(x) f ( x ) ,求 g ( x ) g(x) g ( x ) ,满足 f ( x ) × g ( x ) ≡ 1 ( m o d x n ) f(x)times g(x)equiv 1pmod{x^n} f ( x ) × g ( x ) ≡ 1 ( mod x n ) 。系数对 998244353 998244353 998244353 取模。 1 ≤

    2024年02月02日
    浏览(46)
  • Matlab 多项式拟合

    corrcoef函数 corrcoef函数用来计算矩阵相关系数。 (1)、corrcoef(x):若x为一个矩阵,返回的则是一个相关系数矩阵。 (2)、corrcoef(x,y):计算列向量x、y的相关系数,要求x、y具有相等的元素个数。如果x、y是矩阵,那么corrcoef函数会将其转换为列向量,相当于corrcoef([x(:),y(:)])。   p

    2024年02月11日
    浏览(46)
  • Matlab作图多项式拟合

    一、拟合函数 polyfit(s,y,n) polyval(p,x) poly2str(p,\\\' x \\\' ) 二、拟合步骤 1.做原始数据的散点图 2.选择恰当的次数n,用polyfit指令求得多项式 3.计算多项式p在x处的值 4.画出多项式函数的曲线图 三、拟合实例 对x等于1-10,y大于20小于40的随机数进行多项式拟合 x=1:10;y=20+20*rand(1,10);%定义

    2023年04月25日
    浏览(44)
  • 多项式快速幂(加强版)

    建议阅读我的上一篇博客多项式快速幂 求多项式快速幂,但 a 0 ≠ 1 a_0not=1 a 0 ​  = 1 。 由于求 ln ⁡ ln ln 要求 a 0 = 1 a_0=1 a 0 ​ = 1 ,所以我们要想办法对多项式进行变换,使其满足 a 0 = 1 a_0=1 a 0 ​ = 1 。 如果 f ( x ) f(x) f ( x ) 常数项为 0 0 0 ,那么就整体除去 x x x 的若干次

    2024年02月07日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包