查看: 350|回复: 4

[麦昆 V4.0] 玩转撸狗系列——麦昆巡线

[复制链接]

大家好!我是李诩,我又来啦!今天,我决定玩个“绝的”——撸狗系列 之 麦昆巡线!成品出来后,我觉得非常好玩,所以,我想来给大家分享这个项目。如果大家喜欢,可以学习一下制作这个项目!

那么本项目用到了一个PID算法。那么什么是PID算法呢?

PID 算法,是控制理论中最常用、最经典的算法,在我们的生产和生活中应用的非常广泛。它并不是什么很神圣的东西,大家一定都见过 PID 的实际应用:比如四轴飞行器,再比如平衡小车,还有汽车的定速巡航、3D 打印机上的温度控制器、恒温热水器等。就是类似于这种:需要将某一个物理量“保持稳定”的场合(比如维持平衡,稳定温度、转速等),PID 都会派上大用场。
它的公式为:
Fo0Q2s2hpl7e-huKejEH2ZhgfCX7.png

或者我们在编程时更常用的是数字式的 PID 公式:

FuO0hpV5UzH0ybgyVgtP95Zu2h_s.png

PID 控制系统是一种闭环控制系统,闭环控制是根据控制对象输出的反馈来进行校正的控制方式。它能够根据测量出的实际值与计划值之间的误差 Error,按一定的标准来进行纠正。

根据公式,可以看到 PID 分为 3 个部分:比例调节(P)、积分调节(I)、微分调节(D)。那么这三者之间是什么关系呢?每个部分的调节对巡线会产生什么样的影响呢?我们一个一个来看。

注意:如标题所说,本文是写给小学生看的 PID 调速算法,所以下文中描述的是已经简化的 PID 算法,默认没有去计算每个程序的循环时间,也就是没有去计算积分和微分时间。主要有几个原因:一、是因为程序功能比较单一,每个循环时间差不多,在调节 Kd、Ki 参数时,将时间参数 T 当成一个整体进行调试了;二、省略了时间参数后,程序编写会更加简单,容易看懂与理解。

先来看看我的“小麦坤”吧!

Fk9RnjwDmxtEdNTRr6hynHyDOfDX.gif

这么可爱的“昆昆”是不是看之就很想入手一个呢?那,我们开始制作吧!

步骤1

准备材料

材料清单
1x麦昆: micro:bit教育机器人 V4.0 锂电池豪华套餐赠地图
1xAI视觉传感器 加 硅胶套
1xHusky Lens外壳

步骤2

拼装(有视频教程)






接下来是文字教程

1、先安装锂电池至麦昆主体上面。
FmEW1fOeIip7Lh5YPapicUmUPSDW.gif

2、安装Husky Lens到支架上。
FsXgsLNalQUj4v74bP9XxMvW7jrK.gif

3、安装Husky底座。
Fn6Zf0dc-DaHwvyfFew_2vHkdquE.gif

4、把安装好的Husky Lens安装到底座上。
Fnp0t1PCshNHMzk3xmgwGFsJvnS-.gif

5、装好micro:bit板
Fn498W3NcXcfYM4vbchBu28XRpOJ-1.gif

6、完成
Fn498W3NcXcfYM4vbchBu28XRpOJ.gif

步骤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 参数,会让抖动减小,变化曲线更加平滑。

FutYwJyjZDe5iBlxxQxtZhPXLB-H.gif

补充思考

在本文中,我们利用哈士奇摄像头得到了黑线位置,这里我们只用了黑线的其中一个位置参数:起点坐标 x1。但是另外三个参数都没有用上(起点坐标 y1,终点坐标 x2、y2)。

请你思考,是否将另外几个参数利用起来效果会更好呢?我们在巡线过程中,发现哈士奇屏幕中的黑线经常是斜着的,是否让黑线整体保持竖直,也就是让黑线与哈士奇屏幕的中轴线重合效果会更好呢?不仅去调节黑线与屏幕中间位置的偏差,还要去调节黑线的方向。

FtC83mzk_SWWloHdIibnJFomMsu2.png

赶紧用本文讲的 PID 算法试试吧,你能挑战更快的速度么?

点击链接可以查看相关文章:
我对『PID算法』的理解 —— 原理介绍:http://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

FjD0X1v5verbDfwLF9ZIOwMvOwR6.gif
我的项目分享结束谢谢大家的收看!
喜欢我的文章可以在下方留言、点赞、和转发,谢谢

teu  高级技师

发表于 2020-6-9 20:49:20

回帖奖励 +10 创造力

重复的?
回复

使用道具 举报

teu  高级技师

发表于 2020-6-9 20:53:18


https://mc.dfrobot.com.cn/thread-305824-1-1.html
回复

使用道具 举报

DFHJM_IpFmV  中级技师
 楼主|

发表于 2020-6-10 10:19:54

teu 发表于 2020-6-9 20:53
https://mc.dfrobot.com.cn/thread-305824-1-1.html

昨天修改了一下,加了个演示视频
回复

使用道具 举报

DFHJM_IpFmV  中级技师
 楼主|

发表于 2020-6-10 10:27:23

昨天那个重复的被自动删除了
202006106694..png
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2020 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail