【花雕学编程】Arduino PID 之两轮自平衡机器人
《Arduino 手册(思路与案例)》栏目介绍:
在电子制作与智能控制的应用领域,本栏目涵盖了丰富的内容,包括但不限于以下主题:Arduino BLDC、Arduino CNC、Arduino E-Ink、Arduino ESP32 SPP、Arduino FreeRTOS、Arduino FOC、Arduino GRBL、Arduino HTTP、Arduino HUB75、Arduino IoT Cloud、Arduino JSON、Arduino LCD、Arduino OLED、Arduino LVGL、Arduino PID、Arduino TFT,以及Arduino智能家居、智慧交通、月球基地、智慧校园和智慧农业等多个方面与领域。不仅探讨了这些技术的基础知识和应用领域,还提供了众多具体的参考案例,帮助读者更好地理解和运用Arduino平台进行创新项目。目前,本栏目已有近4000篇相关博客,旨在为广大电子爱好者和开发者提供全面的学习资源与实践指导。通过这些丰富的案例和思路,读者可以获取灵感,推动自己的创作与开发进程。
https://blog.csdn.net/weixin_41659040/category_12422453.html
Arduino PID 之 两轮自平衡机器人
1、主要特点
自平衡控制:
利用 PID 控制算法,机器人能够实时调整其姿态,保持直立和平衡。通过不断监测倾斜角度并进行反馈调节,确保机器人在动态环境中稳定运行。
实时反馈机制:
机器人配备加速度计和陀螺仪等传感器,实时采集其倾斜角度和角速度数据,PID 控制器根据这些数据调整电机的输出,以实现快速稳定的平衡控制。
灵活的运动能力:
机器人可以在不同的地面条件下灵活移动,支持前进、后退、转弯等多种运动,适应多样化的应用场景。
可调参数:
PID 控制器的比例(P)、积分(I)和微分(D)参数可根据不同的机械结构和使用环境进行调节,以优化控制效果。
易于编程与扩展:
基于 Arduino 平台,开发人员可以方便地进行编程和调试,易于实现功能扩展和系统升级。
2、应用场景
教育与科研:
在高校和研究机构,作为控制理论和机器人技术的教学工具,帮助学生理解控制算法和实践应用。
娱乐与竞赛:
自平衡机器人常用于机器人竞赛和娱乐项目,提供趣味性和挑战性,吸引参与者的兴趣。
自动化搬运:
在仓储和物流领域,机器人可以用于自动搬运货物,增加作业效率并降低人力成本。
巡逻与监控:
可用于安防巡逻,机器人在指定区域内自动巡逻,实时监控环境变化。
机器人开发平台:
为开发者提供一个灵活的实验平台,允许他们在此基础上进行更复杂的功能开发和应用。
3、注意事项
参数调节:
PID 参数对系统的稳定性和响应速度影响显著,需要进行细致调试,以避免出现过度振荡或响应迟缓的问题。
传感器选择与布局:
高精度的传感器(如高分辨率加速度计和陀螺仪)是确保控制效果的关键,需合理布局以减少干扰和误差。
电源管理:
机器人在运行中对电源的需求较高,需设计合理的电源管理方案,确保在长时间操作中的稳定性。
机械结构设计:
机械结构的稳定性和重心设计对自平衡效果至关重要,需合理设计机械臂和底盘,以实现最佳的重心分布。
环境适应性:
不同的地面条件(如坡道、湿滑表面等)可能影响机器人的平衡能力,需进行充分测试以优化控制策略。
1、基本的两轮自平衡机器人程序
#include #include // 定义电机引脚int motor1PWM = 3;int motor2PWM = 11;int motor1Dir1 = 12;int motor1Dir2 = 13;int motor2Dir1 = 8;int motor2Dir2 = 9;// 定义 MPU6050 的地址const int MPU_addr = 0x68;// 设置 PID 参数double setpoint = 0;double input, output;double Kp = 2, Ki = 5, Kd = 1;PID pid(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);void setup() { // 初始化串行通信 Serial.begin(9600); // 初始化电机引脚 pinMode(motor1PWM, OUTPUT); pinMode(motor2PWM, OUTPUT); pinMode(motor1Dir1, OUTPUT); pinMode(motor1Dir2, OUTPUT); pinMode(motor2Dir1, OUTPUT); pinMode(motor2Dir2, OUTPUT); // 初始化 MPU6050 Wire.begin(); Wire.beginTransmission(MPU_addr); Wire.write(0x6B); Wire.write(0); Wire.endTransmission(true); // 设置 PID pid.SetMode(AUTOMATIC);}void loop() { // 读取 MPU6050 数据 Wire.beginTransmission(MPU_addr); Wire.write(0x3B); Wire.endTransmission(false); Wire.requestFrom(MPU_addr, 6, true); int16_t AcX = Wire.read()<<8|Wire.read(); // 计算 PID 输出 input = AcX / 16384.0; // 根据 MPU6050 的读数计算输入 pid.Compute(); // 控制电机 if (output > 0) { analogWrite(motor1PWM, output); digitalWrite(motor1Dir1, HIGH); digitalWrite(motor1Dir2, LOW); analogWrite(motor2PWM, output); digitalWrite(motor2Dir1, HIGH); digitalWrite(motor2Dir2, LOW); } else { analogWrite(motor1PWM, -output); digitalWrite(motor1Dir1, LOW); digitalWrite(motor1Dir2, HIGH); analogWrite(motor2PWM, -output); digitalWrite(motor2Dir1, LOW); digitalWrite(motor2Dir2, HIGH); }}
要点解读:
这个程序使用 MPU6050 来读取加速度计数据,通过 PID 控制算法来保持机器人的平衡。
PID 控制器的参数(Kp、Ki、Kd)需要根据实际情况进行调整,以实现最佳的平衡效果。
通过读取 MPU6050 的数据,计算出机器人的倾斜角度,然后使用 PID 控制算法来调整电机的输出,使机器人保持平衡。
2、自平衡机器人加入遥控功能
// 在 setup() 函数中添加以下代码:#include int RECV_PIN = 11;IRrecv irrecv(RECV_PIN);decode_results results;// 在 loop() 函数中添加以下代码:if (irrecv.decode(&results)) { switch(results.value) { case 0xFFA25D: // 按键1:前进 setpoint = 2; break; case 0xFF629D: // 按键2:后退 setpoint = -2; break; case 0xFFE21D: // 按键4:停止 setpoint = 0; break; } irrecv.resume();}
要点解读:
通过红外遥控器控制机器人的运动,可以实现前进、后退、停止等功能。
通过红外接收模块接收来自遥控器的信号,根据不同的按键值设置不同的目标角度,从而控制机器人的运动。
3、基于蓝牙的远程控制功能
// 在 setup() 函数中添加以下代码:#include SoftwareSerial BTSerial(2, 3); // RX, TX// 在 loop() 函数中添加以下代码:if (BTSerial.available()) { char command = BTSerial.read(); switch(command) { case \'F\': // 前进 setpoint = 2; break; case \'B\': // 后退 setpoint = -2; break; case \'S\': // 停止 setpoint = 0; break; }}
要点解读:
通过蓝牙模块实现对机器人的远程控制,可以使用手机应用发送命令。
使用软串口库(SoftwareSerial)来创建一个虚拟串口,实现与蓝牙模块的通信。
通过读取蓝牙串口接收到的命令来控制机器人的运动,实现前进、后退、停止等功能。
4、基础自平衡控制(MPU6050 + 电机驱动)
#include #include #include MPU6050 mpu;double targetAngle = 0.0; // 目标角度(垂直时为0度)double currentAngle = 0;double output = 0;double Kp = 40.0, Ki = 0.5, Kd = 0.8; // 需通过实验调参 // 电机控制引脚#define MOTOR_PWM_L 5#define MOTOR_DIR_L 4#define MOTOR_PWM_R 6#define MOTOR_DIR_R 7 PID balancePID(¤tAngle, &output, &targetAngle, Kp, Ki, Kd, DIRECT); void setup() { Serial.begin(115200); Wire.begin(); mpu.initialize(); mpu.setFullScaleGyroRange(MPU6050_GYRO_FS_250); // 陀螺仪量程±250°/s pinMode(MOTOR_PWM_L, OUTPUT); pinMode(MOTOR_DIR_L, OUTPUT); pinMode(MOTOR_PWM_R, OUTPUT); pinMode(MOTOR_DIR_R, OUTPUT); balancePID.SetMode(AUTOMATIC); balancePID.SetOutputLimits(-255, 255); // PWM范围 balancePID.SetSampleTime(10); // 10ms控制周期} void loop() { // 1. 读取IMU数据并计算倾角(互补滤波) int16_t ax, ay, az, gx, gy, gz; mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); // 简化版角度计算(实际建议用DMP或卡尔曼滤波) double accelAngle = atan2(ay, az) * 180 / PI; double gyroRate = gx / 131.0; // 转换为°/s(量程±250时灵敏度131) static unsigned long lastTime = 0; unsigned long now = millis(); double dt = (now - lastTime) / 1000.0; lastTime = now; // 互补滤波融合 currentAngle = 0.98 * (currentAngle + gyroRate * dt) + 0.02 * accelAngle; // 2. PID计算电机输出 balancePID.Compute(); // 3. 驱动电机(方向判断) if (output > 0) { // 向前倾 digitalWrite(MOTOR_DIR_L, HIGH); digitalWrite(MOTOR_DIR_R, HIGH); } else { // 向后倾 digitalWrite(MOTOR_DIR_L, LOW); digitalWrite(MOTOR_DIR_R, LOW); } analogWrite(MOTOR_PWM_L, abs(output)); analogWrite(MOTOR_PWM_R, abs(output));}
要点解读:
传感器融合:
单纯使用加速度计噪声大,陀螺仪有累积误差,互补滤波(权重0.98:0.02)是简单有效的折中方案。
PID参数调试:
先调Kp使机器人能快速响应倾斜,再调Kd抑制振荡,最后加Ki消除静态误差。
实时性要求:
控制周期需≤10ms,避免积分饱和和响应延迟。
5、路径跟踪平衡机器人(加入编码器反馈)
#include // 新增变量:速度控制double targetSpeed = 0; // cm/sdouble currentSpeed = 0;double speedOutput = 0;double speedKp = 0.8, speedKi = 0.1, speedKd = 0.05; // 编码器计数volatile long encoderCountL = 0, encoderCountR = 0;double wheelRadius = 3.2; // 轮半径(cm)double distancePerCount = (2 * PI * wheelRadius) / 660; // 660脉冲/转 PID speedPID(¤tSpeed, &speedOutput, &targetSpeed, speedKp, speedKi, speedKd, DIRECT); void setup() { // ...(继承案例4的初始化) attachInterrupt(digitalPinToInterrupt(2), countEncoderL, RISING); // 编码器引脚 attachInterrupt(digitalPinToInterrupt(3), countEncoderR, RISING); speedPID.SetMode(AUTOMATIC); speedPID.SetOutputLimits(-100, 100); // 叠加到平衡控制的偏移量 speedPID.SetSampleTime(50); // 速度控制可稍慢} void loop() { // 1. 平衡控制(继承案例4) // ... // 2. 速度控制 static long lastCountL = 0, lastCountR = 0; long countL = encoderCountL; long countR = encoderCountR; double speed = ((countL - lastCountL) + (countR - lastCountR)) / 2.0 * distancePerCount * 20; // 20Hz计算 currentSpeed = speed; lastCountL = countL; lastCountR = countR; speedPID.Compute(); // 3. 叠加速度输出到平衡输出 double finalOutput = output + speedOutput; finalOutput = constrain(finalOutput, -255, 255); // 电机驱动(同案例4) // ...} // 编码器中断函数void countEncoderL() { encoderCountL++; }void countEncoderR() { encoderCountR++; }
要点解读:
速度环嵌套:
速度PID输出作为平衡PID的偏移量,形成串级控制(外环速度→内环平衡)。
编码器处理:
通过中断计数,20Hz计算实际速度(避免高频噪声)。
输出限幅:
最终电机指令需限制在PWM范围内,防止速度控制破坏平衡。
6、坡道适应平衡机器人(加速度补偿)
// 新增变量:坡度补偿double pitchOffset = 0; // 坡度引起的角度偏移double accelKp = 0.3; // 前馈系数 void loop() { // 1. 读取IMU数据(同案例4) int16_t ax, ay, az, gx, gy, gz; mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); // 2. 计算坡度补偿(简化模型:假设前后倾斜) double pitchAccel = atan2(-ax, sqrt(ay*ay + az*az)) * 180 / PI; pitchOffset = accelKp * pitchAccel; // 前馈补偿 // 3. 动态调整目标角度 targetAngle = 0 + pitchOffset; // 基础目标角+坡度补偿 // 4. 平衡控制(继承案例4,但使用补偿后的targetAngle) currentAngle = 0.98 * (currentAngle + gyroRate * dt) + 0.02 * accelAngle; balancePID.Compute(); // 5. 电机驱动(同案例1) // ...}
要点解读:
前馈控制:
通过加速度计直接测量坡度,提前调整目标角度,减轻PID负担。
动态目标角:
平地时targetAngle=0,上坡时自动调整为正角度,下坡为负角度。
鲁棒性增强:
避免PID积分项在坡道上持续累积误差导致失控。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。