So I've tweaked the data processing from the MPU6050 and BMP280 and it looks like it's ready for the first try. I haven't tuned the PID yet because I ran into a problem. When I uncomment analogWrite() in the ControlMotors() function, the program stops responding after a bit of movement on the radio controller and also stops outputting values on the serial monitor. Any ideas?
My current code:
#include<Wire.h>
#include <SPI.h>
#include <RF24.h>
#include <nRF24L01.h>
#include <BME280I2C.h>
#define motor1 3 // left front - dopredu, dozadu kolem Z
#define motor2 5 // right front - doleva, doprava kolem Z
#define motor3 6 // right back - doleva, doprava kolem Z
#define motor4 9 // left back - dopredu, dozadu kolem Z
BME280I2C bme;
const int MPU_addr = 0x68;
RF24 radio(2, 10);
const byte address[10] = "sjSEmD3xK";
double seaLevelPres = 1013.25;
bool camera = false;
// radio input
int data[10];
//sensors
double pitchInput, rollInput, yawInput, altitudeInput;
int counter = 0;
//IMU
double xAcc, yAcc, zAcc, xGyro, yGyro, zGyro;
double currentGyroMillis, previousGyroMillis, deltaGyroTime;
double pitchInputAcc, rollInputAcc, yawInputAcc;
double pitchInputGyro, rollInputGyro, yawInputGyro;
double rollGyroOffset, pitchGyroOffset, yawGyroOffset, rollAccOffset, pitchAccOffset, yawAccOffset;
//kalman's altitude filter
double altitudeInputOffset;
const double QTemp = 0.91; // Procesní šum
const float RTemp = 0.125;// Měřící šum
float xTemp = 0; // Stavová proměnná
float pTemp = 1; // Kovariance stavu
float kTemp;// Kalmanovo zesílení
const float QPres = 0.043; // Procesní šum
const float RPres = 0.007;// Měřící šum
float xPres = 0; // Stavová proměnná
float pPres = 1; // Kovariance stavu
float kPres; // Kalmanovo zesílení
//pid
double pitchKp = 2.833, pitchKi = 0 , pitchKd = 0;
double rollKp = 2.833, rollKi = 0, rollKd = 0;
double yawKp = 0.7083, yawKi = 0, yawKd = 0;
double altitudeKp = 2.55, altitudeKi = 0, altitudeKd = 0;
double pitchSetpoint = 0, rollSetpoint = 0, yawSetpoint = 0, altitudeSetpoint = 0; //radio transmitter values
double pitchLastError, rollLastError, yawLastError, altitudeLastError;
double pitchIntegral, rollIntegral, yawIntegral, altitudeIntegral;
double pitchOutput, rollOutput, yawOutput, altitudeOutput;
unsigned long previousMillis;
unsigned long currentMillis;
const unsigned long interval = 10;
//motor speeds
double sp1 = 0, sp2 = 0, sp3 = 0, sp4 = 0;
//functions initialization
void InitInput();
void GetRadio(double& yawSetpoint, double& rollSetpoint, double& pitchSetpoint, double& altitudeSetpoint, double& seaLevelPres, bool& camera);
void ReadSensors(double& pitchInput, double& rollInput, double& yawInput, double& altitudeInput);
double CalculatePID(double Input, double Kp, double Ki, double Kd, double Setpoint, double& LastError, double& Integral);
void ControlMotors();
void setup() {
InitInput();
}
void loop() {
GetRadio(yawSetpoint, rollSetpoint, pitchSetpoint, altitudeSetpoint, seaLevelPres, camera);
ReadSensors(pitchInput, rollInput, yawInput, altitudeInput);
currentMillis = millis();
if (currentMillis - previousMillis > interval)
{
previousMillis = currentMillis;
pitchOutput = CalculatePID(pitchInput, pitchKp, pitchKi, pitchKd, pitchSetpoint, pitchLastError, pitchIntegral);
rollOutput = CalculatePID(rollInput, rollKp, rollKi, rollKd, rollSetpoint, rollLastError, rollIntegral);
yawOutput = CalculatePID(yawInput, yawKp, yawKi, yawKd, yawSetpoint, yawLastError, yawIntegral);
altitudeOutput = CalculatePID(altitudeInput, altitudeKp, altitudeKi, altitudeKd, altitudeSetpoint, altitudeLastError, altitudeIntegral);
}
/*Serial.println(pitchOutput);
Serial.println(rollOutput);
Serial.println(yawOutput);
Serial.println(altitudeOutput);
Serial.print("Altitude sea level pressure");
Serial.println(seaLevelPres);
Serial.print("altitudeSetpoint");
Serial.println(altitudeSetpoint);
Serial.print("pitchSetpoint");
Serial.println(pitchSetpoint);
Serial.print("rollSetpoint");
Serial.println(rollSetpoint);
Serial.print("yawSetpoint");
Serial.println(yawSetpoint);
Serial.print("Pitch");
Serial.println(pitchInput);
Serial.print("Roll");
Serial.println(rollInput);
Serial.print("Yaw");
Serial.println(yawInput);
Serial.print("Altitude");
Serial.println(altitudeInput);
Serial.print("Altitude Offset");
Serial.println(altitudeInputOffset);*/
ControlMotors();
}
double CalculatePID(double Input, double Kp, double Ki, double Kd, double Setpoint, double &LastError, double &Integral) {
double error = Setpoint - Input;
//Serial.println(error);
Integral += error * interval;
double derivative = (error - LastError) / interval;
double Output = Kp * error + Ki * Integral + Kd * derivative;
LastError = error;
return Output;
}
void ControlMotors() {
sp1 = pitchOutput + altitudeOutput - yawOutput;
sp2 = -rollOutput + altitudeOutput + yawOutput;
sp3 = -pitchOutput + altitudeOutput + yawOutput;
sp4 = rollOutput + altitudeOutput - yawOutput;
sp1 = constrain(sp1, 0, 255);
sp2 = constrain(sp2, 0, 255);
sp3 = constrain(sp3, 0, 255);
sp4 = constrain(sp4, 0, 255);
Serial.print("left front");
Serial.println(sp1);
Serial.print("ritght front");
Serial.println(sp2);
Serial.print("right backt");
Serial.println(sp3);
Serial.print("left back");
Serial.println(sp4); /*
analogWrite(motor1, sp1);
analogWrite(motor2, sp2);
analogWrite(motor3, sp3);
analogWrite(motor4, sp4);*/
}
void GetRadio(double& yawSetpoint, double& rollSetpoint, double& pitchSetpoint, double& altitudeSetpoint, double& seaLevelPres, bool& camera) {
if (radio.available()) {
radio.read(&data, sizeof(data));
yawSetpoint = data[0];
altitudeSetpoint = data[1];
rollSetpoint = data[2];
pitchSetpoint = data[3];
seaLevelPres = data[4];
if ( seaLevelPres == 0) {
seaLevelPres = 1013.25;
}
camera = data[5];
}
}
void InitInput() {
Serial.begin(115200);
radio.begin();
Wire.begin();
bme.begin();
Wire.beginTransmission(MPU_addr);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
//gyro config
Wire.beginTransmission(MPU_addr);
Wire.write(0x1B);
Wire.write(0x10);
Wire.endTransmission(true);
//accelometer config
Wire.beginTransmission(MPU_addr);
Wire.write(0x1C);
Wire.write(0x10);
Wire.endTransmission(true);
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
pinMode(motor1, OUTPUT);
pinMode(motor2, OUTPUT);
pinMode(motor3, OUTPUT);
pinMode(motor4, OUTPUT);
currentGyroMillis = millis();
if (rollAccOffset == 0) {
for (int i = 0; i < 200; i++) {
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr, 6, true);
xAcc = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
yAcc = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
zAcc = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
pitchAccOffset += (atan((yAcc) / sqrt(pow((xAcc), 2) + pow((zAcc), 2))) * RAD_TO_DEG);
rollAccOffset += (atan(-1 * (xAcc) / sqrt(pow((yAcc), 2) + pow((zAcc), 2))) * RAD_TO_DEG);
if (i == 199) {
rollAccOffset = rollAccOffset / 200;
pitchAccOffset = pitchAccOffset / 200;
}
}
}
if (rollGyroOffset == 0) {
for (int i = 0; i < 200; i++) {
Wire.beginTransmission(MPU_addr);
Wire.write(0x43);
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr, 6, true);
xGyro = Wire.read() << 8 | Wire.read();
yGyro = Wire.read() << 8 | Wire.read();
zGyro = Wire.read() << 8 | Wire.read();
rollGyroOffset += yGyro / 32.8 ;
pitchGyroOffset += xGyro / 32.8;
yawGyroOffset += zGyro / 32.8;
if (i == 199) {
rollGyroOffset = rollGyroOffset / 200;
pitchGyroOffset = pitchGyroOffset / 200;
yawGyroOffset = yawGyroOffset / 200;
}
}
}
}
void ReadSensors(double& pitchInput, double& rollInput, double& yawInput, double& altitudeInput) {
//IMU
previousGyroMillis = currentGyroMillis;
currentGyroMillis = millis();
deltaGyroTime = (currentGyroMillis - previousGyroMillis) / 1000;
//gyro
Wire.beginTransmission(MPU_addr);
Wire.write(0x43);
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr, 6, true);
xGyro = Wire.read() << 8 | Wire.read();
yGyro = Wire.read() << 8 | Wire.read();
zGyro = Wire.read() << 8 | Wire.read();
xGyro = (xGyro / 32.8) - pitchGyroOffset;
yGyro = (yGyro / 32.8) - rollGyroOffset;
zGyro = (zGyro / 32.8) - yawGyroOffset;
pitchInputGyro = xGyro * deltaGyroTime ;
rollInputGyro = yGyro * deltaGyroTime;
yawInputGyro = zGyro * deltaGyroTime;
//accelometer
Wire.beginTransmission(MPU_addr);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU_addr, 6, true);
xAcc = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
yAcc = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
zAcc = (Wire.read() << 8 | Wire.read()) / 4096.0 ;
pitchInputAcc = (atan((yAcc) / sqrt(pow((xAcc), 2) + pow((zAcc), 2))) * RAD_TO_DEG) - pitchAccOffset;
rollInputAcc = (atan(-1 * (xAcc) / sqrt(pow((yAcc), 2) + pow((zAcc), 2))) * RAD_TO_DEG) - rollAccOffset;
//complementary filter
rollInput = 0.98 * (rollInput + rollInputGyro) + 0.02 * (rollInputAcc);
pitchInput = 0.98 * (pitchInput + pitchInputGyro) + 0.02 * (pitchInputAcc);
yawInputAcc = atan2((sin(rollInput) * cos(pitchInput) * xAcc + sin(pitchInput) * yAcc + cos(rollInput) * cos(pitchInput) * zAcc), sqrt(pow(sin(rollInput) * sin(pitchInput) * xAcc - cos(rollInput) * sin(pitchInput) * zAcc, 2) + pow(cos(pitchInput) * xAcc, 2))) - 1;
yawInput = 0.98 * (yawInput + yawInputGyro) + 0.02 * (yawInputAcc);
//altitudemeter
//bmp280
float temp(NAN), hum(NAN), pres(NAN);
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
BME280::PresUnit presUnit(BME280::PresUnit_Pa);
bme.read(pres, temp, hum, tempUnit, presUnit);
pres = pres / 100;
counter += 1;
//kalmans filter
float xPredTemp = xTemp;
float pPredTemp = pTemp + QTemp;
float xPredPres = xPres;
float pPredPres = pPres + QPres;
// Výpočet Kalmanova zesílení
kTemp = pPredTemp / (pPredTemp + RTemp);
kPres = pPredPres / (pPredPres + RPres);
// Korekce stavu
xTemp = xPredTemp + kTemp * (temp - xPredTemp );
xPres = xPredPres + kPres * (pres - xPredPres );
// Aktualizace kovariance stavu
pTemp = (1 - kTemp) * pPredTemp ;
pPres = (1 - kPres) * pPredPres ;
altitudeInput = (((pow((1013.15 / xPres), 1 / 5.257) - 1) * (xTemp + 273.15)) / 0.0065);
if (altitudeInputOffset == 0 && counter == 50) {
altitudeInputOffset = altitudeInput;
}
altitudeInput -= altitudeInputOffset;
}