六足机器人技术报告

  • 这是我们参加山东省高校第五届机器人大赛六足机器人比赛的技术报告,使用的是青岛德林科姆公司开发的商业机器人。这里不得不吐槽一下这玩意的问题真的好多,具体来说就是六足机器人的概念,从各种地方拼凑了一份代码。代码的每一部分看起来都有点道理,但是拼在一起就完全不对了(._.)在这里整理一下我们修改过的代码和比赛项目的实现思路。

机器人的总体设计

这个是班主任公司搞的机器人,具体优点就不多说了。单片机选用了我们比较熟悉的飞思卡尔公司的K60,机器人运动是由18路数字舵机组成的六条腿实现的,正常行走使用的是传统的三足步态,如下图: 三角步态
考虑到六足机器人的地形适应性比较强,所以我们还使用了一种多边形步态,如下图: 多边形步态
机器人配备了MPU6050(姿态传感器),红外传感器(用于避障),数字摄像头,超声波传感器(可以测距),机器人的大概样子如下: 机器人外观 上面那个盖子正前方的中间放有摄像头,红外,超声波传感器(在图片没拍到的那一端),这个摄像头的位置安排很不科学,等下在介绍比赛规则的时候再详细说。

比赛内容和实现思路

  • 这个比赛分为两项内容:自主循迹和创意表演.

自主循迹要求机器人按规定的路径自主循迹到达终点,轨迹如下图: 比赛场地 另一项是创意表演,表演内容自定,我们设计的是自主爬楼梯和悬崖预警两个项目。

自主循迹的工作流程

该部分包含摄像头图像处理部分和舵机控制部分,下面分别介绍:
#### 摄像头的图像处理
先说原来不合理的摄像头位置,原先的摄像头视角是水平的,和机器人运动的方向相同,这样子的话看到的赛道面积就非常小。我们按飞思卡尔比赛摄像头车的方案设计了一个摄像头支架,重新安装了摄像头。如图: 摄像头支架 这个系统使用了OmniVision公司生产的OV7725摄像头,该摄像头具有硬件二值化的功能,即图像采集完只有0xFF(白色)和0x00(黑色)两种情况。这种方式大大节省了单片机的资源,使其不需要分奇偶场来采集图像,同时二值化的图像简化了处理的算法,不需要计算灰度阈值来寻找边界。下面介绍该摄像头的配置:
该摄像头的配置接口为标准的SCCB(Serial Camera Control Bus),这种串行相机控制总线的实质和我们使用的IIC总线类似,有一根时钟线和一根数据线。OV7725寄存器的配置见附件代码。配置好摄像头后还需要对SCCB传回的数据进行转换,这里详细介绍: SCCB总线输出的数据为8bits,即八个像素点为一个数据。由于我们需要得知图像中每一个像素点的情况,需要将其解压,在这里提供一个解压图像的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void img_extract(u8 *data1,u8 *data2,u16 lenth)
{
u8 color[2] = {0,255};
u8 temp;
while(lenth--)
{
temp = *data1++;
*data2++ = color[(temp >> 7) & 0x01];
*data2++ = color[(temp >> 6) & 0x01];
*data2++ = color[(temp >> 5) & 0x01];
*data2++ = color[(temp >> 4) & 0x01];
*data2++ = color[(temp >> 3) & 0x01];
*data2++ = color[(temp >> 2) & 0x01];
*data2++ = color[(temp >> 1) & 0x01];
*data2++ = color[(temp >> 0) & 0x01];
}
}

该函数的传入data1数组,并将其解压赋值给data2数组,lenth由摄像头的分辨率决定。 得到摄像头采回的图像后即可对其进行处理。机器人的六足运动特性决定了它的运动速度,所以不需要对图像进行特别精细的分析。我们使用的图像分辨率为320*240,采用中间的一行即第120行进行赛道判断。我们认为第160个像素点为赛道中点,为白色。分别向左右两侧寻找第一个黑点,这两个黑点对应的就是两边的赛道边界。将两个黑色像素点的位置取平均,即可算出机器人所面向的位置和实际赛道方向的偏差Err。因为该机器人的转向角度是离散的,不需要复杂的PID控制。当其方向偏左,调用向右转向的动作组即可,方向偏右时同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
int imgProcess(void){
u32 i;
int err;
LEdge = 10;
REdge = 310;
for(i = 320*50+mid; i < 320*50+310 ;i++){
if(*(img+i) == 0x00){
REdge = i-320*50;
break;
}
}
for(i = 320*50+mid; i > 320*50+10 ;i--){
if(*(img+i) == 0x00){
LEdge = i-320*50;
break;
}
}
mid = (LEdge + REdge)/2;
err = (LEdge + REdge)/2 - 160;
return err;
}
if(imgErr < -22){
motionCtr(left);
}
if(imgErr > 22) {
motionCtr(right);
}
if(imgErr >= -22 && imgErr <= 22){
motionCtr(forward);
}

舵机控制方案

我个人觉得这里是这个商业六足机器人唯一的亮点就是对舵机的系统控制,大大简化了二次开发的流程。据说这套控制方案和上位机是我们专业的一个研究生学姐搞的,先膜一下。
这套方案的亮点在于一个AT24C1024芯片,相当于给单片机连上了一块硬盘。在对三足步态或者多边形步态进行调试的时候,需要确定每个舵机的位置,若是将每个舵机动作的位置按顺序写入单片机程序中肯定会写上好几千行,而且进行修改的时候非常麻烦。通过公司提供的可视化上位机我们可以将动作组写好,并以XML文档的形式存入电脑,完成后通过串口和单片机通信,按一定的规则存入EEPROM。这样当需要调用动作组前进,左转或者右转的时候只需要按规则读取EEPROM中的舵机信息并循环调用舵机控制程序即可。
我们重写了舵机控制的函数,如下:

1
2
3
4
5
6
7
8
9
void motionGet(u8 flag,u8 *servo_dat){
system_run_mark=1;
servo_control_mark=1;
servo_SQ=flag;
servo_group_num[servo_SQ]=ReadAT24C1024_byte(servo_SQ*5000,0x00);
if(servo_group_num[servo_SQ]!=0){ ReadAT24C1024_flash(servo_dat,servo_SQ*5000+1,0x00,servo_group_num[servo_SQ]*160);
}
servo_run_over_mark=1;
}

motionGet函数传入的是动作组调用标志,0为直走,1为左转,2为右转。servo_dat数组为舵机动作的详细参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void motionCtr(u8 *servo_dat){
j=0;
while(j<servo_group_num[servo_SQ]){
if(servo_run_over_mark)
{
for(i=0;i<32;i++){
servo_run_over[i]=0;
}
for(i=0;i<32;i++){
if((servo_dat[ 160*j + 5*i ]-1)==i){
servo_num_set[i]=servo_dat[ 160*j + 5*i ];
servo_position_set[i]=(servo_dat[ 160*j + 5*i + 1]*256 + servo_dat[ 160*j + 5*i + 2 ])/5;
servo_time_set[i]=(servo_dat[ 160*j + 5*i + 3 ]*256 + servo_dat[ 160*j + 5*i + 4])/100;
}
}
SERVO_control(servo_num_set,servo_position_set,servo_time_set);
j++;
}
servo_run_over_mark=1;
SERVO_run_all();
for(i=0;i<32;i++){
servo_run_over_mark=servo_run_over_mark&&servo_run_over[i];
}
}
}

这个函数包含了原程序提供的舵机数据转换和舵机控制代码,主要作用是用来控制舵机动作。
值得注意的是,如果我们在机器人向前不断走动的时候调用读取EEPROM的程序会导致机器人卡顿。原因是从EEPROM读的数据量较大,且数据传输速率有限。解决方案是在机器人开机后先读取EEPROM中的动作组,并将其存入单片机的数据段,这样就相当于在计算机中将数据从硬盘载入内存,提高了机器人运行的效率。
主函数中读取转存的关键代码如下所示:

1
2
3
4
5
6
7
u8 left[5000];
u8 right[5000];
u8 forward[5000];
system_init();
motionGet(0,forward);
motionGet(1,left);
motionGet(2,right);

创意表演的实现

机器人爬台阶

若前方出现障碍,机器人前方安装的水平红外传感器会输出低电平触发外部中断。同时使用机器人上方伸出并与水平面垂直的红外传感器进行障碍高度判定。该传感器的高度已事先根据机器人最大越障高度调整好,若其输出低电平,说明该障碍的高度已超出机器人越障极限,触发中断并放弃翻越。 流程如下图所示: 流程图
这种逻辑确保了机器人不会撞上目标障碍,同时若是障碍过高无法翻越,机器人不会尝试翻越以避免翻倒。 #### 悬崖预警 我们基于红外线传感器给出了一套悬崖预警解决方案。在机身下有一个红外线传感器用以实时监测六足机器人机身与前方地面的距离是否安全。若出现前方判断与地面距离突然变大的情况即认为出现悬崖。红外传感器输出低电平触发外部中断,立即停止机器人所有动作以避免出现危险。

小小的总结

在六足机器人的代码的修改中遇到了各种麻烦的问题。虽说是商业机器人,但是提供的开发程序例程问题相当大,内部有各种冲突的问题。我印象最深的是它配置摄像头的分辨率为320*240,但是寄存器却是按照80*60来配置。我Google了好多资料才找出问题所在。没有给出一份摄像头可用且舵机可操作的代码,我们自己合了一份,也是各种冲突不断,浪费了很多时间。就算是锻炼了改错能力了吧233