循迹小车智能搬运:调车篇
前言
本人最近在准备教育机器人智能搬运项目的比赛。比赛要求制作3台循迹小车,以一定的算法来完成比赛的相关要求。在调车的时候出现了许许多多的问题,在网上不一定能够找到答案,很多时候需要丰富的经验解决。这里将梳理一系列的内容,希望可以帮助正在学习的大家。
一、调车的原则
所谓的调车,是指修改车子的代码中的某些参数,使得车子趋于理想状态运行。而调车有一项很重要的原则,那就是保证硬件的稳定性。
我们做的3台小车分别继承于3个学长。这也就意味着3台车的结构都有些许不同。在最初的时候我们忽略了硬件的因素,却发现怎么调整程序,小车都不按照我们的意愿来执行任务。
更可怕的是,3个学长在装传感器时没有做好绝缘工作,由于车身是铁件,所以当传感器的信号引脚与车身接触时该传感器会短路,不会返回正确的信号。而如果是传感器的接地端碰到车身金属件,整个板子就会短路。这导致我一度认为是板子的芯片烧了。而这块板子的供电芯片发热严重,我甚至给他换了一个新的供电芯片。而万万没想到的是,导致这一系列问题仅仅是因为传感器的短路。
所以说,在一切的软件调试之前,我们应该保证硬件的稳定性。螺丝拧紧没有?(固定铁件、零部件如果可以上四个角的螺丝就一定不要只上对角线的两个螺丝)传感器做好绝缘没有?重心调整好没有?这些问题看着很小,但是却深深地影响了整个系统的稳定性。
二、调车步骤
1.走直线
循迹小车的灵魂就在于循迹。客观上来说,如果赋予小车生命,那他应该是一个二维生物。小车能不能走直线和三个因素有关:
- 传感器的位置
- 两个电机的转速
- 修正函数修正的好坏
1、传感器的位置
传感器的布局决定了我们循迹的策略。这里直接放上我的布局:
中间的灰色粗线我们假装是地图上的黑线。上面两个传感器一直处于黑色线范围内,内咬合着黑色线(一直有信号);而下面两个传感器一直处于黑色线的两边,外咬合着黑色线(一直处于无信号状态)。
我们需要保持上面两个传感器能尽可能地内咬合黑色线。当然,并不需要刚好咬住,我们也应该留有一点余地。
在这种状态下是相对稳定的。只要车子稍微偏一点点,我们就会使用修正函数进行修正。
2、电机的转速
电机一般是用电机驱动板来进行驱动,我们也可以使用360°的舵机。我使用的就是360°的舵机。通过pwm来调制他的脉宽控制舵机的转速。
代码如下(示例):
#define forward SET_MOVE(1600,1395)
#define leftward SET_MOVE(1425,1425)
#define rightward SET_MOVE(1575,1575)
#define leftfix SET_MOVE(1560,1400)
#define rightfix SET_MOVE(1600,1440)
#define stopmove SET_MOVE(1500,1495)
#define backward SET_MOVE(1400,1600)
可以看到我通过SET_MOVE()函数来控制舵机的pwm。由于每个舵机的中值不一样,1500的值可能并不能让舵机停止转动,这个时候我们就要观察舵机是否有轻微的抖动。如果是 顺时针转动意味着 真实的中值<1500;反之则 真实的中值>1500。
同样的,当我们确定好舵机的中值后,舵机应该是不会转动的,而是稳稳地停着。所以我们也应该测量前进时的pwm值,使得两个舵机不会有很明显的差速。这样的话可以为我们的修正函数帮到不少忙。
3、修正函数的好坏
修正函数,贯穿着整个项目工程。在车子往左偏的时候,我们给予右修正;在车子往右偏的时候,我们给予左修正。下面放代码:
void forward_t(){
switch((GET_QTI() & qti_mid)){
case qti_emp: forward; break;
case qti_midleft: leftfix; break;
case qti_midright: rightfix;break;
default :forward; break;
}
}
这是一个带修正的直走功能的函数。可以理解为是循迹向前走。不需要理会其中的细节,我们将关注点放在case执行的动作。其中forwar是向前走,leftfix是左修正,rightfix是右修正。
当车子没有偏的时候,我们就让他正常直走。这里就可以看出第二波调整电机速度的重要性了。如果电机调得好,那我们走的也直,就不需要怎么修正了。
当车子左右偏时,我们会让车子leftfix或者righfix。我们来看这两个动作的代码:
#define leftfix SET_MOVE(1560,1400)
#define rightfix SET_MOVE(1600,1440)
先看leftfix:如果车子往右偏了,那我肯定想让车子往左走。所以leftfix本质上就是让车子慢慢往左边走。那么我们就需要让右边的轮子转快一点,左边的轮子转慢一点。
但是,如果右边轮子转的太快,左边轮子太慢,这就会让车子直接来了个急转弯,一下子就打向左边。
而如果右边轮子太慢,左边轮子也慢,虽然最终确实是往左慢慢走,但是这个过程实在是太慢了,车子可能会因为要执行其他的一些任务而被这个过程产生的误差影响。
所以我们就需要多次实验观察现象。如果车子在修正时不停地抖动那么就代表数值过大。如果修正的时间过长则代表数值过小。
我的经验是先让需要转向的那个方向的轮子保持原来走直线的pwm,加快另一个轮子的转速。然后再进行微调。调好之后的车子应该是能很快修正回正确的方向并且几乎没有抖动修正的情况。
2.左右转90°
由于我这次比赛的地图很特殊,是一个网格状的地图,所以我需要将地图抽象成坐标系,而且需要让小车在其中自由稳健地左右转向90°。
而左右转向很简单,只需要让一个舵机正转,另一个舵机反转就好了。
#define leftward SET_MOVE(1425,1425)
#define rightward SET_MOVE(1575,1575)
我们一般将两个值设为相同。而另一个方向的值,只需要求一下当前方向和中值的绝对值,再±绝对值就好了。
比如代码中左转是(1425,1425),我为了右转速度和他一样。所以求|1425-1500|=75.所以右转的速度是1500+75=1575。所以右转就为(1575,1575)。
我们把左右转速度设置好之后,还不能左右转特定的角度。这里我就用到了延时的方法:
void leftward_90()
{
leftward;
delay_ms(680);
}
这个函数很简单,我想应该就不用细说了吧?唯一需要关注的点就是delay函数,这个需要我们自己测量延时的毫秒值。
3.在十字路口停下
由于我需要将地图抽象为坐标点,那么每个黑线的交叉十字路口就可以看成是一个坐标。当我走到一个十字路口时我希望能够让单片机知道自己已经到了一个坐标点。而为了让我知道他知道了,我需要他给我发送一个信号。
由于到指定坐标点之后车子会停下或者执行其他任务。干脆就将这个信号设置为停下。所以我们接下来要做的就是让车子稳稳地停在十字路口中间。
while(!QTI_Touch())
{
forward_t();
}
delay_ms(550);
stopmove;
这里需要说明一下QTI_Touch()是我单独加在小车最前端的两个传感器的值。就好像昆虫的触角一样,所以叫touch。当他们没有信号时,代表触角还没有碰到交叉线,所以我们会一直以循迹的方式向前进行。当有了值之后会跳出while循环,延时了若干时间(我自己测的是550,因人而异),车子在延时期间保持着向前运动,在这段时间后,车子才会停下来。而理想状态下车子的中心应该是刚刚好对应了十字交叉的中心。
当然,这就需要我们去调整delay的值了。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了调车的一些基本操作,而这些很基本很简单的操作却充满了学问。在整个代码的运行中充满了许多的不确定性,只有我们将最基本的东西做到最稳定,才能保证整个系统的稳定性。在这次的学习中有许多问题是百度上找不到或者很难找到的。将其整理了之后方便以后的复习同时也希望可以给那些正在苦逼调车的兄弟们一些经验,希望能够对你有所帮助。