【发布时间】:2014-08-13 18:20:14
【问题描述】:
我已经浪费了好几天时间与我的 16mhz 8 位 AVR (mega 2560) 作斗争。
目标是标准化我收到的值(加速度计、磁力计等)。
这些值是 16 位有符号(int16),在我想要一个 0.0f-1.0f 的浮点数之后
我将它用于 3d IMU。
常用方法:
int32_t tmp = (int32_t)a*a+b*b+c*c;
float magnitude = sqrt(tmp);
float a_v = a / magnitude;
float b_v = b / magnitude;
float c_v = c / magnitude;
更快的方法:
int32_t tmp = (int32_t)a*a+b*b+c*c;
float imagnitude = InvSqrt(tmp); // like the 'tricky' one for ID software quake source
float a_v = a * imagnitude;
float b_v = b * imagnitude;
float c_v = c * imagnitude;
第二个有一些优点,因为它使用近似而不是 1/sqrt(但也有近似的 sqrt),它需要 3 次乘法而不是除法,这很好,因为 AVR 支持 MUL 但不支持 DIV。 另一方面,由于浮点和 32 位计算,它无论如何都非常慢。
这样的函数通常需要 1-2 毫秒,这对我的代码产生了巨大影响,它试图在一个应该持续最多 2.4 毫秒的周期中解决许多额外的任务和规范化
我进行了很多研究并尝试了许多不同的近似值和想法,但无论我尝试了什么,代码的执行速度都太慢了。
也许还有另一种方法可以标准化我的传感器值..
针对我的特殊问题(加速度计大小)的人的更新: 没有浮点数和 sqrt 我现在正在做这个工作:(忽略额外的演员表:) int16 cal[] 保存 3 个轴的校准加速度计值。
int16 average_sq_1g = CONST_1G / 256;
uint32_t work = (int32_t)((int32_t)cal[0]*cal[0] + (int32_t)cal[1]*cal[1] + (int32_t)cal[2]*cal[2])/256;
work = work * 100L / average_sq_1g;
attitude.acc_magnitude = work;
这对我的事业来说非常专业,因为我正在努力获得加速度的大小,并且我知道我收到的 1G 值(大约 16000)
所以公式 (X^2+Y^2+Z^2)*100/1G^2 返回幅度(100 = 没有额外的加速度,并且可以在不使用浮点数的情况下完成。
我没有检查性能差异,但它应该会快很多。
【问题讨论】:
-
为什么要使用浮点数?可以改用定点(例如 Q15)吗?
-
第一行需要更多的演员表。第一个乘法是 1nt32_t,但其他两个只是在 int 之间。可能会发生溢出。
-
UncleO:我怀疑会有溢出,编译器应该正确处理整个操作。我使用类似的代码,它可以正常工作而不会溢出。 @anatolyg:我目前正在将整个代码更改为 32 位整数,因为我无法让它更快地工作。我不确定定点有多少帮助,AVR-GCC afaik 上没有定点支持。
-
SCNR: +1 地震源 InvSqrt() 参考
-
@UncleO:所有的乘法都在
int之间,而不是int16_t之间,由于促销。
标签: c performance normalization avr approximation