Как получить направление гравитации

Мне нужно рассчитать линейное ускорение на основе акселерометра, гироскопа и магнитометра. Я нашел приложение для Android, которое делает именно то, чего я хочу добиться:

https://play.google.com/store/apps/details?id=com.kircherelectronics.fusedlinearacceleration. https://github.com/KEOpenSource/FusedLinearAcceleration

Я пытаюсь портировать его на чистую java. Поскольку некоторые элементы кода основаны на виртуальных датчиках (датчик гравитации), я хотел бы добиться того же результата, вычислив направление силы тяжести на основе трех основных датчиков. Я читал, что силу гравитации можно рассчитать с помощью Low Pass Filter (тот же, что и в Android ‹ 4.0), но этот метод не дает очень точных результатов.

Начиная с Android 4.0, сила тяжести на каждой оси рассчитывается с помощью слияния датчиков. Я нашел код, отвечающий за эти измерения, но он написан в СРР:

https://github.com/android/platform_frameworks_base/blob/ics-mr1/services/sensorservice/GravitySensor.cpp

Используемый там метод называется "getRotationMatrix". Тот же метод в классе SensorManager.java: https://gitorious.org/android-eeepc/base/source/9cb3e09ec49351401cf19b5ae5092dd9ca90a538%3acore/java/android/hardware/SensorManager.java#L1034

    public static boolean getRotationMatrix(float[] R, float[] I,
        float[] gravity, float[] geomagnetic) {
    // TODO: move this to native code for efficiency
    float Ax = gravity[0];
    float Ay = gravity[1];
    float Az = gravity[2];
    final float Ex = geomagnetic[0];
    final float Ey = geomagnetic[1];
    final float Ez = geomagnetic[2];
    float Hx = Ey*Az - Ez*Ay;
    float Hy = Ez*Ax - Ex*Az;
    float Hz = Ex*Ay - Ey*Ax;
    final float normH = (float)Math.sqrt(Hx*Hx + Hy*Hy + Hz*Hz);
    if (normH < 0.1f) {
        // device is close to free fall (or in space?), or close to
        // magnetic north pole. Typical values are  > 100.
        return false;
    }
    final float invH = 1.0f / normH;
    Hx *= invH;
    Hy *= invH;
    Hz *= invH;
    final float invA = 1.0f / (float)Math.sqrt(Ax*Ax + Ay*Ay + Az*Az);
    Ax *= invA;
    Ay *= invA;
    Az *= invA;
    final float Mx = Ay*Hz - Az*Hy;
    final float My = Az*Hx - Ax*Hz;
    final float Mz = Ax*Hy - Ay*Hx;
    if (R != null) {
        if (R.length == 9) {
            R[0] = Hx;     R[1] = Hy;     R[2] = Hz;
            R[3] = Mx;     R[4] = My;     R[5] = Mz;
            R[6] = Ax;     R[7] = Ay;     R[8] = Az;
        } else if (R.length == 16) {
            R[0]  = Hx;    R[1]  = Hy;    R[2]  = Hz;   R[3]  = 0;
            R[4]  = Mx;    R[5]  = My;    R[6]  = Mz;   R[7]  = 0;
            R[8]  = Ax;    R[9]  = Ay;    R[10] = Az;   R[11] = 0;
            R[12] = 0;     R[13] = 0;     R[14] = 0;    R[15] = 1;
        }
    }
    if (I != null) {
        // compute the inclination matrix by projecting the geomagnetic
        // vector onto the Z (gravity) and X (horizontal component
        // of geomagnetic vector) axes.
        final float invE = 1.0f / (float)Math.sqrt(Ex*Ex + Ey*Ey + Ez*Ez);
        final float c = (Ex*Mx + Ey*My + Ez*Mz) * invE;
        final float s = (Ex*Ax + Ey*Ay + Ez*Az) * invE;
        if (I.length == 9) {
            I[0] = 1;     I[1] = 0;     I[2] = 0;
            I[3] = 0;     I[4] = c;     I[5] = s;
            I[6] = 0;     I[7] =-s;     I[8] = c;
        } else if (I.length == 16) {
            I[0] = 1;     I[1] = 0;     I[2] = 0;
            I[4] = 0;     I[5] = c;     I[6] = s;
            I[8] = 0;     I[9] =-s;     I[10]= c;
            I[3] = I[7] = I[11] = I[12] = I[13] = I[14] = 0;
            I[15] = 1;
        }
    }
    return true;
}

принимает четыре аргумента:

float [] R, float [] I, float [] gravity, float [] Geomagnetic.

Один из них - просто гравитация... Код, над которым я сейчас работаю, похож на

https://github.com/KEOpenSource/FusedLinearAcceleration/blob/master/FusedLinearAcceleration/src/com/kircherelectronics/fusedlinearacceleration/sensor/LinearAccelerationSensor.java,

за исключением методов, которые ссылаются на SensorManager. Они скопированы из источника Android:

https://gitorious.org/android-eeepc/base/source/9cb3e09ec49351401cf19b5ae5092dd9ca90a538%3acore/java/android/hardware/SensorManager.java.

Я не нашел примеров того, как реализовать это на Java.

Итак, мой вопрос: как я могу реализовать метод (в java), основанный только на трех основных датчиках, который возвращает мне массив направления гравитации (x, y, z), аналогичный Android, но без использования Android API.


person Bresiu    schedule 01.07.2014    source источник
comment
Пожалуйста, отформатируйте свой вопрос, чтобы он мог быть читаем человеком   -  person eliasah    schedule 02.07.2014
comment
@eliasah, должен ли я еще больше улучшить читаемость, или теперь все в порядке?   -  person Bresiu    schedule 02.07.2014
comment
Ваш вопрос все еще не ясен   -  person eliasah    schedule 02.07.2014
comment
Это может помочь: stackoverflow.com/a/2986854/1290264   -  person bcorso    schedule 02.07.2014
comment
Я обнаружил, что вектор гравитации можно вычислить из кватернионов. Я использовал следующие методы: gist.github.com/Bresiu/c0a69552881b8df34a2e. В первом методе я вычисляю deltaRotationVector, который является кватернионом, а во втором методе мой код основан на: diydrones.com/forum/topics/ уравнения. Но после того, как я это реализовал, без гравитации вычисляется только одна ось.   -  person Bresiu    schedule 02.07.2014
comment
@bcorso Я думаю, что для вычисления RotationMatrix мне нужен вектор силы тяжести...   -  person Bresiu    schedule 02.07.2014


Ответы (2)


Сила тяжести постоянно влияет на сигналы акселерометра (x, y и z). Таким образом, логически, чтобы изолировать значения силы тяжести в зависимости от времени, просто фильтруйте фильтр нижних частот 3 сигнала акселерометра, например, с частотой 2 Гц. Простой FIR сделает эту работу.

На этом сайте я рассчитал следующие коэффициенты:

[0.000381, 0.001237, 0.002634, 0.004607, 0.007100, 0.009956, 0.012928, 
0.015711, 0.017987, 0.019480, 0.020000, 0.019480, 0.017987, 0.015711, 
0.012928, 0.009956, 0.007100, 0.004607, 0.002634, 0.001237, 0.000381]

на основе этих характеристик: Fa=0Hz, Fb=1Hz, Length=21Pts, Fs=100Hz, Att=60dB.

Вы получите сигнал, который будет представлять собой три значения гравитации в зависимости от времени.

Вы можете найти здесь некоторое объяснение FIR и реализация Java.

person LM.Croisez    schedule 18.11.2014
comment
Я уже сделал это, фильтруя вход низкими/высокими частотами. Однако я посмотрю на это. Спасибо - person Bresiu; 19.11.2014

Вам нужна матрица вращения (SensorManager.getRotationMatrix). Его последние три компонента (т.е. вращение[6], вращение[7], вращение[8]) — это вектор, указывающий прямо вверх, поэтому направление к центру Земли является его отрицательным значением. Чтобы вычесть гравитацию из показаний акселерометра, просто умножьте этот вектор на g (~ 9,8 м/с ^ 2, хотя вам, возможно, захочется узнать это точнее).

person Theodore Sternberg    schedule 01.05.2015