How to count number of degree turned HMC5883L

Hi all, just a quick question about the use of a magnetometer. I require a routine to say

"diff = [newangle - previousangle]"

How do you count the number of degrees it has moved since turning beyond 360 degrees will bring you to 0 and so on. You can't just take for example previous angle(355) - newangle(45). That would give me 310 degrees which infact should be 50 degrees in difference.

I need this to measure the angle which my 4WD robot has turned. :confused:
All help is appreciated.

Thanks,
MatthewPrag

First,, 45-355 = -310, not 310.

So you want to constrain it to +/- 180 degrees... and you know the values will never be larger than +/- 360

int diff(oldangle,newangle){
int a = newangle-oldangle;
return (a>180?a-360:(a<-180)?a+360:a);
}

Hi Matthew

This code should find the acute or obtuse angle between two bearings (i.e. not the reflex angle).

int previousangle = 355;
int newangle = 45;
int diff = abs(previousangle - newangle);
  if (diff > 180)
  {
    diff = 360 - diff;
  }

Regards

Ray

thanks for the help guys!

I have a 4wd robot and a HMC5883L magnetometer attached to the center point of the robot.
My question is how can i set a condition where the motor will turn until a certain angle and stop.

For example at 350 degrees, i want the robot to turn left for 30 degrees. This would mean that the robot has to turn until it reaches 20 degrees according to values from the magnetometer.

However, if i set:

if (headingdegrees < 20)
{
turnleft();
}

else
{
stop();
}

it would only mean to stop right way since the current reading is already more than 20 at 350 degrees. Been stucked on this for hours :frowning:

Is your magnetometer upside down? :wink:

Do you always want to turn in the direction of the shortest arc, or do you specify which direction to take?

Hi Tam, yes it's upside down. I would have to specify which direction it needs to take. For example : left 30 degrees or right 30 degrees.

The issue is that when the robot turns beyond 359 degrees, it would be back to 0 degrees. I'm clueless at setting the condition of when my turning should stop.

My program will constantly check the magnetometer if it has reach its position in a loop.

tammytam:
Is your magnetometer upside down? :wink:

Do you always want to turn in the direction of the shortest arc, or do you specify which direction to take?

This is not ideal in the least, but its quick and dirty. Also if your magno has a brain fart half way across the zero crossing, it'll cause issues. By that I mean if it reads bearing 0, followed by 259, followed by 0 again while turning left, it'll behave incorrectly (might, probably is, bugs in this):

int g_desired_bearing = 0;
bool g_left = false;
bool g_zero_crossing = false;

void loop()
{
  int current_bearing = get_magno_bearing();

  if( g_left )
  {
    if( g_zero_crossing && current_bearing < g_desired_bearing )
    {
      turn_left();
    }
    else
    {
      g_zero_crossing = false;

      if( current_bearing > g_desired_bearing )
      {
        turn_left();
      }
    }
  }
  else
  {
    // do similar thing for right hand side ...
  }
}

void set_bearing( int bearing, bool left )
{
  g_desired_bearing = bearing;
  g_left = left;
  g_zero_crossing = false;
  int current_bearing = get_magno_bearing();

  if( g_left )
  {
    if( g_desired_bearing > current_bearing )
      g_zero_crossing = true;
  }
  else
  {
    if( g_desired_bearing < current_bearing )
      g_zero_crossing = true;
  }
}

As an after thought, you could probably compute the delta angle, and maintain a previous bearing, at the end of the loop deduct the difference between the previous bearing and current bearing from the delta bearing. When delta bearing goes negative, you should be in the ballpark.

tammytam:
This is not ideal in the least, but its quick and dirty. Also if your magno has a brain fart half way across the zero crossing, it'll cause issues. By that I mean if it reads bearing 0, followed by 259, followed by 0 again while turning left, it'll behave incorrectly (might, probably is, bugs in this):

int g_desired_bearing = 0;

bool g_left = false;
bool g_zero_crossing = false;

[code]void loop()
{
 int current_bearing = get_magno_bearing();

if( g_left )
 {
   if( g_zero_crossing && current_bearing < g_desired_bearing )
   {
     turn_left();
   }
   else
   {
     g_zero_crossing = false;

if( current_bearing > g_desired_bearing )
     {
       turn_left();
     }
   }
 }
 else
 {
   // do similar thing for right hand side ...
 }
}

void set_bearing( int bearing, bool left )
{
 g_desired_bearing = bearing;
 g_left = left;
 g_zero_crossing = false;
 int current_bearing = get_magno_bearing();

if( g_left )
 {
   if( g_desired_bearing > current_bearing )
     g_zero_crossing = true;
 }
 else
 {
   if( g_desired_bearing < current_bearing )
     g_zero_crossing = true;
 }
}



[/code]

Hi Tam, thanks for the reply. However i have little to no idea to the code that you've provided me. I'm sorry that i have minimal knowledge about HMC5883L, thus i'm using someone else's library. All i did was extract the output give : "RoundDegreeInt" to which ever variable i need. I then let my robot turn to a certain degree until (RoundDegreeInt = Degree1) where Degree1 can be the previous saved angle.

A sample of the code from void_lopp() can be seen below:

MagnetometerRaw raw = compass.ReadRawAxis();
  // Retrived the scaled values from the compass (scaled to the configured scale).
  MagnetometerScaled scaled = compass.ReadScaledAxis();

  // Values are accessed like so:
  int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)
  int MilliGauss_OnThe_YAxis = scaled.YAxis;
  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(scaled.YAxis, scaled.XAxis);

  // Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
  // Find yours here: http://www.magnetic-declination.com/
  // Mine is: 2� 37' W, which is 2.617 Degrees, or (which we need) 0.0456752665 radians, I will use 0.0457
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = -0.0040724291 ;
  heading += declinationAngle;

  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2*PI;
  
  // Check for wrap due to addition of declination.
  if(heading > 2*PI)
    heading -= 2*PI;
  
  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180/M_PI;

  //correcting the angle issue
  if (headingDegrees >= 1 && headingDegrees < 240) 
  {
    headingDegrees = map(headingDegrees,0,239,0,179);
  }
  else if (headingDegrees >= 240)
  {
    headingDegrees =  map(headingDegrees,240,360,180,360);
  }

  //rounding the angle
  RoundDegreeInt =round(headingDegrees);

  //smoothing value
  if( RoundDegreeInt < (PreviousDegree + 3) && RoundDegreeInt > (PreviousDegree - 3) ) {
    RoundDegreeInt = PreviousDegree;
  }




  PreviousDegree = RoundDegreeInt;

Hackscribble:
Hi Matthew

This code should find the acute or obtuse angle between two bearings (i.e. not the reflex angle).

int previousangle = 355;

int newangle = 45;
int diff = abs(previousangle - newangle);
 if (diff > 180)
 {
   diff = 360 - diff;
 }




Regards

Ray

Dear Ray,

Thank you for your code, it was of great help to me

I found a little error in the results that may not be seen in the Serial Monitor,
but in Excel I've found the key to solve it

NOTE: I have set to negative the results in order to enhance readability
If it is a Negative Value means the device turned to the left on the Z axis
If it is a Positive Value means the device turned to the right on the Z axis

With the previous code, whenever:

Previous Heading (previousangle) - Current Heading (newangle) > 180

There is a missinterpretation of the rotation and the result value is wrong (see the gray area)

The correct code is as follows:

int previousangle = 355;
int newangle = 45;
int diff = previousangle - newangle;
int delta = 0; //the rotation that happened between lectures of the sensor (neg: left; pos: right)

if(diff > 180){
    delta = 360 - abs(diff);  
  } 
  else if(diff < (-180)){
    delta = - (360 - abs(diff));
  } 
  else {delta = - diff;}

Corrected code results for different situations is attached

Erroneous Formula.png

Correct Formula.png