智能车学习日记【一】——让小车跑正方形赛道
- 目录
-
- 开篇
- 舵机
- 赛道图像处理
- 图像处理
- 代码![在这里插入图片描述](https://img-blog.csdnimg.cn/9ec0eb76bd8941a380e4b34b443c0809.png#pic_center)
- 图像处理控制舵机打角
- 最后亿点
目录 开篇
博主最近开始学智能车,开始时遇到了很多困难。我觉得这些问题应该也
是每一个智能车人开始都可能会遇到的,写下来分享一下,也顺便巩固一下自
身所学。如果有错误或可以改进的地方还希望多多指出交流~。
这是博主的第一篇文章,如果觉得有帮助还希望点赞收藏支持一下~
舵机
首先要说说舵机,舵机是让小车车跑好的一大重要因素,网上有很多有关不
同种类舵机的详细原理和使用。这里我就简单说说
- 舵机的作用是控制智能车轮子转向。通过输入不同的脉冲信号来使舵机有不同大小的打角,比如你输入的pwm等于750时舵机打角位于中间,则小车直走,大于750时舵机打角偏向右边,则小车左转。
- 通过处理图像中线位置来给舵机输入对应脉冲信号来控制打角。先对你摄像头拍到的图像进行图像处理(摄像头原理在这不过多解释,后面会大致讲一下简单的图像处理)。比如你的屏幕中线的x坐标为50,而你图像处理后得到的中线x坐标为70,说明你的车偏右了(这里要注意我的坐标系零点是屏幕右下角),要让车左走回正,这时你就要输入对应的脉冲信号,让舵机控制车子向左转。
- 在写对舵机的控制前请一定要让舵机摆正!先写固定的脉冲信号让舵机打一个固定角度,然后轻推小车,小车能沿直线走,就说明摆正了,再进行之后的调试。
图像处理
智能车(摄像头)的图像处理主要就是识别出左右边线(一般边线是黑线,外边
是蓝布,中间赛道是白色),然后拟合出中线,让车子沿着中线跑。
这里我推荐新手使用左右寻线法来拟合中线,至于为什么不用八邻域法我只能说,新手阶段不需要贪那运算速度,左右寻线法写起来容易理解,能跑完赛道最重要,优化算法是之后的事情了。
代码
文章图片
middle是存放中线x坐标的数组
left是存放左边线x坐标的数组
right是存放右边线x坐标的数组
Pixels是像素坐标数组,由摄像头读出并处理生成
pixels为1是白,为0是黑,屏幕x坐标是从0到185
这里放一个我判断左边界的部分代码,首先第0行是从中间开始往左右扫线,比如我这个第0行应该从x=93开始左右扫线 (那我这里为什么是从middle[y-1]开始呢,因为我这是从第1行开始的,我第一行中间起始点是用第0行的中线,第二行中间起始点是用第一行的中线坐标,以此类推) 。以找左边线为例子,右边线类似。从中间起始点开始向左边扫描,如果扫描到白黑黑的部分,则认定为找到左边线,将左边线记录下来。右边线也是找到白黑黑的组合,记录下左右边线。中线就是左右边线之和除2。
至于我为什么还有个x==184的条件,其实这是用在弯道处,
文章图片
这是我将弯道图片上传到上位机后得到的图像,左边是原图,右边是图像处理以后的。可以发现有时左边一直到屏幕边界都还是白色,所以我让左扫描到最左边时,将x=184作为了左边界(其实还有更好的处理的方式,但这里只是介绍给新手们最基本的)。右边同理有个x == 0的边界判断。
小贴士
- 第一帧图像的第0行中间起始点可以选屏幕中间点,因为发车都是直道发车,一般不存在第0行中间像素点为黑的情况。之后每一行中间起始点都是前一行的中点(这样可以防止拐弯时中线丢失)。此后第二帧开始,第0行的中间起始点就选择上一帧的第2行的中点(选第几行按照自己情况而定),这样做其实也是在防止拐弯时中线丢失(中线丢失是什么情况还是希望大家可以自己去尝试一下,有时会发现拐弯到一半车子突然直行,只有自己经历过才能更好理解这些方法)
- 做好赛道类型判断。通过你处理后得到的中线趋势来判断现在是直道还是弯道
// 赛道类型判断
int i;
float sum1 = 0;
float sum2 = 0;
for(i=0 ;
i<20 ;
i++)
{
sum2 += (94-middle[i])*(94-middle[i]);
sum1 += (94-middle[i]);
}if(sqrt(sum2)/20 > 3.2 || sum1/20 > 18.5 || sum1 < -18.5)
{
choose = 1;
//弯道
}else
{
choose = 0;
//直道
}
我这里通过图像中线和屏幕中线的方差和平均值来判断赛道类型,这么做有什么好处呢?有时你会发现,走直道时车一直在左右摇摆,就有可能你没有分类赛道,直道和弯道用的同一个PID算法,这会导致直道有点歪时小车想打角来走直线,却用的是弯道的打角,导致打角太大了,就会一直左右摇摆。以后写什么小弯道啊,环岛的也是要判断的。
下面是我直道的PD算法,直道的PD给的就很小,因为就是微调,弯道的PD要给大一点(要问为什么不写i,因为我之前忘记写了…)
float kp1 = 0.2;
float kd1 = 0.4;
void zhidao()
{float cha=0;
cha = 94 - middle[17];
//与中线差值
duty = (int)(780 - kp1*cha - kd1*(cha - last));
if(duty>840)
{
duty = 840;
}
if(duty<700)
{
duty = 700;
}
//systick_delay_ms(STM0,20);
pwm_duty(ATOM0_CH6_P02_6,duty);
pwm_duty(ATOM1_CH4_P22_3,duty_car_z);
pwm_duty(ATOM1_CH0_P22_1,0);
pwm_duty(ATOM1_CH6_P23_1,0);
pwm_duty(ATOM2_CH5_P33_13,duty_car_z);
}
图像处理控制舵机打角 其实控制舵机打角也是上边的代码
文章图片
上位机直道图
float cha=0;
cha = 94 - middle[17];
//与中线差值
duty = (int)(780 - kp1*cha - kd1*(cha - last));
...
...
pwm_duty(ATOM0_CH6_P02_6,duty);
这里的duty就是我输出给舵机的脉冲信号,来控制小车在直道时的打角
cha是屏幕中线和第17行中线的差值,用于下面的计算
last是上一次的cha值
这个第17行是根据你摄像头高度来选择的,有很多种方式取这个值
最后亿点
小车最开始调试时,可以给直道和弯道不同的PWM,弯道可以慢速点过,保证平滑,
直道再加速。这里面有很多门道的,博主也在慢慢摸索。
新手想要车能跑正方形赛道,最难的应该是图像处理部分了,这部分我其实主要在
讲一些问题,原理性的可能不太详细,因为这是第一次写博客,很多想法很难表达
出来,大家可以先收藏,然后看看网上左右寻线详细原理,等到写上车后发现问题,
再来看应该会更多体会与帮助。
我也是刚刚开始的智能车小白,希望大家能一起互帮互助,一起进步!如果觉得有帮助,还希望点赞收藏多多支持一下~
最后有一句话分享给大家和我自己:如果你的车没有撞过,就说明速度还不够快!加油智能车人!
推荐阅读
- C|梦回童年——基于C语言实现三子棋小游戏
- 进阶C语言|详解字符函数和字符串函数
- 数据结构(C语言实现)|顺序表C语言版
- 数据结构与算法|C语言 创建单链表
- 数据结构|C语言单链表定义及各类操作
- C语言基础|【C语言趣味游戏】猜数字
- c语言|C语言——指针初识(三)(指针和指针数组,一级指针,二级指针详解)
- 指针|C语言中的数组指针和指针数组的区别,代码+图例详解
- #|VSCode 搭建 STM32 开发环境