目录
- 车库元素一览
- 入车库
- 20cm摄像头拍下的车库图
- 起跑线的识别
- 入车库的拉线
- 出车库
车库元素一览
车库元素是今年赛道元素新增的元素。车库赛道元素是在起跑线旁设置的方形区域,它的尺寸如下图所示:
车模出发是从车库驶出,在运行一周之后在驶入车库内。比赛时间是从车模驶出车库和返回车库之间的时间差计算。因此计时线圈是放置在车库门口处。赛道上的斑马线和斑马线下的磁铁放置与往届规则相同。
车库应用于“基础四轮组”,“直立节能组”,“双车接力组”。
入车库
20cm摄像头拍下的车库图
起跑线的识别
因为时间紧迫,这里采用最简单的入车库(左入)斑马线识别方法:
- 找到左前边线的拐点
- 从下(拐点的y坐标)向上(拐点的y坐标+m),从左(拐点的x坐标)到右(中线位置),进行寻找由黑到白的转变边界像素点。
- 找到了第一个边界,以固定的y坐标(转变边界像素点的y坐标+n),从左到右,重新扫线。
- 找到第一个由白转黑的像素点和第一个由黑转白的像素点,记录距离(length),作为判断之后斑马线长度的依据
- 持续向右扫线,每找到一个疑似斑马线的像素块,就把边界像素的距离与length进行比较,在设定误差之内,判定斑马线。
- 记录斑马线数量,与判定起跑线的斑马线数量阈值比较。
代码:
byte Garage_Judge(byte i)
{
byte y = 0, x = 0, count = 0, pixel_cnt = 0, zebraWidth = 0;
//左边车库
if (i == 0)
{
for (OlRow = LeftLine_1.Agl_Row; OlRow <= LeftLine_1.Agl_Row + 20; OlRow++)
{
for (OlLine = (byte)(LeftLine_1.Agl_Line - 5); OlLine >= 93; OlLine--)
{
//SetText_1("Zerba Scan :"+ OlRow+" "+ OlLine);
if (J_Pixels[OlRow][OlLine] == white && J_Pixels[OlRow][OlLine - 1] == black)
{
y = (byte)(OlRow + 3);
//x = (byte)(OlLine-1);
SetText_1("Find Zebra Point: " + y + " " + OlLine);
break;
}
}
if (y != 0)
break;
}
if (y == 0)
{
SetText_1("Lost Zebra Point");
return 0;
}
for (x = LeftLine_1.Agl_Line; x >= 185 - LeftLine_1.Agl_Line; x--)
{
if (J_Pixels[y][x + 1] == white && J_Pixels[y][x] == black)
{
//SetText_1("Zebra Scan Start: " + y + " " + x);
pixel_cnt = 1;
}
else if (J_Pixels[y][x + 1] == black && J_Pixels[y][x] == black)
{
//SetText_1("Zebra Add: " + y + " " + x);
pixel_cnt++;
}
else if (J_Pixels[y][x + 1] == black && J_Pixels[y][x] == white)
{
if (count == 0 && pixel_cnt >= 3 && pixel_cnt <= 10)
{
zebraWidth = pixel_cnt;
SetText_1("zebraWidth = " + zebraWidth);
SetText_1("zebraStart Point: " + x + " " + y);
count = 1;
}
else if (count > 0 && my_fabs(pixel_cnt - zebraWidth) <= 4)
{
//SetText_1("Zebra End: " + y + " " + (x - 1));
count++;
}
else
{
// SetText_1("Zebra Error: " + y + " " + (x - 1));
}
}
}
SetText_1("Zebra Count = " + count);
if (count >= 6)
{
SetText_1("Proved to be Garage");
return 1;
}
else
{
SetText_1("Not Proved to be Garage");
return 0;
}
}
else //右边车库
{
for (OlRow = RightLine_1.Agl_Row; OlRow <= RightLine_1.Agl_Row + 20; OlRow++)
{
for (OlLine = (byte)(RightLine_1.Agl_Line + 5); OlLine <= 93; OlLine++)
{
//SetText_1("Zerba Scan :"+ OlRow+" "+ OlLine);
if (J_Pixels[OlRow][OlLine] == black && J_Pixels[OlRow][OlLine - 1] == white)
{
y = (byte)(OlRow + 3);
//x = (byte)(OlLine-1);
SetText_1("Find Zebra Point: " + y + " " + OlLine);
break;
}
}
if (y != 0)
break;
}
if (y == 0)
{
SetText_1("Lost Zebra Point");
return 0;
}
for (x = RightLine_1.Agl_Line; x <= 185 - RightLine_1.Agl_Line; x++)
{
if (J_Pixels[y][x - 1] == white && J_Pixels[y][x] == black)
{
//SetText_1("Zebra Scan Start: " + y + " " + x);
pixel_cnt = 1;
}
else if (J_Pixels[y][x - 1] == black && J_Pixels[y][x] == black)
{
//SetText_1("Zebra Add: " + y + " " + x);
pixel_cnt++;
}
else if (J_Pixels[y][x - 1] == black && J_Pixels[y][x] == white)
{
if (count == 0 && pixel_cnt >= 3 && pixel_cnt <= 10)
{
zebraWidth = pixel_cnt;
SetText_1("zebraWidth = " + zebraWidth);
SetText_1("zebraStart Point: " + x + " " + y);
count = 1;
}
else if (count > 0 && my_fabs(pixel_cnt - zebraWidth) <= 4)
{
//SetText_1("Zebra End: " + y + " " + (x - 1));
count++;
}
else
{
// SetText_1("Zebra Error: " + y + " " + (x - 1));
}
}
}
SetText_1("Zebra Count = " + count);
if (count >= 6)
{
SetText_1("Proved to be Garage");
return 1;
}
else
{
SetText_1("Not Proved to be Garage");
return 0;
}
}
}
由于入库图像与环岛入环图像较为相似,入车库判断函数的优先级需要高于入环判断函数。
实践证明,识别成功率很高。
入车库的拉线
拉线分为4个阶段:
- 阶段0:判断为车库
- 阶段1:左前拐点消失
- 阶段2:低行右边边线消失
- 阶段3:右边边线重新出现
- 阶段4:左边边线出现
出车库
目前最稳定的方法为直接控制出库。