Я потратил впустую несколько дней, борясь со своим 16-мегагерцовым 8-битным AVR (мега 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 = без дополнительного ускорения и может быть выполнено без использования чисел с плавающей запятой.
Я не проверял разницу в производительности, но это должно быть намного быстрее.
int
, а неint16_t
, из-за повышения. - person Ben Voigt   schedule 25.06.2014int
равноint16_t
. Вот причина моего комментария. Я предполагал, что два множества будут встречаться как целые числа и не будут повышаться доlong int
илиint32_t
до добавления. Джон говорит, что его тесты показывают обратное. - person uncleO   schedule 25.06.2014