PID 控制理论

OUTput与INput区别

程序到引脚那就是,程序是output(理解成给予),从引脚到程序,程序就是intput(理解成获取)

问题背景

速度信息可以以m/s为单位,或者也可以转换成转速 r/s,而电机的转速是由PWM脉冲宽度来控制的,如何根据速度信息量化成合适的PWM值呢?

PWM解释
例如控制LED灯亮度
PWM:脉冲宽度调制技术,设置占空比为LED间歇性供电,PWM的取值范围是[0,255]
0——low,255——high
假设是PWM = 50,high比例是 50/255 约等于1/5,low比例约是4/5
假设是PWM = 100,high比例是 100/255 约等于2/5,low比例约是3/5
亮度多少就是通过设置的数字与255相除,然后计算出来百分比

PID

在这里插入图片描述

  • e(t)作为 PID 控制的输入
  • u(t)作为 PID 控制器的输出和被控对象的输入
  • Kp 控制器的比例系数
  • Ki控制器的积分时间,也称积分系数
  • Kd控制器的微分时间,也称微分系数

在这里插入图片描述

积分调节

在这里插入图片描述

目标速度为100Km/h,假设P为2,初始速度为0
步骤1:PWM = 2*(100-0) = 200,即油门加到200,加到120时,测下速度。
步骤2:PWM = 2*(100-120) = -40,油门给到0,让汽车减速,减到60,测下速度。
步骤3:PWM = 2*(100-60) = 80,油门给到80,油门加到75,测下速度。
步骤4:PWM = 2*(100-75) = 50,油门给到50,测下速度
依次类推

在这里插入图片描述
设置油门与速度比为1:1
在这里插入图片描述
在这里插入图片描述
由此可知,使用比例调节永远不能达到目标值,只能无限接近

积分调节

在这里插入图片描述
在这里插入图片描述

微分调节

在这里插入图片描述
在这里插入图片描述

使用PID控制小车转速 motor04_pid

/*
 * PID代码实现:
 * 准备工作: 复制之前的测速代码 + 控制电机转动的代码 + PID库安装成功
 * 修改:
 *  1. 修改测速的单位时间(50)
 *  2. 添加电机转向的PWM控制引脚
 * 
 * 步骤:
 *  1. 包含头文件
 *  2. 创建PID对象
 *  3. setUp中启用PID对象 
 *  4. 调用 Compute() 输出控制电机的PWM值
 */

/*
 * 实现脉冲计数
 * 
 * 流程:
 *  1.将使用的引脚封装为变量,封装计数变量
 *  2.setup中设置引脚的操作模式(读取编码器相关引脚的输出信号INPUT),设置波特率(结果输出到上位机)
 *  3.为引脚添加中断事件
 *  4.计数逻辑实现
 *  5.要输出到上位机
 */

#include<PID_v1.h>

// 1.将使用的引脚封装为变量,封装计数变量
int encoder_A = 21;// 中断口2
int encoder_B = 20;// 中断口3
volatile int count = 0;
int DIR_LEFT = 4;// 控制转向
int PWM_LEFT = 5;// 控制PWM
// 左电机的M1与M2对应的是引脚4(DIRA)和引脚5(PWMA),引脚4控制转向,引脚5输出PWM
// 右电机的M1与M2对应的是引脚6(PWMB)和引脚7(DIRB),引脚7控制转向,引脚6输出PWM

// 4.计数逻辑实现
void count_a(){
  // 先判断A是否跳变到高电压
  if(digitalRead(encoder_A) == HIGH){
    // 再判断B的电压
    if(digitalRead(encoder_B) == HIGH){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   } else {
    // 再判断B的电压
    if(digitalRead(encoder_B) == LOW){
      count ++;// 正转
     } else {
       count --;// 反转
     }
   }
 }

void count_b(){
  if(digitalRead(encoder_B) == HIGH)
  {
    if(digitalRead(encoder_A) == LOW){
      count ++;
    } else {
      count --;
    }
  }
  else
  {
    if(digitalRead(encoder_A) == HIGH){
      count ++;
    } else {
      count --;
    }
  }
 }
 

// 测试流程:
/*
 * 1. 封装变量 --- 开始时间、单位时间、减速比、一圈输出的脉冲数、使用的N倍频测速
 * 2. 实现逻辑
 *  2.1 获取时间时间戳(当前时间)
 *  2.2 if(当前时间 - 开始时间 >= 单位时间){
 *        // 取消中断
 *        // 计算转速(count)
 *        // count置零
 *        // 将开始时间重置为当前时间,进行重新测速
 *        // 重启中断
 *  }
 *  
 */
long start_time = millis();//一个计算周期的开始时刻,初始值为millis();
int interval_time = 50;//一个计算周期 50ms
int per_round = 90 * 11 * 4;// 四倍频,90 的 减速比,电机转1圈是11个脉冲
double vel;
void get_current_vel(){
  // 获取当前的时间
  long right_now = millis();
  // 判断逝去的时间是否大于单位时间
  long past_time = right_now - start_time;
  if(past_time >= interval_time){
    // 取消中断,中断里面做着count++ 或者count--,取消中断不再做count++或者--的操作
    noInterrupts();
    // 计算转速(count),数据类型转换
    // 转的圈数/时间*1000*60,将原来的r/ms转换成r/min
    double vel = (double)count / per_round / past_time * 1000 * 60;
    Serial.println(vel);
    // count置零
    count = 0;
    // 将开始时间重置为当前时间,进行重新测速
    start_time = right_now;
    // 重启中断进行重新测速
    interrupts();
  }
}

/*
 * 参数1: 输入(当前转速)
 * 参数2: 输出(根据PID算法计算PWM值---油门)
 * 参数3: 目标值
 * 参数4-6: PID系数值
 * 参数7: DIRECT | REVERSE (后者的P、I、D值会取反)
 * 
 */
double pwm;
double target = 80;
double Kp = 2, Ki = 5, Kd = 1;
PID pid(&vel,&pwm,&target,Kp,Ki,Kd,DIRECT);//PID对象

// 调速函数
void update_vel(){
  // 调用测速函数
  get_current_vel();
  // 调用 Compute 函数生成 PWM 值
  pid.Compute();
  // 将 PWM 值写入电机
  digitalWrite(DIR_LEFT,HIGH);
  analogWrite(PWM_LEFT,pwm);  
 }

void setup() {
  // put your setup code here, to run once:
  // 设置波特率
  Serial.begin(57600);
  // 2.setup中设置引脚的操作模式(INPUT)
  pinMode(encoder_A,INPUT);// 从引脚读数据
  pinMode(encoder_B,INPUT);
  pinMode(DIR_LEFT,4);
  pinMode(PWM_LEFT,5);
  // 3.为引脚添加中断函数
  // 参数1: 中断口 参数2: 回调函数 参数3: 触发时机
  // 单倍频或双倍频只需要为编码器的A相添加中断函数,CHANGE 当引脚电平发生改变时,触发中断
  attachInterrupt(2,count_a,CHANGE);// 2对应的中断口是21,3对应的中断口是20
  attachInterrupt(3,count_b,CHANGE);//当电平发生改变时触发中断函数
  pid.SetMode(AUTOMATIC);// 自动
}

void loop() {
  // put your main code here, to run repeatedly:
  // delay(2000);
  // Serial.println(count);
  // get_current_vel();//获取当前速度,通过串口输出
  delay(10);//每隔10ms调用
  update_vel();// 调速
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

2021 Nqq

你的鼓励是我学习的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值