空间换时间:查表法 (LUT) 与 CORDIC 算法

Evek Golden Lv4

在 PC 上,计算 sin(x) 只需要包含 <math.h> 然后调用就行了。但在资源受限的微控制器(如 Cortex-M0/M3)上,标准库的 sin() 往往意味着几 KB 的代码量和数千个周期的耗时。

如果你的应用(如电机 FOC 控制、逆变器)需要高频计算三角函数,标准库是绝对不行的。我们有两个选择:查表法 (Look-Up Table)CORDIC 算法

1. 查表法 (LUT):简单粗暴

原理很简单:预先算出 0~90 度的正弦值,存到数组里。

1.1 基础实现

1
2
3
4
5
6
const float sin_table[91] = {0.0, 0.0174, ... 1.0};

float fast_sin(int angle_deg) {
if (angle_deg < 0 || angle_deg > 90) return 0; // 简化处理
return sin_table[angle_deg];
}

缺点:精度太低!只有整数角度。如果我要算 sin(45.5) 怎么办?

1.2 线性插值 (Linear Interpolation)

假设 $x$ 在 $x_1$ 和 $x_2$ 之间,那么 $y$ 就在 $y_1$ 和 $y_2$ 之间。使用线性公式:

$$ y = y_1 + (x - x_1) \frac{y_2 - y_1}{x_2 - x_1} $$

这样即使表很小(比如每隔 5 度存一个值),通过插值也能获得不错的精度。

2. CORDIC 算法:移位加减的奇迹

如果 Flash 也很小,存不下大表怎么办?或者你需要极高的精度?
CORDIC (Coordinate Rotation Digital Computer) 算法登场了。它只需要移位、加法、减法,不需要乘法器,更不需要浮点单元。

2.1 几何意义

CORDIC 的核心是伪旋转。将一个向量 $(x, y)$ 旋转 $\theta$ 角,可以通过多次旋转一连串预定义的小角度($\arctan(2^{-i})$)来逼近。

为什么选 $\arctan(2^{-i})$?因为 $\tan(\theta) = 2^{-i}$,乘法就变成了移位!

2.2 迭代过程

我们预先存储一个很小的角度表(几十字节):
atan_table = [45.0, 26.565, 14.036, ...]

对于第 $i$ 次迭代:

  • 如果当前角度小于目标角度,就逆时针转(加法)。
  • 如果当前角度大于目标角度,就顺时针转(减法)。
  • 坐标更新公式仅涉及 >> i
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 伪代码
void cordic_sin_cos(int theta, int *sin_val, int *cos_val) {
int x = K_GAIN; // 为了抵消模长伸缩系数
int y = 0;
int z = theta;

for (int i = 0; i < ITERATIONS; i++) {
int d = (z >= 0) ? 1 : -1;
int x_new = x - d * (y >> i);
int y_new = y + d * (x >> i);
z -= d * atan_table[i];

x = x_new;
y = y_new;
}
*cos_val = x;
*sin_val = y;
}

3. 对比总结

维度查表法 (LUT)CORDIC
速度极快 O(1)较慢 O(k),跟迭代次数有关
内存大 (取决于精度)极小 (几十字节)
代码简单略复杂
适用内存够,追求极致速度内存紧缺,或需要动态高精度

结论:在电机控制 (FOC) 中,通常用插值查表法,因为速度第一。而在科学计算器或 FPGA 逻辑中,CORDIC 是首选。

  • Title: 空间换时间:查表法 (LUT) 与 CORDIC 算法
  • Author: Evek Golden
  • Created at : 2025-07-05 23:30:00
  • Updated at : 2026-06-12 08:57:02
  • Link: https://blog.cocodemo.uno/posts/math9k3/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments