《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 之 倒立摆平衡系统
主要特点
- 动态平衡控制:
倒立摆平衡系统利用 PID 控制算法实现对摆杆的动态平衡,能够在不稳定状态下使其保持竖直平衡,适应快速变化的外部条件。 - 高精度控制:
PID 控制器通过比例、积分和微分三种控制方式的组合,能够实现高精度的平衡控制,减少系统的稳态误差和瞬时波动。 - 实时反馈机制:
系统通过传感器(如加速度计和陀螺仪)实时监测摆杆的角度和速度,并根据反馈信息调整控制输出,从而保持稳定。 - 可调参数:
PID 控制器的比例(P)、积分(I)和微分(D)参数可以根据具体应用需求进行调节,以实现最佳的控制效果。 - 友好的用户界面:
借助 Arduino 开发平台,用户可以通过简单的编程和调试,快速实现控制算法的实现和参数的调整,降低了开发门槛。
应用场景
- 教育与科研:
倒立摆系统广泛应用于高校和研究机构的控制理论教学与实验,帮助学生理解控制系统的基本概念和算法。 - 机器人技术:
在机器人控制中,倒立摆平衡系统可以作为动态平衡的基础,应用于行走机器人、平衡车等。 - 控制系统设计:
用于控制系统设计与验证,帮助工程师测试和优化不同控制算法的性能。 - 自动化设备:
在自动化生产线中,可应用于物料搬运和运输设备的动态平衡控制,提高设备的稳定性和安全性。 - 游戏控制器:
在游戏设备中,倒立摆平衡原理可以用于实现更加真实的控制体验,如平衡车和模拟飞行器等。
注意事项
- 参数调节:
PID 参数的选择和调节对系统的性能影响显著,需进行多次实验以找到最佳的参数组合,避免出现过度振荡或响应迟缓。 - 传感器精度:
传感器的精度和稳定性对系统的反馈控制至关重要,需选择高质量的加速度计和陀螺仪,以确保准确的数据采集。 - 系统延迟:
在反馈控制过程中,系统延迟可能导致不稳定,需优化控制算法以减少延迟对系统性能的影响。 - 电源管理:
确保系统的电源稳定,避免因电源问题导致的控制失效,特别是在移动平台上应用时。 - 环境因素:
环境变化(如温度、湿度等)可能影响传感器性能,需考虑这些因素对系统稳定性的影响,必要时进行补偿。
1、基础 PID 控制
#include <PID_v1.h>
// PID 控制参数
double setpoint, input, output;
double Kp = 2, Ki = 5, Kd = 1; // PID 参数
// 创建 PID 对象
PID myPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(9600);
setpoint = 0; // 目标角度
myPID.SetMode(AUTOMATIC);
}
void loop() {
// 读取传感器(例如陀螺仪或加速度计)数据并更新 input
input = readSensor(); // 假设有一个函数读取传感器数据
myPID.Compute(); // 计算 PID 输出
applyControl(output); // 根据 PID 输出控制电机
Serial.print("Input: ");
Serial.print(input);
Serial.print(" Output: ");
Serial.println(output);
delay(100); // 更新频率
}
double readSensor() {
// 读取传感器数据的逻辑
// 返回当前角度
return 0; // 示例返回值
}
void applyControl(double control) {
// 控制电机的逻辑
// 根据 PID 输出调整电机速度或方向
}
2、增强的 PID 控制(带积分风up限制)
#include <PID_v1.h>
// PID 控制参数
double setpoint, input, output;
double Kp = 2, Ki = 5, Kd = 1; // PID 参数
// 创建 PID 对象
PID myPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(9600);
setpoint = 0; // 目标角度
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-255, 255); // 限制输出范围
}
void loop() {
input = readSensor(); // 读取传感器数据
myPID.Compute(); // 计算 PID 输出
applyControl(output); // 控制电机
Serial.print("Input: ");
Serial.print(input);
Serial.print(" Output: ");
Serial.println(output);
delay(100); // 更新频率
}
double readSensor() {
// 读取传感器数据的逻辑
return 0; // 示例返回值
}
void applyControl(double control) {
// 控制电机的逻辑
}
3、倒立摆完整示例(结合传感器与电机控制)
#include <PID_v1.h>
#include <Wire.h>
#include <MPU6050.h> // 使用 MPU6050 陀螺仪
MPU6050 mpu;
double setpoint, input, output;
double Kp = 2, Ki = 5, Kd = 1; // PID 参数
// 创建 PID 对象
PID myPID(&input, &output, &setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(9600);
mpu.initialize(); // 初始化传感器
setpoint = 0; // 目标角度
myPID.SetMode(AUTOMATIC);
}
void loop() {
// 读取陀螺仪数据
input = getAngle(); // 假设有一个函数获取当前角度
myPID.Compute(); // 计算 PID 输出
applyControl(output); // 根据 PID 输出控制电机
Serial.print("Input: ");
Serial.print(input);
Serial.print(" Output: ");
Serial.println(output);
delay(100); // 更新频率
}
double getAngle() {
// 从 MPU6050 获取角度的逻辑
return 0; // 示例返回值
}
void applyControl(double control) {
// 控制电机的逻辑
}
要点解读
基础 PID 控制实现:
第一个示例展示了如何使用 PID 控制库实现基础的倒立摆控制。通过读取传感器数据并计算 PID 输出,能够实现对摆杆的稳定控制。
增强的 PID 控制:
第二个示例在基础控制的基础上,增加了输出范围限制。这可以防止 PID 输出过大而导致电机过载或系统不稳定,适用于实际应用中的安全性考虑。
完整的倒立摆控制示例:
第三个示例结合了 MPU6050 陀螺仪来获取实时角度,并将其与 PID 控制相结合。这是一个完整的倒立摆控制方案,适合于实现稳定的平衡控制。
传感器与电机控制的整合:
在完整示例中,读取传感器数据和控制电机的逻辑是核心。通过有效的传感器反馈,系统能够快速响应并调整控制策略,以维持平衡。
实时更新与反馈:
所有示例中都采用 delay(100) 控制更新频率,以确保系统能够实时响应。在实际应用中,适当的更新频率对于稳定性至关重要,能够及时反映当前状态并进行调整。
4、基础PID控制(单角度环)
#include <PID_v1.h>
#define ENCODER_A 2 // 编码器A相(中断0)
#define ENCODER_B 3 // 编码器B相(中断1)
#define MOTOR_PWM 9 // 电机PWM输出
#define MOTOR_DIR 8 // 电机方向控制
volatile long encoderCount = 0;
double angle, setpoint = 0.0; // 目标角度0度(垂直)
double Kp = 0.04, Ki = 0.0005, Kd = 0.0011; // 基础PID参数
PID myPID(&angle, &output, &setpoint, Kp, Ki, Kd, DIRECT);
void setup() {
Serial.begin(9600);
pinMode(ENCODER_A, INPUT_PULLUP);
pinMode(ENCODER_B, INPUT_PULLUP);
pinMode(MOTOR_PWM, OUTPUT);
pinMode(MOTOR_DIR, OUTPUT);
// 编码器中断配置
attachInterrupt(0, readEncoderA, FALLING);
attachInterrupt(1, readEncoderB, FALLING);
// PID初始化
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-255, 255); // PWM输出范围
myPID.SetSampleTime(20); // 采样周期20ms
}
void loop() {
// 计算摆杆角度(简化版,实际需通过编码器脉冲数转换)
angle = (encoderCount * 0.01); // 假设每脉冲对应0.01度
myPID.Compute(); // 计算PID输出
// 电机控制
if (output > 0) {
digitalWrite(MOTOR_DIR, HIGH); // 正转
analogWrite(MOTOR_PWM, output);
} else {
digitalWrite(MOTOR_DIR, LOW); // 反转
analogWrite(MOTOR_PWM, -output);
}
}
void readEncoderA() { encoderCount++; }
void readEncoderB() { encoderCount--; }
要点解读:
PID参数整定:
初始参数参考案例中Kp=0.04、Ki=0.0005、Kd=0.0011,需根据实际摆杆长度/质量调整。
微分项(Kd)用于抑制超调,若摆杆抖动严重可适当增大Kd。
编码器角度计算:
实际需通过编码器脉冲数与摆杆长度的几何关系换算角度(如angle = arcsin(pulse_count / (2000 * π * rod_length)))。
采样周期:
采样时间设为20ms,需与电机响应速度匹配,过短可能导致噪声放大。
5、双环控制(角度环+位置环)
#include <PID_v1.h>
#define ANGLE_PID_PERIOD 10 // 角度环采样周期10ms
#define POS_PID_PERIOD 50 // 位置环采样周期50ms
// 角度环PID
double angleSetpoint = 0.0, angleInput = 0.0, angleOutput = 0.0;
double angleKp = 0.05, angleKi = 0.001, angleKd = 0.002;
PID anglePID(&angleInput, &angleOutput, &angleSetpoint, angleKp, angleKi, angleKd, DIRECT);
// 位置环PID
double posSetpoint = 0.0, posInput = 0.0, posOutput = 0.0;
double posKp = 1.2, posKi = 0.0, posKd = 0.5;
PID posPID(&posInput, &posOutput, &posSetpoint, posKp, posKi, posKd, DIRECT);
void setup() {
// 初始化角度环PID
anglePID.SetMode(AUTOMATIC);
anglePID.SetOutputLimits(-255, 255);
anglePID.SetSampleTime(ANGLE_PID_PERIOD);
// 初始化位置环PID
posPID.SetMode(AUTOMATIC);
posPID.SetOutputLimits(-30.0, 30.0); // 输出为角度环目标值偏移量
posPID.SetSampleTime(POS_PID_PERIOD);
}
void loop() {
static unsigned long lastPosTime = 0;
// 1. 更新角度环输入(编码器反馈)
angleInput = readAngleEncoder(); // 实际需实现编码器到角度的转换
anglePID.Compute();
// 2. 定期更新位置环(每50ms)
if (millis() - lastPosTime >= POS_PID_PERIOD) {
posInput = readPositionEncoder(); // 小车位置反馈
posSetpoint = 0.0; // 目标位置0
posPID.Compute();
angleSetpoint = posOutput; // 将位置环输出作为角度环目标偏移
lastPosTime = millis();
}
// 电机控制(同案例4)
// ...
}
要点解读:
级联控制逻辑:
位置环输出作为角度环的目标偏移量(如angleSetpoint = posOutput),实现“位置偏差→角度调整→电机动作”的闭环。
采样周期分离:
角度环需快速响应(10ms),位置环可较慢(50ms),避免两环相互干扰。
参数整定顺序:
先整定角度环PID,确保摆杆能独立平衡;再整定位置环,避免引入振荡。
6、抗干扰优化(带前馈补偿的PID)
#include <PID_v1.h>
#define FEEDFORWARD_GAIN 0.8 // 前馈增益
double angle, angleRate; // 角度与角速度
double setpoint = 0.0;
double Kp = 0.04, Ki = 0.0005, Kd = 0.002;
double output, feedforward = 0.0;
PID myPID(&angle, &output, &setpoint, Kp, Ki, Kd, DIRECT, P_ON_M); // 使用P_ON_M模式
void setup() {
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(-255, 255);
myPID.SetSampleTime(20);
}
void loop() {
static double lastAngle = 0.0;
// 1. 读取传感器
angle = readAngleEncoder();
angleRate = (angle - lastAngle) / 0.02; // 角速度(假设采样周期20ms)
lastAngle = angle;
// 2. 计算前馈补偿(基于角速度预测扰动)
feedforward = angleRate * FEEDFORWARD_GAIN;
// 3. PID计算(P_ON_M模式仅在误差变化时更新输出)
myPID.Compute();
// 4. 合成控制量(PID输出 + 前馈)
double totalOutput = output + feedforward;
// 电机控制(同案例4)
// ...
}
要点解读:
前馈补偿原理:
通过角速度预测摆杆即将受到的扰动(如惯性力),提前施加反向控制力(feedforward = angleRate * K)。
P_ON_M模式:
传统PID(P_ON_E)在误差恒定时仍持续积分,可能导致超调;P_ON_M模式仅在误差变化时更新输出,减少稳态振荡。
参数调整:
前馈增益(FEEDFORWARD_GAIN)需根据实际摆杆质量分布调整,过大可能导致系统不稳定。
注意,以上案例只是为了拓展思路,仅供参考。它们可能有错误、不适用或者无法编译。您的硬件平台、使用场景和Arduino版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。