Как можно отследить краткосрочную траекторию 6-осевого инерциального измерительного блока?

esp32 mpu6050 filter

Я искал и искал на форумах, писал одно сообщение за другим в DeepSeek и ChatGPT, но наткнулся на препятствие.

Сейчас я работаю над проектом, в котором хочу писать числа в воздухе, используя MPU6050, привязанный к кончику моего пальца вместе с микроконтроллером ESP32, поэтому я не хочу отслеживать положение в долгосрочной перспективе. Однако я не уверен, возможно ли это сделать. Датчик имеет гироскоп и акселерометр. Я попытался реализовать фильтр Mahony из библиотеки Adafruit AHRS, чтобы дважды интегрировать значения ускорения, исключить вектор силы тяжести и смотреть только на значения X и Z. На Python у меня есть программа визуализации, которая берет значения из плоскости XZ и строит график. Программа работает нормально, если писать очень быстро, не слишком крупно и сверху вниз (например, как вы бы написали 5, 3 или 2). Но для чисел вроде 8 или 6 приходится возвращаться к началу, и здесь программа дает сбой. Число становится искаженным и нечитаемым. Кроме того, когда я кладу датчик на стол, значения X и Z, как сообщается, становятся все более отрицательными с линейной скоростью, и я думал, что фильтр это отфильтрует... Есть ли что-то, что мне следует учесть? Есть ли какие-то упущения? Буду очень благодарен за совет!

Ниже представлена программа, которую я сейчас использую в Arduino IDE:

#include <Adafruit_MPU60.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_AHRS_Mahony.h>

Adafruit_MPU6050 mpu;
Adafruit_Mahony filter;

// Переменные положения и скорости
float vel_x = 0, vel_z = 0;
float pos_x = 0, pos_z = 0;
unsigned long last_time = 0;
bool recording = false;

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1);
  }

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

  filter.begin(30); // Инициализируем фильтр
}

void loop() {
  // Обработка команд запуска/остановки из Python
  if (Serial.available()) {
    char c = Serial.read();
    if (c == 'S') {
      recording = true;
      pos_x = 0;
      pos_z = 0;
      vel_x = 0;
      vel_z = 0;
      last_time = millis();
    } else if (c == 'E') {
      recording = false;
    }
  }

  if (recording) {
    sensors_event_t accel, gyro, temp;
    mpu.getEvent(&accel, &gyro, &temp);

    float gx = gyro.gyro.x;
    float gy = gyro.gyro.y;
    float gz = gyro.gyro.z;
    filter.updateIMU(gx, gy, gz, accel.acceleration.x, accel.acceleration.y, accel.acceleration.z);

    // Получить кватернион и повернуть линейное ускорение в мировую систему отсчета
    float q0, q1, q2, q3;
    filter.getQuaternion(&q0, &q1, &q2, &q3);
    float grav_x = 2.0f * (q1*q3 - q0*q2);
    float grav_y = 2.0f * (q0*q1 + q2*q3);
    float grav_z = 2.0f * (q0*q0 - 0.5f + q3*q3);

    // Вычислить линейное ускорение (рама датчика)
    float lax = accel.acceleration.x - grav_x * 9.81;
    float lay = accel.acceleration.y - grav_y * 9.81;
    float laz = accel.acceleration.z - grav_z * 9.81;

    // Повернуть в мировую систему координат (упрощенно для плоскости XZ)
    float ax_w = (1 - 2*q2*q2 - 2*q3*q3) * lax + (2*q1*q2 - 2*q0*q3) * lay + (2*q1*q3 + 2*q0*q2) * laz;
    float az_w = (2*q1*q3 - 2*q0*q2) * lax + (2*q2*q3 + 2*q0*q1) * lay + (1 - 2*q1*q1 - 2*q2*q2) * laz;

    // Интегрируем для получения позиции
    float dt = (millis() - last_time) / 1000.0;
    last_time = millis();

    vel_x += ax_w * dt;
    vel_z += az_w * dt;
    pos_x += vel_x * dt;
    pos_z += vel_z * dt;

    // Отправляем данные в Python
    Serial.print(pos_x);
    Serial.print(",");
    Serial.println(pos_z);

    delay(30);
  }
}

, 👍-1

Обсуждение

попробуйте это https://forum.arduino.cc/t/finally-mpu6050-fifo-dmp-quaternion-code-for-arduino-made-easy/598424, @jsotola

Не уверен, что это ваша проблема, но в любом случае: 1. Не вызывайте millis() дважды: вызовите его один раз и используйте одно и то же значение для вычисления dt и для обновления last_time. 2. Не используйте (0, 0, 9.81) в качестве вектора гравитации: выполняйте калибровку перед каждым запуском., @Edgar Bonet

Я не вижу никакой калибровки. В зависимости от используемых датчиков, это может означать, что перемещение по горизонтали или вертикали может быть практически нецелесообразным, если фактически не двигаться относительно Земли. Если это не так, я мог бы написать ответ с кратким описанием того, что требуется. Это проект POV? Часто эти датчики/программное обеспечение используются в компасах наклона. Не трясётся, как баллончик с краской. Интересно, нужно ли увеличить частоту дискретизации, чтобы успевать за изменениями движения. Кажется, я видел, как в проектах POV использовались простые методы. Например, датчики наклона. Затем предположим среднюю скорость человеческой руки., @st2000

Вы можете фильтровать фильтр Калмана., @liaifat85


2 ответа


0

Если устройство на основе акселерометра IMU сообщает о движении в состоянии покоя, как правило, ему требуется калибровка:

Большинство, если не все, инерциальные измерительные блоки (ИИБ) требуют калибровки. Без калибровки постоянная ошибка смещения может быть интерпретирована как постоянное ускорение, которое можно интегрировать в постоянно возрастающую скорость, которую можно интегрировать в изменения местоположения.

Как правило, инерциальный измерительный блок (IMU) содержит датчики для трёх пространственных измерений, называемые датчиками X, Y и Z. При калибровке учитываются как смещение, так и амплитуда этих датчиков. Вкратце, калибровочные смещения X, Y и Z добавляются или вычитаются из исходных значений датчиков таким образом, чтобы результирующие максимальные (направленные к Земле) и минимальные (направленные от Земли) значения для любого одного датчика были одинаковыми. После применения смещений амплитуду двух датчиков, вероятно, потребуется скорректировать, чтобы она соответствовала амплитуде третьего. Выбор датчика для согласования, а какой для корректировки, произволен. Это описание подходит для акселерометра.

Смещение гироскопа определить проще. Оставьте гироскоп в состоянии покоя и снимите показания датчиков X, Y и Z. Считываемые значения можно умножить на -1 и использовать в качестве смещений калибровки гироскопа по осям X, Y и Z.

Калибровки, выходящие за рамки ответа на стековый обмен:

  • Калибровка амплитуды гироскопа сложна и требует определённого уровня механического контроля. Короче говоря, поворот гироскопа в каждом из трёх положений на фиксированный угол может предоставить информацию, необходимую для расчёта значений калибровки амплитуды гироскопа.
  • Датчики, не идеально ортогональные. Для этого требуется найти калибровочные коэффициенты в матрице 3x3, где три диагональных значения описаны выше в этом ответе. Однако оставшиеся шесть значений представляют собой (неожиданный) вклад других (предположительно идеально) ортогональных датчиков.
,

0

Вы столкнулись с распространённой проблемой: дрейфом акселерометра и ошибкой интегрирования с течением времени, особенно при двойном интегрировании для определения местоположения. Даже с фильтром Махони небольшие смещения акселерометра быстро накапливаются, приводя к дрейфу оценок местоположения, что объясняет искажение и дрейф значений в состоянии покоя.

Чтобы улучшить краткосрочное отслеживание:

Обнулите скорость, когда движение останавливается (например, исходя из малой величины ускорения).

Используйте пороговые значения, чтобы игнорировать очень малые значения (шум).

Ограничьте время интеграции — выполняйте интеграцию только во время письменного жеста.

Для повышения краткосрочной точности рассмотрите возможность использования фильтра Калмана или сглаживания скользящим окном.

Однако в целом отслеживание истинного положения с помощью 6-осевых инерциальных измерительных блоков (IMU) без внешних источников (таких как камеры или UWB) по своей сути ненадежно для чего-либо, кроме коротких, ограниченных движений.

Вам нужна помощь в реализации сегментации жестов или логики обнуления скорости?

,