Я пытаюсь создать функцию для нахождения среднего значения датчика на Arduino для его калибровки, однако суммирование не работает должным образом и, следовательно, среднее значение не является правильным. В таблице ниже приведен пример выходных данных. Левый столбец должен быть скользящей суммой выходных данных, которые отображаются в правом столбце (Как туда попадают негативы?)
-10782 17112
6334 17116
23642 17308
-24802 17092
-7706 17096
9326 17032
26422 17096
-21986 17128
calibrateSensors()
функция, которая должна выполнять это, показана ниже
void calibrateSensors(int16_t * accelOffsets){
int16_t rawAccel[3];
int sampleSize = 2000;Serial.println("Accelerometer calibration in progress...");
for (int i=0; i<sampleSize; i ++){
readAccelData(rawAccel); // get raw accelerometer data
accelOffsets[0] += rawAccel[0]; // add x accelerometer values
accelOffsets[1] += rawAccel[1]; // add y accelerometer values
accelOffsets[2] += rawAccel[2]; // add z accelerometer values
Serial.print(accelOffsets[2]);
Serial.print("\t\t");
Serial.print(rawAccel[2]);
Serial.print(" \t FIRST I:");
Serial.println(i);
}
for (int i=0; i<3; i++)
{
accelOffsets[i] = accelOffsets[i] / sampleSize;
Serial.print("Second I:");
Serial.println(i);
}
Serial.println("Accelerometer calibration complete");
Serial.println("Accelerometer Offsets:");
Serial.print("Offsets (x,y,z): ");
Serial.print(accelOffsets[0]);
Serial.print(", ");
Serial.print(accelOffsets[1]);
Serial.print(", ");
Serial.println(accelOffsets[2]);
}
и readAccelData()
функция заключается в следующем
void readAccelData(int16_t * destination){
// x/y/z accel register data stored here
uint8_t rawData[6];
// Read the six raw data registers into data array
I2Cdev::readBytes(MPU6050_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]);
// Turn the MSB and LSB into a signed 16-bit value
destination[0] = (int16_t)((rawData[0] << 8) | rawData[1]) ;
destination[1] = (int16_t)((rawData[2] << 8) | rawData[3]) ;
destination[2] = (int16_t)((rawData[4] << 8) | rawData[5]) ;
Любая идея, где я иду не так?
У вас есть две проблемы:
int array[5] = {};
Это приведет к массиву, который изначально выглядит [0,0,0,0,0]
Ваша вторая проблема заключается в том, что вы создаете массив из 16-битных целых чисел со знаком.
16-разрядное целое число может хранить 65536 различных значений. Проблема в том, что, поскольку вы используете тип со знаком, вы можете использовать только 32767 положительных целочисленных значений. Вы переполняетесь и получаете отрицательные числа, когда пытаетесь сохранить значение больше этого.
Я считаю, что Arduino поддерживает 32-битные целые. Если вам нужны только положительные числа, используйте тип без знака.
Чтобы использовать явное 32-разрядное целое число:
#include <stdint.h>
int32_t my_int = 0;
Некоторая информация о стандартных размерах переменных (обратите внимание, что они могут быть разных размеров в зависимости от модели arduino, для которой построен код):
https://www.arduino.cc/en/Reference/Int
На Arduino Uno (и других платах на базе ATMega) int хранит
16-битное (2-байтовое) значение. Это дает диапазон от -32,768 до 32,767
(минимальное значение -2 ^ 15 и максимальное значение (2 ^ 15) — 1). На
Платы Arduino Due и SAMD (например, MKR1000 и Zero), инт
хранит 32-битное (4-байтовое) значение. Это дает диапазон -2 147 483 648
до 2 147 483 647 (минимальное значение -2 ^ 31 и максимальное значение (2 ^ 31)
— 1)
https://www.arduino.cc/en/Reference/UnsignedInt
На платах Uno и других платах ATMEGA, неподписанные целые числа (без знака)
целые числа) аналогичны целым числам в том, что они хранят 2-байтовое значение.
Вместо того, чтобы хранить отрицательные числа, они хранят только положительные
значения, дающие полезный диапазон от 0 до 65 535 (2 ^ 16) — 1).В Due хранится 4-байтовое (32-битное) значение в диапазоне от 0 до
4,294,967,295 (2 ^ 32 — 1).
https://www.arduino.cc/en/Reference/UnsignedLong
Длинные переменные без знака являются переменными расширенного размера для числа
хранить и хранить 32 бита (4 байта). В отличие от стандартных длинных без знака
longs не будет хранить отрицательные числа, делая их диапазон от 0 до
4,294,967,295 (2 ^ 32 — 1).
С этим кодом:
void calibrateSensors(int16_t * accelOffsets){
int16_t rawAccel[3];
// [...]
accelOffsets[0] += rawAccel[0];
Существует очевидная проблема: вы добавляете два 16-битные целые числа со знаком Вот. Типичное максимальное значение для 16-битного целого числа со знаком 0x7fff
(первый бит будет использоваться как знаковый бит), в десятичном виде 32767
,
Учитывая ваши первые два номера образцов, 17112 + 17116
уже 34228
, так что вы переполняете свой целочисленный тип.
Переполнение целого числа со знаком неопределенное поведение в с, потому что разные реализации могут использовать разные представления для отрицательных чисел. Для программы с неопределенное поведение, ты не можешь ожидать любой конкретный результат. Весьма вероятный результат состоит в том, что значение «обернется» в отрицательный диапазон.
Как вы уже используете типы из stdint.h
, решение простое: использовать uint32_t
для ваших сумм этот тип имеет достаточно битов для значений до 4294967295
,
Как правило: если вам никогда не нужно отрицательное значение, просто придерживайтесь беззнакового типа. Я не вижу причины, по которой вы используете int16_t
здесь, просто используйте uint16_t
,