Hi Everyone,
I've been doing some digging around with getting angular (pitch & roll) data from accelerometer data. I wish to return the angular data in degrees. I've found this in a forum post and implemented it in to calculate the yaw and roll
roll = atan2 (Ax,Az) * RAD_TO_DEG;
pitch = atan2 (Ay, Az) * RAD_TO_DEG;
Now this does appear to do something... but doesn't appear to result in the correct values. When I rotate the device approx 90 degrees it returns values closer to half the actual value. I'm cautious of just doubling this value and calling it a day because I can't say with any confidence that it is indeed 90 degrees I am holding the flight controller at.
I have seen a series of other posts suggesting other solutions such as
pitch = 180 * atan2(accelX, sqrt(accelY*accelY + accelZ*accelZ))/PI;
roll = 180 * atan2(accelY, sqrt(accelX*accelX + accelZ*accelZ))/PI;
This example seems to result in a value even further from reality and returns a value in the 30s when held at approx 90 degrees. I've also seen the following which seems to suggest I need to do something with the raw data of the accelerometer. It seems to create two signed integers but doesn't assign the variable a name or value so not entirely sure what to make of that other than to perhaps multiply the accel data by 3.9.
ccelerationX = (signed int)(((signed int)rawData_X) * 3.9);
accelerationY = (signed int)(((signed int)rawData_Y) * 3.9);
accelerationZ = (signed int)(((signed int)rawData_Z) * 3.9);
pitch = 180 * atan (accelerationX/sqrt(accelerationY*accelerationY + accelerationZ*accelerationZ))/M_PI;
roll = 180 * atan (accelerationY/sqrt(accelerationX*accelerationX + accelerationZ*accelerationZ))/M_PI;
.
I've also noticed in both of these examples both the pitch and the roll change when rotating the device even if rotating it along only one axis. Again no idea why...
Context: This is all to go into my flight control for an RC Starship project i'm working on. I'm using an MPU9250 for IMU data and using the "Bolder Flight System" library to read the IMU.
Below is the the whole of the code so far just incase there is something else I am doing wrong. Any criticism is welcome too. I intend to change the PID system to QuickPID rather than the version I've created so I can use the autotune features as recommended by Dlloyd,
#include <Arduino.h>
#include <ESP32Servo.h>
#include <MPU9250.h>
MPU9250 IMU(Wire,0x68);
int status;
Servo Roll;
Servo Pitch;
int YawPos = 90;
int PitchPos= 90;
int pot = 0;
unsigned long start = 0;
unsigned long current = 0;
unsigned long iterationtime = 0;
float PitchBias = 0;
float RollBias = 0;
float pitch = 0;
float roll = 0;
int Kp = 0;
int Ki = 0;
int Kd = 0;
long output (int setpoint, int Input, int Kp, int Ki, int Kd, int delaytime) {
static float integral_prior = 0;
static float error_prior = 0;
start = millis();
static float error = setpoint - Input;
float integral = integral_prior + error * iterationtime;
float derivative = (error - error_prior) /iterationtime;
long output = Kp * error + Ki * integral + Kd * derivative;
error_prior = error;
integral_prior = integral;
delay(delaytime);
current = millis();
iterationtime = current - start;
return output;
}
int getAvg(float axis){
int iterations = 20;
int totalacc = 0;
float average = 0;
for (int i = 0; i < iterations; i++){
totalacc += axis;
delay(50);
}
average = totalacc / iterations;
return average;
}
void setup() {
Serial.begin(115200);
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
Roll.setPeriodHertz(50);
Pitch.setPeriodHertz(50);
Roll.attach(32, 500, 2400);
Pitch.attach(4, 500, 2400);
Roll.write(90);
Pitch.write(90);
status = IMU.begin();
if (status < 0) {
Serial.println("IMU initialization unsuccessful");
Serial.println("Check IMU wiring or try cycling power");
Serial.print("Status: ");
Serial.println(status);
while(1) {}
}
IMU.readSensor();
float pitchval = 0;
float rollval = 0;
IMU.readSensor();
float Ax, Ay, Az;
Ax = IMU.getAccelX_mss();
Ay = IMU.getAccelY_mss();
Az = IMU.getAccelZ_mss();
rollval = atan2 (Ax,Az) * RAD_TO_DEG;
pitchval = atan2 (Ay, Az) * RAD_TO_DEG;
RollBias = getAvg(rollval);
PitchBias = getAvg(pitchval);
Serial.print("Pitch Bias = ");
Serial.print(PitchBias);
Serial.print(" / Roll Bias = ");
Serial.println(RollBias);
}
void loop() {
IMU.readSensor();
float Ax, Ay, Az;
Ax = IMU.getAccelX_mss();
Ay = IMU.getAccelY_mss();
Az = IMU.getAccelZ_mss();
int pitch;
int roll;
roll = atan2 (Ax,Az) * RAD_TO_DEG;
pitch = atan2 (Ay, Az) * RAD_TO_DEG;
roll = roll - RollBias;
pitch = pitch - PitchBias;
float rolloutput = output(0, roll, Kp, Ki, Kd, 20);
float pitchoutput = output(0, pitch, Kp, Ki, Kd, 20);
Roll.write(rolloutput);
Pitch.write(pitchoutput);
Serial.print("Roll = ");
Serial.print(roll);
Serial.print(" / Pitch = ");
Serial.println(pitch);
}
Thanks in advance!