Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Magnetometer calibration and heading (yaw) #3

Open
pmavros opened this issue Oct 13, 2014 · 44 comments
Open

Magnetometer calibration and heading (yaw) #3

pmavros opened this issue Oct 13, 2014 · 44 comments

Comments

@pmavros
Copy link

pmavros commented Oct 13, 2014

Hi Kris,
Amazing work porting the MARG into a nice arduino sketch!

I have got it to work with the https://www.sparkfun.com/products/11486, but the yaw values don't range 360 degrees as I rotate the sensors on its axis (rotating around itself, flat on the table mag-z) and there is not good response, so I may turn it 90 degrees North and it only registers 10º change. The other two pitch and roll seem to work fine.

How good is the calibration? Is it worth implementing something like this? http://www.camelsoftware.com/firetail/blog/uavs/3-axis-magnetometer-calibration-a-simple-technique-for-hard-soft-errors/

Many thanks,
Panos

@kriswiner
Copy link
Owner

Panos,

The sketch has no calibration for the magnetometer except the manual calibration I might have done for my particular MPU9150. I suggest at least you record the min/max value on all three axes and subtract the average so as to re-center the mag response surface. This is all I did. If you suggest something more sophisticated and automatic and get it to work well I would love to incorporate it into my MPU9x50 and LSM9DS0 sketches.

Kris

From: ΠΜ
Sent: Monday, October 13, 2014 12:36 PM
To: kriswiner/MPU-9150
Subject: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Hi Kris,
Amazing work porting the MARG into a nice arduino sketch!

I have got it to work with the https://www.sparkfun.com/products/11486, but the yaw values don't range 360 degrees as I rotate the sensors on its axis (rotating around itself, flat on the table mag-z) and there is not good response, so I may turn it 90 degrees North and it only registers 10º change. The other two pitch and roll seem to work fine.

How good is the calibration? Is it worth implementing something like this? http://www.camelsoftware.com/firetail/blog/uavs/3-axis-magnetometer-calibration-a-simple-technique-for-hard-soft-errors/

Many thanks,
Panos


Reply to this email directly or view it on GitHub.

@pmavros
Copy link
Author

pmavros commented Oct 14, 2014

Kris,

will have a thought on that. The link I said has a good solution to remove both hard-iron and soft-iron bias. Also quite quick to implement.

My question holds. the yaw (headings) gives neither a 360º nor a -180º to 180º output but rather a almost random result. I am using the mpu9150 sparkfun SM11486. Also I am confused with which sensor alignment you finally choose as reference the acc or the mag? So which xyz should I use to find north etc?

Finally I had a look on the MPU6050_9Axis_MotionApps41.h and it seems it also has functions to calculate the euler quaternions etc (e.g. MPUgetyawptichroll) - but you are not using those, correct? If that's the case why?

Cheers,
Panos

@kriswiner
Copy link
Owner

Hi Panos,

I am using the x,y,z of the accel/gyro as reference. If you do not calibrate the magnetometer the Euler values of yaw, pitch, and roll can be quite non-sensical. I wrote my own AHRS routines based on open-source Madgwick and Mahoney sensor fusion algorithms. I don’t like the way Invensense programs are structured nor do I like their dependence on a binary blob for the DMP, which they also depend on.

Kris

From: ΠΜ
Sent: Tuesday, October 14, 2014 5:41 AM
To: kriswiner/MPU-9150
Cc: Kris Winer
Subject: Re: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Kris,

will have a thought on that. The link I said has a good solution to remove both hard-iron and soft-iron bias. Also quite quick to implement.

My question holds. the yaw (headings) gives neither a 360º nor a -180º to 180º output but rather a almost random result. I am using the mpu9150 sparkfun SM11486. Also I am confused with which sensor alignment you finally choose as reference the acc or the mag? So which xyz should I use to find north etc?

Finally I had a look on the MPU6050_9Axis_MotionApps41.h and it seems it also has functions to calculate the euler quaternions etc (e.g. MPUgetyawptichroll) - but you are not using those, correct? If that's the case why?

Cheers,
Panos


Reply to this email directly or view it on GitHub.

@akhilmohan4487
Copy link

Hello Kris,

After implementing Madwick filter, we have the quaternion representing orientation of MPU9150 in global co-ordinate frame. Now if I want to represent the quaternion in the body co-ordinate frame what should I do ?
I have tried to implement (qSB = qGB x q*GS where x is a quaternion multiplication) where s -sensor, b- body and G - global coordinate frame, am I right in doing this ?. After this orientation of MPU in body coordinate frame and expressed in the sensor frame (qSB) is converted to euler angles and I plotted the same. But now if am rotating MPU by 180 I should be able to see a corresponding change in the plot, am I right ? (I have some confusion in using quaternions and am trying to figure it out).

Thanks in advance,
Akhil

@kriswiner
Copy link
Owner

Hi Akhil,

I am not sure what you want to do. The quaternion represents the sensor's
absolute orientation with respect to an external (yes, global) standard.
This is usually True North. What do you mean by the body coordinate frame in
this case?

Kris

-----Original Message-----
From: Akhil Mohan [mailto:notifications@github.com]
Sent: March 12, 2015 7:13 AM
To: kriswiner/MPU-9150
Cc: Kris Winer
Subject: Re: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Hello Kris,

After implementing Madwick filter, we have the quaternion representing
orientation of MPU9150 in global co-ordinate frame. Now if I want to
represent the quaternion in the body co-ordinate frame what should I do ?
I have tried to implement (qSB = qGB x q*GS where x is a quaternion
multiplication) where s -sensor, b- body and G - global coordinate frame, am
I right in doing this ?. After this orientation of MPU in body coordinate
frame and expressed in the sensor frame (qSB) is converted to euler angles
and I plotted the same. But now if am rotating MPU by 180 I should be able
to see a corresponding change in the plot, am I right ? (I have some
confusion in using quaternions and am trying to figure it out).

Thanks in advance,
Akhil

Reply to this email directly or view it on GitHub
#3 (comment) .
<https://github.com/notifications/beacon/AGY1qsn3HDq27TfiuRGSIbtV28fnTfhaks5
n0ZZngaJpZM4CuKSq.gif>

@akhilmohan4487
Copy link

Hello,
I have a wireless board ready where multiple MPU's can be plugged in and the data can be logged on to a PC. Now on the PC i wanted to animate the motion that humans do in real-time. Say, I am attaching the MPU on elbow and forearm and wanted to measure the joint angle, I have to represent the information in the body coordinate frame (the location where MPU is mounted). I am bit uncertain how to do this ? What is your opinion in this regard ?

Thanks for the quick reply.
Akhil

@kriswiner
Copy link
Owner

If you have absolute orientation for each sensor, can you not rather simply calculate the relative angle between, say, the two IMU's x-axes? You have two orientation vectors wrt the same global absloute reference; hwy can you not simply compare these two orientations directly? Why do you need a transform?

@akhilmohan4487
Copy link

Yes, we will get a relative orientation of one segment with other in this way. But the problem is i don't know the value of a at time = 0. This can be known only when you keep your body in a fixed pose. For that, initially the segment under measurement is calibrated.

For calibration - (R)GB = (R)SB x (R)GS
(R)GB - orientation info of body wrt global (known for a fixed pose)
(R)SB - orientation info of body wrt sensor
(R)GS - orientation info of sensor wrt global (Madgwick out data converted in rotation matrix form)

We can do all these algebra in quaterion form and is mathematically and computationally useful.

For me, I am nor sure how do we represent (R)GB (Is it a (I)3x3 matrix at rest) ? I don't know.

@kriswiner
Copy link
Owner

I am not sure what you are asking. If you know each sensors absolute orientation (i.e., quaternion) wrt a fixed global reference like True North, don't you know everything you need to know?

@akhilmohan4487
Copy link

Hello,
I would try my best to explain what I wanted to do. After the Madgwick filter update, I have a quaternion which represents the absolute orientation of sensor wrt a global co-ordinate frame(NED). Now I am attaching my Imu's to forearm(segment1) and upper-arm(segment2). Now I want to express segment kinematics in global frame. For that, I have to perform an initial calibration where the orientation of the sensor module wrt segment and the relative distances are measured.

Using rotation matrix I know that (R)GB = (R)SB x (R)GS.
Now how can I do the same using quaternion where I wanted to know qSB given qGB abd qGS ?

I have tried doing qSB = qGB x q*GS (where * corresponds to Quaternion conjugate and x quaternion multiplication). Here qGB = [0.707, 0, 0, -0.707] and which results in singularities and I don't know how to solve this.

Hope this explanation is fine. I am really sorry if I am not able to convey what I want.

Thanks for your patience Kris.

@Ecollot
Copy link

Ecollot commented Apr 9, 2015

Hi Kris,
Do you now have a calibration for the magnetometer? Just to know if I need to figure one out myself or not =)
Great work by the way, thanks so much!
Emilie

@kriswiner
Copy link
Owner

Here is a mag calibration routine for the MPU9250 which can be adapted for the MPU9150:

void magcalMPU9250(float * dest1)
{
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0};
int16_t mag_max[3] = {0, 0, 0}, mag_min[3] = {0, 0, 0}, mag_temp[3] = {0, 0, 0};
Serial.println("Mag Calibration: Wave device in a figure eight until done!");
delay(4000);
sample_count = 64;
for(ii = 0; ii < sample_count; ii++) {
readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj++) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Serial.println("mag x min/max:"); Serial.println(mag_max[0]); Serial.println(mag_min[0]);
// Serial.println("mag y min/max:"); Serial.println(mag_max[1]); Serial.println(mag_min[1]);
// Serial.println("mag z min/max:"); Serial.println(mag_max[2]); Serial.println(mag_min[2]);
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in counts
mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in counts
mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in counts
dest1[0] = (float) mag_bias[0]_mRes_magCalibration[0]; // save mag biases in G for main program
dest1[1] = (float) mag_bias[1]_mRes_magCalibration[1];
dest1[2] = (float) mag_bias[2]_mRes_magCalibration[2];
Serial.println("Mag Calibration done!");
}

@Ecollot
Copy link

Ecollot commented Apr 10, 2015

Thanks so much for answering so fast !

Still working with the code, probably got another few questions popping up,
but the main one is : you calculate the biases for the accelerometer and
gyroscope, but you never use these when considering the raw values in any
way it seems. If you do, where do you use these corrections ?
And for now, the magnetometer biases are just put in by hand right ?

On 10 April 2015 at 11:13, Kris Winer notifications@github.com wrote:

Here is a mag calibration routine for the MPU9250 which can be adapted for
the MPU9150:

void magcalMPU9250(float * dest1)
{
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0};
int16_t mag_max[3] = {0, 0, 0}, mag_min[3] = {0, 0, 0}, mag_temp[3] = {0,
0, 0};
Serial.println("Mag Calibration: Wave device in a figure eight until
done!");
delay(4000);
sample_count = 64;
for(ii = 0; ii < sample_count; ii++) {
readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj++) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Serial.println("mag x min/max:"); Serial.println(mag_max[0]);
Serial.println(mag_min[0]);
// Serial.println("mag y min/max:"); Serial.println(mag_max[1]);
Serial.println(mag_min[1]);
// Serial.println("mag z min/max:"); Serial.println(mag_max[2]);
Serial.println(mag_min[2]);
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in
counts
mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in
counts
mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in
counts
dest1[0] = (float) mag_bias[0]_mRes_magCalibration[0]; // save mag biases
in G for main program
dest1[1] = (float) mag_bias[1]_mRes_magCalibration[1];
dest1[2] = (float) mag_bias[2]_mRes_magCalibration[2];
Serial.println("Mag Calibration done!");
}


Reply to this email directly or view it on GitHub
#3 (comment).

@kriswiner
Copy link
Owner

I write the accel and gyro biases to user bias registers. These bias values
are automatically subtracted from the raw sensor data. You can also choose
to not write any values there and manually subtract the accel and gyro
biases like the mag biases are done. I sometimes do this because there is a
temperature bit in the accel bias register that has to be preserved and I
find sometimes the biases applied automatically by using thebias register
value are not quite right for the accelerometer, so I often still do this
one by hand.

Kris

-----Original Message-----
From: Ecollot [mailto:notifications@github.com]
Sent: April 9, 2015 7:19 PM
To: kriswiner/MPU-9150
Cc: Kris Winer
Subject: Re: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Thanks so much for answering so fast !

Still working with the code, probably got another few questions popping up,
but the main one is : you calculate the biases for the accelerometer and
gyroscope, but you never use these when considering the raw values in any
way it seems. If you do, where do you use these corrections ?
And for now, the magnetometer biases are just put in by hand right ?

On 10 April 2015 at 11:13, Kris Winer notifications@github.com wrote:

Here is a mag calibration routine for the MPU9250 which can be adapted for
the MPU9150:

void magcalMPU9250(float * dest1)
{
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0};
int16_t mag_max[3] = {0, 0, 0}, mag_min[3] = {0, 0, 0}, mag_temp[3] = {0,
0, 0};
Serial.println("Mag Calibration: Wave device in a figure eight until
done!");
delay(4000);
sample_count = 64;
for(ii = 0; ii < sample_count; ii++) {
readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj++) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Serial.println("mag x min/max:"); Serial.println(mag_max[0]);
Serial.println(mag_min[0]);
// Serial.println("mag y min/max:"); Serial.println(mag_max[1]);
Serial.println(mag_min[1]);
// Serial.println("mag z min/max:"); Serial.println(mag_max[2]);
Serial.println(mag_min[2]);
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in
counts
mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in
counts
mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in
counts
dest1[0] = (float) mag_bias[0]_mRes_magCalibration[0]; // save mag biases
in G for main program
dest1[1] = (float) mag_bias[1]_mRes_magCalibration[1];
dest1[2] = (float) mag_bias[2]_mRes_magCalibration[2];
Serial.println("Mag Calibration done!");
}

Reply to this email directly or view it on GitHub
#3 (comment).

Reply to this email directly or view it on GitHub
#3 (comment) .
<https://github.com/notifications/beacon/AGY1qj7yBPwEtmTuSvDNUBHBT4Sqsnoxks5
n9yqsgaJpZM4CuKSq.gif>

@Ecollot
Copy link

Ecollot commented Apr 10, 2015

Ok, got that. One more thing, how do define the user-environmental x,y,z
axis corrections for the magnetometer? Where do these values come from?

On 10 April 2015 at 14:24, Kris Winer notifications@github.com wrote:

I write the accel and gyro biases to user bias registers. These bias values
are automatically subtracted from the raw sensor data. You can also choose
to not write any values there and manually subtract the accel and gyro
biases like the mag biases are done. I sometimes do this because there is a
temperature bit in the accel bias register that has to be preserved and I
find sometimes the biases applied automatically by using thebias register
value are not quite right for the accelerometer, so I often still do this
one by hand.

Kris

-----Original Message-----
From: Ecollot [mailto:notifications@github.com]
Sent: April 9, 2015 7:19 PM
To: kriswiner/MPU-9150
Cc: Kris Winer
Subject: Re: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Thanks so much for answering so fast !

Still working with the code, probably got another few questions popping up,
but the main one is : you calculate the biases for the accelerometer and
gyroscope, but you never use these when considering the raw values in any
way it seems. If you do, where do you use these corrections ?
And for now, the magnetometer biases are just put in by hand right ?

On 10 April 2015 at 11:13, Kris Winer notifications@github.com wrote:

Here is a mag calibration routine for the MPU9250 which can be adapted
for
the MPU9150:

void magcalMPU9250(float * dest1)
{
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0};
int16_t mag_max[3] = {0, 0, 0}, mag_min[3] = {0, 0, 0}, mag_temp[3] = {0,
0, 0};
Serial.println("Mag Calibration: Wave device in a figure eight until
done!");
delay(4000);
sample_count = 64;
for(ii = 0; ii < sample_count; ii++) {
readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj++) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Serial.println("mag x min/max:"); Serial.println(mag_max[0]);
Serial.println(mag_min[0]);
// Serial.println("mag y min/max:"); Serial.println(mag_max[1]);
Serial.println(mag_min[1]);
// Serial.println("mag z min/max:"); Serial.println(mag_max[2]);
Serial.println(mag_min[2]);
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in
counts
mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in
counts
mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in
counts
dest1[0] = (float) mag_bias[0]_mRes_magCalibration[0]; // save mag biases
in G for main program
dest1[1] = (float) mag_bias[1]_mRes_magCalibration[1];
dest1[2] = (float) mag_bias[2]_mRes_magCalibration[2];
Serial.println("Mag Calibration done!");
}

Reply to this email directly or view it on GitHub
#3 (comment).

Reply to this email directly or view it on GitHub
#3 (comment) .
<
https://github.com/notifications/beacon/AGY1qj7yBPwEtmTuSvDNUBHBT4Sqsnoxks5
n9yqsgaJpZM4CuKSq.gif>


Reply to this email directly or view it on GitHub
#3 (comment).

@kriswiner
Copy link
Owner

Do you mean the mag biases, as below or what?

The real mag response surface is generally off-center and ellipsoidal, not
the centered, spherical ideal. You can record the min.max mag values in the
three axis directions to recenter the response surface. More complicated
matrix corrections are required to respherize the response surface. The
below function just does the recentering. Or do you mean something else?

-----Original Message-----
From: Ecollot [mailto:notifications@github.com]
Sent: April 9, 2015 8:52 PM
To: kriswiner/MPU-9150
Cc: Kris Winer
Subject: Re: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Ok, got that. One more thing, how do define the user-environmental x,y,z
axis corrections for the magnetometer? Where do these values come from?

On 10 April 2015 at 14:24, Kris Winer notifications@github.com wrote:

I write the accel and gyro biases to user bias registers. These bias
values
are automatically subtracted from the raw sensor data. You can also choose
to not write any values there and manually subtract the accel and gyro
biases like the mag biases are done. I sometimes do this because there is
a
temperature bit in the accel bias register that has to be preserved and I
find sometimes the biases applied automatically by using thebias register
value are not quite right for the accelerometer, so I often still do this
one by hand.

Kris

-----Original Message-----
From: Ecollot [mailto:notifications@github.com]
Sent: April 9, 2015 7:19 PM
To: kriswiner/MPU-9150
Cc: Kris Winer
Subject: Re: [MPU-9150] Magnetometer calibration and heading (yaw) (#3)

Thanks so much for answering so fast !

Still working with the code, probably got another few questions popping
up,
but the main one is : you calculate the biases for the accelerometer and
gyroscope, but you never use these when considering the raw values in any
way it seems. If you do, where do you use these corrections ?
And for now, the magnetometer biases are just put in by hand right ?

On 10 April 2015 at 11:13, Kris Winer notifications@github.com wrote:

Here is a mag calibration routine for the MPU9250 which can be adapted
for
the MPU9150:

void magcalMPU9250(float * dest1)
{
uint16_t ii = 0, sample_count = 0;
int32_t mag_bias[3] = {0, 0, 0};
int16_t mag_max[3] = {0, 0, 0}, mag_min[3] = {0, 0, 0}, mag_temp[3] =
{0,
0, 0};
Serial.println("Mag Calibration: Wave device in a figure eight until
done!");
delay(4000);
sample_count = 64;
for(ii = 0; ii < sample_count; ii++) {
readMagData(mag_temp); // Read the mag data
for (int jj = 0; jj < 3; jj++) {
if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj];
if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj];
}
delay(135); // at 8 Hz ODR, new mag data is available every 125 ms
}
// Serial.println("mag x min/max:"); Serial.println(mag_max[0]);
Serial.println(mag_min[0]);
// Serial.println("mag y min/max:"); Serial.println(mag_max[1]);
Serial.println(mag_min[1]);
// Serial.println("mag z min/max:"); Serial.println(mag_max[2]);
Serial.println(mag_min[2]);
mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in
counts
mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in
counts
mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in
counts
dest1[0] = (float) mag_bias[0]_mRes_magCalibration[0]; // save mag
biases
in G for main program
dest1[1] = (float) mag_bias[1]_mRes_magCalibration[1];
dest1[2] = (float) mag_bias[2]_mRes_magCalibration[2];
Serial.println("Mag Calibration done!");
}

Reply to this email directly or view it on GitHub
#3 (comment).

Reply to this email directly or view it on GitHub
#3 (comment) .
<

https://github.com/notifications/beacon/AGY1qj7yBPwEtmTuSvDNUBHBT4Sqsnoxks5
n9yqsgaJpZM4CuKSq.gif>

Reply to this email directly or view it on GitHub
#3 (comment).

Reply to this email directly or view it on GitHub
#3 (comment) .
<https://github.com/notifications/beacon/AGY1qgWW-WzFm7GoSYZbGw5MmgMp_I56ks5
n90BCgaJpZM4CuKSq.gif>

@PatelKishanJ
Copy link

Hello kris,

 I have using atan formula to find magnetic direction.But its working good if Device put in position by which  magnetic- z direction will vertically down.
So,if i change the device position like that magnetic x direction will vertically down.So,below formula to find the direction will be wrong.

heading_dir = 90 - ( atan ( mx / my ) ) * ( 180 / PI ) - 23.5; //here, 23.5 is a declination angle.

So,any idea by which i can find that which direction is vertically down?.

kindly reply.

@kriswiner
Copy link
Owner

Why don't you want to use 9 DoF sensor fusion for heading estimation? It is a much more accurate solution.

Your approach can only work in some limited circumstances. And then not very well.

@PatelKishanJ
Copy link

I have no idea about 9 DoF sensor.Can you explain me,how can i use and how can access and how it is useful?.

@PatelKishanJ
Copy link

Actually,I have using IMU/mpu-9250 to detect position of device. In our project,we will attach our device by OBD connector.

But problem is critical for that.Because the OBD connector has attached in different types for different device.So,magnetic direction will changes.

@kriswiner
Copy link
Owner

kriswiner commented May 13, 2017 via email

@PatelKishanJ
Copy link

so,its for find yaw,pitch and roll...right?

@PatelKishanJ
Copy link

I refer code which you told.
Actually ,i want to measure direction and its work.But problem is that,device installation-direction is not fix.So,device movement direction will change(false).So,how can i use yaw,pitch and roll reading to measure
device's movement with direction?.

@kriswiner
Copy link
Owner

kriswiner commented Jun 1, 2017 via email

@PatelKishanJ
Copy link

PatelKishanJ commented Jun 3, 2017

imu_direction_issue1

see this image....
it reveals that device put in any direction ,when it start it shows same direction....
then when i rotate device clockwise, direction will in this sequence like NW -> NE -> SE -> SW ....
This sequence is same for all 4 positions ....

now,still issues occur.....see as below....

imu_direction_issue2

now if 2 cars stay in opposite direction and both moving clockwise.....then it shows same direction sequence...( NW -> NE -> SE -> SW ) .....

In our condition....car_direction not fixed....device_direction not fixed....so this type of issue happen......

so,how can i use magnetometer data....to detect car movement with direction....

@PatelKishanJ
Copy link

i can get completely result from IMU to detect car movement.But it will right when IMU put at fixed position.But in our condition if many cars stay in north direction , but IMU may be attached in car with different type of position.So,if all cars moving in one direction.But it shows different direction.Because of device attached in different style in cars.

@kriswiner
Copy link
Owner

kriswiner commented Jun 3, 2017 via email

@PatelKishanJ
Copy link

i show you my code , wherever i have used your code ,i just call that function(not write definition)
i have initialize imu and do configure as your code...
then i have get raw data of accelerometer and check which axis is pointing to the vertically down.
on basis of it,heading direction will fixed....
i have made changes in code.so you can see some are commented....

initMPU9250(); // its your function
initial_compensation(); // in this function i have checking that which axis is pointing vertically down.

//see the definition of the above function
void initial_compensation()
{
static int ini_counter,accelCount[3];

do
{
	readByte_jt(MPU9250_ADDRESS,INT_STATUS,data);

	if(data[0] & 0x01)
	{
		readAccelData_jt(accelCount);
		Bias[0] += (float)accelCount[0];
	    Bias[1] += (float)accelCount[1];
		Bias[2] += (float)accelCount[2];

		ini_counter++;
	}
}while(ini_counter<10);

Bias[0] /=10;
Bias[1] /=10;
Bias[2] /=10;


/*NOTE :-
 * 		Below code is useful to find that which accelero/gyro meter's axis ( X,Y or Z ) is in vertical down direction.
 * 		It is necessary.Because formula for finding device location is depend on axis.So,for different vertical down direction-axis,axis in formula will be change.
 */

 mag_dir_x = (Bias[0]/16384) * 9.8;
 mag_dir_y = (Bias[1]/16384) * 9.8;
 mag_dir_z = (Bias[2]/16384) * 9.8;

// while(1) //useful to see data on the terminal...
// {
//
// uart_transmit_string_jt("mag_dir_x = ",12);
// uart_transmit_float_jt(mag_dir_x);
// uart_transmit_char_jt(' ');
// uart_transmit_float_jt(Bias[0]);
// uart_transmit_char_jt('\n');
//
// uart_transmit_string_jt("mag_dir_y = ",12);
// uart_transmit_float_jt(mag_dir_y);
// uart_transmit_char_jt(' ');
// uart_transmit_float_jt(Bias[1]);
// uart_transmit_char_jt('\n');
//
// uart_transmit_string_jt("mag_dir_z = ",12);
// uart_transmit_float_jt(mag_dir_z);
// uart_transmit_char_jt(' ');
// uart_transmit_float_jt(Bias[2]);
// uart_transmit_char_jt('\n');
//
// }

 if( (mag_dir_x > 8) ||	(mag_dir_x < (-8)) )
 {
	 //	vertical_down_direction is  accelero/gyro meter x-axis.

	 if( mag_dir_x > 8 )
		direction_section = 1;			// +ve X-axis

	else
		direction_section = 2;			// -ve X-axis
}

else if( (mag_dir_y > 8) ||	(mag_dir_y < (-8)) )
{
	//	vertical_down_dirextion is accelero/gyro meter y-axis.

	if( mag_dir_y > 8 )
		direction_section = 3;			// +ve Y-axis

	else
		direction_section = 4;			// -ve Y-axis
}

else if( (mag_dir_z > 8) ||	(mag_dir_z < (-8)) )
{
	//	vertical_down_dirextion is accelero/gyro meter z-axis.

	if( mag_dir_z > 8 )
		direction_section = 5;			// +ve Z-axis

	else
		direction_section = 6;			// -ve Z-axis
}

}

initAK8963(); // its also your function

readGyroData_jt(gyroCount);
gx = (float)gyroCount[0]/(float)131;// - gyroBias[0]; // get actual gyro value, this depends on scale being set
gy = (float)gyroCount[1]/(float)131;// - gyroBias[1];
gz = (float)gyroCount[2]/(float)131;// - gyroBias[2];

// if( ((gx < -2.6) || (gx > 2.6)) || ((gy < -1.2) || (gy > 1.2)) || ((gz < -3.0) || (gz > 3.0)) )
// if( ((gx < -10.0) || (gx > 10.0)) || ((gy < -10.0) || (gy > 10.0)) || ((gz < -5.0) || (gz > 5.0)) )

		if( ((gx < -5.0) || (gx > 5.0)) || ((gy < -5.0) || (gy > 5.0)) || ((gz < -5.0) || (gz > 5.0)) )
			car_moving = TRUE;
		else
			car_moving = FALSE;

		readAccelData_jt(accelCount);
		accelCount[0] -=(int) Bias[0];
		accelCount[1] -=(int) Bias[1];
		accelCount[2] -=(int) Bias[2];
		 ax = (float)accelCount[0]/(float)16384;//((float)2/32768);// - accelBias[0];  // get actual g value, this depends on scale being set
		 ay = (float)accelCount[1]/(float)16384;// - accelBias[1];
		 az = (float)accelCount[2]/(float)16384;// - accelBias[2];
		 ax *= 9.8;
		 ay *= 9.8;
		 az *= 9.8;

		 if(ax < 0.3 && ax > -0.3)
			 ax=0;
		 if(ay < 0.3 && ay > -0.3)
			 ay=0;
		 if(az < 0.3 && az > -0.3)
			 az=0;

readMagData_jt(magCount);

		 // Calculate the magnetometer values in milliGauss
		     // Include factory calibration per data sheet and user environmental corrections
		 mx = (float)magCount[0]*(1/0.6);//*mRes*magCalibration[0] - magbias_1[0];  // get actual magnetometer value, this depends on scale being set
		 my = (float)magCount[1]*(1/0.6);//*mRes*magCalibration[1] - magbias_1[1];
		 mz = (float)magCount[2]*(1/0.6);//*mRes*magCalibration[2] - magbias_1[2];
		 switch_to_acce_gyro_meter_jt();

// if(counter == 0)
// {
// switch(direction_section)
// {
//
// case 1: side_dir = mx;//mx;
// down_dir = mz;//mz;
// break;
//
// case 2: side_dir = mx;//mz; //if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
// down_dir = -mz;//mx; //if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
// break;
//
// case 3:
// if( ((my > 150)&&(mx<250)) && ((mz > -50)&&(mz<160)) )
// {
// counter = 1;
// break;
// }
//
// else if( ((my > -250)&&(mx < -100)) && ((mz>150)&&(mz<240)) )
// {
// counter = 2;
// break;
// }
//
// else if( ((my > -380)&&(mx < -300)) && ((mz > -230)&&(mz < -90)) )
// {
// counter = 3;
// break;
// }
//
// else if( ((my > -140)&&(mx<170)) && ((mz > -240)&&(mz < -400)) )
// {
// counter = 4;
// break;
// }
//
// case 4:
// if( ((my > -430)&&(mx < -280)) && ((mz > 30)&&(mz<210)) )
// {
// counter = 1;
// break;
// }
//
// else if( ((my > -110)&&(mx < 70)) && ((mz>310)&&(mz<210)) )
// {
// counter = 2;
// break;
// }
//
// else if( ((my > 120)&&(mx < 200)) && ((mz > -210)&&(mz < -70)) )
// {
// counter = 3;
// break;
// }
//
// else if( ((my > -330)&&(mx < -150)) && ((mz > -330)&&(mz < -200)) )
// {
// counter = 4;
// break;
// }
//
//
// case 5:
// if( ((mx > -100)&&(mx<150)) && ((my>100)&&(my<250)) )
// {
// counter = 1;
// break;
// }
//
// else if( ((mx > -230)&&(mx < -120)) && ((my > -390)&&(my < -350)) )
// {
// counter = 2;
// break;
// }
//
// else if( ((mx > -400)&&(mx < -430)) && ((my > -80)&&(my<80)) )
// {
// counter = 3;
// break;
// }
//
// else if( ((mx > 180)&&(mx<210)) && ((my > -220)&&(my < -140)) )
// {
// counter = 4;
// break;
// }
//
// case 6:
// if( ((mx > -60)&&(mx < 80)) && ((my > -380)&&(my < -290)) )
// {
// counter = 1;
// break;
// }
//
// else if( ((mx > 140)&&(mx < 190)) && ((my > -100)&&(my < 60)) )
// {
// counter = 2;
// break;
// }
//
// else if( ((mx > -260)&&(mx < -110)) && ((my > 180)&&(my<210)) )
// {
// counter = 3;
// break;
// }
//
// else if( ((mx > -420)&&(mx < -360)) && ((my > -270)&&(my < -90)) )
// {
// counter = 4;
// break;
// }
//
// default:
// break;
// }
// }

switch(direction_section)
{

		case 1:	side_dir = mx;//mx;
				down_dir = mz;//mz;
				break;

		case 2: side_dir = mx;//mz;			//if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
				down_dir = -mz;//mx;			//if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
				break;

		case 3:

// if( counter == 1 )
// {
// side_dir = mz; //mx;
// down_dir = my; //my;
//
// break;
// }
//
// else if( counter == 2 )
// {
// side_dir = -my; //mx;
// down_dir = mz; //my;
//
// break;
// }
//
// else if( counter == 3 )
// {
// side_dir = -mz; //mx;
// down_dir = -my; //my;
//
// break;
// }
//
// else if( counter == 4 )
// {
// side_dir = my; //mx;
// down_dir = -mz; //my;
//
// break;
// }

			side_dir = my;//mz;
			down_dir = -mz;//my;
			break;

		case 4:

// if( counter == 1 )
// {
// side_dir = mz; //mx;
// down_dir = -my; //my;
//
// break;
// }
//
// else if( counter == 2 )
// {
// side_dir = my; //mx;
// down_dir = mz; //my;
//
// break;
// }
//
// else if( counter == 3 )
// {
// side_dir = -mz; //mx;
// down_dir = my; //my;
//
// break;
// }
//
// else if( counter == 4 )
// {
// side_dir = -my; //mx;
// down_dir = -mz; //my;
//
// break;
// }

			side_dir = my;//my;			//if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
			down_dir = mz;//mz;			//if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
			break;

		case 5:

//// if( (mx>0) && (my>0) )
// if( counter == 1 )
// {
// side_dir = mx; //mx;
// down_dir = my; //my;
//
// break;
// }
////
//// else if( (mx<0) && (my<0) )
// else if( counter == 2 )
// {
// side_dir = -mx; //mx;
// down_dir = -my; //my;
//
// break;
// }
//// else if( (mx<0) && (my>0) )
// else if( counter == 3 )
// {
// side_dir = my; //mx;
// down_dir = -mx; //my;
//
// break;
// }
////
//// else if( (mx>0) && (my<0) )
// else if( counter == 4 )
// {
// side_dir = -my; //mx;
// down_dir = mx; //my;
//
// break;
// }

				side_dir = mx; //mx;
				down_dir = my; //my;
				break;

		case 6:

// if( counter == 1 )
// {
// side_dir = mx; //mx;
// down_dir = -my; //my;
//
// break;
// }
//
// else if( counter == 2 )
// {
// side_dir = my; //mx;
// down_dir = mx; //my;
//
// break;
// }
//
// else if( counter == 3 )
// {
// side_dir = -mx; //mx;
// down_dir = my; //my;
//
// break;
// }
//
// else if( counter == 4 )
// {
// side_dir = -my; //mx;
// down_dir = -mx; //my;
//
// break;
// }

			side_dir = mx;//my;			//if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
			down_dir = -my;//mx;			//if here changing sign(-ve),nissan_car/swift_car detect same in direction,but angle may be different.
			break;

		default:
				break;

	}

	/*NOTE :-
	 * 		here, 23.5 is a  declination angle,it is changing at every day(means it is difference for all 365 days).
	 *
	 * 		"atan" is the function implemented by programming languages such as C++ for arctan,
	 * 		 and returns values (in radians) in the range: -PI/2 <= atan(x) <= PI/2.
	 *
	 * 		if you want these values in degrees, you must calculate atan(x*180/PI).
	 */

	if(down_dir == 0 && side_dir < 0)
		heading_dir = 180 - 23.5;

	else if(down_dir == 0 && side_dir > 0)
		heading_dir = 360 - 23.5;

	else if(down_dir > 0)
	{
		heading_dir = 90 - (atan(side_dir/down_dir))*(180/PI) - 23.5;

		if(heading_dir < 0)
			heading_dir += 360;
	}

	else if(down_dir < 0)
	{
		heading_dir = 270 - (atan(side_dir/down_dir))*(180/PI) - 23.5;

		if(heading_dir > 360)
			heading_dir -= 360;
	}


	char data_1[3]={0};

	if(heading_dir == 0)
		data_1[0]='N';

	else if(heading_dir == 180)
		data_1[0]='S';

	else if(heading_dir > 0 && heading_dir < 90)
	{
		data_1[0]='N';
		data_1[1]='W';
	}

	else if(heading_dir > 90 && heading_dir < 180)
	{
		data_1[0]='S';
	 	data_1[1]='W';
	}

	else if(heading_dir > 180 && heading_dir < 270)
	{
		data_1[0]='S';
	 	data_1[1]='E';
	}

	else if(heading_dir > 270 && heading_dir < 360)
	{
		data_1[0]='N';
	 	data_1[1]='E';
	}

	else
		;

	heading_dir = 360 - heading_dir;
	find_position_jt();
	dx = (float)distance*cos(heading_dir);
	dy = (float)distance*sin(heading_dir);
	delta_longi = (float)dx/(111320 * cos(LAT));
	delta_lat = (float)dy / 110540;
	F_longi = LONGI +(float)delta_longi;
	F_lat = LAT +(float) delta_lat;

// if( car_moving == TRUE )
{
sprintf(data_sensor1,"Heading Direction= %f %s\n",heading_dir, data_1);
uart_transmit_string_jt(data_sensor1,strlen(data_sensor1));
sprintf(data_sensor1,"vx = %f vy = %f vz = %f\n",vx,vy,vz);
uart_transmit_string_jt(data_sensor1,strlen(data_sensor1));
sprintf(data_sensor1,"px = %f py = %f pz = %f\nTime = %fsecs\n distance = %f \n lat = %f long = %f",px,py,pz,t, distance,F_lat,F_longi);
uart_transmit_string_jt(data_sensor1,strlen(data_sensor1));
uart_transmit_string_jt("\n\n",2);
}
// else
// uart_transmit_string_jt("CAR IS STATIONARY\n",18);

@kriswiner
Copy link
Owner

kriswiner commented Jun 5, 2017 via email

@PatelKishanJ
Copy link

for gyrometer....

readGyroData_jt(gyroCount);
gx = (float)gyroCount[0]/(float)131;// - gyroBias[0]; // get actual gyro value, this depends on scale being set
gy = (float)gyroCount[1]/(float)131;// - gyroBias[1];
gz = (float)gyroCount[2]/(float)131;// - gyroBias[2];

// if( ((gx < -2.6) || (gx > 2.6)) || ((gy < -1.2) || (gy > 1.2)) || ((gz < -3.0) || (gz > 3.0)) )
// if( ((gx < -10.0) || (gx > 10.0)) || ((gy < -10.0) || (gy > 10.0)) || ((gz < -5.0) || (gz > 5.0)) )

		if( ((gx < -5.0) || (gx > 5.0)) || ((gy < -5.0) || (gy > 5.0)) || ((gz < -5.0) || (gz > 5.0)) )
			car_moving = TRUE;
		else
			car_moving = FALSE;

for accelerometer....

		readAccelData_jt(accelCount);
		accelCount[0] -=(int) Bias[0];
		accelCount[1] -=(int) Bias[1];
		accelCount[2] -=(int) Bias[2];
		 ax = (float)accelCount[0]/(float)16384;//((float)2/32768);// - accelBias[0];  // get actual g value, this depends on scale being set
		 ay = (float)accelCount[1]/(float)16384;// - accelBias[1];
		 az = (float)accelCount[2]/(float)16384;// - accelBias[2];
		 ax *= 9.8;
		 ay *= 9.8;
		 az *= 9.8;

if(ax < 0.3 && ax > -0.3)
ax=0;
if(ay < 0.3 && ay > -0.3)
ay=0;
if(az < 0.3 && az > -0.3)
az=0;

is it?....

@kriswiner
Copy link
Owner

kriswiner commented Jun 5, 2017 via email

@PatelKishanJ
Copy link

do
{
readByte_jt(MPU9250_ADDRESS,INT_STATUS,data);

if(data[0] & 0x01)
{
	readAccelData_jt(accelCount);
	Bias[0] += (float)accelCount[0];
    Bias[1] += (float)accelCount[1];
	Bias[2] += (float)accelCount[2];

	ini_counter++;
}

}while(ini_counter<10);

Bias[0] /=10;
Bias[1] /=10;
Bias[2] /=10;

@Gubbifisken
Copy link

Gubbifisken commented Jun 9, 2018

Hello Kris

Thanks for the great work with your lib. I could really use your input.

I have read so many posts, browsed through different examples, tried different things, Madgwick/Mahony updates, +mz, -mz, different calibration methods etc. but nothing yields a usable result for my purpose.

Goal and setup:
I am using

I want to use the MPU9250 and compensation to get a north heading, so the I can get a north heading compared to the orientation of the MPU9250. Which axis to use is not important, since I can mount the MPU9250 however I like for my project.

I would really appreciate a complete code example of how to set up, how to calibrate and how to use orientation compensation (when MPU9250 is rotated around x, y, z) to get heading to north (0-360 or -180-180). Please do not just link to other github libs, since I have already tried thess, and tried to implement calibration, magBias etc. and nothing yields anything usable. I have the code up and running, and reading etc. So not a I2C ussue or wiring usse. But yaw makes no sense and is not the same when rorating back to same position over and over again.

Hope to get some input, so I can get my gadget geocache out in the field, for many geocacher to enjoy!

Thanks in advance to anybody giving input
Gubbifisken

@kriswiner
Copy link
Owner

kriswiner commented Jun 9, 2018 via email

@Gubbifisken
Copy link

Hello Kris
Thansk for the super quick reply. What I meant with not github link was that the ones I already have have inconsistencies between them (complete repo but also comments online). An example is that many write about calibration. Hence I wonder why the calibration function (min, maxand avg bias) is not just a part of the libs I have found, and then commeted out (with a comment that one needs to use for finfing specific HW bias for their setup).

I will look into the links you send, and look at upgrading MCU to F4.

I wonder why the Chinese products are that bad. I have a 6050 that works great for my gyro project. I also fly quadcopter with parts from China, and they works well. And here we are talking about a quad spinning with 1200 degrees/s very smooth.

Do you have a suggestion where to get whatever MPU in EU that it workth buying. I need one that can farely give me north heading (making kind of digital compass). A few degrees of is fine and not a problem

Gubbifisken

@kriswiner
Copy link
Owner

kriswiner commented Jun 9, 2018 via email

@JabberwockPL
Copy link

Hello,

I seem to have the same issue...

I have been running the calibration routine that was posted above, but I get rather inconsistent results from it and, when the values are pasted into the code, I still get nonsensical yaw results.

@kriswiner
Copy link
Owner

kriswiner commented Sep 6, 2018 via email

@JabberwockPL
Copy link

Yes, I am using MPU9150.

I am not sure what you mean by NED - I am running the calibration procedure you have given above and am plugging the results (dest[0] to [3]) to magbias[0] to [3], as that is what the original MPU9250 program seems to be doing. The values I have obtained were -42; 79; 35; although they vary from measurement to measurement.

@kriswiner
Copy link
Owner

kriswiner commented Sep 6, 2018 via email

@JabberwockPL
Copy link

For now I am just testing your code and the only changes I have made are removing the display references, changing the declination data and putting the magbias values manually.

I have run the calibration again and when using the new values it works much better. It is still rather inaccurate for yaw (turning the module about 90 degrees goes from 0 to 160), but I guess additional calibration will help with that.

@kriswiner
Copy link
Owner

kriswiner commented Sep 7, 2018 via email

@dsstewart
Copy link

Hi Kris,

I've seen you mention several times that you should to run the fusion algorithm much more frequently than the sample rate. Can you explain why this is the case? I've been trying to figure out why that is, but so far havn't come up with anything.

@kriswiner
Copy link
Owner

kriswiner commented Nov 2, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants