Where to use atan2f function to determine degrees of the X axis instead of linear mapping?

Where would I use the atan2f function to determine degrees of the X axis instead of linear mapping as I've done in the code? (apparently the 'small angle approximation is not optimal')

Was told to use "float angle = atan2f(x, z) * 180 / M_PI;" but It might I'm not sure where/how to use it.

For further context I'm interested in the best measurement of the X axis in degrees and what the code would actually look like as X will be changing quite fast.

Thanks so much for any help :slight_smile:

#include <Arduino_LSM9DS1.h>

#define fmap(value, oldmin, oldmax, newmin, newmax) (((value) - (oldmin)) * ((newmax) - (newmin)) / ((oldmax) - (oldmin)) + (newmin)) // allows for mapping of decimals


float x, y, z;

float degreesX = 0;
float degreesY = 0;


void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Started");

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");                          
    while (1);
  }

  Serial.print("Accelerometer sample rate = ");
  Serial.print(IMU.accelerationSampleRate());
  Serial.println("Hz");
}

void loop() {

  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);

  }

  if (x > 0) {
    x = 100 * x;
    degreesX = fmap(x, 0, 97, 0, 90);
    Serial.print("Tilting up ");
    Serial.print(degreesX);
    Serial.println("  degrees");
  }
  if (x < -0) {
    x = 100 * x;
    degreesX = fmap(x, 0, -100, 0, 90);
    Serial.print("Tilting down ");
    Serial.print(degreesX);
    Serial.println("  degrees");
  }

  delay(100);
}

This tutorial gives one of the several correct formulas for accurate tilt measurements (there are several definitions for Euler angles).

Note that for two tilt angles, it matters which order the rotations are applied.

Linear mapping would be used when you have a device that measures angles directly. The arctangent is used when you have measurements on two orthogonal axes.

The arctangent is the mathematically correct approach to perform the vector analysis. The "small angle" linear approximation works well only if the tilt angle is less than a few degrees.

I see, where would I use the float angle = atan2f(x, z) * 180 / M_PI; inside my code? I think it might be a bit beyond me.

After you read 'x' and 'z' values from your accelerometer/magnetometer.

1 Like

Right. The Taylor Series expansion of tan(x) is:
image
So obviously for abs(x) << 1, anything beyond the first term is insignificant.

Thanks! I've got it set up like below just to test and the angle reads different to the linear mapping I did (I'm using the Arduino 33 BLE) I'm guessing I've done something wrong?

Serial output: Screenshot - 4b38c4cc048593ade5baf47b68cac4c4 - Gyazo

#include <Arduino_LSM9DS1.h>

#define fmap(value, oldmin, oldmax, newmin, newmax) (((value) - (oldmin)) * ((newmax) - (newmin)) / ((oldmax) - (oldmin)) + (newmin)) // allows for mapping of decimals


float x, y, z;

float degreesX = 0;
float degreesY = 0;

float angle;


void setup() {
  Serial.begin(9600);
  while (!Serial);
  Serial.println("Started");

  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");                          
    while (1);
  }

  Serial.print("Accelerometer sample rate = ");
  Serial.print(IMU.accelerationSampleRate());
  Serial.println("Hz");
}

void loop() {

  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(x, y, z);
    
    angle = atan2f(x, z) * 180 / M_PI;

  }

  Serial.print (angle);

  if (x > 0) {
    x = 100 * x;
    degreesX = fmap(x, 0, 97, 0, 90);
    Serial.print("Tilting up ");
    Serial.print(degreesX);
    Serial.println("  degrees");
  }
  if (x < -0) {
    x = 100 * x;
    degreesX = fmap(x, 0, -100, 0, 90);
    Serial.print("Tilting down ");
    Serial.print(degreesX);
    Serial.println("  degrees");
  }

  delay(100);
}

Remove all the following code in order to see the correct angle, rather than the poor approximation.

 if (x > 0) {
    x = 100 * x;
    degreesX = fmap(x, 0, 97, 0, 90);
    Serial.print("Tilting up ");
    Serial.print(degreesX);
    Serial.println("  degrees");
  }
  if (x < -0) {
    x = 100 * x;
    degreesX = fmap(x, 0, -100, 0, 90);
    Serial.print("Tilting down ");
    Serial.print(degreesX);
    Serial.println("  degrees");
  }

To accurately calculate both pitch and roll angles, study the tutorial linked in reply #2.

1 Like

So the Arduino IMU example is THAT inaccurate?! I just added the fmap so I could get decimals also.

The code you've highlighted there is just the code that I had initially, I left it in to compare it to the new value.

I only need the X axis in degrees, so this should work great, project looks like this (PID in X axis): Screenshot - febcc111e817d845743214b0992c0994 - Gyazo

Thanks a bunch! :slight_smile:

Those "decimals" are simply wrong, so what is the point of looking at them?

The accelerometer is noisy enough that even when using the correct (atan2) formula, you can't accurately resolve 0.1 degree differences in tilt, unless you very carefully calibrate the accelerometer, and average hundreds of measurements.

1 Like

Had no idea was just told that that's how I should do it.

This is just a simple bit of fun, does not need to be very precise but I'll filter the IMU output somehow (might be better or worse than the IR sensor I used for this. Not sure how to do it yet, but that's the fun :slight_smile:

Consider using a Tilt Sensor ($) or an Angle Sensor ($$ to $$$)

1 Like

Just jumped on this one because it was built into the board, thanks though! Hopefully it will be fine, seemed to be pretty responsive/accurate enough even using fmap for what I want :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.