第 8 章 电机调速(自学二刷笔记)

news2024/12/28 20:45:54

重要参考:

课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ

讲义链接:Introduction · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

 

8.3.5 电机调速01_PID控制理论

场景:

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

比如:现有一辆行驶中的无人车,要求将车速调整至100KM/h,那么应该如何向电机输出PWM值?或换言之,如何控制油门?

调速实现策略由多种,PID其中较为常用。

PID简介

PID算法是一种经典、简单、高效的动态速度调节方式,P代表比例,I代表积分,D代表微分。

PID公式如下:

  • e(t)作为 PID 控制的输入;

  • u(t)作为 PID 控制器的输出和被控对象的输入;

  • Kp 控制器的比例系数;

  • Ki控制器的积分时间,也称积分系数;

  • Kd控制器的微分时间,也称微分系数。

上述公式稍显晦涩,PID控制原理框架图更有助于理解:

1.P

如果实现上述场景中的车速控制,一种简单的实现方式是:确定目标速度,获取当前速度,使用(目标速度-当前速度)*某一系数 计算结果为输出的PWM,再获取当前速度,使用(目标速度-当前速度)*某一系数 计算结果为输出的PWM并输出...如此循环在上述模型中,调速实现是一个闭环,每一次循环都会根据当前时速与目标时速的差值,再乘以以固定系数,计算出需要输出的PWM值,这其中的系数,称之为比例。

2.I

上述模型算法中,最终速度与预期速度存在稳态误差,这意味着最终结果可能永远无法达成预期,解决的方法就是使用积分I。每次调速时,输出的PWM还要累加根据积分I计算的结果,以消除静态误差。

3.D

当I值设置的过大时,可能会出先"超速"的情况,超速之后可能需要多次调整,产生系统震荡,解决这种情况可以使用D微分,当速度越是接近目标速度时,D就会越施加反方向力,减弱P的控制,起到类似”阻尼“的作用。通过D的使用可以减小系统震荡。


综上,PID闭环控制实现是结合了比例、积分和微分的一种控制机制,通过P可以以比例的方式计算输出,通过I可以消除稳态误差,通过D可以减小系统震荡,三者相结合,最终是要快速、精准且稳定的达成预期结果,而要实现该结果,还需要对这三个数值反复测试、调整...下一节将介绍在 Arduino 中PID控制的具体实现,其中就包括PID库的调用,以及PID调试的具体方式。

 

8.3.6 电机调速02_PID控制实现

了解了PID原理以及计算公式之后,我们可以在程序中自实现PID相关算法实现,不过,在Arduino中该算法已经被封装了,直接整合调用即可,从而提高程序的安全性与开发效率。该库是:Arduino-PID-Library,接下来通过一个案例演示该库的使用。

需求:通过PID控制电机转速,预期转速为 80r/m。

实现流程:

  1. 添加Arduino-PID-Library;
  2. 编写Arduino程序直接调用相关实现;
  3. 使用串口绘图器调试PID值。
1.添加Arduino-PID-Library

首先在 GitHub 下载 PID 库: git clone GitHub - br3ttb/Arduino-PID-Library

然后将该文件夹移动到 arduino 的 libraries下: sudo cp -r Arduino-PID-Library /home/用户xxx/Arduino/libraries

还要重命名文件夹: sudo mv Arduino-PID-Library ArduinoPIDLibrary

最后重启 ArduinoIDE

2.编码

PID调速中,测速是实现闭环的关键实现,所以需要复制之前的电机控制代码以及测速代码。

完整代码实现:

/*
 * PID 调速实现:
 * 1.代码准备,复制并修改电机控制以及测速代码
 * 2.包含PID头文件
 * 3.创建PID对象
 * 4.在setup中启用自动调试
 * 5.调试并更新PWM
 * 
 */

#include <PID_v1.h> 

int DIRA = 4;
int PWMA = 5;

int motor_A = 21;//中端口是2
int motor_B = 20;//中断口是3
volatile int count = 0;//如果是正转,那么每计数一次自增1,如果是反转,那么每计数一次自减1 


void count_A(){
  //单频计数实现
  //手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比
  /*if(digitalRead(motor_A) == HIGH){

    if(digitalRead(motor_B) == LOW){//A 高 B 低
      count++;  
    } else {//A 高 B 高
      count--;  
    }


  }*/

  //2倍频计数实现
  //手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比 * 2
  if(digitalRead(motor_A) == HIGH){

    if(digitalRead(motor_B) == HIGH){//A 高 B 高
      count++;  
    } else {//A 高 B 低
      count--;  
    }


  } else {

    if(digitalRead(motor_B) == LOW){//A 低 B 低
      count++;  
    } else {//A 低 B 高
      count--;  
    }  

  }

}

//与A实现类似
//4倍频计数实现
//手动旋转电机一圈,输出结果为 一圈脉冲数 * 减速比 * 4
void count_B(){
  if(digitalRead(motor_B) == HIGH){

    if(digitalRead(motor_A) == LOW){//B 高 A 低
      count++;
    } else {//B 高 A 高
      count--;
    }


  } else {

    if(digitalRead(motor_A) == HIGH){//B 低 A 高
      count++;
    } else {//B 低 A 低
      count--;
    }

  }

}


int reducation = 90;//减速比,根据电机参数设置,比如 15 | 30 | 60
int pulse = 11; //编码器旋转一圈产生的脉冲数该值需要参考商家电机参数
int per_round = pulse * reducation * 4;//车轮旋转一圈产生的脉冲数 
long start_time = millis();//一个计算周期的开始时刻,初始值为 millis();
long interval_time = 50;//一个计算周期 50ms
double current_vel;

//获取当前转速的函数
void get_current_vel(){
  long right_now = millis();  
  long past_time = right_now - start_time;//计算逝去的时间
  if(past_time >= interval_time){//如果逝去时间大于等于一个计算周期
    //1.禁止中断
    noInterrupts();
    //2.计算转速 转速单位可以是秒,也可以是分钟... 自定义即可
    current_vel = (double)count / per_round / past_time * 1000 * 60;
    //3.重置计数器
    count = 0;
    //4.重置开始时间
    start_time = right_now;
    //5.重启中断
    interrupts();

    Serial.println(current_vel);

  }
}

//-------------------------------------PID-------------------------------------------
//创建 PID 对象
//1.当前转速 2.计算输出的pwm 3.目标转速 4.kp 5.ki 6.kd 7.当输入与目标值出现偏差时,向哪个方向控制
double pwm;//电机驱动的PWM值
double target = 80;
double kp=1.5, ki=3.0, kd=0.1;
PID pid(&current_vel,&pwm,&target,kp,ki,kd,DIRECT);

//速度更新函数
void update_vel(){
  //获取当前速度
  get_current_vel();
  pid.Compute();//计算需要输出的PWM
  digitalWrite(DIRA,HIGH);
  analogWrite(PWMA,pwm);

}

void setup() {
  Serial.begin(57600);//设置波特率  
  pinMode(18,INPUT);
  pinMode(19,INPUT);
  //两个电机驱动引脚都设置为 OUTPUT
  pinMode(DIRA,OUTPUT);
  pinMode(PWMA,OUTPUT);

  attachInterrupt(2,count_A,CHANGE);//当电平发生改变时触发中断函数
  //四倍频统计需要为B相也添加中断
  attachInterrupt(3,count_B,CHANGE);

  pid.SetMode(AUTOMATIC);
}



void loop() {
  delay(10);
  update_vel();

}

核心代码解释:

1.包含PID头文件

#include <PID_v1.h>

2.创建PID对象

//创建 PID 对象
//1.当前转速 2.计算输出的pwm 3.目标转速 4.kp 5.ki 6.kd 7.当输入与目标值出现偏差时,向哪个方向控制
double pwm;//电机驱动的PWM值
double target = 120;
double kp=1.5, ki=3.0, kd=0.1;
PID pid(&current_vel,&pwm,&target,kp,ki,kd,DIRECT);

3.setup中启用PID自动控制

pid.SetMode(AUTOMATIC);

4.计算输出值

pid.Compute();
4.调试

PID控制的最终预期结果,是要快速、精准、稳定的达成预期结果,P主要用于控制响应速度,I主要用于控制精度,D主要用于减小震荡增强系统稳定性,三者的取值是需要反复调试的,调试过程中需要查看系统的响应曲线,根据响应曲线以确定合适的PID值。

在 Arduino 中响应曲线的查看可以借助于 Serial.println() 将结果输出,然后再选择菜单栏的工具下串口绘图器以图形化的方式显示响应结果:

PID调试技巧:

参数整定找最佳,从小到大顺序查

先是比例后积分,最后再专把微分加

曲线振属荡很频繁,比例度盘要放大

曲线漂浮绕大湾,比例度盘往小扳

曲线偏离回复慢,积分时间往下降

曲线波动周期长,积分时间再加长

曲线振荡频率快,先把微分降下来

动差大来波动慢。微分时间应加长

理想曲线两个波,前高后低4比1

一看二调多分析,调节质量不会低

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第18讲:Ceph集群CrushMap的核心概念、默认规则与完整定义

文章目录 1.CrushMap核心概念2.集群默认的CrushMap规则剖析2.1.CrushMap列表显示内容剖析2.2.对默认的CrushMap规则进行深度的剖析2.3.完整的CrushMap定义信息 1.CrushMap核心概念 CrushMap官方文档&#xff1a;https://docs.ceph.com/en/pacific/rados/operations/crush-map/…

【Alluxio】文件系统锁模型之InodeLockList

InodeLockList接口,表示在inode tree里一个加了锁的路径。 沿着path,inodes和edges都被加锁了。path可能从edge或inode任意一个开始。 锁列表总是包含了一定数量的读锁(0个或多个),随后跟随着一些数量的写锁(0个或多个)。 举个例子: 对 /a/b/c/d 进行加锁,c->d这…

QGraphicsItem的prepareGeometryChange 和 update方法区别

prepareGeometryChange 这个函数用于为图形的几何形状变化做准备。在改变一个项目的边界矩形之前调用此函数&#xff0c;以保持 QGraphicsScene 的索引是最新的。如果必要的话&#xff0c;prepareGeometryChange() 会调用 update()。QGraphicsScene认为所有图元的boundingRect…

ReactFlow的ReactFlow实例事件传参undefined处理状态切换

1.问题 ReactFlow的ReactFlow实例有些事件我们在不同的状态下并不需要&#xff0c;而且有时候传参会出现其它渲染效果&#xff0c;比如只读状态下我们不想要拖拉拽onEdgesChange连线重连或删除的功能。 2.思路 事件名称类型默认值onEdgesChange(changes: EdgeChange[]) >…

.NET邮箱API发送邮件的步骤?怎么配置API?

.NET邮箱API发送邮件需要注意哪些&#xff1f;如何使用API发信&#xff1f; 在.NET环境中&#xff0c;使用邮箱API发送邮件是一个常见的需求。无论是企业级的邮件通知&#xff0c;还是个人项目中的邮件验证&#xff0c;都少不了.NET邮箱API的帮助。下面&#xff0c;AokSend将详…

MT3033 新的表达式

代码&#xff1a; #include <bits/stdc.h> using namespace std; bool is_op(char c) {return c & || c |; } int priority(char op) { // 运算优先级。如果有-*/等别的运算符&#xff0c;则这个函数很有必要if (op & || op |){return 1;}return -1; } voi…

内网渗透(二)

预备知识 什么是域&#xff1f; 域是若干台计算机组成的集合&#xff0c;一个电脑也是。域中的电脑是分等级的&#xff0c;分为域控和成员机。 如何安装域&#xff1f; 在服务器管理中添加服务器角色&#xff0c;添加域服务 如何加入域? 首先一定要修改DNS服务器 ip为域…

Davinci工程CAN模块讲解

CAN模块是用来配置CAN Driver的&#xff0c;里面有CanConfigSet是用来配置驱动内容的&#xff0c;CanGeneral配置参数。涉及四个文件Can_Lcfg.c/Can_Lcfg.h/Can_Cfg.c/Can_Cfg.h CanConfigSet CanControllers CAN控制器&#xff0c;我们这里的CAN控制器只有一个&#xff0c;名…

Gradle报错Cause: zip END header not found,构建问题解决

问题描述 构建报错&#xff1a;Cause: zip END header not found 解决办法 File>>setting>>Build,Execution,Deployment>>Gradle 选择你本地的Gradke路径 问题解决

探索Java的未来

探索 Java 的未来是一个非常有趣的话题。Java 是一种广泛使用的编程语言&#xff0c;自 1995 年诞生以来&#xff0c;它已经在软件开发领域占据了重要的地位。尽管有些人担心 Java 可能会因为新技术的出现而变得不再相关&#xff0c;但实际情况并非如此。让我们来看看一些关于 …

MySQL 依据扫码记录统计人员进出区域的时间

解决这样一个问题&#xff1a; 如下图人员在区域的扫码记录&#xff0c;进出区域的时候都必须扫码&#xff0c;中间扫码的不算&#xff0c;统计每个人员进入区域、出区域的时间。 有了人员在区域停留的组号&#xff0c;还差一步group by&#xff08;在最后&#xff09;&#xf…

淘宝订单详情与物流电子面单API接口:提升电商物流效率的利器

前言 在电子商务蓬勃发展的今天&#xff0c;物流作为电商交易的重要环节&#xff0c;其效率和准确性直接关系到消费者的购物体验和商家的运营效率。淘宝作为中国最大的电商平台之一&#xff0c;一直致力于提升物流效率和服务质量。其中&#xff0c;淘宝订单详情与物流电子面单A…

Flutter-Statewidget 创建State过程State<XXXX> createState() => _XXXXState()的解释

文章目录 创建widget 的状态对象示例代码解析 完整的代码示例总结 创建widget 的状态对象 今天有个同学问了我下State createState() > _XXXXState()时什么意思。这个代码在flutter开发中一直看到&#xff0c;很多人都不关心这个&#xff0c;直接当模板使用。今天来介绍下这…

运维自动化工具:Ansible 概念与模块详解

目录 前言 一、运维自动化工具有哪些 二、Ansible 概述 1、Ansible 概念 2、Ansible 特点 3、Ansible 工作流程 4、Ansible 架构 4.1 Ansible 组成 4.2 Ansible 命令执行来源 5、Ansible 的优缺点 三、Ansible 安装部署 1、环境部署 2、管理节点安装 Ansible 3、…

智能商品计划系统:引领未来零售业的革新之路

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;和大数据技术已成为推动各行业革新的关键动力。在零售行业中&#xff0c;智能商品计划系统的出现&#xff0c;正逐步改变着传统的商品规划与管理方式&#xff0c;为品牌注入新的活力与竞争力。本文将对智能商…

源码部署与SaaS账号:企业软件选择的自建房与租赁公寓之辩

在数字化运营的时代&#xff0c;企业选择软件解决方案就如同在选择住所&#xff1a;源码部署类似于“自建房屋”&#xff0c;而SaaS账号则更像是“租赁公寓”。 自建房屋&#xff08;源码部署&#xff09; 当你选择自建房屋时&#xff0c;你需要投入大量的时间和资金来购买土地…

什么是香草看涨期权?香草看涨期权有哪些特点?

什么是香草看涨期权&#xff1f;香草看涨期权有哪些特点&#xff1f; 香草看涨期权&#xff0c;通常也称为香草期权&#xff0c;是金融市场上的一种金融衍生品&#xff0c;由券商或金融机构推出。它允许投资者以较小的费用获取相应股票市值的收益权&#xff0c;主要用于风险管…

如何添加、编辑、调整WordPress菜单

我们最近在使用WordPress建站建设公司网站。我们是使用的hostease的主机产品建设的WordPress网站。在建设网站使用遇到了一些WordPress菜单使用方面的问题。好在hostease提供了不少帮助。 下面把WordPress菜单使用心得分享一下。 本文将详细介绍WordPress菜单的各种功能&#x…

hive日常使用时忘记部分补充(不定时)

1、date_formate、unix_timestamp、from_unixtime用法&#xff1a; 2、lag&#xff08;&#xff09;、lead()用法&#xff1a; lag&#xff08;)窗口函数返回分区中当前行之前行&#xff08;可以指定第几行&#xff09;的值。 如果没有行&#xff0c;则返回null。 lead()窗口…

组件通信-props详解

目录 一、什么是prop 二、props校验 三、组件中prop和data的区别 一、什么是prop Prop定义&#xff1a;组件上注册的一些自定义属性。 Prop作用&#xff1a;向子组件传递数据。 特点&#xff1a; 可以传递任意数量的prop可以传递任意类型的prop 二、props校验 组件的pr…