I am working on a drone using only Arduino and Arduino components after some researching these are all the parts i am using in this project
- Arduino Nano (copy)
- X2-l293D motor driver
- NRF+NRF adapter
- MPU6050
everything in my project seems to work just fine and so does the MPU however no matter what i do i just can't seem to get proper values IE - yaw pitch roll are always insane values and when i try to calculate any angle (x,y) it gives me insane values in the thousands here is my code
#include <Wire.h>
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <MPU6050.h>
#include <PID_v1.h>
RF24 radio(8, 9); // CE, CSN pins
MPU6050 mpu;
enum MotorState {
OFF,
ON,
WAIT_OFF
};
MotorState motorState = OFF;
unsigned long startTime = 0;
const unsigned long motorDuration = 2000; // 4 seconds
// Create a struct to hold the received data
struct DataPacket {
int X;
int Y;
int switchState;
int switchState2;
};
const byte address[6] = "00001";
int enA = 2;
int in1 = 7;
int in2 = A7;
int enB = 3;
int in3 = A1;
int in4 = A0;
int enC = 4;
int in2_1 = 6;
int in2_2 = A6;
int enD = 5;
int in2_3 = A3;
int in2_4 = A2;
int16_t Acc_rawX, Acc_rawY, Acc_rawZ, Gyr_rawX, Gyr_rawY, Gyr_rawZ;
float Acceleration_angle[2];
float Gyro_angle[2];
float Total_angle[2];
float elapsedTime, time, timePrev;
int i;
float rad_to_deg = 180 / 3.141592654;
float PID, pwm1, pwm2, pwm3, pwm4, error, previous_error;
float pid_p = 0;
float pid_i = 0;
float pid_d = 0;
/////////////////PID CONSTANTS/////////////////
double kp = 3.00; //3.55
double ki = 0.003; //0.003
double kd = 2.0; //2.05
///////////////////////////////////////////////
double throttle = 150; //initial value of throttle to the motors
float desired_angle = 0; //This is the angle in which we want the balance to stay steady
void setup() {
Wire.begin(); //begin the wire communication
Wire.beginTransmission(0x68);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
Serial.begin(600);
radio.begin();
radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
time = millis(); //Start counting time in milliseconds
pinMode(enA, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
pinMode(enC, OUTPUT);
pinMode(in2_1, OUTPUT);
pinMode(in2_2, OUTPUT);
pinMode(enD, OUTPUT);
pinMode(in2_3, OUTPUT);
pinMode(in2_4, OUTPUT);
analogWrite(enA, 0);
analogWrite(enB, 0);
analogWrite(enC, 0);
analogWrite(enD, 0);
Serial.println("Calibrating MPU6050. Keep the device stationary...");
delay(5000); // Adjust as needed
calibrateMPU6050();
}
void calibrateMPU6050() {
const int numSamples = 500;
int16_t accX_offset = 0, accY_offset = 0, accZ_offset = 0;
int16_t gyroX_offset = 0, gyroY_offset = 0, gyroZ_offset = 0;
// Collect samples for calibration
for (int i = 0; i < numSamples; i++) {
int16_t accX, accY, accZ, gyroX, gyroY, gyroZ;
mpu.getMotion6(&accX, &accY, &accZ, &gyroX, &gyroY, &gyroZ);
accX_offset += accX;
accY_offset += accY;
accZ_offset += accZ;
gyroX_offset += gyroX;
gyroY_offset += gyroY;
gyroZ_offset += gyroZ;
delay(5); // Adjust as needed based on your application
}
// Calculate average offsets
accX_offset /= numSamples;
accY_offset /= numSamples;
accZ_offset /= numSamples;
gyroX_offset /= numSamples;
gyroY_offset /= numSamples;
gyroZ_offset /= numSamples;
// Set the offsets
Acc_rawX -= accX_offset;
Acc_rawY -= accY_offset;
Acc_rawZ -= accZ_offset;
Gyr_rawX -= gyroX_offset;
Gyr_rawY -= gyroY_offset;
Gyr_rawZ -= gyroZ_offset;
}
void loop() {
if (radio.available()) {
// Read the received struct
DataPacket data;
radio.read(&data, sizeof(data));
// Display received data
Serial.print("Received - Switch: ");
Serial.println(data.switchState);
Serial.print("Received - Switch2: ");
Serial.println(data.switchState2);
Serial.print("Received - X-axis: ");
Serial.println(data.X);
Serial.print("Received - Y-axis: ");
Serial.println(data.Y);
if (data.switchState == 0) {
motorState = ON;
startTime = millis(); // Record the start time
} else if (data.switchState2 == 0) {
motorState = OFF; // Turn off motors if switchState is 1
}
}
// IMU and PID code
processIMU(); // Read and filter IMU data
computePID(); // Calculate PID control
updateMotors(); // Apply PID control to motors
// Manage motor states
switch (motorState) {
case ON:
directionControl(); // Adjust motor speed without PID
break;
case OFF:
// Turn off motors
analogWrite(enA, 0);
analogWrite(enB, 0);
analogWrite(enC, 0);
analogWrite(enD, 0);
break;
}
}
void directionControl() {
// Your motor driver control code here...
// Use the angles obtained from MPU6050 to adjust motor speed with PID
// AnalogWrite values can be adjusted based on the PID output
analogWrite(enA, 200);
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
analogWrite(enB, 200);
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
analogWrite(enC, 200);
digitalWrite(in2_1, HIGH);
digitalWrite(in2_2, LOW);
analogWrite(enD, 200);
digitalWrite(in2_3, HIGH);
digitalWrite(in2_4, LOW);
}
void updateMotors() {
// Your motor driver control code here...
// Use pwm1, pwm2, pwm3, pwm4 to control motor speed
// Map these values to your specific motor driver setup
// ...
analogWrite(enA, pwm1);
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
analogWrite(enB, pwm2);
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
analogWrite(enC, pwm3);
digitalWrite(in2_1, HIGH);
digitalWrite(in2_2, LOW);
analogWrite(enD, pwm4);
digitalWrite(in2_3, HIGH);
digitalWrite(in2_4, LOW);
}
void processIMU() {
// Read raw values from MPU6050
int16_t accX, accY, accZ, gyroX, gyroY, gyroZ;
mpu.getMotion6(&accX, &accY, &accZ, &gyroX, &gyroY, &gyroZ);
// Check if denominator is not close to zero
const float epsilon = 0.0001;
if (fabs(accZ) > epsilon) {
// Convert raw values to angles
float roll = atan2(accY, accZ) * RAD_TO_DEG;
float pitch = atan2(-accX, sqrt(accY * accY + accZ * accZ)) * RAD_TO_DEG;
// Apply complementary filter with angle normalization:
float dt = (millis() - timePrev) / 1000.0;
Total_angle[0] = fmod(0.99 * (Total_angle[0] + gyroX * dt) + 0.01 * roll, 360.0);
Total_angle[1] = fmod(0.99 * (Total_angle[1] + gyroY * dt) + 0.01 * pitch, 360.0);
// Print total angles
Serial.print("Total Angle X: "); Serial.println(Total_angle[0]);
Serial.print("Total Angle Y: "); Serial.println(Total_angle[1]);
// Update previous time for the next iteration
timePrev = millis();
}
else {
Serial.println("Acc Z close to zero. Skipping angle calculation.");
}
}
void computePID() {
// Calculate error between desired angle and measured angle
float errorX = Total_angle[0] - desired_angle;
float errorY = Total_angle[1] - desired_angle;
// Proportional term
pid_p = kp * errorX;
// Integral term (optional, depending on your system)
if (-3 < errorX && errorX < 3) {
pid_i += ki * errorX;
// Saturate the integral term to prevent it from growing too large
pid_i = constrain(pid_i, -100, 100); // Adjust the saturation limits as needed
}
// Derivative term
pid_d = kd * (errorX - previous_error) / elapsedTime;
// Calculate PID output
PID = pid_p + pid_i + pid_d;
// Update previous error for the next iteration
previous_error = errorX;
// Debug prints
Serial.print("PID: "); Serial.println(PID);
// Calculate PWM values
pwm1 = throttle + PID;
pwm2 = throttle - PID;
pwm3 = throttle + PID;
pwm4 = throttle - PID;
// Debug prints
Serial.print("PWM1: "); Serial.println(pwm1);
Serial.print("PWM2: "); Serial.println(pwm2);
Serial.print("PWM3: "); Serial.println(pwm3);
Serial.print("PWM4: "); Serial.println(pwm4);
// Ensure PWM values are within the acceptable range
pwm1 = constrain(pwm1, 150, 255);
pwm2 = constrain(pwm2, 150, 255);
pwm3 = constrain(pwm3, 150, 255);
pwm4 = constrain(pwm4, 150, 255);
}
I have tried a lot of things and i have ran into many issues while making this project mainly (NRF) but after solving that i just can't seem to be able to fix this so if anyone has any advice or can help i would appreciate it immensely
p.s this is how the chip looks like irl (its very messy and i am not proud but as a first time project ever i am happy that it works except the MPU ).