【平衡小车入门】(PID、FreeRTOS、hal库)

本篇博客记录自己复刻的平衡小车

    • 前言
    • 一、硬件需求
    • 二、最终效果
    • 三、整体流程
      • 第一步:stm32通过DRV8833电机驱动模块使用PWM驱动直流减速电机
      • 第二步:理解PID算法在平衡小车中的应用
      • 第三步:PID调参
    • 四、源代码获取

前言

从代码上看,平衡小车的实现是比较简单的,特别是只实现平衡。在平衡的基础上可以加上其他功能:视觉、循迹、避障、蓝牙控制等。项目源码直接使用b站up主:会飞的摄影师呀,然后自己对标准库用的比较多,hal库的库函数大多数不熟悉,在复刻完成之后又在b站看了另一个up(天下行走)的视频,使用标准库+裸机开发,代码很简单,之后可能会在发一篇讲讲后面这个。

一、硬件需求

①MPU6050
②STM32F103C8T6
③oled液晶屏
④18650电池两节
⑤DRV8833电机驱动模块
⑥MP1584EN(5V固定输出降压模块)
⑦HC-05蓝牙模块
⑧JGA25-370直流减速电机(6V/280转每分钟)
⑨面包板或者pcb
⑩底座(用于将两电机连接固定)
其余就是一些小东西:铜柱(M3*60+6)、螺丝,螺母(M3)、杜邦线、万用表、电烙铁套件、双面胶或热熔胶、拨动开关(SS-12F44 3脚2档立式)
下面是pcb板:
在这里插入图片描述
pcb版小车:
在这里插入图片描述
面包板小车:

在这里插入图片描述
搞下面这个面包板的原因是pcb版的效果不好,调参调了挺久还是不那么稳,我想着应该是结构问题,pcb过宽,高度也太高,小车因为惯性就不好平稳,然后换成面包板之后重心降低了很多,重量也轻了,所以之后可能会自己画一个板子,画窄一些。

二、最终效果

①pcb平衡小车:


②面包板平衡小车:


从视频也可以看出来,面包板小车更加平稳。

三、整体流程

第一步:stm32通过DRV8833电机驱动模块使用PWM驱动直流减速电机

这是所有小车的第一步,电机不动那还怎么接下来的步骤,STM32使用定时器可以输出不同的PWM波,将PWM波输出至电机驱动模块,就可以驱动电机转起来了。
在这里插入图片描述
在这里插入图片描述
本项目中就使用上图所示的PWM控制电机速度表来控制两个电机,AIN1连接PA7、AIN2连接PA3(电机1);BIN1连接PA6、BIN2连接PA4(电机2)。AO1、AO2接电机的驱动口,一般是最左和最右那两个,中间四个接口是电机的编码器接口。在进行组装之前,先测试电机的转动方向和转速,并且测量电机的死区,这在设置电机转速时会用到。死区的测法就是从低开始给PWM,看PWM为多少时电机开始转动,那死区就是不转动的那部分PWM区,要想让电机转动起来,那给的PWM就必须大于死区。设置电机最终转速的函数如下:

void Set_PWM(int motor1,int motor2)
{if(motor1>0)//正转 {PWMA2=Dead_Zone+(abs(motor1))*1.17;PWMA1=0;}else//反转{PWMA2 =0;PWMA1=Dead_Zone+(abs(motor1))*1.17;}if(motor2>0)//正转{PWMB2 = Dead_Zone+(abs(motor2))*1.17;PWMB1 = 0;}else{   PWMB2 = 0;PWMB1=Dead_Zone+(abs(motor2))*1.17;}
}

其中Dead_Zone就是死区值,abs是绝对值函数。

第二步:理解PID算法在平衡小车中的应用

在平衡小车中想实现PID算法,编码器和姿态传感器(比如MPU6050)是必不可少的。
平衡小车实现PID控制算法,一共需要三个部分:直立环、速度环、转向环。如果只想小车上电之后保持直立,不施加外部干扰的话,只需要直立环就够了;如果想要直立,并且能够抗外界干扰的话就需要直立环+速度环;如果需要蓝牙遥控啥的那就需要再加上转向换。

PID控制就是对偏差进行比例、积分和微分控制,最终目的就是使偏差接近于0。可以看出PID分成三个部分,可以任意组合,直立环一般使用PD、速度环使用PI、转向环使用PD。

总的公式是:PWM = Kpe(k)+Kie(k)的积分+Kd*e(k)的微分
e(k)就是所谓的偏差,也就是真实值-设定值。
各个环的输入都是不一样的:
①直立环的输入就是目标角度、真实角度、y轴角速度,这里的目标角度就是机械中值,需要在调试PID参数之前确定,确定方法可以看视频,由于mpu6050摆放的方向不一样,那么需要观察的值也就不一样。真实角度就是mpu6050的姿态角,根据我的摆放位置,此值就是pitch。y轴角速度就是角度偏差的微分(此次偏差-上次偏差),就是上面总的公式中Kd乘的那部分。
②速度环的输入是左右编码器读取的值、目标速度,这里如果只实现直立的话,那么目标速度肯定是0,因为想要小车静止不动的。
③转向环,根据不同的需求,参数各不相同,本项目的转向环输入是目标偏航角、z轴角速度。偏航角顾名思义就是转向相关,z轴角速度就是转向偏差的微分(此次偏差-上次偏差)。
上面说的这些直接看视频更加直接,而且很容易理解:https://www.bilibili.com/video/BV1j7411z7uX/?p=3&spm_id_from=pageDriver&vd_source=2a10d30b8351190ea06d85c5d0bfcb2a

本项目使用的是并级PID,还有一种串级PID,下面的图就是两种方法对比:

在这里插入图片描述
在这里插入图片描述
PID部分的代码也是非常简单,前提是移植了别人写好的MPU6050相关函数,不然要自己解算姿态角的话还是很麻烦的:

/**************************************************************************************************************
*函数名:Vertical_Ring_PD()
*功能:直立环PD控制
*形参:(float Angle):x轴的角度/(float Gyro):y轴的角速度
*返回值:经过PID转换之后的PWM值
**************************************************************************************************************/
//直立环的PDint	Vertical_Ring_PD(float Angle,float Gyro)
{float Bias;//目标偏差int balance;Bias=Angle-Mechanical_balance;//目标值减去机械中值(不一定为0)即测量值-理论值这是p算法要用的balance=PID.Balance_Kp*Bias+ Gyro*PID.Balance_Kd;//这里的Kd是偏差的微分,离散系统中两次角度的差值也就是微分,对应就是角速度,这里传入的Gyro就是y轴角速度,//这要根据平衡小车上MPU6050摆放的方向有关,因为小车只会往两个方向倒下,这里看mpu6050上的标注就好了,我这里是沿着y轴两边倒下,也就是y轴的角速度。//第一个参数是当前角度,这就是读取mpu6050的原始数据,然后经过dmp解算之后得到的欧拉角,这里传进来的就是pitch,俯仰角,就是此时小车直立的角度。return balance;//printf("balance = %f\n",balance);
}/**************************************************************************************************************
*函数名:Vertical_speed_PI()
*功能;速度环PI控制
*形参:(int encoder_left):左轮编码器值/(int encoder_right):编码器右轮的值/(float Angle):x轴角度值
*返回值:
**************************************************************************************************************/int Vertical_speed_PI(int encoder_left,int encoder_right,float Angle,float Movement )
{static float Velocity,Encoder_Least,Encoder;//Encoder是经过一定程度滤波后的速度测量值,Encoder_Least是最新数据static float Encoder_Integral;Encoder_Least =(encoder_left+encoder_right)-0;    //获取最新速度偏差=测量速度(左右编码器之和)-目标速度(此处为零)Encoder *= 0.8f;																	//一阶低通滤波器 ,上次的速度占80%,f为滤波系数Encoder += Encoder_Least*0.2f;                   //一阶低通滤波器, 本次的速度占20%   Encoder_Integral +=Encoder;                       //积分出位移 积分时间:10msEncoder_Integral=Encoder_Integral-Movement;//if(Movement == 0 ) Encoder_Integral=0;if(Encoder_Integral>10000)  	Encoder_Integral=10000;           //积分限幅if(Encoder_Integral<-10000)	  Encoder_Integral=-10000;            //积分限幅Velocity=Encoder*PID.Velocity_Kp+Encoder_Integral*PID.Velocity_Ki;//速度控制,P算法为Kp*误差,误差等于测量值-理论值,此时目标速度为0//,所以直接Kp*测量值(这里的测量值是经过一阶低通滤波后的值)if(Turn_off(Angle)==1)   Encoder_Integral=0;            //电机关闭后清除积分return Velocity;
}/**************************************************************************************************************
*函数名:Vertical_turn_PD()
*功能:转向环PD
*形参:taget_yaw 目标yaw, yaw 陀螺仪yaw , gyro 陀螺仪yaw方向角速度
*返回值:无
***************************************************************************************************************/
int Vertical_turn_PD(float taget_yaw,float yaw,float gyro)
{float Turn;float Bias_yaw;Bias_yaw=taget_yaw-yaw;if (Bias_yaw<-180) Bias_yaw+=360;if (Bias_yaw>180) Bias_yaw-=360;Turn=-Bias_yaw*PID.Turn_Kp-gyro*PID.Turn_Kd;return Turn;
}

第三步:PID调参

这里强烈建议观看b站视频,跟着进行调参:https://www.bilibili.com/video/BV1ib411v7YU/?p=10&spm_id_from=pageDriver&vd_source=2a10d30b8351190ea06d85c5d0bfcb2a
调参才是整个小车制作过程中最折磨的,会一直觉得调的还不够好,但其实这是多方面因素决定的。

四、源代码获取

关注+点赞就可以私信免费获取

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

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

相关文章

Codeforces Round 260 (Div. 1)A. Boredom(dp)

最开始写了一发贪心wa了&#xff0c;然后这种选和不选的组合优化问题&#xff0c;一般是考虑动态规划 d p [ i ] [ 0 ] &#xff1a; dp[i][0]&#xff1a; dp[i][0]&#xff1a;表示第i个数不选的最大值 d p [ i ] [ 1 ] &#xff1a; dp[i][1]&#xff1a; dp[i][1]&#xf…

JavaScript 入门 完整版

目录 第一个知识点&#xff1a;引入js文件 内部引用: 外部引用: 第二个知识点&#xff1a;javascript的基本语法 定义变量&#xff1a; 条件控制(if - else if - else) 第三个知识点&#xff1a;javascript里的数据类型、运算符&#xff1a; 数字类型 字符串类型 布尔…

算法之双指针系列1

目录 一&#xff1a;双指针的介绍 1&#xff1a;快慢指针 2&#xff1a;对撞指针 二&#xff1a;对撞指针例题讲述 一&#xff1a;双指针的介绍 在做题中常用两种指针&#xff0c;分别为对撞指针与快慢指针。 1&#xff1a;快慢指针 简称为龟兔赛跑算法&#xff0c;它的基…

前端JavaScript篇之对闭包的理解

目录 对闭包的理解用途循环中使用闭包解决 var 定义函数的问题 对闭包的理解 闭包是指一个函数能够访问并操作其词法作用域&#xff08;定义时所在的作用域&#xff09;之外的变量的能力。它可以通过在一个函数内部创建另一个函数来实现。内部函数可以访问外部函数的局部变量、…

学生速看:免费领取一台阿里云学生服务器2024申请入口

2024年阿里云学生服务器免费领取&#xff0c;先完成学生认证即可免费领取一台云服务器ECS&#xff0c;配置为2核2G、1M带宽、40G系统盘&#xff0c;在云服务器ECS实例过期之前&#xff0c;完成实验与认证任务&#xff0c;还可以免费续费6个月&#xff0c;阿里云百科aliyunbaike…

springboo冬奥会科普平台源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理平台应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

ssm+vue的校园一卡通密钥管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的校园一卡通密钥管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

【集合系列】HashMap 集合

HashMap 集合 1. 概述2. 方法3. 遍历方式4. 代码示例15. 代码示例26. 注意事项 其他集合类 父类 Map 实现类 LinkedHashMap 集合类的遍历方式 具体信息请查看 API 帮助文档 1. 概述 HashMap 是 Java 中的一种集合类&#xff0c;它实现了 Map 接口。HashMap 使用键值对存储数据…

先跑一跑Depth Anything

1. Depth Anything 24年1月开年王炸&#xff0c;论文“Depth Anything: Unleashing the Power of Large-Scale Unlabeled Data“&#xff0c;来自香港大学、字节、浙江实验室和浙江大学。 Depth Anything&#xff0c;这是一种用于鲁棒单目深度估计的解决方案。目标是建立一个…

6、5 门关于 AI 和 ChatGPT 的免费课程,带您从 0-100

5 门关于 AI 和 ChatGPT 的免费课程,带您从 0-100 想在 2024 年免费了解有关 AI 和 ChatGPT 的更多信息吗? 图片由 DALLE 3 提供 活着是多么美好的时光啊。还有什么比现在更适合了解生成式人工智能(尤其是 ChatGPT)等人工智能元素的呢!许多人对这个行业感兴趣,但有些…

LeetCode Python - 3.无重复字符的最长子串

文章目录 题目答案运行结果 题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释:…

MySQL之体系结构

华子目录 MySQL简介MySQL的特性MySQL版本MySQL常见版本 数据库排名网站MySQL结构体系查看最大连接数查询缓存配置情况 一条SQL语句执行流程 MySQL简介 MySQL是一个小型关系数据库管理系统&#xff0c;开发者为瑞典MySQL AB公司。在2008年1月16号被sun公司10亿美金收购。2009年…