巡线方法
1.迷宫巡线法
按照上海交通大学的方法,通过使用迷宫巡线(种子生长法)在巡线的过程中进行图象的自适应阈值,这样可极大的减少计算量。具体思想参考上交文章,以下内容为对代码(以左手迷宫巡线为例)的解析。
-
首先我们将**灰度图(时刻谨记)**作为输入,根据经验取图像下方中间偏左一点作为起始点(注意该点要在白色的赛道上),我们依次向左取像素点,直到像素点的值超过某一值(代码中thres=140),则认为该点已经靠近赛道。
// 原图找左右边线 int x1 = img_raw.width / 2 - begin_x, y1 = begin_y; ipts0_num = sizeof(ipts0) / sizeof(ipts0[0]); for (; x1 > 0; x1--) if (AT_IMAGE(&img_raw, x1 - 1, y1) < thres) // 寻找边线起始点 break; if (AT_IMAGE(&img_raw, x1, y1) >= thres) findline_lefthand_adaptive(&img_raw, block_size, clip_value, x1, y1, ipts0, &ipts0_num); else ipts0_num = 0; // 如果没有找到像素点大于thres(140)的,则会将ipts_num置为0.
-
参考上交文档,“小明”要一直左手扶墙(勿忘)且脚踩白色像素点,所以“小明“面临三种情况,同时要确保左手扶墙”小明“在三种情况下,分别有三种确定对策:
- 前进方向像素为黑——向右转。
- 前进方向像素为白,且左前方像素为白(墙角)——斜着走(x,y值均改变),且向左转。
- 前进方向像素为白,左前方像素为黑——向前走。
-
代码及参数解释:
参数解释:
image_t * img
:存放图像相关数据的指针。block_size
:自适应阈值的范围。clip_value
:经验值,防止出现强制分割的现象(一般为2~5)。x
:”小明“出生位置x坐标。y
:"小明"出生位置y坐标。int pts[ ][2]
用来存放边线数据。int *num
:存放边线像素点的个数。
代码:
/* 前进方向定义: * 0 * 3 1 * 2 */ AT_DTCM_SECTION_ALIGN_INIT(const int dir_front[4][2], 8) = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}}; AT_DTCM_SECTION_ALIGN_INIT(const int dir_frontleft[4][2], 8) = {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}; // 左手迷宫巡线 AT_ITCM_SECTION_INIT(void findline_lefthand_adaptive(image_t *img, int block_size, int clip_value, int x, int y, int pts[][2], int *num)) { assert(img && img->data); // 不满足则退出执行 assert(num && *num >= 0); assert(block_size > 1 && block_size % 2 == 1); int half = block_size / 2; int step = 0, dir = 0, turn = 0; // step表示前进的步数;dir通过改变索引改变当前小人朝向的方向 while (step < *num && half < x && x < img->width - half - 1 && half < y && y < img->height - half - 1 && turn < 4) { int local_thres = 0; for (int dy = -half; dy <= half; dy++) // for循环用来计算block区域的像素值之和 { for (int dx = -half; dx <= half; dx++) { local_thres += AT(img, x + dx, y + dy); } } local_thres /= block_size * block_size; local_thres -= clip_value; // (x,y)点block区域内的阈值 int current_value = AT(img, x, y); int front_value = AT(img, x + dir_front[dir][0], y + dir_front[dir][1]); int frontleft_value = AT(img, x + dir_frontleft[dir][0], y + dir_frontleft[dir][1]); if (front_value < local_thres) // 前进方向像素为黑色 { dir = (dir + 1) % 4; // 遇到前方为黑色需要右转一次 turn++; } else if (frontleft_value < local_thres) // 前方像素为白色,且左前方像素为黑色 { x += dir_front[dir][0]; y += dir_front[dir][1]; pts[step][0] = x; // 用来存放边线坐标信息 pts[step][1] = y; step++; turn = 0; } else // 前方为白色,左前方为白色(墙角) { x += dir_frontleft[dir][0]; // 遇到墙角要斜着走 y += dir_frontleft[dir][1]; dir = (dir + 3) % 4; // 遇到墙角要左转一次 pts[step][0] = x; pts[step][1] = y; step++; turn = 0; } } *num = step; }
代码说明:
-
dir:用来控制”小明”转向,如
dir = (dir + 1) % 4
,dir
对应的索引会向下移动一位(向右转一次) -
dir_front
:存放前进步伐,dir
会索引到,当前面向的前方。 -
dir_frontleft
:存放左前进的步伐,dir
会索引到当前面向的左前方。(见图) -
代码中存在一些宏定义的名称,如
AT(img, x + dx, y + dy)
为求对应像素点的像素值,这些定义需要在全部代码中查找。