Coder Social home page Coder Social logo

fusion's Introduction

Tags Build Pypi Python License

Fusion

Fusion is a sensor fusion library for Inertial Measurement Units (IMUs), optimised for embedded systems. Fusion is a C library but is also available as the Python package, imufusion. Two example Python scripts, simple_example.py and advanced_example.py are provided with example sensor data to demonstrate use of the package.

AHRS algorithm

The Attitude And Heading Reference System (AHRS) algorithm combines gyroscope, accelerometer, and magnetometer data into a single measurement of orientation relative to the Earth. The algorithm also supports systems that use only a gyroscope and accelerometer, and systems that use a gyroscope and accelerometer combined with an external source of heading measurement such as GPS.

The algorithm is based on the revised AHRS algorithm presented in chapter 7 of Madgwick's PhD thesis. This is a different algorithm to the better-known initial AHRS algorithm presented in chapter 3, commonly referred to as the Madgwick algorithm.

The algorithm calculates the orientation as the integration of the gyroscope summed with a feedback term. The feedback term is equal to the error in the current measurement of orientation as determined by the other sensors, multiplied by a gain. The algorithm therefore functions as a complementary filter that combines high-pass filtered gyroscope measurements with low-pass filtered measurements from other sensors with a corner frequency determined by the gain. A low gain will 'trust' the gyroscope more and so be more susceptible to drift. A high gain will increase the influence of other sensors and the errors that result from accelerations and magnetic distortions. A gain of zero will ignore the other sensors so that the measurement of orientation is determined by only the gyroscope.

Initialisation

Initialisation occurs when the algorithm starts for the first time and during angular rate recovery. During initialisation, the acceleration and magnetic rejection features are disabled and the gain is ramped down from 10 to the final value over a 3 second period. This allows the measurement of orientation to rapidly converge from an arbitrary initial value to the value indicated by the sensors.

Angular rate recovery

Angular rates that exceed the gyroscope measurement range cannot be tracked and will trigger an angular rate recovery. Angular rate recovery is activated when the angular rate exceeds the 98% of the gyroscope measurement range and equivalent to a reinitialisation of the algorithm.

Acceleration rejection

The acceleration rejection feature reduces the errors that result from the accelerations of linear and rotational motion. Acceleration rejection works by calculating an error as the angular difference between the instantaneous measurement of inclination indicated by the accelerometer, and the current measurement of inclination provided by the algorithm output. If the error is greater than a threshold then the accelerometer will be ignored for that algorithm update. This is equivalent to a dynamic gain that deceases as accelerations increase.

Prolonged accelerations risk an overdependency on the gyroscope and will trigger an acceleration recovery. Acceleration recovery activates when the error exceeds the threshold for more than 90% of algorithm updates over a period of t / (0.1p - 9), where t is the recovery trigger period and p is the percentage of algorithm updates where the error exceeds the threshold. The recovery will remain active until the error exceeds the threshold for less than 90% of algorithm updates over the period -t / (0.1p - 9). The accelerometer will be used by every algorithm update during recovery.

Magnetic rejection

The magnetic rejection feature reduces the errors that result from temporary magnetic distortions. Magnetic rejection works using the same principle as acceleration rejection, operating on the magnetometer instead of the accelerometer and by comparing the measurements of heading instead of inclination.

Algorithm outputs

The algorithm provides three outputs: quaternion, linear acceleration, and Earth acceleration. The quaternion describes the orientation of the sensor relative to the Earth. This can be converted to a rotation matrix using the FusionQuaternionToMatrix function or to Euler angles using the FusionQuaternionToEuler function. The linear acceleration is the accelerometer measurement with the 1 g of gravity removed. The Earth acceleration is the accelerometer measurement in the Earth coordinate frame with the 1 g of gravity removed. The algorithm supports North-West-Up (NWU), East-North-Up (ENU), and North-East-Down (NED) axes conventions.

Algorithm settings

The AHRS algorithm settings are defined by the FusionAhrsSettings structure and set using the FusionAhrsSetSettings function.

Setting Description
convention Earth axes convention (NWD, ENU, or NED).
gain Determines the influence of the gyroscope relative to other sensors. A value of zero will disable initialisation and the acceleration and magnetic rejection features. A value of 0.5 is appropriate for most applications.
gyroscopeRange Gyroscope range (in degrees per second). Angular rate recovery will activate if the gyroscope measurement exceeds 98% of this value. A value of zero will disable this feature. The value should be set to the range specified in the gyroscope datasheet.
accelerationRejection Threshold (in degrees) used by the acceleration rejection feature. A value of zero will disable this feature. A value of 10 degrees is appropriate for most applications.
magneticRejection Threshold (in degrees) used by the magnetic rejection feature. A value of zero will disable the feature. A value of 10 degrees is appropriate for most applications.
recoveryTriggerPeriod Acceleration and magnetic recovery trigger period (in samples). A value of zero will disable the acceleration and magnetic rejection features. A period of 5 seconds is appropriate for most applications.

Algorithm internal states

The AHRS algorithm internal states are defined by the FusionAhrsInternalStates structure and obtained using the FusionAhrsGetInternalStates function.

State Description
accelerationError Angular error (in degrees) of the algorithm output relative to the instantaneous measurement of inclination indicated by the accelerometer. The acceleration rejection feature will ignore the accelerometer if this value exceeds the accelerationRejection threshold set in the algorithm settings.
accelerometerIgnored true if the accelerometer was ignored by the previous algorithm update.
accelerationRecoveryTrigger Acceleration recovery trigger value between 0.0 and 1.0. Acceleration recovery will activate when this value reaches 1.0 and then deactivate when when the value reaches 0.0.
magneticError Angular error (in degrees) of the algorithm output relative to the instantaneous measurement of heading indicated by the magnetometer. The magnetic rejection feature will ignore the magnetometer if this value exceeds the magneticRejection threshold set in the algorithm settings.
magnetometerIgnored true if the magnetometer was ignored by the previous algorithm update.
magneticRecoveryTrigger Magnetic recovery trigger value between 0.0 and 1.0. Magnetic recovery will activate when this value reaches 1.0 and then deactivate when when the value reaches 0.0.

Algorithm flags

The AHRS algorithm flags are defined by the FusionAhrsFlags structure and obtained using the FusionAhrsGetFlags function.

Flag Description
initialising true if the algorithm is initialising.
angularRateRecovery true if angular rate recovery is active.
accelerationRecovery true if acceleration recovery is active.
magneticRecovery true if a magnetic recovery is active.

Gyroscope offset correction algorithm

The gyroscope offset correction algorithm provides run-time calibration of the gyroscope offset to compensate for variations in temperature and fine-tune existing offset calibration that may already be in place. This algorithm should be used in conjunction with the AHRS algorithm to achieve best performance.

The algorithm calculates the gyroscope offset by detecting the stationary periods that occur naturally in most applications. Gyroscope measurements are sampled during these periods and low-pass filtered to obtain the gyroscope offset. The algorithm requires that gyroscope measurements do not exceed +/-3 degrees per second while stationary. Basic gyroscope offset calibration may be necessary to ensure that the initial offset plus measurement noise is within these bounds.

Sensor calibration

Sensor calibration is essential for accurate measurements. This library provides functions to apply calibration parameters to the gyroscope, accelerometer, and magnetometer. This library does not provide a solution for calculating the calibration parameters.

Inertial calibration

The FusionCalibrationInertial function applies gyroscope and accelerometer calibration parameters using the calibration model:

ic = Ms(iu - b)

  • ic is the calibrated inertial measurement and return value
  • iu is the uncalibrated inertial measurement and uncalibrated argument
  • M is the misalignment matrix and misalignment argument
  • s is the sensitivity diagonal matrix and sensitivity argument
  • b is the offset vector and offset argument

Magnetic calibration

The FusionCalibrationMagnetic function applies magnetometer calibration parameters using the calibration model:

mc = S(mu - h)

  • mc is the calibrated magnetometer measurement and return value
  • mu is the uncalibrated magnetometer measurement and uncalibrated argument
  • S is the soft iron matrix and softIronMatrix argument
  • h is the hard iron offset vector and hardIronOffset argument

Fast inverse square root

Fusion uses Pizer's implementation of the fast inverse square root algorithm for vector and quaternion normalisation. Including the definition FUSION_USE_NORMAL_SQRT in FusionMath.h or adding this as a preprocessor definition will use normal square root operations for all normalisation calculations. This will slow down execution speed for a small increase in accuracy. The increase in accuracy will typically be too small to observe on any practical system.

fusion's People

Contributors

xiotechnologies avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fusion's Issues

What format is input gyroscope and accel?

 * @param gyroscope Gyroscope measurement in degrees per second.
void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer, const // ...

I have three guesses:

  • Pitch, roll, yaw
  • Roll, pitch, yaw
  • Normalized 3d vector x, y, z, ie requires processing of raw gyro measurements into a vector. (Most likely?)

Re accel: I'm assuming it's x, y, z. Is that correct?

Thank you!

Gravity acceleration removal

Hi, thank you for your work! I was wondering if your code removes the gravity acceleration. It seems that it does it, but I'm not sure how. Also, does it need magnetometer data to remove the gravity, or is it sufficient to have accelerometer and gyroscope data? Because I tried the code with my collected data and the gravity was not removed.

Understanding FusionMatrix gyroscopeMisalignment

When I rotate my sensor around the Z axis (on a turntable), I get the below graphs which shows some wobble in the roll and pitch with .gain to set to 0.0f to ignore accel input.
I think I understand this "wobble" is due to a gyro axis misalignment. I'm trying to understand how the FusionMatrix works, but I'm not quite sure of the method to get the values needed for it. I understand each values is a multiplier to add some of the other axis input in another one, but I don't know how to tell which value and by how much... I've found this article but it hasn't quite helped me that much.

Matrix initial

and zoomed in version to focus on pitch and roll, recorded here are two full rotation so 720deg rotation it was turned by hand on a solid center axle, hence the roughness of the curve. I'm also aware of the gyro drift. I recorded this example right after powering up the IMU, it had not stabilized yet:

Matrix initial zoomed in

Somewhat confused by the Euler angle conversion

Hi!

I'm having some issues with understanding the relation between the imu input axes and the euler angles from the resulting quaternion.
Here's an example: I have equal acceleration in the Y and the Z axis. I would guess that in this case I should be getting 45 degrees of pitch, but instead I am getting 45 degrees of roll? I was expecting to have Z upwards, X to the side and Y in the front/rear axis, but it seems that isn't the case here?

#include "Fusion/Fusion.h"
#include <stdio.h>

int main() {
	FusionAhrs ahrs;
	FusionAhrsInitialise(&ahrs);
	for(int i = 0; i < 100; i++) {
		FusionVector acc, gyr;
		acc.axis.x = 0;
		acc.axis.y = 1;
		acc.axis.z = 1;
		gyr.axis.x = 0;
		gyr.axis.y = 0;
		gyr.axis.z = 0;
		FusionAhrsUpdateNoMagnetometer(&ahrs, gyr, acc, 0.01);
	    FusionEuler eul = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs));
	    printf("p,r,y = %.3f, %.3f, %.3f\n", eul.angle.pitch, eul.angle.roll, eul.angle.yaw);
            //prints p,r,y = 0.000, 45.012, 0.000
	}
}

Automatic / dynamic gain calculation

In my application the amount of noise varies a lot. 98% of the time there is a lot of noise in the data (a high standard deviation on every accelero and gyro measurement) - Period HN, 2% of the time there is much less noise (a very low standard deviation) but there is high acceleration - Period LN.

I need the most accurate orientation determination period LN, orientation during HN I don't care about.

The problem is that if I take a gain of 0.5 to deal with HN, the orientation determination is not so good during LN. If I take a gain of 0.1 the orientation determination works well for LN but get pretty off during the HN.

Although I'm not interested in orientation during period HN I also need it to be fairly decent, because otherwise I start of my calculations in the LN period based on a wrong start orientation. During the HN period the device is relative in the same orientation, but exposed to a lot of noise (bouncing).

What I ended up doing was toggling the Gain programmatically between 0.5 and 0.1 based on my own HN/LN detection. This works pretty reasonable, but is not so sophisticated. I learned that in some data 0.5 /0.1 works , in other 0.4/0.2, e.t.c. Also I'm nog happy with the hard toggling, I guess I should implement some ramping up/down.

Is there any recommendation on how to find optimal and dynamic Gain values? It would be quite awesome to do some kind of drift detection and then automatically ramp up/down gain based on that.

Do you welcome code improvement?

Adding Fusion to our project, it is now being analyzed by Sonarcloud and around 30 issues have been discovered.

Such as using reference to const to pass large objects.

image

Do you welcome PRs to fix those?

Get displacement point vector

I wonder if it is possible to obtain a point vector corresponding to the initial position when the motion started?

Can't import imufusion

hello,

When importing imufusion in my code i get the following error :

import imufusion
ImportError: numpy.core.multiarray failed to import

Did you already encounter this problem or do you have an idea of how to fix it?

Algorithm problems

Hi, I tried to use this algorithm to obtain the acceleration in the global reference frame by multiplying accelerometer data for the rotation matrix obtained by the Madgwick filter. When I move the object in the direction of one axis, I notice that the acceleration rises in all the axis almost equally. What can I do to solve the problem?

magnetometer unit

Hi. I am trying to visualize the IMU data and I referred to this issue (#42) to send data serially to the X-IMU GUI. IMU Visualization is changing its orientation faster and it's not stable. It's moving randomly even though the IMU is stable. So, From the manual, the unit of accelerometer data is G and gyroscope data is DPS. Unit of magnetometer is given as a.u. so I have magnetometer output in gauss. So, Do I need to convert it into microtesla before transferring it serially?

Fusion calibration matrices

I have questions regarding the matrices used in following equation:

ic = Ms(iu - b)

I understand iu and b.

How to calculate M for accelero and gyro?
What are is input format for M?

is s (sensitivity matrix) sensitivity of sensor or calibration model?

question: about quaternion usage convention based on JPL or Hamilton in Fusion project ?

In your code the "halfGravity" vector is computed using JPL convention. which is the same as the Madgwick's PhD thesis , the related math is shown as follows,
image However, when computing the euler angle from the quaternion pose, the corresponding function code named by "FusionQuaternionToEuler" is different from Madgwick's paper(in the paper, roll = atan2(2(q_yq_z - q_w q_x)... but in your code roll=atan2(2(q_yq_z + q_w q_x)... are they the same?
image

x and y axes are zero-d out on my implementation.

Hi, firstly, this is awesome.

Im am trying to implement this on an IOS device for lols. I have imported the C lib and have it running, but the x and y axis are zerod out and only the z axis is being altered over time.

Im using it with magnetometer data. Any idea why this might be happening?

No matching distribution found for imufusion, Arm, Raspberry Pi

I would love to use this for a project Im working on, but does not seem like there is any support for Pi boards right now.

When I try to run "pip install imufusion" it errors out
ERROR: Could not find a version that satisfies the requirement imufusion
ERROR: No matching distribution found for imufusion

After digging adding the -v flag and digging into the output it appears to be because there is currently no Arm support. Is something like that possible?

Suggestion on calculating the calibration parameters

As I read on README:
This library does not provide a solution for calculating the calibration parameters.

Do you have any suggestion on how to calculate calibration parameters, especially misalignment and offset?
The IMU is installed in an unknown orientation.

The results of X-aix and Z-axis are affected by Y-axis

Thanks for sharing your great job!
In practice, I only rotate the Y axis, but the results of X-axis and Z-axis will also output values, which means that Pitch and Yaw values are affected by the Y-axis.
image

The gyroscope value as follows:
image

Is there something wrong in my case? I record the data using a samsung mobile phone.

Rotation of global acceleration

Hi, I am using your library to obtain the acceleration in a global reference frame considered as the initial position of my sensor. So in order to remove the initial rotation of the yaw angle given by the magnetic field, I rotate my acceleration vector of this angle:
linacc[0] = linacc[0] * math.cos(angle ) - linacc[1] * math.sin( angle) linacc[1] = linacc[0] * math.sin(angle ) + linacc[1] * math.cos( angle)
Unfortunately, This does not solve the problem can you help to understand the problem? I noticed that if I just translate the sensor in the XY plane, without any rotation, I obtain instability in the yaw measurements.
image
Am I doing something wrong?
This is the code I am using:
g = offset.update(g) ahrs.update(g, a, MagVals, deltat) euler = ahrs.quaternion.to_euler() linacc= ahrs.earth_acceleration linacc= linacc * 9.81
This is the initialization of the algorithm:
offset = imufusion.Offset(100) #sample rate ahrs = imufusion.Ahrs() ahrs.settings = imufusion.Settings(0.5, # gain 10, # acceleration rejection 0, # magnetic rejection 500) # rejection timeout = 5 seconds

I tried also with a higher sample rate (180 Hz) and I have better results in the rotation but still unstable yaw.
image

X axis never change

Hi there,

First of all i would like to thank you for you work, it's simply quite amazing, second i tried to use the gait tracking script, but when use the FusionAhrsGetEarthAcceleration(&ahrs); the x axis never change, indeed the z axis neither, but any way as an example here's what i'm getting as output,

First read:
earth.axis ={
x float 1494086.62
y float 1.10672033e-007
z float -0.99994278
}

Now moving;
earth.axis ={
x float 1494086.62
y float 4.33112182e-005
z float -0.999985158
}

As stated before, the same value for Z and X axis, so yes, the IMU is located at my foot, yes i made soft and hard movements, yes the Euler angles works, yes the library used is the last version of fusion, btw;

The MCU used is a STM32, i guess it doesn't matter, but what could be important is the IMU, is a lsm6dso from st, the settings are:

  • acelerometer = 2g @ 1667Hz
  • gyroscope = 2000 dps @ 6667Hz
  • Sample rate = 2

I'm not using the magnetometer, but as far as i can tell it is not used by the function.
So any light on this issue would be very appreciated,

Question: Regarding Basic Orientation Update

I have a quick question regarding using the basic orientation update (so ignoring the correction terms).
I've been working on an inertial navigation system as a pet project (so tracking both the changes in orientation and changes from the original position).
I've been using the euler angle to quaternion algorithm for updating the orientation, but I like that the technique you present in your paper didn't require using any trig functions to construct the quaternion. However in practice I can't get the updated quaternion to work the way the other one did, and am not sure if it has to be used in a different way.

So ignoring the correction terms, this is how the general orientation update should work

`
initial(){
orientation = buildQuaternion(1,0,0,0);
}

loop(){
deltaQuat = buildQuaternion(0, Yaw, Pitch, Roll);
deltaQuat = mult(orientation, deltaQuat) * (0.5 * deltaTime);
orientation += deltaQuat
orientation = normalize(orientation);

            // From here I can rotate the acceleration data, and use it to update the velocity and position fields
	// accelVec = (orientation * accelVec) * inverse(orientation);

}
`
The problem is that when I use the orientation quaternion to rotate the accelerometer values, it's not giving me the correct values.

As a basic proof of concept, I tested the method by using having it rotate vector (0,1,0) by 180 degrees, by setting a gyroscope vector to (0,0,PI), with deltaTime = 1
`
orientation = buildQuaternion(1,0,0,0);
vector = buildQuaternionFromVector(0,1,0);
deltaTime = 1;
orientation = madgwickOrientationUpdate(orientation, gyroData) // just have it do the same code i showed above in the loop method

// rotate the vector using the new orientation quaternion
vector = rotateVectorByQuaternion(orientation, vector);
`

Assuming I haven't looked over something horribly simple, the updated vector should be (0, -1, 0), which is what I also get
when I tested this out on the euler angle to quaternion method, however the madgwickQuaternionUpdate instead gives (-0.9715, 0.2368, 0)

So does the quaternion the Madgwick code builds not work in this way, or is there another way we're suppose to use it to rotate vectors?

Appreciate any help anyone can provide.

Why FusionAhrsGetQuaternion() gets conjugated quaternion?

/**
 * @brief Returns the quaternion describing the sensor relative to the Earth.
 * @param ahrs AHRS algorithm structure.
 * @return Quaternion describing the sensor relative to the Earth.
 */
FusionQuaternion FusionAhrsGetQuaternion(const FusionAhrs *const ahrs) {
    return FusionQuaternionConjugate(ahrs->quaternion);
}

I tried to compair Fusion with original MadgewickAHRS, and found that Fusion gives reversed quaternion result. It appears that quaternion get by FusionAhrsGetQuaternion() always be conjugated, thus result in reversed quaternion orientation. But if i change it to return ahrs->quaternion; then FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs)); returns reversed euler angle.

According to https://quaternions.online/, convert quaternion wxyz{0.96,-0.009,-0.28,-0.024} to euler angle should be xyz{-2.085,-32.467,-3.471}. But FusionQuaternionToEuler((FusionQuaternion){0.96,-0.009,-0.28,-0.024}); returns {2.084,32.491,3.469}, which every axis are reversed. So I think something goes wrong in conversion between quaternion to euler angle.

The problem can be fixed by both applying the method from Wikipedia or just simply reverse signs in the original formula.

Original

static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion) {
#define Q quaternion.element
    const float qwqwMinusHalf = Q.w * Q.w - 0.5f; // calculate common terms to avoid repeated operations
    FusionEuler euler;
    euler.angle.roll = FusionRadiansToDegrees(atan2f(Q.y * Q.z - Q.w * Q.x, qwqwMinusHalf + Q.z * Q.z));
    euler.angle.pitch = FusionRadiansToDegrees(-1.0f * asinf(2.0f * (Q.x * Q.z + Q.w * Q.y)));
    euler.angle.yaw = FusionRadiansToDegrees(atan2f(Q.x * Q.y - Q.w * Q.z, qwqwMinusHalf + Q.x * Q.x));
    return euler;
#undef Q
}

Sign reversed

// Sign reversed
static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion) {
#define Q quaternion.element
    const float qwqwMinusHalf = Q.w * Q.w - 0.5f; // calculate common terms to avoid repeated operations
    FusionEuler euler;
    euler.angle.roll = FusionRadiansToDegrees(-1.0f * atan2f(Q.y * Q.z - Q.w * Q.x, qwqwMinusHalf + Q.z * Q.z));
    euler.angle.pitch = FusionRadiansToDegrees(asinf(2.0f * (Q.x * Q.z + Q.w * Q.y)));
    euler.angle.yaw = FusionRadiansToDegrees(-1.0f * atan2f(Q.x * Q.y - Q.w * Q.z, qwqwMinusHalf + Q.x * Q.x));
    return euler;
#undef Q
}

Method from Wikipedia

// Source:https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
static inline FusionEuler FusionQuaternionToEuler(const FusionQuaternion quaternion) {
#define Q quaternion.element
    FusionEuler euler;
    // Source:https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles
    // roll (x-axis rotation)
    float sinr_cosp = 2 * (Q.w * Q.x + Q.y * Q.z);
    float cosr_cosp = 1 - 2 * (Q.x * Q.x + Q.y * Q.y);
    euler.angle.roll = FusionRadiansToDegrees(atan2f(sinr_cosp, cosr_cosp));

    // pitch (y-axis rotation)
    float sinp = 2 * (Q.w * Q.y - Q.z * Q.x);
    if (fabsf(sinp) >= 1)
        euler.angle.pitch = FusionRadiansToDegrees(copysignf(M_PI / 2, sinp)); // use 90 degrees if out of range
    else
        euler.angle.pitch = FusionRadiansToDegrees(asinf(sinp));

    // yaw (z-axis rotation)
    float siny_cosp = 2.0f * (Q.w * Q.z + Q.x * Q.y);
    float cosy_cosp = 1.0f - 2.0f * (Q.y * Q.y + Q.z * Q.z);
    euler.angle.yaw = FusionRadiansToDegrees(atan2f(siny_cosp, cosy_cosp));
    return euler;
#undef Q
}

I currently use the 'Sign reversed' version, and a pull request will be sent, tell me if I miss something.

On Gyroscope input ordering

Related to #16.

The AHRS algorithm takes gyroscope measurements as 3-point vectors:

void FusionAhrsUpdate(FusionAhrs *const ahrs, const FusionVector gyroscope, const FusionVector accelerometer,

Here is the simple example of its use: (lists units, but not order)

const FusionVector gyroscope = {0.0f, 0.0f, 0.0f}; // replace this with actual gyroscope data in degrees/s

These vectors use x, y, and z, in that order internally:
From FusionMath.h:

typedef union {
    float array[3];

    struct {
        float x;
        float y;
        float z;
    } axis;
} FusionVector;

The official examples imply the order in these vecs is roll, pitch, yaw.
From official examples:

const FusionEuler euler = FusionQuaternionToEuler(FusionAhrsGetQuaternion(&ahrs));
printf("Roll %0.1f, Pitch %0.1f, Yaw %0.1f\n", euler.angle.roll, euler.angle.pitch, euler.angle.yaw);

Point 1: This implies Gyro X -> roll, gyro Y -> pitch, gyro Z -> yaw

Image from a common IMU, that matches intuition:
image

Point 2: This would imply pitch is movement around the x axis and reported in IMU gyro X registers, and roll is movement around the y axis and reported in IMU gyro Y registers.

How do we reconcile these opposite pitch and roll mappings? Thank you.

Use fusion output to determine rotation along local axis

Hello,

I am using this library for a project, but I am having trouble figuring out how to manipulate the output quaternions.

This issue is not specific to this project, so if I should ask somewhere else like stackoverflow, please close this issue. However, I am asking because this functionality might be useful to include in the library.

Essentially, I am only interested in rotation along a local axis of my gyro, specifically the z axis. I started by manually integrating the z gyro output to get angle, which is the format of the data I want. However, there was drift, and I wanted to use a proper sensor fusion algorithm to include the accelerometer. That way, if the sensor is spinning vertically, it can use gravity to prevent drift.

I switched to using this library, and it works amazingly well at giving me a global rotation quaternion. However, I am not sure how to go from a global quaternion back to the local rotation along the z axis. I want the values to not be constrained +-180 because I want to use them to count rotations.

To attempt to solve this, I started by identifying the direction of the local z axis in the global frame:

quat = FusionAhrsGetQuaternion(&ahrs);
direction = FusionVectorNormalise(FusionMatrixMultiplyVector(FusionQuaternionToMatrix(quat), {{0.0, 0.0, 1.0}}));

Then, my question can be re-stated as "what is my rotation along this axis". However, here I am stuck, and I am also not sure if this is the correct approach at all, especially if I want unbounded angles.

Thanks!

Acceleration Rejection based on orientation change or magnitude of acc vec?

First of all: totally amazing this repo. I was working with the original Madgwick algo for a while already and now switched to this implementation, also getting great results.

To the point. In the documentation it says that the acceleration rejection is based on "Threshold (in degrees) used by the acceleration rejection feature. A value of zero will disable this feature. A value of 10 degrees is appropriate for most applications."

This sounds like accelero values are rejected based on rotation of the orientation, but when I look in the code it looks like it's actually checking against the magnitude of the acceleration vector.

Could someone elaborate a bit on the meaning of the acceleration rejection value and what would be recommended for my application where there is a LOT of lineair acceleration?

GPS Example

Hi
the readme state its compatible with system with gps correction, it seem like the solution for lets say a fixed wing model airplane if its in a bank long enough you need a gps to make the fusion work correctly
but I do no see example using gps data, nor gps input function the the code.
can you help me there?

Calibration in IMU library or Fusion?

I am using Fusion on an Arduino Nano 33 BLE. The IMU library, I am using a library that has sensor calibration and correction with a calibrate output option and a raw output option. It also has calibration scripts for all sensors. Should I use the pre-calibrated output to send to Fusion? Or should I use the raw output and have Fusion use its calibration (I believe I can adapt the calibration scripts data from the IMU library to Fusion). Thanks!

FusionCalibrationInertial vs FusionOffsetUpdate

In our project, we've implemented the Advanced example provided, that you can see here:

https://github.com/leka/LekaOS/blob/develop/spikes/lk_sensors_imu_lsm6dsox_fusion_calibration/main.cpp

We have followed the following protocol:

1. Calculate static accel + gyro offsets

  • log raw accelerometer + gyroscopes values without calibration
  • record the values for 2min using x-IMU3 software
  • calculate the average of the values to get the offset for each axis

2. Apply previously obtained offsets

Using then apply the offsets using FusionCalibrationInertial.

As a side note, only using FusionCalibrationInertial reduces the yaw drift greatly.

3. Apply runtime gyroscope offset correction

Using FusionOffsetUpdate we apply runtime calibration to the gyroscope, as explained here


During code review, one our team member tried using only FusionOffsetUpdate without using FusionCalibrationInertial.

He said the effect seemed to be the same: reduce yaw drift to almost zero giving us stable values over time when the device is not moving.

He then asked the following question:

What's the difference between the two? Why do we need both? Would using FusionOffsetUpdate be enough?

My answer was "trust the experts and follow the canonical example first, before trying to do things differently. Then we can experiment".

But I was not happy with my answer, so I'd like to add more information.

What would you say in that situation?

Question: about the output quaternion regarding to "North-West-Up (NWU)"

hello, can you explain why the output quaternion is based on the North-West-Up (NWU) ? I have no idea about this discription, " The quaternion describes the orientation of the sensor relative to the Earth using the North-West-Up (NWU) convention" . And I want to know the principle, can you give me some tips about this ? thanks a lot

Using asinf may leed to nan (not a number)

Using 'asinf()' may lead to nan. In the function 'FusionQuaternionToEuler' a call is done to 'asinf', if the input of this is larger then 1 or less then -1, the result will be nan. All calculations using this pitch value (as a nan) will fail as well.

A potential solution might be something like:

float asinf_safe(float val) {
if (val >= 1.0f) { return (M_PI / 2.0f); }
if (val <= -1.0f) { return (M_PI / -2.0f); }
return asinf(val);
}

Accuracy and Precision Compared to the MadgwickAHRS

Hello Mr.Sebastian,

I have a some research about the library which you provide in this repository.

When I work with the old version of MadgwickAHRSupdateIMU which you provided at "19/02/2012 SOH Madgwick Magnetometer measurement is normalised" , I record the Pitch and Roll angles for a 60 seconds and i get the results as follows :

Pitch angle error : 0.001990 Degree
Roll angle error : 0.001998 Degree

However, when I implement same structure with Fusion repository and record them for a 60 seconds i get bad result than the first one. The results as follows for Fusion Lib. :

Pitch angle error : -0.005449 Degree
Roll angle error : 0.009339 Degree

Settings : gyroMisalignments , gyroscopeSensitivity , accelerometerMisalignment and accelerometerSensitivity was setted as default.

  • Sensor sampling rate is 1000Hz
  • Beta was setted as 0.02 in old version of MadgwickAHRS
  • Fusion gain also setted as 0.02 in the test.

Calibration Process for both repo (MadgwickAHRS version and Fusion version) offsets calculated as follows :

  • Collect accel,gyro data for 5 seconds while the device at rest ( zero motion ) when the system has been started.
  • Sum them up, and divide it into 5000 ( sample rate = 1000Hz, collect data for 5 sec equals to 5000)
  • Only Accel z axis offset = 1 - offsetValue

Therefore, although all the process are same with the MadgwickAHRS version the deviation in the pitch and roll angle is much higher.
By the way, I didn't forget to put FusionOffsetUpdate in every calculation process and I dont work with Magnetometer.

What is your suggestion in that case how I can improve the accuracy ?

Thank you,
Abdulmecid Pamuk

Axis Interconnection/Bleed

Hi all, I'm implementing fusion on an arduino 33 ble to create a head tracker for PC games.

I am currently encountering a problem where my yaw and roll axis work as they should, but my pitch axis affects yaw as well as pitch. I'm hoping someone more knowledgeable in fusion (and math generally) could point me to some possible causes of this behavior so I can look into them.

I have already ensured that the weird orientation on the 33 ble's mag is being compensated for when updating the quaternion, but the pitch axis still effects yaw somehow. Any details, explanations, or other advice would be greatly appreciated.

Here are some plots demonstrating the behavior:
https://imgur.com/a/FJZQ8XB

I'm using this implementation, its fusion files are 98% identical to this repo, just modified for this specific arduino.

Weird wrong orientation obtained

Dear xioTechnologies and all,

I am using the Fusion library to get an quaternion description of the global orientation of my 9DOF IMU (accelerometer, magnetometer and gyroscope. All axes aligned).

Since I am quite new to orientations, I followed the advanced example from the library and used Adafruit's 3D orientation visualizer to get a sense of the orientation.

The accelerometer and the magnetometer have been calibrated. Their calibration data is entered in the code, as instructed in the example.

However, the orientation I get seemed wrong and unstable. Please see the video included for a demo of the issue. https://youtu.be/hq4xZufC_eM

I have also included the code, hoping it helps identifying the issue.

The sudden change in orientation can be the result of magnetic rejection kicking in. I wish I could come up with more possible causes but unfortunately I am out of ideas.

I am grateful to any insight on the problem. Many thanks.

Hardware info:

  • Microcontroller used: SONY SPRESENSE
  • IDE: Arduino
  • Gyroscope : FXAS21002C
  • Accelerometer and Magnetometer : FXOS8700

My code:

#include "Fusion.h"
#include <Adafruit_FXOS8700.h>
#include <Adafruit_FXAS21002C.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

#define BAUD_RATE             115200
#define G_VALUE               9.81f
#define DEGREES_PER_RADIAN    57.29578
#define PI                    3.1415926
#define SAMPLE_RATE           100   // gyroscope set to 100Hz output rate

/* Assign a unique ID to this sensor at the same time */
Adafruit_FXAS21002C gyro = Adafruit_FXAS21002C(0x0021002C);
Adafruit_FXOS8700 accelmag = Adafruit_FXOS8700(0x8700A, 0x8700B);

// Define calibration
const FusionMatrix gyroscopeMisalignment = {  1.0f, 0.0f, 0.0f, 
                                              0.0f, 1.0f, 0.0f, 
                                              0.0f, 0.0f, 1.0f  };

const FusionVector gyroscopeSensitivity = {1.0f, 1.0f, 1.0f};

const FusionVector gyroscopeOffset = {0.0f, 0.0f, 0.0f};        // the zero rate is super low, so I assume it's just zeros

const FusionMatrix accelerometerMisalignment = {  1.0f, 0.0f, 0.0f, 
                                                  0.0f, 1.0f, 0.0f, 
                                                  0.0f, 0.0f, 1.0f  };

const FusionVector accelerometerSensitivity = {1.0f, 1.0f, 1.0f};

const FusionVector accelerometerOffset = {0.02f, 0.02f, 1.02f};

const FusionMatrix softIronMatrix = { 1.007f, 0.015f, 0.007f, 
                                      0.015f, 0.931f, 0.01f, 
                                      0.007f, 0.01f, 1.067f      };

const FusionVector hardIronOffset = {-21.7f, -44.64f, -63.13f};

FusionOffset offset;
FusionAhrs ahrs;
// Set AHRS algorithm settings
const FusionAhrsSettings settings = {
        .gain = 0.5f,
        .accelerationRejection = 10.0f,
        .magneticRejection = 20.0f,//60.0f,
        .rejectionTimeout = 5 * SAMPLE_RATE , /* 5 seconds */
};

/*  declare sensor events  */
sensors_event_t   acc_event;
sensors_event_t   gyro_event;
sensors_event_t   mag_event;

float time_now = 0.0f;
float time_pre = 0.0f;

void setup() {

  Wire.begin();
  Serial.begin( BAUD_RATE );
  Wire.setClock(400000); // 400KHz

    /* Initialise the sensors */
  while(!accelmag.begin())
  {
    Serial.println(F("Connection with Accelerometer and Magnetometer NOT established"));
    delay(3000);
  }
    
  Serial.println("Accelerometer and Magnetometer initialized");

  while(!gyro.begin())
  {
    Serial.print("Connection with Gyro NOT established ");
    delay(3000);
  }

  Serial.println("Gyroscope initialized");

  // set range
  gyro.setRange( GYRO_RANGE_500DPS );
  accelmag.setAccelRange(ACCEL_RANGE_2G);

  Serial.print("Acclerometer Range: ");  Serial.print( accelmag.getAccelRange()); Serial.println(" ");
  Serial.print("Acc Mag Hybrid output rate: "); Serial.println( accelmag.getOutputDataRate() );
  Serial.print("Gyro Range: ");  Serial.print( gyro.getRange() ); Serial.println(" ");


  // Initialise algorithms
  FusionOffsetInitialise(&offset, SAMPLE_RATE);
  FusionAhrsInitialise(&ahrs);
  FusionAhrsSetSettings(&ahrs, &settings);
}

void loop(void) {

  /* update sensor events */
  accelmag.getEvent(&acc_event, &mag_event);
  gyro.getEvent(&gyro_event);

  // Acquire latest sensor data
  FusionVector gyroscope    = {   gyro_event.gyro.x  , 
                                  gyro_event.gyro.y  , 
                                  gyro_event.gyro.z        }; // actual gyroscope data in degrees/s
  FusionVector accelerometer = {  acc_event.acceleration.x, 
                                  acc_event.acceleration.y, 
                                  acc_event.acceleration.z  }; // actual accelerometer data in g
  FusionVector magnetometer = {   mag_event.magnetic.x, 
                                  mag_event.magnetic.y, 
                                  mag_event.magnetic.z      }; // actual magnetometer data in uTesla
  
  time_now = micros(); // replace this with actual gyroscope timestamp

  // Apply calibration
  gyroscope     = FusionCalibrationInertial(gyroscope, gyroscopeMisalignment, gyroscopeSensitivity, gyroscopeOffset);
  accelerometer = FusionCalibrationInertial(accelerometer,  accelerometerMisalignment, accelerometerSensitivity, accelerometerOffset);
  magnetometer  = FusionCalibrationMagnetic(magnetometer,   softIronMatrix,   hardIronOffset);

  // Update gyroscope offset correction algorithm
  gyroscope = FusionOffsetUpdate(&offset, gyroscope);

  float deltaTime = (time_now - time_pre) / 1000000.0f;  // time difference in seconds
  time_pre = time_now;

  // Update gyroscope AHRS algorithm
  FusionAhrsUpdate(&ahrs, gyroscope, accelerometer, magnetometer, deltaTime);
  // Serial.println(deltaTime);

  // get quaternion rotation
  FusionQuaternion q = FusionAhrsGetQuaternion(&ahrs);

  Serial.print("Quaternion: ");
  Serial.print(q.element.w);
  Serial.print(", ");
  Serial.print(q.element.x);
  Serial.print(", ");
  Serial.print(q.element.y);
  Serial.print(", ");
  Serial.println(q.element.z);

  delay(10);
}

Orientation is not stable

Hi. I am trying to visualize the orientation using the x-IMU3 GUI. The orientation is stable with small drfit when the beta value is 0.033 but then if I rotate and then comeback to the initial position it takes lot of time to update. When I Increase the beta value the drift increases and the update rate seems to be high. The acclerometer and magnetometer data is stable whereas gyro has noise. Do you have any suggestions?

I am using MadgwickAHRS.c file to get the quaternion data.
sampling rate: 1024 HZ

Magnetometer Calibration

We are using "Magneto 1.2" app for calculating soft iron matrix and hard iron offset. The calculated matrix and offset are indicated below.
image
To verify calibrated data, we are doing the same process with updated software and expect that soft iron matrix and hard iron offset values are close to zero. However, values are almost the same with uncalibrated matrixes.
image

Matrixes are:
FusionMatrix soft_iron_matrix_{{{1.160382f, 0.100225f, -0.360193f},
{0.100225f, 1.258027f, 0.169535f},
{-0.360193f, 0.169535f, 0.681843f}}};
FusionVector hard_iron_offset_{{55.865986f, -44.026598f, 27.720618f}};

For the calibrating magnetometer we are calling FusionCalibrationMagnetic function:
magnetometer = FusionCalibrationMagnetic(magnetometer, soft_iron_matrix_, hard_iron_offset_);

Is there an issue with magnetometer calibration, or am I doing something wrong?

Python how to read quaternion

Hi, thanks for your work.
I'm curently using imufusion in order to determine the orientation of differents IMU, but i don't find how to read the quaternion before the convertion to euler angle. How can i directly read the quaternion output ?

Unstable yaw data

Hi, I am sorry to bother you again, I am looking to have accurate results in the acceleration data but I found out that the yaw measure is very unstable. I tried to test the device by slowly rotating it to 45 degrees each step and here are the results.
image
The rotations of 45 degrees are 3 and have been performed with the same orientation.
I calibrate the magnetometer based on LS ellipsoid fitting of 15000 samples and for the gyroscope and accelerometer just the offset in static conditions obtained by mediating 500 samples. Therefore, I don't think can influence the algorithm.
Can you help me to understand what could be the problem?

pose derivation in FusionAhrsUpdate algorithm

Recently, I'm learning your fusion algorithm. Thanks for your great work!
I have tested the algorithm without the feedback of acc or mag, and contrasted with the direct integration of the gyro data. The results show that the quaternion implementaion with a half gyro is more stable than the direct integration of single axis gyro data, which has less drift over time while the direct integration caused drift is very obvious.
Therefore, I'm wondering why the fusion algorithm deal with the gyro, acc, and mag data by scaling 0.5. Could you please give me some learning material regarding the quaternion implementation for devriving pose from gyro data in this code.
Many Thanks!

Euler angles problem

I'm using MPU6500 to receive raw data form gyro and accelerometer. Sampling frequency is set at 500Hz.

I'm using your python gait-tracking example to data visualisation. With your short_walk.csv file, algorithm works well.

I can't obtain any good results with my data.
I tilted MPU about 45 degrees and backed to starting position.

As you can see pitch value didn't return to starting position but rose.
I can't figure out what's wrong.

I attach my sample file log.csv.

log.csv
pitch_problem2
pitch_problem1

Slowly getting to the correct orientation

Dear xioTechnologies,

Thanks to the X-IMU3 GUI, I was able to visualize the orientation of my 9DOF IMU (Acclerometer, magnetometer and gyro).

I am using the default parameters for the algorithm's initialization:

  • gain = 0.5
  • accelerationRejection = 10 degrees
  • magneticRejection = 20 degrees
  • rejectionTimeout = 5 * 200 samples , /* stands for 5 seconds */. The gyro sampling rate is set to 200 Hz.

However, as I rotated the IMU, the measured orientation slowly approached the correct orientation but not exactly right. Usually the measured orientation became correct after a reinitialization triggered by the timeout.

I also tried disabling the magnetic rejection but had similar observations. I demonstrated this issue in a short video.
https://youtu.be/yuEquhkZYPk

My guess is that the rejection parameters need to be tuned, or maybe disabled? The application I'm working on involves constant rotation of the imu, not much accleration though. I wonder what I should do to obtain accurate orientation measurements, for example like in the video https://youtu.be/BXsGWoOMtmU

Many thanks for your insight.

Finally my hardware info, thought I don't think it matters:

Microcontroller used: SONY SPRESENSE. It has 6 ARM® Cortex®-M4F cores running at 156 MHz
IDE: Arduino
Gyroscope : FXAS21002C
Accelerometer and Magnetometer : FXOS8700

Control flow error in `FusionAhrsUpdate`

Update function checks acceleration/magnetometer rejection time against rejection timeout form setting,
as follows for accelerometer:

if (ahrs->accelerationRejectionTimer > ahrs->settings.rejectionTimeout) {
            const FusionQuaternion quaternion = ahrs->quaternion;
            FusionAhrsReset(ahrs);
            ahrs->quaternion = quaternion;
            ahrs->accelerationRejectionTimer = 0;
            ahrs->accelerationRejectionTimeout = true;
        }

later in this funciton exist check with magic number "10":

ahrs->accelerationRejectionTimer -= ahrs->accelerationRejectionTimer >= 10 ? 10 : 0;

In case if ahrs->settings.rejectionTimeout more than "10" we won't never get inside if
Same happens for magnetometer.

FusionOffsetInitialise() never uses user defined gyroscopeOffset.

In my previous issue ticket, you noticed I had some drift in my gyro at startup and you had mentioned to use FusionOffsetInitialise() to solve it. It actually was already on, but there is an issue with that function. when called , Line 44 of FusionOffset.c zeroes out the gyroscopeOffset value I defined in my sketch. So the gyro always starts off calibration, then, after a couple of minutes it slowly drifts to the center.

Line 44:
offset->gyroscopeOffset = FUSION_VECTOR_ZERO;

On a side note, I would love to be able to access the updated values used by

Best delta time + calibration

Hi team!

First I want to thank you all for the great library. We just started using it with our LSM6DSOX and the results are very good.

We have a few questions to make sure we use the library to the best of its abilities.

Best delta time

What would be the best delta time between calls to FusionAhrsUpdateNoMagnetometer?

In our testing setup we used 80ms with good accuracy but we are wondering what the limit would be.

Dynamic delta time

This one is a bit related to the previous one.

In our case, we are using the library with an RTOS (MbedOS) with different parts of the product running in different threads (LEDs animations, video, IMU, etc.)

The IMU thread is sleeping for 80ms between each iterations. Because of scheduling and thread priority, the delay can be 80ms ±10ms.

In the advanced example, we saw that delta time can be dynamic but is a ±10ms variation too much for the algorithm?

Calibration

To improve precision, we want to calibrate the gyroscope, especially the z rotation Offset.

We recorded ~3000 samples for 5 minutes and calculated the average gz to set in gyroscopeOffset.

Is that what we are supposed to do?

Regarding Misalignment, we are not sure how to handle that so we want to enter the "default" values.

Would that be 0.0f everywhere?

const FusionMatrix gyroscopeMisalignment = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};

Regarding Sensitivity our guess is that the default is 1.0f, is that okay?

const FusionVector gyroscopeSensitivity = {1.0f, 1.0f, 1.0f};

Difference between FusionCalibrationInertial and FusionOffsetUpdate

In the advanced example, the following happen:

gyroscope = FusionCalibrationInertial(gyroscope, gyroscopeMisalignment, gyroscopeSensitivity, gyroscopeOffset);

// then

gyroscope = FusionOffsetUpdate(&offset, gyroscope);

What is the difference between the two? It seems that we are applying the offset correction twice, is that the case? Is that intended?

Thanks a lot for your kind help :)

Can I used the library with two IMU

I'm implementing a device based on two IMUs, I was wondering if I can use the madgwick function update for both the sensors contemporaneously.

Yaw angle different between devices

Hi team!

Following #58, we've followed your recommandations and implemented Fusion using gyroscope data ready interrupt to update the values.

Our hardware is mass produced and we are using the LSM6DSOX sensor with no magnetometer using 52Hz ODR and 104Hz ODR. The change in ODR doesn't seem to make any difference.

We have made 2 tests programs:

Key findings

👍 Calibration does stabilize yaw drift

Using calibration, we were able to reduce/remove the yaw drift, giving us very stable values over time, which is good.

👎 Calibration + offset on gyroscope gives wrong values for accelerometer

This one is strange!

Using those functions:

https://github.com/leka/LekaOS/blob/5d93e18265e885fe2b004b0da307c3bf780a58d9/spikes/lk_imu_kit_fusion_calibration/main.cpp#L98-L102

// ? Apply calibration
gyroscope = FusionCalibrationInertial(gyroscope, FUSION_IDENTITY_MATRIX, FUSION_VECTOR_ONES, gyroscopeOffset);

// ? Update gyroscope offset correction algorithm
gyroscope = FusionOffsetUpdate(&offset, gyroscope);

either together or separately gives us an accelerometer value on the Z axis of 0.25 instead of 1.0 (?!)

🙋 do you have any ideas what could be causing that?

👎 Yaw rotations are not measured correctly

This happens both with and without calibration.

When rotation the device 180° in real life, the yaw measurement is not ~180°:

  • for one device we are missing ~20° --> 180° IRL ≡ ~160° for the device
  • for another one, we get ~10° more --> 180° IRL ≡ ~190° for the device

The measures are stable for each device, but it's a big issue for us to not be able to have our device rotate correctly when we need them to.

🙋 do you have any ideas what could be causing that? and how to fix it?

Thanks a lot for your kind help :)

acceleration units

can you please mention the acceleration units for the functions :

  • FusionAhrsGetEarthAcceleration( )
  • FusionAhrsGetEarthAcceleration( )

one more thing, thanks for the great library 👌.

Couldn't download imufusion Python package

I couldn't install the python package. Was wondering if I missed anything..

Error message:

pip install imufusion
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
ERROR: Could not find a version that satisfies the requirement imufusion
ERROR: No matching distribution found for imufusion

OS Version:

PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"

Python version: 3.9.2
Numpy version: 1.24.2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.