#include <Wire.h>
#include <MPU6050.h>
#include <SimpleFOC.h>
MPU6050 mpu;
BLDCMotor motor(7);
Encoder encoder(2, 3);
float Kp = 40.0, Ki = 10.0, Kd = 0.5;
float targetAngle = 0.0;
float previousError = 0, integral = 0;
float alpha = 0.98;
float dt = 0.01;
float filteredAngle = 0;
void setup() {
Serial.begin(9600);
Wire.begin();
mpu.initialize();
mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250);
mpu.setFullScaleAccelRange(MPU6050_ACCEL_FS_2);
motor.init();
encoder.init();
motor.linkEncoder(&encoder);
}
void loop() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
float accelAngle = atan2(ay, az) * RAD_TO_DEG;
float gyroRate = gx / 131.0;
static float gyroAngle = 0;
gyroAngle += gyroRate * dt;
filteredAngle = alpha * (filteredAngle + gyroAngle * dt) + (1 - alpha) * accelAngle;
float error = targetAngle - filteredAngle;
integral += error * dt;
float derivative = (error - previousError) / dt;
float output = Kp * error + Ki * integral + Kd * derivative;
previousError = error;
motor.move(constrain(output, -10, 10));
Serial.print("Angle: ");
Serial.print(filteredAngle);
Serial.print(" Output: ");
Serial.println(output);
delay(dt * 1000);
}
#include <Wire.h>
#include <MPU6050.h>
#include <Servo.h>
MPU6050 mpu;
Servo motors[4];
float Kp_pitch = 1.2, Ki_pitch = 0.05, Kd_pitch = 0.8;
float Kp_roll = 1.2, Ki_roll = 0.05, Kd_roll = 0.8;
float targetPitch = 0, targetRoll = 0;
float alpha = 0.95;
float pitchAngle = 0, rollAngle = 0;
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
for(int i = 0; i < 4; i++) {
motors[i].attach(5 + i);
motors[i].write(90);
}
}
void computeAngles() {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
float accelPitch = atan2(-ax, sqrt(ay * ay + az * az)) * RAD_TO_DEG;
float accelRoll = atan2(ay, az) * RAD_TO_DEG;
static float gyroPitch = 0, gyroRoll = 0;
gyroPitch += gy / 131.0 * 0.01;
gyroRoll += gx / 131.0 * 0.01;
pitchAngle = alpha * (pitchAngle + gyroPitch) + (1 - alpha) * accelPitch;
rollAngle = alpha * (rollAngle + gyroRoll) + (1 - alpha) * accelRoll;
}
void loop() {
static unsigned long lastTime = 0;
unsigned long now = millis();
if(now - lastTime >= 10) {
lastTime = now;
computeAngles();
static float iTerm_pitch = 0, iTerm_roll = 0;
float errorPitch = targetPitch - pitchAngle;
float errorRoll = targetRoll - rollAngle;
iTerm_pitch += errorPitch * 0.01;
iTerm_roll += errorRoll * 0.01;
float outputPitch = Kp_pitch * errorPitch + Ki_pitch * iTerm_pitch;
float outputRoll = Kp_roll * errorRoll + Ki_roll * iTerm_roll;
int throttle = 1100;
int m1 = throttle + outputPitch + outputRoll;
int m2 = throttle - outputPitch + outputRoll;
int m3 = throttle - outputPitch - outputRoll;
int m4 = throttle + outputPitch - outputRoll;
for(int i = 0; i < 4; i++) {
int val = i == 0 ? m1 : i == 1 ? m2 : i == 2 ? m3 : m4;
motors[i].write(constrain(map(val, 1000, 2000, 0, 180), 0, 180));
}
}
}
#include <Wire.h>
#include <MPU6050.h>
#include <SimpleFOC.h>
MPU6050 mpu;
BLDCMotor motor(7);
Encoder encoder(2, 3);
float Kp = 2.0, Ki = 0.1, Kd = 0.05;
float targetAngle = 90.0;
float alpha = 0.92;
float filteredAngle = 0;
void setup() {
Serial.begin(115200);
Wire.begin();
mpu.initialize();
motor.init();
encoder.init();
motor.linkEncoder(&encoder);
}
void loop() {
static unsigned long lastTime = 0;
unsigned long now = millis();
float dt = (now - lastTime) / 1000.0;
if(dt > 0.1) dt = 0.1;
uint16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
float accelAngle = atan2(ay, az) * RAD_TO_DEG;
float gyroRate = gx / 131.0;
static float gyroAngle = 0;
gyroAngle += gyroRate * dt;
filteredAngle = alpha * (filteredAngle + gyroAngle * dt) + (1 - alpha) * accelAngle;
static float integral = 0;
float error = targetAngle - filteredAngle;
integral += error * dt;
float derivative = (error - (lastTime ? (targetAngle - (alpha * (filteredAngle + gyroAngle * dt) + (1 - alpha) * accelAngle)) : 0)) / dt;
float output = Kp * error + Ki * integral + Kd * derivative;
motor.move(output);
if(now - lastTime >= 50) {
Serial.print("Target: ");
Serial.print(targetAngle);
Serial.print(" Actual: ");
Serial.print(filteredAngle);
Serial.print(" Output: ");
Serial.println(output);
lastTime = now;
}
}
#include <SimpleFOC.h>
#include <MPU6050_tockn.h>
#include <Wire.h>
#include <PID_v1.h>
MPU6050 mpu6050(Wire);
#define IMU_SDA 21
#define IMU_SCL 22
BLDCMotor motor(7);
Encoder encoder(2, 3, 2048);
void doA(){ encoder.handleA(); }
void doB(){ encoder.handleB(); }
struct ComplementaryFilter {
float angle = 0.0;
float angleVelocity = 0.0;
float alpha = 0.98;
float dt = 0.01;
float accAngle = 0.0;
float gyroBias = 0.0;
float lastGyroZ = 0.0;
};
ComplementaryFilter compFilter;
struct PIDControl {
double input, output, setpoint;
double kp, ki, kd;
double integral = 0;
double lastError = 0;
double outputLimit = 12.0;
double integralLimit = 10.0;
bool antiWindup = true;
};
PIDControl balancePID = {0, 0, 0, 25.0, 0.5, 0.2};
struct CalibrationData {
float accX_offset = 0, accY_offset = 0, accZ_offset = 0;
float gyroX_offset = 0, gyroY_offset = 0, gyroZ_offset = 0;
float temperature_offset = 0;
bool calibrated = false;
};
CalibrationData calib;
struct SafetyParams {
float maxAngle = 0.5;
float maxAngularVel = 3.0;
float maxCurrent = 5.0;
int faultCount = 0;
int maxFaults = 10;
};
SafetyParams safety;
void setup() {
Serial.begin(115200);
Serial.println("===== IMU 角度读取 + PID 控制 + 互补滤波 =====");
Serial.println("基于 MPU6050 的自平衡控制系统");
Wire.begin(IMU_SDA, IMU_SCL);
delay(100);
if(!initIMU()) {
Serial.println("IMU 初始化失败!");
while(1);
}
calibrateIMU();
initMotor();
initFilter();
Serial.println("系统初始化完成");
Serial.println("等待 3 秒后开始平衡...");
delay(3000);
}
bool initIMU() {
Serial.println("初始化 MPU6050...");
mpu6050.begin();
uint8_t deviceID = mpu6050.readByte(MPU6050_ADDRESS, MPU6050_WHO_AM_I);
if(deviceID != 0x68) {
Serial.print("错误的设备 ID: 0x");
Serial.println(deviceID, HEX);
return false;
}
mpu6050.writeByte(MPU6050_ADDRESS, MPU6050_PWR_MGMT_1, 0x00);
mpu6050.writeByte(MPU6050_ADDRESS, MPU6050_SMPLRT_DIV, 0x07);
mpu6050.writeByte(MPU6050_ADDRESS, MPU6050_CONFIG, 0x00);
mpu6050.writeByte(MPU6050_ADDRESS, MPU6050_GYRO_CONFIG, 0x18);
mpu6050.writeByte(MPU6050_ADDRESS, MPU6050_ACCEL_CONFIG, 0x10);
Serial.println("MPU6050 初始化成功");
return true;
}
void calibrateIMU() {
Serial.println("开始 IMU 校准...");
Serial.println("请保持设备静止 10 秒");
float accX_sum = 0, accY_sum = 0, accZ_sum = 0;
float gyroX_sum = 0, gyroY_sum = 0, gyroZ_sum = 0;
int samples = 1000;
for(int i = 0; i < samples; i++) {
int16_t ax, ay, az, gx, gy, gz;
readRawIMU(ax, ay, az, gx, gy, gz);
accX_sum += ax;
accY_sum += ay;
accZ_sum += az;
gyroX_sum += gx;
gyroY_sum += gy;
gyroZ_sum += gz;
delay(10);
if(i % 100 == 0) Serial.print(".");
}
Serial.println();
calib.accX_offset = accX_sum / samples;
calib.accY_offset = accY_sum / samples;
calib.accZ_offset = accZ_sum / samples;
calib.gyroX_offset = gyroX_sum / samples;
calib.gyroY_offset = gyroY_sum / samples;
calib.gyroZ_offset = gyroZ_sum / samples;
float accZ_calib = (accZ_sum / samples - calib.accZ_offset);
if(abs(accZ_calib) > 100) {
float scale = 16384.0 / accZ_calib;
calib.accX_offset *= scale;
calib.accY_offset *= scale;
}
Serial.println("校准完成");
Serial.print("加速度计偏移:");
Serial.print(calib.accX_offset);
Serial.print(", ");
Serial.print(calib.accY_offset);
Serial.print(", ");
Serial.println(calib.accZ_offset);
Serial.print("陀螺仪偏移:");
Serial.print(calib.gyroX_offset);
Serial.print(", ");
Serial.print(calib.gyroY_offset);
Serial.print(", ");
Serial.println(calib.gyroZ_offset);
calib.calibrated = true;
}
void readRawIMU(int16_t& ax, int16_t& ay, int16_t& az, int16_t& gx, int16_t& gy, int16_t& gz) {
mpu6050.readBytes(MPU6050_ADDRESS, MPU6050_ACCEL_XOUT_H, 14);
ax = (mpu6050.buffer[0] << 8) | mpu6050.buffer[1];
ay = (mpu6050.buffer[2] << 8) | mpu6050.buffer[3];
az = (mpu6050.buffer[4] << 8) | mpu6050.buffer[5];
gx = (mpu6050.buffer[8] << 8) | mpu6050.buffer[9];
gy = (mpu6050.buffer[10] << 8) | mpu6050.buffer[11];
gz = (mpu6050.buffer[12] << 8) | mpu6050.buffer[13];
}
void loop() {
static unsigned long lastTime = 0;
unsigned long now = micros();
float dt = (now - lastTime) / 1000000.0;
if(dt >= 0.01) {
processIMUData(dt);
complementaryFilter(dt);
pidControl(dt);
applyMotorControl();
safetyCheck();
motor.loopFOC();
lastTime = now;
}
static unsigned long lastDisplay = 0;
if(millis() - lastDisplay >= 50) {
displayStatus();
lastDisplay = millis();
}
}
void processIMUData(float dt) {
int16_t ax_raw, ay_raw, az_raw, gx_raw, gy_raw, gz_raw;
readRawIMU(ax_raw, ay_raw, az_raw, gx_raw, gy_raw, gz_raw);
float ax = (ax_raw - calib.accX_offset) / 16384.0;
float ay = (ay_raw - calib.accY_offset) / 16384.0;
float az = (az_raw - calib.accZ_offset) / 16384.0;
float gx = (gx_raw - calib.gyroX_offset) / 131.0;
float gy = (gy_raw - calib.gyroY_offset) / 131.0;
float gz = (gz_raw - calib.gyroZ_offset) / 131.0;
gx *= PI / 180.0;
gy *= PI / 180.0;
gz *= PI / 180.0;
float acc_mag = sqrt(ax*ax + ay*ay + az*az);
if(acc_mag > 0.5 && acc_mag < 1.5) {
compFilter.accAngle = atan2(ay, az);
}
compFilter.lastGyroZ = gz;
compFilter.angleVelocity = gz;
}
void complementaryFilter(float dt) {
if(!calib.calibrated) return;
float gyroAngle = compFilter.angle + compFilter.lastGyroZ * dt;
compFilter.angle = compFilter.alpha * gyroAngle + (1.0 - compFilter.alpha) * compFilter.accAngle;
compFilter.dt = dt;
static float driftIntegral = 0;
float driftError = compFilter.accAngle - compFilter.angle;
driftIntegral += driftError * dt;
driftIntegral = constrain(driftIntegral, -0.1, 0.1);
compFilter.gyroBias = driftIntegral * 0.01;
compFilter.lastGyroZ -= compFilter.gyroBias;
}
void pidControl(float dt) {
balancePID.setpoint = 0.0;
balancePID.input = compFilter.angle;
double error = balancePID.setpoint - balancePID.input;
double pTerm = balancePID.kp * error;
balancePID.integral += error * dt;
if(balancePID.antiWindup) {
if(balancePID.integral > balancePID.integralLimit) {
balancePID.integral = balancePID.integralLimit;
} else if(balancePID.integral < -balancePID.integralLimit) {
balancePID.integral = -balancePID.integralLimit;
}
}
double iTerm = balancePID.ki * balancePID.integral;
double dTerm = balancePID.kd * (error - balancePID.lastError) / dt;
balancePID.lastError = error;
balancePID.output = pTerm + iTerm + dTerm;
if(balancePID.output > balancePID.outputLimit) {
balancePID.output = balancePID.outputLimit;
} else if(balancePID.output < -balancePID.outputLimit) {
balancePID.output = -balancePID.outputLimit;
}
if(abs(error) < 0.01 && abs(compFilter.angleVelocity) < 0.1) {
balancePID.output = 0;
}
}
void applyMotorControl() {
if(safety.faultCount >= safety.maxFaults) {
motor.disable();
return;
}
motor.enable();
motor.move(balancePID.output);
static float lastOutput = 0;
float outputChange = abs(balancePID.output - lastOutput);
lastOutput = balancePID.output;
float maxChange = 10.0 * compFilter.dt;
if(outputChange > maxChange) {
balancePID.output = lastOutput + (balancePID.output > lastOutput ? maxChange : -maxChange);
}
}
void safetyCheck() {
if(abs(compFilter.angle) > safety.maxAngle) {
safety.faultCount++;
Serial.print("角度超限:");
Serial.print(compFilter.angle * 180 / PI);
Serial.println("°");
if(safety.faultCount >= safety.maxFaults) {
emergencyStop();
}
}
if(abs(compFilter.angleVelocity) > safety.maxAngularVel) {
safety.faultCount++;
Serial.print("角速度超限:");
Serial.print(compFilter.angleVelocity);
Serial.println("rad/s");
}
static int imuErrorCount = 0;
int16_t ax, ay, az, gx, gy, gz;
readRawIMU(ax, ay, az, gx, gy, gz);
float accMag = sqrt(ax*ax + ay*ay + az*az) / 16384.0;
if(accMag < 0.8 || accMag > 1.2) {
imuErrorCount++;
if(imuErrorCount > 5) {
safety.faultCount++;
Serial.print("加速度异常:");
Serial.println(accMag);
}
} else {
imuErrorCount = 0;
}
static unsigned long lastFaultTime = 0;
if(safety.faultCount > 0 && safety.faultCount < safety.maxFaults) {
if(millis() - lastFaultTime > 1000) {
safety.faultCount--;
lastFaultTime = millis();
}
}
}
void emergencyStop() {
Serial.println("紧急停止激活!");
motor.disable();
balancePID.integral = 0;
balancePID.lastError = 0;
safety.faultCount = 0;
delay(1000);
motor.enable();
}
void displayStatus() {
Serial.print("角度:");
Serial.print(compFilter.angle * 180 / PI, 1);
Serial.print("° 角速度:");
Serial.print(compFilter.angleVelocity, 2);
Serial.print("rad/s 输出:");
Serial.print(balancePID.output, 2);
Serial.print("V 故障:");
Serial.println(safety.faultCount);
}