In my project, I have an MPU6050 sensor mounted 2.5 cm away from the center of an SG90 servo motor arm. The servo is controlled using PWM, and it moves in a sinusoidal pattern, while the MPU6050 measures angular velocity (gyro) and linear acceleration. My goal is to accurately calculate the yaw angle (rotation around the Z-axis) of the MPU6050 as the servo moves, along with the linear acceleration due to its position on the servo arm. However, I’m facing a problem with gyro drift and inaccurate readings due to improper offset values. I need a reliable method to calibrate the MPU6050 and ensure it provides accurate yaw angle and linear acceleration measurements even when moving.
thanks!
#include <MPU6050.h>
#include <Servo.h>
MPU6050 mpu;
Servo myServo;
// Servo and MPU6050 Parameters
const int SERVO_MIN_PWM = 1000; // -90°
const int SERVO_MAX_PWM = 2000; // +90°
const int SERVO_MID_PWM = 1500; // 0°
const float ARM_LENGTH = 0.025; // Servo arm length (meters - 2.5 cm)
float yawAngle = 0;
float omegaZ = 0;
float alphaZ = 0;
int gz_offset = 0;
unsigned long lastTime;
unsigned long lastPrintTime = 0;
// Calibration Function
void calibrateMPU6050() {
long sum_gz = 0;
const int N = 1000;
Serial.println(">> Gyro Z-axis offset calibration...");
delay(1000);
for (int i = 0; i < N; i++) {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
sum_gz += gz;
delay(5);
}
gz_offset = sum_gz / N;
Serial.print(">> GZ Offset: ");
Serial.println(gz_offset);
}
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
if (!mpu.testConnection()) {
Serial.println("MPU6050 connection failed!");
while (1);
}
myServo.attach(3);
myServo.write(0); // Start servo at 0°
yawAngle = 0; // Start yaw angle at 0°
lastTime = micros();
calibrateMPU6050();
Serial.println(">> System initialized.");
}
void loop() {
unsigned long now = micros();
float dt = (now - lastTime) / 1000000.0;
lastTime = now;
// Sinusoidal Servo Movement (-45° to +45°)
float t = now / 1000000.0;
float sineAngle = 45.0 * sin(2 * PI * 0.5 * t); // -45° to +45°
float commandAngle = sineAngle;
// PWM Calculation for Servo
int pulseWidth = SERVO_MID_PWM + (sineAngle * (SERVO_MAX_PWM - SERVO_MIN_PWM) / 180.0);
pulseWidth = constrain(pulseWidth, SERVO_MIN_PWM, SERVO_MAX_PWM);
myServo.writeMicroseconds(pulseWidth);
// Reading MPU6050 Data
int16_t ax_raw, ay_raw, az_raw, gx, gy, gz_raw;
mpu.getMotion6(&ax_raw, &ay_raw, &az_raw, &gx, &gy, &gz_raw);
// Gyro Data (Z-axis - Yaw Calculation)
float gz_dps = (gz_raw - gz_offset) / 131.0; // Gyro sensitivity (MPU6050: 131.0 for 250°/s)
omegaZ = gz_dps; // Angular Velocity (Yaw Rate)
yawAngle += omegaZ * dt; // Yaw Angle Calculation (Integral of Angular Velocity)
// Linear Acceleration Calculation (Due to Rotation)
float ax_linear = -ARM_LENGTH * pow(omegaZ * PI / 180.0, 2) * sin(yawAngle * PI / 180.0);
float ay_linear = ARM_LENGTH * pow(omegaZ * PI / 180.0, 2) * cos(yawAngle * PI / 180.0);
// Printing Data Every 50 ms
if (millis() - lastPrintTime >= 50) {
lastPrintTime = millis();
Serial.print("Servo Command Angle: ");
Serial.print(commandAngle, 2);
Serial.println("°");
Serial.print("MPU6050 Measured Yaw Angle: ");
Serial.print(yawAngle, 2);
Serial.println("°");
Serial.print("Yaw Rate (ω): ");
Serial.print(omegaZ, 2);
Serial.println(" °/s");
Serial.print("Linear Acceleration X (ax): ");
Serial.print(ax_linear, 4);
Serial.println(" m/s²");
Serial.print("Linear Acceleration Y (ay): ");
Serial.print(ay_linear, 4);
Serial.println(" m/s²");
// CSV Format for Data Logging (Matlab, Excel, etc.)
Serial.print(commandAngle, 2);
Serial.print(",");
Serial.print(yawAngle, 2);
Serial.print(",");
Serial.print(omegaZ, 2);
Serial.print(",");
Serial.print(ax_linear, 4);
Serial.print(",");
Serial.println(ay_linear, 4);
Serial.println();
}
delay(5);
}