MPU6050 Проблема с NodeMCU
Я использую MPU6050 как часть IMU моего дрона, и я написал эту программу для Arduino Nano, чтобы получить ориентацию модуля MPU6050 в 3D-пространстве. И это прекрасно работает, но когда я загрузил этот точный код в NodeMCU, вывод начал только увеличиваться, т.е. всякий раз, когда происходит какое-либо изменение ориентации модуля, вывод вместо того, чтобы показывать конкретную ориентацию, увеличивается и на. Не могли бы вы помочь мне с интерфейсом MPU6050 с моим NodeMCU. Я предоставляю программу внизу.
#include <Wire.h>
const int MPU = 0x68; // I2C-адрес MPU6050
char status; // Объявление всех необходимых переменных
double T, P;
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll_sensor, pitch_sensor, yaw_sensor;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;
void setup() {
Serial.begin(115200);
Wire.begin(); // Инициализация связи
Wire.beginTransmission(MPU); // Начать связь с MPU6050 // MPU=0x68
Wire.write(0x6B); // Обращение к регистру 6B
Wire.write(0x00); // Сделать сброс - поместить 0 в регистр 6B
Wire.endTransmission(true); // конец передачи
calculate_IMU_error(); // Вызовите эту функцию, если вам нужно получить значения ошибок IMU для вашего модуля
delay(20);
}
void loop() {
// === Чтение данных ускорителя === //
Wire.beginTransmission(MPU);
Wire.write(0x3B); // Начать с регистра 0x3B (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(MPU, 14, true); //Чтение всего 6 регистров, значение каждой оси хранится в 2 регистрах
//Для диапазона +-2g нам нужно разделить необработанные значения на 16384, согласно таблице данных
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // Значение по оси X
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Значение по оси Y
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // значение по оси Z
// Расчет roll_sensor и pitch_sensor по данным акселерометра
accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * (180 / PI)) - AccErrorX;
accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * (180 / PI)) - AccErrorY;
// === Чтение данных гироскопа === //
previousTime = currentTime; // Предыдущее время сохраняется перед чтением фактического времени
currentTime = millis(); // Текущее время фактическое время чтения
elapsedTime = (currentTime - previousTime) / 1000; // Делим на 1000, чтобы получить секунды
Wire.beginTransmission(MPU);
Wire.write(0x43); // Адрес первого регистра данных гироскопа 0x43
Wire.endTransmission(false);
Wire.requestFrom(MPU, 14, true); //Чтение всего 4 регистра, значение каждой оси хранится в 2 регистрах
GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // Для диапазона 250 град/с мы должны сначала разделить необработанное значение на 131,0, согласно техническому описанию
GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
// Исправьте выходные данные с вычисленными значениями ошибки
GyroX = GyroX - GyroErrorX;
GyroY = GyroY - GyroErrorY;
GyroZ = GyroZ - GyroErrorZ;
// В настоящее время необработанные значения выражены в градусах в секунду, град/с, поэтому нам нужно умножить на сендоны (с), чтобы получить угол в градусах
gyroAngleX = gyroAngleX + (GyroX * elapsedTime); // град/с * с = град
gyroAngleY = gyroAngleY + (GyroY * elapsedTime);
yaw_sensor = yaw_sensor + (GyroZ * elapsedTime);
// Дополнительный фильтр - объединить значения акселерометра и угла гироскопа
roll_sensor = (0.94 * gyroAngleX) + (0.06 * accAngleX);
pitch_sensor = (0.94 * gyroAngleY) + (0.06 * accAngleY);
// Печатаем значения на последовательном мониторе
Serial.print(" roll_GY: "); Serial.print(roll_sensor);
Serial.print(" pitch_GY: "); Serial.print(pitch_sensor);
Serial.print(" yaw_GY: "); Serial.print(yaw_sensor);
Serial.println();
}
void calculate_IMU_error() {
// Мы можем вызвать эту функцию в разделе настройки, чтобы рассчитать погрешность данных акселерометра и гироскопа. Отсюда мы получим значения ошибок, используемые в приведенных выше уравнениях, напечатанных на последовательном мониторе.
// Обратите внимание, что мы должны разместить IMU горизонтально, чтобы получить правильные значения, чтобы мы могли затем получить правильные значения
// Чтение значений акселерометра 300 раз
while (c < 300) {
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0 ;
// Суммируем все показания
AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * (180 / PI)));
AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * (180 / PI)));
c++ ;
}
// Делим сумму на 200, чтобы получить значение ошибки
AccErrorX = AccErrorX / 300;
AccErrorY = AccErrorY / 300;
c = 0;
// Чтение значений гироскопа 300 раз
while (c < 300) {
Wire.beginTransmission(MPU);
Wire.write(0x43);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
GyroX = Wire.read() << 8 | Wire.read();
GyroY = Wire.read() << 8 | Wire.read();
GyroZ = Wire.read() << 8 | Wire.read();
// Суммируем все показания
GyroErrorX = GyroErrorX + (GyroX / 131.0);
GyroErrorY = GyroErrorY + (GyroY / 131.0);
GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
c++;
}
// Делим сумму на 300, чтобы получить значение ошибки
GyroErrorX = GyroErrorX / 300;
GyroErrorY = GyroErrorY / 300;
GyroErrorZ = GyroErrorZ / 300;
}
@Pritam Sarkar, 👍1
Обсуждение1 ответ
Лучший ответ:
Я вижу, что вы читаете 16-битные целые числа из IMU следующим образом:
// не работает на 32-битных системах
void loop() {
...
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // Значение по оси X
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Значение по оси Y
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // значение по оси Z
...
GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // ...
GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
....
}
Хотя слишком часто я вынужден использовать "#ifdef" настроить специфичный для платформы код для каждой платформы, в этом случае я почти уверен, что возможно написать кросс-платформенный совместимый код, который будет работать на обеих платформах; возможно, что-то вроде этого:
// предупреждение: не проверено, но думаю должно работать
int16_t wire_read_16(){
int16_t value = (Wire.read() << 8 | Wire.read());
return value;
}
void loop() {
...
AccX = (wire_read_16()) * (1.0f / 16384.0f); // Значение по оси X
AccY = (wire_read_16()) * (1.0f / 16384.0f); // Значение по оси Y
AccZ = (wire_read_16()) * (1.0f / 16384.0f); // значение по оси Z
...
GyroX = (wire_read_16()) * (1.0f / 131.0f); // Для диапазона 250 град/с мы должны сначала разделить необработанное значение на 131,0, согласно техническому описанию
GyroY = (wire_read_16()) * (1.0f / 131.0f);
GyroZ = (wire_read_16()) * (1.0f / 131.0f);
....
}
Я вижу это У onehorse, по-видимому, точно такая же проблема в "определения целочисленных типов в Teensy 3.1"
Похоже, вы наткнулись на особенность языка программирования C, где "int" варьируется от системы к системе. (См. Идо Гендель, "Проблема с целыми числами"). "инт" в некоторых системах (таких как ардуино на базе ATmega) это 16-битное целое число. "инт" в некоторых других системах (таких как ардуино на базе ARM, возможно, включая NodeMCU) это 32-битное целое число. ( inttypes и интегральные типы, определенные Arduino ; "Справочник по Arduino: int" ) В других системах "int"; множество других размеров.
Например, когда ваш IMU пытается отправить вам "-2", он отправляет 0xFFFE. Когда nano помещает это в свой «int»; которое представляет собой 16-битное целое число, оно затем правильно интерпретирует его как значение "-2". Я подозреваю, что происходит то, что когда другая система помещает это значение в свой "int" которое представляет собой 32-битное целое число, которое каким-то образом оказывается содержащим 0x0000FFFE, что означает +65534, но что должно произойти, так это "расширение подписи" так что 32-битное целое число в конечном итоге содержит 0xFFFFFFFE, что означает "-2". Поместив это значение в промежуточное значение "int16_t"; переменная, мы явно сообщаем компилятору, что это 16-битное целое число со знаком, поэтому позже, когда этот компилятор вставляет его в 32-битное целое число, он правильно расширяет знак.
Компилятор C (правильно) обрабатывает целое, разделенное на число с плавающей запятой, путем преобразования обоих значений в числа с плавающей запятой (и правильного расширения знака), а затем деления. То же самое с целым числом, разделенным на двойное число.
да! Я решил это, приведя необработанные данные MPU6050 к int16_t
как
AccelX = (((int16_t)Wire.read()<<8) | Wire.read())
, но снова преобладает одна проблема, а именно millis()
, которая применима для Arduino Nano, но ее проблематичное взаимодействие с Nodemcu до сих пор мне непонятно, не могли бы вы предложить способ измерения времени выполнения в NodeMCU! Большое спасибо, кстати!, @Pritam Sarkar
https://arduinoprosto.ru/q/56712/nodemcu-millis-simple-counter-how-long-is-led-on отвечает на ваш вопрос? Если нет, не могли бы вы опубликовать новый новый вопрос?, @David Cary
- Как подключить MPU9250 к NodeMCU с помощью SPI или I2C Slave?
- Не удается преобразовать строку в целое число при получении данных из pubnub
- Ведомое устройство Arduino с двумя мастерами, использующими одну и ту же шину I2C?
- Использовать NodeMCU 12E (ESP8266) в качестве экрана Wi-Fi для Arduino Nano
- Arduino Nano IOT LSM6DS3 получить угол гироскопа в градусах
- Как можно стабилизировать Arduino-квадрокоптер?
- Не удалось преобразовать «digitalWrite(8u, 1u)» из «void» в «bool»
- Как реализовать компас с компенсацией наклона?
«выход начал только увеличиваться» - что именно это означает?, @chrisl
О, я прошу прощения за то, что был неконкретен раньше, и я отредактировал это!, @Pritam Sarkar
gyroAngleX никогда не сбрасывается, только добавляется., @dandavis
Нет, все в порядке, так как в зависимости от знака
GyroX
,GyroY
,GyroZ
он дает ожидаемые цифры., @Pritam Sarkar