ИСПРАВЛЕНА ОШИБКА при использовании библиотеки проводов для получения направления по компасу
Я использую модуль магнитометра HMC-5983 для определения направления компаса автономного автомобиля, который я создаю с использованием платформы arduino.Я использую Ось Z и ось X для получения заголовка.
Однако, когда переменная headingFiltered равна 359, а автомобиль превращается в ноль, значение headingFiltered становится равным 0 по всем значениям от 0 до 360, а не просто становится 0. Это приводит к тому, что моя машина постоянно поворачивается из стороны в сторону, когда движется в этом соответствующем направлении. Кроме того, если в любом случае есть возможность улучшить эту функцию, я открыт для предложений.
Функция, которую я использую, заключается в следующем.
void chead() {
//---- Ось X
Wire.beginTransmission(Magnetometer); // передача на устройство
Wire.write(Magnetometer_mX1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1)
{
mX0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // передача на устройство
Wire.write(Magnetometer_mX0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1)
{
mX1 = Wire.read();
}
//---- Ось Y
Wire.beginTransmission(Magnetometer); // передача на устройство
Wire.write(Magnetometer_mY1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1)
{
mY0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // передача на устройство
Wire.write(Magnetometer_mY0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1)
{
mY1 = Wire.read();
}
//---- Ось Z
Wire.beginTransmission(Magnetometer); // передача на устройство
Wire.write(Magnetometer_mZ1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1)
{
mZ0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // передача на устройство
Wire.write(Magnetometer_mZ0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1)
{
mZ1 = Wire.read();
}
//---- Ось X
mX1=mX1<<8;
mX_out =mX0+mX1; // Необработанные данные
// Из таблицы данных: 0,92 мГ/цифра
Xm = mX_out*0.00092; // Единица Гаусса
// * Магнитное поле Земли колеблется от 0,25 до 0,65 Гаусса, так что это те значения, которые нам нужно получить приблизительно.
//---- Ось Y
mY1=mY1<<8;
mY_out =mY0+mY1;
Ym = mY_out*0.00092;
//---- Ось Z
mZ1=mZ1<<8;
mZ_out =mZ0+mZ1;
Zm = mZ_out*0.00092;
// ==============================
//Вычисление курса
heading = atan2(Zm, Xm);// тангенс дуги z/x
// Корректировка курса с учетом угла склонения в зависимости от вашего местоположения
declination = 0.03717551307 ;
heading += declination;
// Исправление, когда знаки почитаются
if(heading <0) heading += 2*PI;
// Корректировка за счет добавления угла склонения
if(heading > 2*PI)heading -= 2*PI;
headingDegrees = heading * 180/PI; // Заголовок в градусах
// Сглаживание выходного угла / Фильтр нижних частот
headingFiltered = headingFiltered*0.85 + headingDegrees*0.15;
//Отправка значения заголовка через последовательный порт в среду IDE обработки
// это было сделано, чтобы исправить ошибку. которая заключалась в том, что когда компас был повернут на 180 °, полученные показания не изменились на 180 °.
if(headingFiltered >= 0 && headingFiltered <= 89){
c_head=map(headingFiltered,0,90,270,359);
}
if(headingFiltered > 89 && headingFiltered <= 204){
c_head=map(headingFiltered,90,204,0,90);
}
if(headingFiltered > 204 && headingFiltered <= 290){
c_head=map(headingFiltered,205,290,91,180);
}
if(headingFiltered > 290){
c_head=map(headingFiltered,291,359,181,269);
}
//Дальнейшая калибровка
if(c_head>=0 && c_head<8){
c_head=352+c_head;
}
if(c_head>=8){
c_head=c_head-8;
}
// Serial.print("Градусы - ");
// Serial.println(c_head);
float teta1 = radians(clatval);
float teta2 = radians(dlatval);
float delta1 = radians(dlatval-clatval);
float delta2 = radians(dlongval-clongval); //================== Расчет формулы заголовка================//
float y = sin(delta2) * cos(teta2);
float x = cos(teta1)*sin(teta2) - sin(teta1)*cos(teta2)*cos(delta2);
float brng = atan2(y,x);
degris = degrees(brng);// радианы в градусах
degris = ( ((int)degris + 360) % 360 );
dif_h=c_head - degris;
abs_dif_h=abs(dif_h);
}
@AfiJaabb, 👍2
1 ответ
Лучший ответ:
Проблема возникает из-за используемого вами фильтра нижних частот:
// Сглаживание выходного угла / Фильтр нижних частот
headingFiltered = headingFiltered*0.85 + headingDegrees*0.15;
Этот фильтр предназначен для сглаживания быстрых изменений измеряемого курса. Если курс внезапно перескочит с 359 ° на 0 °, фильтр заменит этот скачок непрерывным изменением, которое проходит через промежуточные значения.
Я могу придумать три различных варианта решения этой проблемы.
Вариант 1: отфильтровать декартовы координаты
В отличие от направления движения, компоненты магнитного поля в плоскости плавно изменяются при вращении транспортного средства. Вы можете отфильтровать эти компоненты перед вычислением заголовка следующим образом:
const float filter_constant = 0.15;
float filtered_angle(float y, float x)
{
static float x_f, y_f; // отфильтрованные компоненты
x_f += filter_constant * (x - x_f);
y_f += filter_constant * (y - y_f);
return atan2(y, x);
}
Тогда вы бы заменили
heading = atan2(Zm, Xm);
Автор:
heading = filtered_angle(Zm, Xm);
Вариант 2: отфильтруйте развернутый заголовок
Другой возможный подход заключается в том, чтобы развернуть заголовок перед его фильтрацией. Это похоже на разворачивание фазы: вместо того, чтобы требовать, чтобы заголовок ограничивался интервалом [−π, π] (или [0, 360 °), если вы работаете с градусами), вы позволяете ему принимать произвольные значения. Каждый раз, когда ваш автомобиль совершает полный поворот, направление движения в развернутом положении меняется на ±360°. Вот простой разворачивающий фильтр:
// Разверните угол в радианах.
float unwrap(float angle)
{
static int revolutions;
static float previous_angle;
float delta = angle - previous_angle;
previous_angle = angle;
if (delta >= M_PI)
revolutions--;
else if (delta < -M_PI)
revolutions++;
return 2*M_PI*revolutions + angle;
}
Развернутый заголовок является непрерывным и может быть безопасно отфильтрован по нижним частотам
, не создавая проблем, которые у вас есть сейчас. После
фильтра нижних частот вы можете снова обернуть его с помощью функции fmod()
, если хотите.
Вариант 3: оберните различия в заголовках внутри фильтра
Предыдущую идею можно применить и в обратном направлении: вместо того, чтобы разворачивать заголовок, вы можете обернуть различия заголовков в фильтр нижних частот, чтобы сохранить их в пределах [−π, +π) (т. е. [−180°, +180°)). Затем, если отфильтрованный заголовок равен 359 °, и вы получаете новый необработанный заголовок 0 °, разница составляет -359 °, но вы переносите ее на +1 °. Это приведет к незначительному увеличению отфильтрованного выходного сигнала до 359,15° при выбранной вами константе фильтра 0,15.
Вот фильтр нижних частот, основанный на этой идее, который знает, как должны обтекаться углы:
const float filter_constant = 0.15;
// Заверните угол в [-pi, pi).
float wrap(float angle)
{
while (angle < -M_PI) angle += 2*M_PI;
while (angle >= M_PI) angle -= 2*M_PI;
return angle;
}
// Фильтр нижних частот под углом.
float filter_angle(float angle)
{
static float filtered_angle;
filtered_angle += filter_constant * wrap(angle - filtered_angle);
filtered_angle = wrap(filtered_angle);
return filtered_angle;
}
Я думаю, что этот вариант мне нравится больше, чем два других, потому что он выполняет свою
арифметику таким образом, который согласуется с тем, как предполагается
переносить углы, а также потому, что он
сохраняет меньше состояния (только одна статическая
переменная). Кроме того, вы
реализовали что-то очень похожее на описанную выше функцию wrap(),
чтобы обернуть заголовок в [0, 2π). Вместо этого вы могли бы повторно использовать
обернуть()
следующим образом:
// Обернуть в [0, 2 пи).
heading = wrap(heading - M_PI) + M_PI;
Обновление: Существует недавно опубликованная библиотека Arduino, которая реализует
этот третий вариант. Он “предоставляет класс runningAngle
, который вычисляет
экспоненциально взвешенное среднее значение ряда углов, таких как
показания компаса. Он знает, как углы переносятся по модулю 360 ° ”.
Библиотека называется runningAngle и доступна через
диспетчер библиотек Arduino IDE.
- Не могу заставить MMC3416xPJ работать
- Направление по компасу не изменяется линейно
- Отправка и получение различных типов данных через I2C в Arduino
- Как выбрать альтернативные контакты I2C на ESP32?
- Альтернативы библиотеке Wire для I2C
- Библиотека Wire.h работает на Uno, но не компилируется для ATtiny85
- Вызов I2C во время Wire.requestFrom
- I2C связь между Arduino Uno и Nodemcu32-s (ESP32)