第 8 章 机器人底盘Arduino端PID控制(自学二刷笔记)

重要参考:

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

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

8.4.5 底盘实现_04Arduino端PID控制

上一节最后测试时,电机可能会出现抖动、顿挫的现象,显而易见的这是由于PID参数设置不合理导致的,本节将介绍ros_arduino_bridge中的PID调试,大致流程如下:

  1. 了解ros_arduino_bridge中PID调试的流程;
  2. 实现PID调试。
1.ros_arduino_bridge中PID调试源码分析

基本思想:

  1. 先定义调试频率(周期),并预先设置下一次的结束时刻;
  2. 当当前时刻大于预设的结束时刻时,即进行PID调试,且重置下一次调试结束时刻
  3. PID代码在diff_controller中实现,PID的目标值是命令输入的转速,当前转速则是通过读取当前编码器计数再减去上一次调试结束时记录的编码器计数获取;
  4. 最后输出 PWM

ROSArduinoBridge.ino 中和PID控制相关的变量:

#ifdef USE_BASE/* Motor driver function definitions */#include "motor_driver.h"/* Encoder driver function definitions */#include "encoder_driver.h"/* PID parameters and functions */#include "diff_controller.h"/* Run the PID loop at 30 times per second */#define PID_RATE           30     // Hz PID调试频率/* Convert the rate into an interval */const int PID_INTERVAL = 1000 / PID_RATE; // PID调试周期/* Track the next time we make a PID calculation */unsigned long nextPID = PID_INTERVAL; //PID调试的结束时刻标记/* Stop the robot if it hasn't received a movement commandin this number of milliseconds */#define AUTO_STOP_INTERVAL 5000long lastMotorCommand = AUTO_STOP_INTERVAL;
#endif

ROSArduinoBridge.ino 的 runCommand()函数中:

#ifdef USE_BASEcase READ_ENCODERS:Serial.print(readEncoder(LEFT));Serial.print(" ");Serial.println(readEncoder(RIGHT));break;case RESET_ENCODERS:resetEncoders();resetPID();Serial.println("OK");break;case MOTOR_SPEEDS: //---------------------------------------------/* Reset the auto stop timer */lastMotorCommand = millis();if (arg1 == 0 && arg2 == 0) {setMotorSpeeds(0, 0);resetPID();moving = 0;}else moving = 1;//设置左右电机目标转速分别为参数1和参数2leftPID.TargetTicksPerFrame = arg1;rightPID.TargetTicksPerFrame = arg2;Serial.println("OK"); break;case UPDATE_PID:while ((str = strtok_r(p, ":", &p)) != '\0') {pid_args[i] = atoi(str);i++;}Kp = pid_args[0];Kd = pid_args[1];Ki = pid_args[2];Ko = pid_args[3];Serial.println("OK");break;
#endif

ROSArduinoBridge.ino 的 loop()函数中:

#ifdef USE_BASE//如果当前时刻大于 nextPID,那么就执行PID调速,并在 nextPID 上自增一个PID调试周期if (millis() > nextPID) {updatePID();nextPID += PID_INTERVAL;}// Check to see if we have exceeded the auto-stop intervalif ((millis() - lastMotorCommand) > AUTO_STOP_INTERVAL) {;setMotorSpeeds(0, 0);moving = 0;}
#endif

diff_controller.h 中的PID调试代码:

/* Functions and type-defs for PID control.Taken mostly from Mike Ferguson's ArbotiX code which lives at:http://vanadium-ros-pkg.googlecode.com/svn/trunk/arbotix/
*//* PID setpoint info For a Motor */
typedef struct {double TargetTicksPerFrame;    // target speed in ticks per frame 目标转速long Encoder;                  // encoder count 编码器计数long PrevEnc;                  // last encoder count 上次的编码器计数/** Using previous input (PrevInput) instead of PrevError to avoid derivative kick,* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-derivative-kick/*/int PrevInput;                // last input//int PrevErr;                   // last error/** Using integrated term (ITerm) instead of integrated error (Ierror),* to allow tuning changes,* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/*///int Ierror;int ITerm;                    //integrated termlong output;                    // last motor setting
}
SetPointInfo;SetPointInfo leftPID, rightPID;/* PID Parameters */
int Kp = 20;
int Kd = 12;
int Ki = 0;
int Ko = 50;unsigned char moving = 0; // is the base in motion?/*
* Initialize PID variables to zero to prevent startup spikes
* when turning PID on to start moving
* In particular, assign both Encoder and PrevEnc the current encoder value
* See http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-initialization/
* Note that the assumption here is that PID is only turned on
* when going from stop to moving, that's why we can init everything on zero.
*/
void resetPID(){leftPID.TargetTicksPerFrame = 0.0;leftPID.Encoder = readEncoder(LEFT);leftPID.PrevEnc = leftPID.Encoder;leftPID.output = 0;leftPID.PrevInput = 0;leftPID.ITerm = 0;rightPID.TargetTicksPerFrame = 0.0;rightPID.Encoder = readEncoder(RIGHT);rightPID.PrevEnc = rightPID.Encoder;rightPID.output = 0;rightPID.PrevInput = 0;rightPID.ITerm = 0;
}/* PID routine to compute the next motor commands */
//左右电机具体调试函数
void doPID(SetPointInfo * p) {long Perror;long output;int input;//Perror = p->TargetTicksPerFrame - (p->Encoder - p->PrevEnc);input = p->Encoder - p->PrevEnc;Perror = p->TargetTicksPerFrame - input;//根据 input 绘图//Serial.println(input);/** Avoid derivative kick and allow tuning changes,* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-derivative-kick/* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/*///output = (Kp * Perror + Kd * (Perror - p->PrevErr) + Ki * p->Ierror) / Ko;// p->PrevErr = Perror;output = (Kp * Perror - Kd * (input - p->PrevInput) + p->ITerm) / Ko;p->PrevEnc = p->Encoder;output += p->output;// Accumulate Integral error *or* Limit output.// Stop accumulating when output saturatesif (output >= MAX_PWM)output = MAX_PWM;else if (output <= -MAX_PWM)output = -MAX_PWM;else/** allow turning changes, see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-tuning-changes/*/p->ITerm += Ki * Perror;p->output = output;p->PrevInput = input;
}/* Read the encoder values and call the PID routine */
//PID调试
void updatePID() {/* Read the encoders */leftPID.Encoder = readEncoder(LEFT);rightPID.Encoder = readEncoder(RIGHT);/* If we're not moving there is nothing more to do */if (!moving){/** Reset PIDs once, to prevent startup spikes,* see http://brettbeauregard.com/blog/2011/04/improving-the-beginner%E2%80%99s-pid-initialization/* PrevInput is considered a good proxy to detect* whether reset has already happened*/if (leftPID.PrevInput != 0 || rightPID.PrevInput != 0) resetPID();return;}/* Compute PID update for each motor */doPID(&rightPID);doPID(&leftPID);/* Set the motor speeds accordingly */setMotorSpeeds(leftPID.output, rightPID.output);
}
2.PID调试

调试时,需要在 diff_controller.h 中打印 input 的值,然后通过串口绘图器输入命令: m 参数1 参数2,根据绘图结果调试:Kp、Ki和Kd的值。

  • 调试时,可以先调试单个电机的PID,比如,可以先注释 doPID(&rightPID);
  • PID算法不同,即便算法相同,如果参与运算的数据单位不同,都会导致不同的调试结果,不可以直接复用之前的调试结果。

PID调试技巧可以参考之前介绍。

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

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

相关文章

ubuntu下安装pwndbg

安装pwndbg 如果可以科学上网 首先安装git apt install git 然后拉取git库 git clone GitHub - pwndbg/pwndbg: Exploit Development and Reverse Engineering with GDB Made Easy 进入到pwngdb的文件夹中 cd pwngdb 执行 ./setup.sh 而后输入gdb 出现红色pwndgb就是安装成功…

解决springboot+vue静态资源刷新后无法访问的问题

一、背景 原项目是有前后端分离设计&#xff0c;测试环境是centos系统&#xff0c;采用nginx代理和转发&#xff0c;项目正常运行。 项目近期上线到正式环境&#xff0c;结果更换了系统环境&#xff0c;需要放到一台windows系统中&#xff0c;前后端打成一个jar包&#xff0c;…

Python重复文件清理小工具

针对电脑长期使用产生的重复文件&#xff0c;尤其是微信电脑版每转发一次生成一个重复文件的问题&#xff0c;用python写了一个批量清理重复文件的小工具&#xff0c;记录备用。 import shutil import tkinter from tkinter import filedialog import os import threading imp…

美港通正规股票交易市场人民币突然拉升,市场开启“大风车”模式?

查查配今天上午,市场又开启了“大风车”模式,多个热点轮番拉升。 一则关于地产行业利好的小作文流出,地产产业链上午爆发,租售同权、房地产服务、房地产开发等板块大涨,光大嘉宝、天地源等个股涨停。万科A涨超4%。 美港通证券以其专业的服务和较低的管理费用在市场中受到不少…

如何在Sui智能合约中验证是否为多签地址

通过多签合约实现多个用户可访问的安全账户。多签&#xff08;multi-sig&#xff09;钱包和账户通过允许多个用户在预定义条件下访问共享资产&#xff0c;或让单个用户实施额外的安全措施&#xff0c;从而增强密钥管理。例如&#xff0c;多签钱包可以用于管理去中心化自治组织&…

智慧教育平台:选课系统的Spring Boot实现

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

Verilog基础语法——条件语句if-else与case

Verilog基础语法——条件语句case、if-else 写在前面一、if-else语句二、case语句2.1 case语句2.2 casez语句2.3 casex语句 写在后面 写在前面 在Verilog语法中&#xff0c;常用的条件语句有if-else语句和case语句&#xff0c;用于判断条件是否为真&#xff0c;并执行判断条件后…

第 N 个泰波那契数

题目链接 第 N 个泰波那契数 题目描述 注意点 0 < n < 37答案保证是一个 32 位整数 解答思路 动态规划根据前三个数字推出新的泰波那契数 代码 class Solution {public int tribonacci(int n) {if (n 0) {return 0;}if (n 1 || n 2) {return 1;}int x 0;int x…

JSON格式化输出html——数组+对象+JSON字符串+汉字——基础积累——@pgrabovets/json-view

昨天写了一篇关于JSON格式化输出到页面上——数组对象JSON字符串汉字——基础积累的文章&#xff0c;效果是可以实现的 但是如果要实现右侧部分的展开/折叠&#xff0c;则可以使用到下面的插件了pgrabovets/json-view github链接&#xff1a;https://github.com/pgrabovets/j…

软考笔记随记

原码:(0正1负) 原码是最直观的编码方式,符号位用0表示正数,用1表示负数,其余位表示数值的大小。 例如,+7的原码为00000111,-7的原码为10000111。 原码虽然直观,但直接用于加减运算会导致计算复杂,且0有两种表示(+0和-0),不唯一。 反码: 反码是在原码的基础上得…

如何在VS Code中安装插件并进行中文化。

相关文章推荐: 如何下载和安装Visual Studio Code&#xff08;VSCode&#xff09; 在使用Visual Studio Code&#xff08;简称VS Code&#xff09;进行开发时&#xff0c;安装插件可以极大地提升开发效率和使用体验。而将VS Code插件界面进行中文化&#xff0c;则能更好地满足中…

十四、Redis Cluster集群

Redis Cluster是Redis提供的一个分布式解决方案&#xff0c;在3.0推出。Redis Cluster可以自动将数据分片分布到不同的master节点上&#xff0c;同时提供了高可用的支持&#xff0c;当某个master节点挂了之后&#xff0c;整个集群还是可以正常工作。1、为什么要用Redis Cluster…

rocketmq的流程

生产过程 消费过程 存储 在RocketMQ中&#xff0c;一个Broker的所有Topic的消息都会被写入到同一个CommitLog文件中。 每个队列&#xff08;Queue&#xff09;都有对应的ConsumeQueue文件。 ConsumeQueue每个记录定长&#xff0c;20字节&#xff0c;消息在commitlog中的偏移量…

外贸客户采集软件有哪些?

外贸客户采集软件可以帮助企业收集潜在客户的信息&#xff0c;以便进行市场分析和客户开发。以下是一些常用的外贸客户采集软件&#xff1a; 易谷歌地图数据采集大师&#xff1a;基于谷歌地图数据采集的软件&#xff0c;能够采集任意国家、地区的企业地址、电话号码、邮件地址等…

SpringCloud 2023.0.1

本文介绍如何使用 springboot3及cloud2023 进行微服务模块化开发 采用父-module 模块开发 父工程 demo-java pom.xml <!--配置 springboot的依赖的版本号, 方便 module 进行继承--><dependencyManagement><dependencies><!--增加 springboot的依赖--&g…

浅谈-数据分析之道--数据思维的培养

第一篇数据思维 数据分析中最重要的是数据思维&#xff0c;对于业务场景中常见的问题&#xff0c;只要有分析问题的思路和方法&#xff0c;无论用什么工具都可以得到结果。 数据思维是数据分析师分析问题的思路和角度。 第一章&#xff0c;什么是数据思维 什么是数据治理&a…

适合建站的香港服务器有哪些,企业和个人建站的

香港服务器适合外贸建站、个人和企业建站&#xff0c;尤其是中小企业官网非常适合放在香港服务器上&#xff0c;因为香港服务器在国内外的访问速度都很快&#xff0c;也就意味着全球客户都能访问到你的网站。 对于很多新手小白来说不知道怎么才能买到靠谱稳定的香港服务器&…

mysql主从热备+keepalived 部署mysql高可用主备模式

目录 1、环境准备 2、分别在主服务器和备用服务器上安装keepalived 3、修改keepalived服务的配置文件 3.1 修改主服务器上的keepalive服务的配置文件 3.2 修改备用服务器上的keepalive服务配置文件 4、编写mysql监控脚本放到主服务器上 5、在主服务器和备用服务器上查看…

水泡传感器内部结构

水泡传感器内部结构&#xff1a; 水泡传感器放大电路 电路是基于1.6V做的TIA I2V&#xff0c; 也就是输出部分基于1.6V做电压的增加或减少。

Milvus 快速入门

引言 在本篇文章中&#xff0c;我们将介绍 Milvus 的基本概念&#xff0c;并通过一个简单的示例展示如何在 Milvus 中创建集合、插入向量和执行搜索。最后&#xff0c;我们将概览 Milvus 提供的 API。 一、基本概念 1.1 集合 (Collection) 在 Milvus 中&#xff0c;集合类似…