嵌入式系统中的三角函数

12

sincos函数在嵌入式系统上的运行速度很慢,需要大量资源。如何以更省资源、更快速的方式计算sincos函数?


如果输入参数是“角度”,那么查找表是不错的选择,但如果只涉及对边和斜边,则可以使用带有降低精度的除法。 - Alphaneo
11个回答

16

计算 泰勒傅里叶 级数总是耗时的。

在嵌入式系统中,应考虑使用查找表

也许在互联网上可以找到有关惠普如何优化早期科学计算器中此类计算的有趣信息。

我记得当时看到过这样的东西。(在当时)


2
一个简单的256元素表格可以给你一个小的正弦部分;其他所有内容都可以通过简单的对称规则推导出来。 - S.Lott
是的,查找表是理想的。+1 - Brian Liang
3
如果我没记错的话,针对0到90度的512个条目查找表,在归一化为0到2^15的情况下,通过线性插值可提供高达21位的精度。这只比一个正确的正弦函数差了两位。 - Nils Pipenbrinck
4
当然没有必要坚持使用线性插值。sin(x)函数表现良好,因此高阶插值也可以使用。本质上,存在一个完整的连续体,介于“所有数据,无插值”和“sin(0)=0,sin(90)=1,对其余所有进行插值”之间。 - MSalters
@S.Lott -- 你能澄清一下“0到45度”的部分吗?我想你需要对正弦和余弦表格都进行0到45度的处理,或者只对正弦表格进行0到90度的处理。 - Craig McQueen
显示剩余4条评论

10

使用插值法的查找表无疑是最有效的解决方案。如果你想使用更少的内存,CORDIC 是一种计算三角函数值的相当高效的算法,通常在手持计算器中实现。

另外,使用傅里叶级数来表示这些函数没有任何意义,因为你只是创造了一个循环问题,如何评估级数的正弦/余弦项。泰勒级数是一种众所周知的逼近方法,但在许多情况下,误差被证明是不可接受的。

你还可以查看这个问题及其答案,关于Java快速三角函数的内容(因此代码可以轻松移植),其中提到了CORDIC和Chebyshev逼近等方法,其中之一无疑会适合您的需求。


“check out this question” 是否缺少链接? - Craig McQueen

4

这取决于你需要它做什么。如果您不太关心角度精度(例如,最近的一度就可以),那么只需使用值查找表即可。如果您没有FPU,请使用定点算术

计算sin/cos函数的一种简单方法是使用泰勒级数(如在此处所示的三角函数)。使用的项数越少,值越不准确,但计算速度越快。

傅里叶级数计算需要知道一些sin/cos值。如果您大部分时间将事物存储在频域中,您可能可以节省计算量-具体取决于您正在做什么。


4

3

请查看 Stack Overflow 上的问题 三角函数是如何工作的? 其中被接受的回答详细解释了如何进行范围缩减,然后使用CORDIC算法,并进行进一步的优化。


2
  1. 查找表
  2. 泰勒级数,就像你所说的一样

请注意,使用查找表时,您可以通过限制域来优化事物,例如将角度表示为无符号字符,使您在圆周周围只有256个步骤,但也具有非常紧凑的表格。类似的事情也可以用于值,比如使用定点数。


0

我有点晚了,但是我想分享一个使用查找表(包括表生成器)的现成有效解决方案:DFTrig

DFTrig由两部分组成:

  • 表生成器tablegen(用Java编写,但并不重要)接收多个选项并生成C代码(const结构与查找表)
  • tablegen生成的查找表一起工作的小型C模块。

当然,查找表仅包含最少量的信息:单个象限的正弦值,即[0, 90]度。这足以计算任何角度的正弦/余弦。

行为非常可定制。您可以指定:

  • 在每张表上,每个项目在查找表中的乘数因子;
  • 表格中每个项目之间的度数步长(在每个表上); 表中项目的类型(整个C项目通用)。

因此,根据您的需求,您可以:

  • 使用最大因子为整个应用程序生成单个表,以便您 C 项目的任何子系统都可以使用该单个表,提供所需的因子,并且如果请求的因子与表的因子不同,则将重新计算;
  • 生成多个表,每个表都有特定的临时因子,您 C 项目的每个子系统都使用其专用表。然后,可以直接从表中返回值,无需重新计算;这样可以更快地运行。

我在我的嵌入式项目中使用它,效果很好。


0

0

这里有一个很好的伪代码示例在这里和显式代码在这里

不过,就像@unwind建议的那样,你可能想尝试在一台性能良好的计算机上预先计算这些表格,并将表格加载到嵌入式设备中。

如果您的答案不需要非常准确,查找表会相当小,您可以将其存储在设备的内存中。如果您需要更高的精度,则需要在设备内计算它。这是内存、时间和所需精度之间的权衡;答案取决于您项目的具体性质。


0

2
平方根与正弦/余弦几乎没有关系。它们相对容易计算。如果按照简单的方法计算,速度可能会有点慢,但是很容易。对于正弦/余弦来说,使用计算平方根的方法是无用的,因为你不能在没有正弦表的情况下连续改进你的近似值。而对于Quake的演示...哦天啊。除了找平方根之外,它根本没有任何用处。这是纯粹的hackery,不是有用数学的真实例子。 - cHao
我确实意识到sqrt!= sin/cos,更多的是关于代码背后的思维过程,并能够使用巧妙的近似方法在给定应用程序中权衡精度和速度。 - John U
John,我刚刚读了那篇文章和其他几篇有关NR迭代和魔术常数的相关文章。谢谢你提供的链接。这确实给了我一些使用巧妙解决方案的灵感。+1 - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接