Determining angle of rotation with gyro and accelerometer or some other sensor

Hi,

I need to be able to determine the angle of rotation of a robot that I am building as a school project. While I thought a magnetometer acting as a compass would by far the simplest and most accurate solution, I cannot get a magnetometer shipped to me fast enough.

I do ,however, have an accelerometer and a gyro. Based on my understanding, I assume this would require mathematical computations. This is something that I can derive and compute without issue. However, I'm unfamiliar how some of these computations could be effectively incorporated into an arduino.

In general, what would be a simple way to determine how much my robot rotated, either using a gyro/accelerometer (How would the math be done??), or some other intuitive method?

Or even a mechanical solution in combination with a sensor would be great.

I have photoresistors, lasers, IR diodes, microwave/doppler sensor, Motion sensor, Wireless communication radios, leds etc.

I'm not necessarily looking for an orthodox/common solution. I'm open to very clever solutions as well.

Note**: I only need to measure rotation on a perfectly flat floor (no y or tilting). The robot is simply pivoting.

Thank you!!

You need to tell us more about this orientation angle. Is it changing, and if so how quickly?

If not changing, or you don't know the orientation history, then you need an external reference sensor like a magnetometer for North or a sensor that could detect a beacon, cracks in floorboards or tile, etc.

Thanks for replying. This robot is small and so it completes a 90 degree turn in about 2 seconds. About the angle, like I said above its on a perfectly flat surface

Also its worth mentioning that the robot will be operated on a burgundy carpet that is uniform with no distinct patterning.

That helps, but it is still not clear what you want to do.

If you want to know the robot orientation at all times, you MUST have an external reference.

I think I might not have been completely clear in my post about this, but I actually just need a way to measure that my robot turns exactly 90 degrees. I don't really need to the position at all times, I just meed a way to be able to determine that 1. Driving forward 2. Rotate 90 degree turn 3. Drive forward a little more 4. 90 degree turn.

So I'm really not interested in what the orientation is at all times. Its just that hardcoding a turn on carpet proves to be extrenely unreliable.I just need a way to know that it has turned 90 degrees relative to where it used to be.

Clearly, magnetometer would be perfect, but like I said, its impossible for me to get one.

I was even thinking of having an external wheel with holes that is in contact with the ground. A laser would shine through the holes and onto a phptoresistor which could count the number of turns, and knowing the radius, it might be one way to do it, but I'm not sure how reliable this would be.

I was even thinking of having an external wheel with holes that is in contact with the ground.

This has been done.

If the robot is 2 wheel drive differential, you can do pretty well with wheel encoders, providing there is little wheel slippage.

what would your opinion be on the accuracy of ServoRead to determine how much the wheel has travelled? Would it be just as accurate, or potentially even more accurate than using a DIY laser/photoresistor encoder?

The reference page indicates that it will only return values between 0 and 180 degrees. However, I'm sure this can be overcome in some way or there is another similar function that will do the full 360.....

Sorry, no idea about ServoRead.

Hi guys,
Looks like I have a similar problem, so I’ll skip making new topic. My robot also needs to drive on a carpet, but looks like even after checking everything twice, even slight weight misalignment, carpet bump etc causes the robot to change direction, so I don’t want to rely on luck. I control separately 4 wheels, each is turned by a servo.

I have this Adafruit module Adafruit 10-DOF IMU Breakout - L3GD20H + LSM303 + BMP180 : ID 1604 : $29.95 : Adafruit Industries, Unique & fun DIY electronics and kits with magnetometer, accelerometer, gyro and couple other sensors. I tried magnetometer first, but even mounted on a pole, to separate it from the influance of motors, and servos, it’s still very unreliable. Robot changes direction when passing next to a metal table leg, or close to a power strip.

So I started digging, and found a method, or actually couple methods on calculating the angle from gyro and accelerometer only. The methods, I’m looking at are called Complementary Filter and Kalman Filter. http://robottini.altervista.org/wp-content/uploads/2014/04/filter.pdf

I’m not an engineer, rather an enthusiast, but sounds like the complementary filter should be enough for my needs. I might time to time check the readout from the magnetometer, but would mostly rely on gyro and acc.

I did my best chopping & stitching a code from http://robottini.altervista.org/tag/complementary-filter to make it work with my sensor, but still non of the filters gives me rotation around Z-axis (normal from the ground). I will be grateful for advises, and pointing my mistakes

#include <Adafruit_Sensor.h>
#include <Adafruit_L3GD20_U.h>
#include <Adafruit_10DOF.h>

#include <math.h>                     // include for atan2

Adafruit_10DOF dof = Adafruit_10DOF();
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
Adafruit_LSM303_Mag_Unified   mag   = Adafruit_LSM303_Mag_Unified(30302);
Adafruit_L3GD20_Unified       gyro  = Adafruit_L3GD20_Unified(20);

int sensorValue[3]  = {0, 0, 0};
int sensorZero[3]  = {0, 0, 0};
int ACC_angle;
int GYRO_rate;

int val_gyro = 0;                    //value of individual accelerometer or gyroscope sensor
float accZ = 0;
float accY = 0;
float x1 = 0;
float y1 = 0;
float x2 = 0;
float y2 = 0;
float actAngle = 0;
float actAngleC = 0;
float actAngleRG = 0;
float actAngleRA = 0;
float actAngleC2 = 0;

unsigned long timer = 0;             //timer
unsigned long  delta_t = 0;          //delta time or how long it takes to execute data acquisition
int lastLoopTime = 5;
float x_angle = 0;
float x_angleC = 0;
float x_angleRG = 0;
float  x_angleRA = 0;
float x_angle2C = 0;

void setup()
{
  analogReference(EXTERNAL);     //using external analog ref of 3.3V for ADC scaling
  Serial.begin(115200);          //setup serial

  DDRC = B00000000;              //make all analog ports as inputs - just in case....

  calibrateSensors();           // obtain zero values

  delay (100);

  timer = millis();
}

void loop()
{
  sensors_event_t event;

  delta_t = millis() - timer;                         // calculate time through loop i.e. acq. rate
  timer = millis();                                   // reset timer

  updateSensors();                                    // obtain sensors read values

  //  ACC_angle = getAccAngle() * 180 / PI;               // accelerometers. in degrees
  //  GYRO_rate = getGyroRate();                          // gyro in degrees/sec
  accel.getEvent(&event);
  ACC_angle = event.acceleration.y;
  Serial.print(ACC_angle);        //print accelerometer angle
  Serial.print (" , \t");

  gyro.getEvent(&event);
  GYRO_rate = event.gyro.y;
  Serial.print(GYRO_rate);        //print accelerometer angle
  Serial.print (" , \t");

  actAngleRA = RowAcc(ACC_angle, delta_t);            // calculate Absolute Angle with only accelerometer
  actAngleRG = RowGyro(GYRO_rate, delta_t);           // calculate Absolute Angle with only gyro
  actAngle = kalmanCalculate(ACC_angle, GYRO_rate, delta_t);   // calculate Absolute Angle with Kalman
  actAngleC = Complementary(ACC_angle, GYRO_rate, delta_t);    // calculate Absolute Angle with complementary filter
  actAngleC2 = Complementary2(ACC_angle, GYRO_rate, delta_t);

  Serial.print(actAngle);        //print accelerometer angle
  Serial.print (" , \t");
  Serial.print(actAngleC);        //print complementary angle
  Serial.println(" , \t");
  Serial.print(actAngleC2);        //print accelerometer angle
  Serial.print (" , \t");

  delay(10);             //loop delay;
}
float tau=0.075;
float a=0.0;

// a=tau / (tau + loop time)
// newAngle = angle measured with atan2 using the accelerometer
// newRate =  angle measured using the gyro
// looptime = loop time in millis() 


float Complementary(float newAngle, float newRate,int looptime) {
  float dtC = float(looptime)/1000.0;                                    
  a=tau/(tau+dtC) ;
  x_angleC= a* (x_angleC + newRate * dtC) + (1-a) * (newAngle);

  return x_angleC;
}

@mhol

Have you tried this?

gdsports: Have you tried this?

https://learn.adafruit.com/ahrs-for-adafruits-9-dof-10-dof-breakout/introduction

Thank you for the link, but the library still uses the magnetometer to get angles, and it's still really susceptible to distortions. I would still prefer to find other way than using the compass

Thanks for contributing to the thread.

I haven't looked at your code yet, but I did come across a forum post that might be useful: https://forum.arduino.cc/index.php?topic=58048.0

I’m new to this forum and have same question. Don’t mean to hijack this thread. Not sure this system is what I’m looking for, but maybe you can give me some direction. I need a device that can also give me rotation angle using gyro. I need to set a reference starting point (A) and then manually move the device to a location upwards of 30 feet away. (B) and read the angle of the unit relative to (A). I need a accuracy of .05 degrees. Only just starting on this and was quoted $8500 from one vendor. Suggestions? The unit can be wireless or wired