【问题标题】:Trigonometric functions on embedded system嵌入式系统上的三角函数
【发布时间】:2009-10-15 10:23:25
【问题描述】:

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

【问题讨论】:

  • 如果输入参数是“角度”,那么查找表是好的,但如果它只是对面和斜边,那么你可以使用精度降低的除法

标签: c math embedded trigonometry


【解决方案1】:

计算TaylorFourier 系列总是很耗时。

在嵌入式系统中,您应该考虑lookup tables

“网络”上可能还有一些有趣的信息,关于惠普如何在其早期的科学计算器中优化此类计算。

我记得当时看到过这样的东西

【讨论】:

  • 一个简单的 256 元素表会给你一小段正弦;其他一切都可以通过简单的对称规则推导出来。
  • 是的,我似乎记得在惠普对其计算器算法的解释中看到过那种东西。
  • 澄清一下,泰勒级数的问题不在于速度,而是速度与准确性的结合。对于低精度计算,这可能是最快的方法,但如果您想要很多小数位,效果就不太好。傅里叶级数在这里无关紧要。
  • 如果我没记错的话,一个 0..90° 的 512 条目查找表,归一化为 0..2^15 给出 - 使用线性插值 - 高达 21 位的精度。这只是比正弦差两位。
  • 当然没有理由坚持线性插值。 sin(x) 表现得相当好,因此高阶插值也有效。本质上,“所有数据,无插值”和“sin(0)=0,sin(90)=1,对其他所有数据进行插值”之间存在一个完整的连续统一体。
【解决方案2】:

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

顺便说一句,使用傅立叶级数表示这些函数没有任何意义,因为您只是在创建一个循环问题,即如何评估级数的 sin/cos 项。泰勒级数是一种众所周知的近似方法,但在许多情况下,误差会大到无法接受。

您可能还想查看this question and its answers,了解 Java 的快速三角函数(因此可以轻松移植代码)。它提到了 CORDIC 和 Chebyshev 近似等。其中之一无疑会满足您的需求。

【讨论】:

  • “检查这个问题”是否缺少链接?
【解决方案3】:

取决于你需要什么。如果您对角度精度不是很在意(例如,如果到最接近的度数就可以了),那么只需使用值查找表。如果您没有 FPU,请在 fixed-point 工作。

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

傅立叶级数计算需要知道一些正弦/余弦值。但是,如果您大部分时间将内容存储在 frequency domain 中,则可能会节省计算费用 - 取决于您正在执行的操作。

【讨论】:

    【解决方案4】:

    Dobb 博士的这篇文章:Optimizing Math-Intensive Applications with Fixed-Point Arithmetic 很好地解释了 CORDIC 算法,并提供了文章中讨论的库的完整源代码。

    【讨论】:

      【解决方案5】:
      1. 查找表
      2. Taylor series,就像你说的那样

      请注意,使用查找表,您通常可以通过限制域来优化事物,例如将角度表示为无符号字符,只为您提供 256 步绕圆,但也是一个非常紧凑的表格。可以对值做类似的事情,比如使用定点。

      【讨论】:

        【解决方案6】:

        查看 Stack Overflow 问题 How do Trigonometric functions work? 接受的答案解释了如何减少范围,然后使用 CORDIC,然后进行一些进一步优化的一些细节。

        【讨论】:

          【解决方案7】:

          似乎有一个很好的伪代码示例here 和显式代码here

          但是,正如@unwind 所建议的,您可能想尝试在一台体面的计算机上预先计算这些表并将这些表加载到嵌入式设备。

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

          【讨论】:

            【解决方案8】:

            在某些情况下,只需使用 IIR 滤波器即可进行管理,将其调谐到所需频率的谐振。 看这里:http://www.ee.ic.ac.uk/pcheung/teaching/ee3_Study_Project/Sinewave%20Generation(708).pdf

            【讨论】:

              【解决方案9】:

              这可能会有所帮助/启发: Magical square root in Quake III

              【讨论】:

              • 平方根与 sin/cos 关系不大。它们计算起来相当简单。如果你用简单的方法来做会有点慢,但微不足道。对于 sin/cos,计算平方根的方法将毫无用处,因为如果没有正弦表,您将无法连续改进近似值。而对于 Quake 的演绎……oooboy。在求平方根的上下文之外,它根本没有用处。这纯粹是骇客,不是有用数学的真实例子。
              • 我确实意识到 sqrt != sin/cos,这更多是关于代码背后的思考过程,以及能够使用给定应用程序的巧妙近似来权衡准确性和速度。
              • John,我刚刚阅读了那篇文章以及其他一些关于 NR 迭代和魔法常数的相关文章。谢谢链接。绝对给了我一些使用聪明解决方案的灵感。 +1
              【解决方案10】:

              我参加聚会有点晚了,但无论如何我想分享一个使用查找表(包括表生成器)的现成有效解决方案:DFTrig

              DFTrig 由两部分组成:

              • 查找表生成器tablegen(用 Java 编写,但这并不重要)接收多个选项并生成 C 代码(带有查找表的 const 结构)
              • tablegen 生成的查找表配合使用的小型 C 模块。

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

              行为是完全可定制的。您可以指定:

              • 查找表中每个项目乘以的因子(基于每个表);
              • 表中每个项目之间的步长(基于每个表); 表中项的类型(整个 C 项目通用)。

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

              • 为整个应用程序生成具有最大因子的单个表,以便您的 C 项目的任何子系统都可以使用该单个表,提供所需的因子,如果请求的因子不是表的因子,它将重新计算;李>
              • 生成多个表,每个表都有特定因素,C 项目的每个子系统都使用其专用表。然后,可以按原样从表中返回值,而无需重新计算;运行速度更快。

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

              【讨论】:

                【解决方案11】:

                您可以查看这个用于 8 位 AVR 微控制器的任意定点库: https://community.atmel.com/projects/afp-arbitrary-fixed-point-lib

                编辑:链接已更新

                【讨论】:

                • 这两个链接都失效了。在这里复制图书馆会很棒。
                • 我已经更新了链接。来源太大,无法在此处复制/粘贴。
                猜你喜欢
                • 2011-09-05
                • 2010-09-15
                • 1970-01-01
                • 2014-10-23
                • 1970-01-01
                • 2012-09-27
                • 2014-04-20
                • 2013-09-07
                • 2020-10-05
                相关资源
                最近更新 更多