LSM303 не может компенсировать наклон
Я работаю над компасом с компенсацией наклона с LSM303DLHC. Мне удается откалибровать магнитную и ускорительную часть. Мне удалось рассчитать тангаж и крен. Но когда дело доходит до расчета компенсации наклона компаса, что-то идет не так. Мои значения на последовательном мониторе просто не имеют смысла ... Если я вычисляю направление компаса из калиброванных значений магнето, он работает с использованием оси Y в качестве магнитного вектора. Как бы я мог изменить это на ось X?
LSM303DLHC including accelerometer to correct for position
*/
#include <Wire.h>
const int LSM303_ADDR = 0x19;
const int LSM303_ADDR_MAG = 0x1E;
// адреса регистров управления
const int CTRL_REG1_ADDR = 0x20;
const int CTRL_REG2_ADDR = 0x21;
const int CTRL_REG3_ADDR = 0x22;
const int CTRL_REG4_ADDR = 0x23;
const int CTRL_REG5_ADDR = 0x24;
const int CTRL_REG6_ADDR = 0x25;
const int CRA_REG_M_ADDR = 0x00;
const int CRB_REG_M_ADDR = 0x01;
const int MR_REG_M_ADDR = 0x02;
//регистр данных адресует сначала младший бит для акселератора, первый старший бит для магнето
const int Accelero_First_data_addr = 0x28;
const int Magneto_First_data_addr = 0x03;
const int Temp_out_data_addr = 0x31;// первые 12 бит MSB
int CTRL_REG1_A_value = 0x47; // 50Hz Low Power отключен, все оси включены
int CTRL_REG2_A_value = 0x00; //
int CTRL_REG3_A_value = 0x00; //нет прерываний или водяных знаков
int CTRL_REG4_A_value = 0x00; // Полная шкала 2G, высокое разрешение, режим SPI не выбран
int CTRL_REG5_A_value = 0x00; // ничего не включено
int CTRL_REG6_A_value = 0x00; // ничего не включено
int CRA_REG_M_value = 0x14; // Датчик температуры включен, скорость передачи данных 15 Гц
int CRB_REG_M_value = 0x20; // 2G — это данные 1100–980 G/LSB
int MR_REG_M_value = 0x00;
int16_t MagRaw_X_axis_value, MagRaw_Y_axis_value, MagRaw_Z_axis_value;
int16_t AccRaw_X_axis_value, AccRaw_Y_axis_value, AccRaw_Z_axis_value;
float pitch, roll;
float Xm_calibrated, Ym_calibrated, Zm_calibrated;
float Xm_norm, Ym_norm, Zm_norm, Xa_norm, Ya_norm, Za_norm;
float norm_a, norm_m;
void setup() {// поместите сюда код установки для однократного запуска:
Serial.begin(9600);
Wire.begin();
setupLSM303();
Serial.println("Setup complete..");
}
void loop() {// поместите сюда свой основной код для повторного запуска:
MagnetoDataRead();
AcceleroDataRead();
}
void setupLSM303() {
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG1_ADDR);
Wire.write(CTRL_REG1_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG2_ADDR);
Wire.write(CTRL_REG2_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG3_ADDR);
Wire.write(CTRL_REG3_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG4_ADDR);
Wire.write(CTRL_REG4_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG5_ADDR);
Wire.write(CTRL_REG5_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG6_ADDR);
Wire.write(CTRL_REG6_A_value);
Wire.endTransmission();
//настройка магниторегистра
Wire.beginTransmission(LSM303_ADDR_MAG);
Wire.write(CRA_REG_M_ADDR);
Wire.write(CRA_REG_M_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR_MAG);
Wire.write(CRB_REG_M_ADDR);
Wire.write(CRB_REG_M_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR_MAG);
Wire.write(MR_REG_M_ADDR);
Wire.write(MR_REG_M_value);
Wire.endTransmission();
}
void MagnetoDataRead() {
Wire.beginTransmission(LSM303_ADDR_MAG );
Wire.write(Magneto_First_data_addr);
Wire.endTransmission();
Wire.requestFrom(LSM303_ADDR_MAG , 6);
if (Wire.available() <= 6) {
MagRaw_X_axis_value = (Wire.read() << 8) | (Wire.read());
MagRaw_Y_axis_value = (Wire.read() << 8) | (Wire.read());
MagRaw_Z_axis_value = (Wire.read() << 8) | (Wire.read());
float X_mag_cal = MagRaw_X_axis_value / 1100.0;
float Y_mag_cal = MagRaw_Y_axis_value / 1100.0;
float Z_mag_cal = MagRaw_Z_axis_value / 980.0;
//вводим коэффициенты калибровки
float Xm_offset_corrected = MagRaw_X_axis_value - 0.041423;
float Ym_offset_corrected = MagRaw_Y_axis_value + 0.022448;
float Zm_offset_corrected = MagRaw_Z_axis_value + 0.078354;
Xm_calibrated = 94.622065 * Xm_offset_corrected + 0.831029 * Ym_offset_corrected + 1.647934 * Zm_offset_corrected;
Ym_calibrated = 0.831029 * Xm_offset_corrected + 93.462985 * Ym_offset_corrected + 3.416885 * Zm_offset_corrected;
Zm_calibrated = 1.647934 * Xm_offset_corrected + 3.416885 * Ym_offset_corrected + 76.477691 * Zm_offset_corrected;
//формула нормализации
norm_m = sqrt(sq(Xm_calibrated) + sq(Ym_calibrated) + sq(Zm_calibrated));
Xm_norm = Xm_calibrated / norm_m;
Ym_norm = Ym_calibrated / norm_m;
Zm_norm = Zm_calibrated / norm_m;
//заголовок с плавающей запятой = ((atan2(Ym_calibrated, Xm_calibrated)) * 180 ) / PI;
//если (Заголовок < 0) {
// Заголовок = 360 + Заголовок;
//}
/*
Serial.print(X_mag_cal, 10);
Serial.print("\t");
Serial.print(Y_mag_cal, 10);
Serial.print("\t");
Serial.print(Z_mag_cal, 10);
Serial.println("\t");
*/
delay(50);
}
}
void AcceleroDataRead() {
Wire.beginTransmission(LSM303_ADDR);
Wire.write(Accelero_First_data_addr | 0x80);
Wire.endTransmission();
Wire.requestFrom(LSM303_ADDR, 6);
if (Wire.available() <= 6) {
AccRaw_X_axis_value = (Wire.read()) | (Wire.read() << 8);
AccRaw_Y_axis_value = (Wire.read()) | (Wire.read() << 8);
AccRaw_Z_axis_value = (Wire.read()) | (Wire.read() << 8);
float X_Acc = (AccRaw_X_axis_value >> 4);
float Y_Acc = (AccRaw_Y_axis_value >> 4);
float Z_Acc = (AccRaw_Z_axis_value >> 4);
//калибровать смещение
float X_acc_offset_corrected = X_Acc + 6.676443;
float Y_acc_offset_corrected = Y_Acc - 6.622381;
float Z_acc_offset_corrected = Z_Acc + 55.408804;
//калибровать
float X_acc_calibrated = 0.977273 * X_acc_offset_corrected - 0.003458 * Y_acc_offset_corrected + 0.012639 * Z_acc_offset_corrected;
float Y_acc_calibrated = -0.003458 * X_acc_offset_corrected + 0.979674 * Y_acc_offset_corrected - 0.001130 * Z_acc_offset_corrected;
float Z_acc_calibrated = 0.012639 * X_acc_offset_corrected - 0.001130 * Y_acc_offset_corrected + 0.897083 * Z_acc_offset_corrected;
// нормализовать
norm_a = sqrt(sq(X_acc_calibrated) + sq(Y_acc_calibrated) + sq(Z_acc_calibrated));
Xa_norm = X_acc_calibrated / norm_a;
Ya_norm = Y_acc_calibrated / norm_a;
Za_norm = Z_acc_calibrated / norm_a;
// вычисляем тангаж и крен
pitch = asin(-Xa_norm) * 180 / PI;
roll = (atan2(Ya_norm, Za_norm) * 180) / PI;
//рассчитываем векторы для компенсации наклона
float Xh = Xm_norm * cos(pitch * PI / 180) + Zm_norm * sin(pitch * PI / 180);
float Yh = Xm_norm * sin(roll * PI / 180) * sin(pitch * PI / 180) + Ym_norm * cos(roll * PI / 180) - Zm_norm * sin(roll * PI / 180) * cos(pitch * PI / 180);
// вычислить курс с компенсацией наклона
float Heading = ((atan2(Yh, Xh)) * 180.0 ) / PI;
if (Heading < 0) {
Heading = 360 + Heading;
}
Serial.print("Pitch \t");
Serial.print(pitch);
Serial.print("\t roll \t");
Serial.print(roll);
Serial.print("\t");
Serial.println(Heading);
delay(50);
}
}
Я застрял на этом уже несколько дней. Любая помощь приветствуется!
@sanrays10, 👍-1
Обсуждение2 ответа
Лучший ответ:
Кажется, у меня все получилось! Большое спасибо за вашу помощь, я должен вам несколько пива. Для всех, кто следит и интересуется, вот мой код:
LSM303DLHC including accelerometer to correct for position
Vector calculation to be implemented
*/
#include <Wire.h>
#include <Math.h>
const int LSM303_ADDR = 0x19;
const int LSM303_ADDR_MAG = 0x1E;
// адреса регистров управления
const int CTRL_REG1_ADDR = 0x20;
const int CTRL_REG2_ADDR = 0x21;
const int CTRL_REG3_ADDR = 0x22;
const int CTRL_REG4_ADDR = 0x23;
const int CTRL_REG5_ADDR = 0x24;
const int CTRL_REG6_ADDR = 0x25;
const int CRA_REG_M_ADDR = 0x00;
const int CRB_REG_M_ADDR = 0x01;
const int MR_REG_M_ADDR = 0x02;
//регистр данных адресует сначала младший бит для акселератора, первый старший бит для магнето
const int Accelero_First_data_addr = 0x28;
const int Magneto_First_data_addr = 0x03;
const int Temp_out_data_addr = 0x31;// первые 12 бит MSB
int CTRL_REG1_A_value = 0x47; // 50Hz Low Power отключен, все оси включены
int CTRL_REG2_A_value = 0x00; //
int CTRL_REG3_A_value = 0x00; //нет прерываний или водяных знаков
int CTRL_REG4_A_value = 0x00; // Полная шкала 2G, высокое разрешение, режим SPI не выбран
int CTRL_REG5_A_value = 0x00; // ничего не включено
int CTRL_REG6_A_value = 0x00; // ничего не включено
int CRA_REG_M_value = 0x14; // Датчик температуры включен, скорость передачи данных 15 Гц
int CRB_REG_M_value = 0x20; // 2G — это данные 1100–980 G/LSB
int MR_REG_M_value = 0x00;
int16_t MagRaw_X_axis_value, MagRaw_Y_axis_value, MagRaw_Z_axis_value;
int16_t AccRaw_X_axis_value, AccRaw_Y_axis_value, AccRaw_Z_axis_value;
float pitch, roll;
float Xm_calibrated, Ym_calibrated, Zm_calibrated;
float Xm_norm, Ym_norm, Zm_norm, Xa_norm, Ya_norm, Za_norm;
float norm_a, norm_m;
void setup() {// поместите сюда код установки для однократного запуска:
Serial.begin(9600);
Wire.begin();
setupLSM303();
Serial.println("Setup complete..");
}
void loop() {// поместите сюда свой основной код для повторного запуска:
MagnetoDataRead();
AcceleroDataRead();
}
void setupLSM303() {
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG1_ADDR);
Wire.write(CTRL_REG1_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG2_ADDR);
Wire.write(CTRL_REG2_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG3_ADDR);
Wire.write(CTRL_REG3_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG4_ADDR);
Wire.write(CTRL_REG4_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG5_ADDR);
Wire.write(CTRL_REG5_A_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR);
Wire.write(CTRL_REG6_ADDR);
Wire.write(CTRL_REG6_A_value);
Wire.endTransmission();
//настройка магниторегистра
Wire.beginTransmission(LSM303_ADDR_MAG);
Wire.write(CRA_REG_M_ADDR);
Wire.write(CRA_REG_M_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR_MAG);
Wire.write(CRB_REG_M_ADDR);
Wire.write(CRB_REG_M_value);
Wire.endTransmission();
Wire.beginTransmission(LSM303_ADDR_MAG);
Wire.write(MR_REG_M_ADDR);
Wire.write(MR_REG_M_value);
Wire.endTransmission();
}
void MagnetoDataRead() {
Wire.beginTransmission(LSM303_ADDR_MAG );
Wire.write(Magneto_First_data_addr);
Wire.endTransmission();
Wire.requestFrom(LSM303_ADDR_MAG , 6);
if (Wire.available() <= 6) {
MagRaw_X_axis_value = (Wire.read() << 8) | (Wire.read());
MagRaw_Z_axis_value = (Wire.read() << 8) | (Wire.read());
MagRaw_Y_axis_value = (Wire.read() << 8) | (Wire.read());
float X_mag_cal = MagRaw_X_axis_value / 1100.0;
float Y_mag_cal = MagRaw_Y_axis_value / 1100.0;
float Z_mag_cal = MagRaw_Z_axis_value / 980.0;
//вводим коэффициенты калибровки
float Xm_offset_corrected = X_mag_cal - 0.032874;
float Ym_offset_corrected = Y_mag_cal + 0.047876;
float Zm_offset_corrected = Z_mag_cal + 0.039649;
Xm_calibrated = 92.387197 * Xm_offset_corrected - 0.307374 * Ym_offset_corrected + 1.151625 * Zm_offset_corrected;
Ym_calibrated = -0.307374 * Xm_offset_corrected + 85.002110 * Ym_offset_corrected + 1.169755 * Zm_offset_corrected;
Zm_calibrated = 1.151625 * Xm_offset_corrected + 1.169755 * Ym_offset_corrected + 80.276037 * Zm_offset_corrected;
//формула нормализации
norm_m = sqrt(sq(Xm_calibrated) + sq(Ym_calibrated) + sq(Zm_calibrated));
Xm_norm = Xm_calibrated / norm_m;
Ym_norm = Ym_calibrated / norm_m;
Zm_norm = Zm_calibrated / norm_m;
/*
Serial.print(X_mag_cal, 10);
Serial.print("\t");
Serial.print(Y_mag_cal, 10);
Serial.print("\t");
Serial.print(Z_mag_cal, 10);
Serial.println("\t");
*/
//заголовок с плавающей запятой = ((atan2(Ym_norm, Xm_norm)) * 180.0) / PI;
//если (Заголовок < 0) {
// Заголовок = 360 + Заголовок;
//}
//Serial.println(Заголовок);
// задержка (50);
}
}
void AcceleroDataRead() {
Wire.beginTransmission(LSM303_ADDR);
Wire.write(Accelero_First_data_addr | 0x80);
Wire.endTransmission();
Wire.requestFrom(LSM303_ADDR, 6);
if (Wire.available() <= 6) {
AccRaw_X_axis_value = (Wire.read()) | (Wire.read() << 8);
AccRaw_Y_axis_value = (Wire.read()) | (Wire.read() << 8);
AccRaw_Z_axis_value = (Wire.read()) | (Wire.read() << 8);
float X_Acc = (AccRaw_X_axis_value >> 4);
float Y_Acc = (AccRaw_Y_axis_value >> 4);
float Z_Acc = (AccRaw_Z_axis_value >> 4);
//калибровать смещение
float X_acc_offset_corrected = X_Acc + 6.676443;
float Y_acc_offset_corrected = Y_Acc - 6.622381;
float Z_acc_offset_corrected = Z_Acc + 55.408804;
//калибровать
float X_acc_calibrated = 0.977273 * X_acc_offset_corrected - 0.003458 * Y_acc_offset_corrected + 0.012639 * Z_acc_offset_corrected;
float Y_acc_calibrated = -0.003458 * X_acc_offset_corrected + 0.979674 * Y_acc_offset_corrected - 0.001130 * Z_acc_offset_corrected;
float Z_acc_calibrated = 0.012639 * X_acc_offset_corrected - 0.001130 * Y_acc_offset_corrected + 0.897083 * Z_acc_offset_corrected;
// нормализовать
norm_a = sqrt(sq(X_acc_calibrated) + sq(Y_acc_calibrated) + sq(Z_acc_calibrated));
Xa_norm = X_acc_calibrated / norm_a;
Ya_norm = Y_acc_calibrated / norm_a;
Za_norm = Z_acc_calibrated / norm_a;
// вычисляем тангаж и крен
/*
pitch = asin(-Xa_norm) * 180 / PI;
roll = asin(Ya_norm / cos(pitch * PI / 180)) * 180 / PI;
*/
roll = atan2(Ya_norm, Za_norm);
float Gz2 = Ya_norm * sin(roll) + Za_norm * cos(roll);
pitch = atan(-Xa_norm / Gz2);
float By2 = Zm_norm * sin(roll) - Ym_norm * cos(roll);
float Bz2 = Ym_norm * sin(roll) + Zm_norm * cos(roll);
float Bx3 = Xm_norm * cos(pitch) + Bz2 * sin(pitch);
float yaw = (atan2(By2, Bx3)*180)/PI;
if (yaw < 0) {
yaw = 360 + yaw;
}
float tilt = atan2(sqrt((sq(Xa_norm)+(sq(Ya_norm)))),Za_norm);
//рассчитываем векторы для компенсации наклона
float Xh = Xm_norm * cos(pitch) + Zm_norm * sin(pitch);
float Yh = Xm_norm * sin(roll ) * sin(pitch) + Ym_norm * cos(roll) - Zm_norm * sin(roll) * cos(pitch);
// расчет курса с компенсацией наклона
float Heading = ((atan2(Yh, Xh)) * 180.0 ) / PI;
if (Heading < 0) {
Heading = 360 + Heading;
}
Serial.print("Pitch \t");
Serial.print(pitch * 180 / PI);
Serial.print("\t roll \t");
Serial.print(roll * 180 / PI);
Serial.print("\t tilt \t");
Serial.print(tilt*180/PI);
Serial.print("\t");
Serial.println(Heading);
delay(500);
}
}
Нашел...Данные Magneto выходят XZY, а не XYZ...когда я это изменил, все заработало! Извините, RTFMA сразу бы дал мне ответ.
Рад слышать это. Но вы разместили ответ 10 часов назад и сделали комментарий, что он все еще не работает 8 часов назад. Что он?, @st2000
Извините за путаницу. Данные датчика для магнето и акселератора работают независимо. Но когда мне приходится комбинировать 2 для компенсации наклона... это не компенсирует..., @sanrays10
Попробуйте изменить знак вашего значения Z-нормаль, прежде чем использовать его в ваших уравнениях Xh и Yh (примерно за 15 строк до конца вашей программы). Если это сработает, я опубликую ответ, объясняющий, почему., @st2000
Я пробовал Za_norm = -Z_acc_calibrated/norm_a; но не повезло. Я также пытался изменить знак в формуле Xh... тоже не повезло..., @sanrays10
облом. Меня тоже интересует расчет тона. Это отличается от бумаги STMicro, на которую я смотрю. Вы используете [этот документ stmicro 2010](https://www.pololu.com/file/0J434/LSM303DLH-compass-app-note.pdf)? Я даже не могу найти его на веб-сайте STMicro. Попробуйте [этот документ stmicro 2018](https://www.st.com/content/ccc/resource/technical/document/design_tip/group0/56/9a/e4/04/4b/6c/44/ef/DM00269987/ files/DM00269987.pdf/jcr:content/translations/en.DM00269987.pdf). Похоже, расчет шага отличается. Вы можете распечатать значения Pitch и Roll для проверки., @st2000
Спасибо, что прислали мне эту ссылку! Это очень полезно. Расчеты по тангажу и крену были распечатаны и оказались правильными. Я попробую реализовать расчеты из документа, но для этого мне нужно время. На данный момент я уже могу сказать вам, что эти формулы также работают. На следующий шаг!, @sanrays10
- Как отфильтровать или исключить показания магнитного поля Земли вокруг 3D-магнитного датчика?
- В чем разница между акселерометром, гироскопом и датчиком магнитометра?
- Проблемы с подключением I2C на ESP8266 — 12F, какие контакты использовать?
- Почему dtostrf() не работает для этого значения?
- Отправка данных из ESP8266 в PHP
- Могут ли ESP8266 и HC-SR04 дружить?
- Как определить наличие воды с помощью всего двух проводов
- Wifi Регистратор данных без Задержки
Вы разрабатываете оценочный комплект STMicro? Если это так, вы можете использовать библиотеки STMicro MotionMC и MotionEC (внимание, без исходного кода!) для реализации наклонного компаса. Плохо, это форум Arduino - конечно, вы не используете комплект разработки STMicro!, @st2000
Кроме того, вы хотите наклонить свой компас сверх нормы? В смысле больше, чем, скажем, 30 градусов? Если вы хотите, чтобы ваш компас находился под углом 90 градусов относительно местной поверхности Земли, вам придется сделать больше, чем просто реализовать наклон компаса., @st2000
Привет! Спасибо за комментарий! Я разрабатываю NodeMcu с Arduino IDE. Мой компас не будет наклонен дальше таких углов. Формулы, используемые в моем коде, взяты из примечаний к приложению lsm303., @sanrays10
Хм, в другом чипе STMicro (lsm9ds1) один из векторов акк находится в обратном направлении WRT магнита. Я не думаю, что это проблема с LSM303. Могут ли у вас возникнуть проблемы с преобразованием в/из градусов/радианов? Это может сбивать с толку в зависимости от того, откуда вы получаете свою триггерную функцию. Кроме того (просто любопытно), вы держите все в углах Эйлера или используете кватернионы?, @st2000
Я где-то читал, что для dlhc магнитное поле Земли выровнено по оси Y. Позвольте мне еще раз проверить источник. Хотя это лишь частично проблема, которая у меня есть., @sanrays10
Не должно быть. В разделе 1.2 на рис. 2 спецификаций LSM303DLHC показаны lms303 acc X+ и mag X+ (а также acc Y+ и mag Y+) в одном направлении. Обратите внимание, что не все микросхемы STMicro настроены таким образом. Будьте осторожны, чтобы не использовать пример кода неправильного чипа!, @st2000
Извините, я действительно ошибся. Он выровнен по оси X. Я должен направить ось Y на землю, чтобы получить достоверные показания. Что в моем случае означает установку печатной платы вертикально. Хотя моя формула для заголовка взята из примечаний к применению..., @sanrays10
Это звучит неправильно. Все примеры компасов STMicro, которые я видел, имеют оси X и Y, параллельные поверхности земли, и Z, перпендикулярные поверхности земли. (Примечание: не подносите постоянные магниты близко к магнитометру. Я так и сделал, и мне пришлось выкопать старый инструмент для размагничивания, чтобы магнитометр снова заработал.), @st2000
Я должен вернуться к началу, я думаю, в этом случае. Я буду отображать необработанные значения датчиков по одному, чтобы увидеть, что происходит. Все это просто пока не имеет для меня смысла., @sanrays10
Теперь, когда у меня работают магнитометр и акселерометр, кажется, что если я наклоняю компас, мои направления все еще колеблются... хотя я применяю формулы из примечания к приложению...., @sanrays10