【花雕学编程】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 控制算法实现对摆杆的动态平衡,能够在不稳定状态下使其保持竖直平衡,适应快速变化的外部条件。
  2. 高精度控制:
    PID 控制器通过比例、积分和微分三种控制方式的组合,能够实现高精度的平衡控制,减少系统的稳态误差和瞬时波动。
  3. 实时反馈机制:
    系统通过传感器(如加速度计和陀螺仪)实时监测摆杆的角度和速度,并根据反馈信息调整控制输出,从而保持稳定。
  4. 可调参数:
    PID 控制器的比例(P)、积分(I)和微分(D)参数可以根据具体应用需求进行调节,以实现最佳的控制效果。
  5. 友好的用户界面:
    借助 Arduino 开发平台,用户可以通过简单的编程和调试,快速实现控制算法的实现和参数的调整,降低了开发门槛。

应用场景

  1. 教育与科研:
    倒立摆系统广泛应用于高校和研究机构的控制理论教学与实验,帮助学生理解控制系统的基本概念和算法。
  2. 机器人技术:
    在机器人控制中,倒立摆平衡系统可以作为动态平衡的基础,应用于行走机器人、平衡车等。
  3. 控制系统设计:
    用于控制系统设计与验证,帮助工程师测试和优化不同控制算法的性能。
  4. 自动化设备:
    在自动化生产线中,可应用于物料搬运和运输设备的动态平衡控制,提高设备的稳定性和安全性。
  5. 游戏控制器:
    在游戏设备中,倒立摆平衡原理可以用于实现更加真实的控制体验,如平衡车和模拟飞行器等。

注意事项

  1. 参数调节:
    PID 参数的选择和调节对系统的性能影响显著,需进行多次实验以找到最佳的参数组合,避免出现过度振荡或响应迟缓。
  2. 传感器精度:
    传感器的精度和稳定性对系统的反馈控制至关重要,需选择高质量的加速度计和陀螺仪,以确保准确的数据采集。
  3. 系统延迟:
    在反馈控制过程中,系统延迟可能导致不稳定,需优化控制算法以减少延迟对系统性能的影响。
  4. 电源管理:
    确保系统的电源稳定,避免因电源问题导致的控制失效,特别是在移动平台上应用时。
  5. 环境因素:
    环境变化(如温度、湿度等)可能影响传感器性能,需考虑这些因素对系统稳定性的影响,必要时进行补偿。

在这里插入图片描述
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版本可能影响使用方法的选择。实际编程时,您要根据自己的硬件配置、使用场景和具体需求进行调整,并多次实际测试。您还要正确连接硬件,了解所用传感器和设备的规范和特性。涉及硬件操作的代码,您要在使用前确认引脚和电平等参数的正确性和安全性。

在这里插入图片描述

### 关于在Arduino上使用LVGL库的示例教程 #### 示例代码解析 为了帮助理解如何在Arduino平台上利用LVGL库构建图形用户界面,下面提供了一个简单的`HelloWorld`实例。此例子展示了基本的初始化过程以及创建一个按钮的方法。 ```cpp #include "lvgl/lvgl.h" #include "lv_arduino.h" void setup() { Serial.begin(9600); // 初始化TFT屏幕和触摸屏 tft.init(); ts.begin(); // 初始化LVGL库 lv_init(); lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); /*Basic display driver initialization*/ disp_drv.flush_cb = my_disp_flush; // 设置刷新回调函数 lv_disp_drv_register(&disp_drv); // 创建一个按钮并设置其属性 lv_obj_t * btn1 = lv_btn_create(lv_scr_act()); lv_obj_set_pos(btn1, 10, 10); /*Set its position*/ lv_obj_set_size(btn1, 120, 50); /*Set its size*/ // 给按钮添加标签 lv_obj_t * label = lv_label_create(btn1); lv_label_set_text(label, "Button"); } void loop() { lv_task_handler(); /*Let the GUI do its work*/ delay(5); /*Sleep a bit not to waste resources*/ } ``` 这段代码首先包含了必要的头文件,并定义了`setup()`函数来完成硬件初始化工作[^2]。接着,在屏幕上创建了一个按钮对象,并通过调用`lv_btn_create()`方法将其放置到当前活动窗口中;之后调整位置大小并通过`lv_label_create()`给该按钮附加文字说明。最后,在主循环里不断更新GUI状态以响应事件处理程序。 #### 主要组件解释 - **lvgl/** 文件夹下存放着核心图形引擎及相关模块; - **examples/** 中有多个子目录分别对应不同类型的案例研究,比如最基础的`HelloWorld`入门级应用; - **libraries/** 收录了第三方依赖项和其他支持包; - **tools/** 提供了一些实用工具,例如图像转换器等辅助开发的小应用程序[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驴友花雕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值