大家好!我是李诩,我又来啦!今天,我决定玩个“绝的”——撸狗系列 之 麦昆巡线!成品出来后,我觉得非常好玩,所以,我想来给大家分享这个项目。如果大家喜欢,可以学习一下制作这个项目!
那么本项目用到了一个PID算法。那么什么是PID算法呢?
PID 算法,是控制理论中最常用、最经典的算法,在我们的生产和生活中应用的非常广泛。它并不是什么很神圣的东西,大家一定都见过 PID 的实际应用:比如四轴飞行器,再比如平衡小车,还有汽车的定速巡航、3D 打印机上的温度控制器、恒温热水器等。就是类似于这种:需要将某一个物理量“保持稳定”的场合(比如维持平衡,稳定温度、转速等),PID 都会派上大用场。
它的公式为:
或者我们在编程时更常用的是数字式的 PID 公式:
PID 控制系统是一种闭环控制系统,闭环控制是根据控制对象输出的反馈来进行校正的控制方式。它能够根据测量出的实际值与计划值之间的误差 Error,按一定的标准来进行纠正。
根据公式,可以看到 PID 分为 3 个部分:比例调节(P)、积分调节(I)、微分调节(D)。那么这三者之间是什么关系呢?每个部分的调节对巡线会产生什么样的影响呢?我们一个一个来看。
注意:如标题所说,本文是写给小学生看的 PID 调速算法,所以下文中描述的是已经简化的 PID 算法,默认没有去计算每个程序的循环时间,也就是没有去计算积分和微分时间。主要有几个原因:一、是因为程序功能比较单一,每个循环时间差不多,在调节 Kd、Ki 参数时,将时间参数 T 当成一个整体进行调试了;二、省略了时间参数后,程序编写会更加简单,容易看懂与理解。
先来看看我的“小麦坤”吧!
这么可爱的“昆昆”是不是看之就很想入手一个呢?那,我们开始制作吧!
步骤1
准备材料
材料清单
1x麦昆: micro:bit教育机器人 V4.0 锂电池豪华套餐赠地图
1xAI视觉传感器 加 硅胶套
1xHusky Lens外壳
步骤2
拼装(有视频教程)
接下来是文字教程
1、先安装锂电池至麦昆主体上面。
2、安装Husky Lens到支架上。
3、安装Husky底座。
4、把安装好的Husky Lens安装到底座上。
5、装好micro:bit板
6、完成
步骤3
切割外形
步骤4
编写程序
代码
/*!
* MindPlus
* maqueen
*
*/
#include <Maqueen_Motor.h>
#include <DFRobot_HuskyLens.h>
// 动态变量
volatile float mind_n_x1, mind_n_offset, mind_n_error, mind_n_Kp, mind_n_V_turn,
mind_n_V_straight, mind_n_V_left, mind_n_V_right, mind_n_Ki, mind_n_Kd,
mind_n_lastError, mind_n_integral, mind_n_derivative;
// 函数声明
void DF_MaiKunYunDongKongZhi_ZuoLunSuDu(float mind_n_V_left, float mind_n_V_right);
void DF_PXunXian();
void DF_PuTongXunXian1();
void DF_PIDCanShuChuShiHua();
void DF_PuTongXunXian2();
void DF_PDCanShuChuShiHua();
void DF_PuTongXunXian3();
void DF_PCanShuChuShiHua();
void DF_PIDXunXian();
void DF_PDXunXian();
// 创建对象
DFRobot_HuskyLens huskylens;
Maqueen_Motor motor;
// 主程序开始
void setup() {
huskylens.beginI2CUntilSuccess();
DF_PIDCanShuChuShiHua();
}
void loop() {
mind_n_x1 = huskylens.readArrowParameter(1).xOrigin;
DF_PIDXunXian();
}
// 自定义函数
void DF_MaiKunYunDongKongZhi_ZuoLunSuDu(float mind_n_V_left, float mind_n_V_right) {
if ((mind_n_V_left>=0)) {
motor.motorRun(motor.LEFT,motor.CW,mind_n_V_left);
}
else {
motor.motorRun(motor.LEFT,motor.CCW,(abs(mind_n_V_left)));
}
if ((mind_n_V_right>=0)) {
motor.motorRun(motor.RIGHT,motor.CW,mind_n_V_right);
}
else {
motor.motorRun(motor.RIGHT,motor.CCW,(abs(mind_n_V_right)));
}
}
void DF_PXunXian() {
mind_n_error = (mind_n_x1 - mind_n_offset);
mind_n_V_turn = (mind_n_Kp * mind_n_error);
mind_n_V_left = (mind_n_V_straight + mind_n_V_turn);
mind_n_V_right = (mind_n_V_straight - mind_n_V_turn);
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(mind_n_V_left, mind_n_V_right);
}
void DF_PuTongXunXian1() {
if ((mind_n_x1==160)) {
// 直行
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(60, 60);
}
if ((mind_n_x1<160)) {
// 左转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(35, 85);
}
if ((mind_n_x1>160)) {
// 右转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(85, 35);
}
}
void DF_PIDCanShuChuShiHua() {
mind_n_V_straight = 120;
mind_n_offset = 160;
mind_n_Kp = 0.55;
mind_n_Ki = 0.00025;
mind_n_Kd = 3.5;
mind_n_error = 0;
mind_n_lastError = 0;
mind_n_integral = 0;
mind_n_derivative = 0;
}
void DF_PuTongXunXian2() {
if (((mind_n_x1>=150) && (mind_n_x1<=170))) {
// 直行
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(70, 70);
}
if ((mind_n_x1<150)) {
// 左转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(40, 100);
}
if ((mind_n_x1>170)) {
// 右转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(100, 40);
}
}
void DF_PDCanShuChuShiHua() {
mind_n_V_straight = 120;
mind_n_offset = 160;
mind_n_Kp = 0.55;
mind_n_Kd = 3.5;
mind_n_error = 0;
mind_n_lastError = 0;
mind_n_derivative = 0;
}
void DF_PuTongXunXian3() {
if (((mind_n_x1>=150) && (mind_n_x1<=170))) {
// 直行
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(80, 80);
}
if (((mind_n_x1>=130) && (mind_n_x1<150))) {
// 左小转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(70, 90);
}
if (((mind_n_x1>=110) && (mind_n_x1<130))) {
// 左中转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(60, 100);
}
if ((mind_n_x1<110)) {
// 左大转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(40, 120);
}
if (((mind_n_x1>170) && (mind_n_x1<=190))) {
// 右小转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(90, 70);
}
if (((mind_n_x1>190) && (mind_n_x1<=210))) {
// 右中转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(100, 60);
}
if ((mind_n_x1>210)) {
// 右大转
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(120, 40);
}
}
void DF_PCanShuChuShiHua() {
mind_n_V_straight = 120;
mind_n_offset = 160;
mind_n_Kp = 0.55;
mind_n_error = 0;
}
void DF_PIDXunXian() {
mind_n_error = (mind_n_x1 - mind_n_offset);
mind_n_integral = (mind_n_integral + mind_n_error);
mind_n_derivative = (mind_n_error - mind_n_lastError);
mind_n_V_turn = (((mind_n_Kp * mind_n_error) + (mind_n_Ki * mind_n_integral)) + (mind_n_Kd * mind_n_derivative));
mind_n_V_left = (mind_n_V_straight + mind_n_V_turn);
mind_n_V_right = (mind_n_V_straight - mind_n_V_turn);
mind_n_lastError = mind_n_error;
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(mind_n_V_left, mind_n_V_right);
}
void DF_PDXunXian() {
mind_n_error = (mind_n_x1 - mind_n_offset);
mind_n_derivative = (mind_n_error - mind_n_lastError);
mind_n_V_turn = ((mind_n_Kp * mind_n_error) + (mind_n_Kd * mind_n_derivative));
mind_n_V_left = (mind_n_V_straight + mind_n_V_turn);
mind_n_V_right = (mind_n_V_straight - mind_n_V_turn);
mind_n_lastError = mind_n_error;
DF_MaiKunYunDongKongZhi_ZuoLunSuDu(mind_n_V_left, mind_n_V_right);
}
步骤5
最后来看看效果吧!
当然本文讲的是最基本的 PID 算法,这个算法在实际应用中还有很多可以进一步完善的地方。基于 PID 控制理论设计的新算法也很多,但是最基本的原理都是类似的。
最后用一张动图来总结一下本文讲的 PID 算法。从下图中,我们可以直观理解 PID 中三个参数 Kp、Ki、Kd 的作用。图中红色虚线为目标位置,蓝色实线为系统输出。
- 当调节 Kp 参数时,可以让系统输出越来越接近目标位置,但是存在抖动、以及存在稳定误差调节力度不够的问题;
- 调节 Ki 参数,可以弥补 Kp 参数调节力度不够的问题,但是也会增加抖动;
- 调节 Kd 参数,会让抖动减小,变化曲线更加平滑。
补充思考
在本文中,我们利用哈士奇摄像头得到了黑线位置,这里我们只用了黑线的其中一个位置参数:起点坐标 x1。但是另外三个参数都没有用上(起点坐标 y1,终点坐标 x2、y2)。
请你思考,是否将另外几个参数利用起来效果会更好呢?我们在巡线过程中,发现哈士奇屏幕中的黑线经常是斜着的,是否让黑线整体保持竖直,也就是让黑线与哈士奇屏幕的中轴线重合效果会更好呢?不仅去调节黑线与屏幕中间位置的偏差,还要去调节黑线的方向。
赶紧用本文讲的 PID 算法试试吧,你能挑战更快的速度么?
点击链接可以查看相关文章:
我对『PID算法』的理解 —— 原理介绍:https://mc.dfrobot.com.cn/thread-14783-1-1.html
用于乐高机器人的PID控制器:https://bbs.cmnxt.com/thread-5688-1-1.html
撸狗初体验 | 手把手教你上手 HuskyLens 哈士奇人工智能摄像头:https://mc.dfrobot.com.cn/thread-299462-1-1.html
我的项目分享结束谢谢大家的收看!
喜欢我的文章可以在下方留言、点赞、和转发,谢谢
|