避障比赛赛后复盘总结
避障比赛赛后复盘总结
在见到避障比赛的文件之后,对比赛算法的设计并没有我想象中那么困难:机器人直行——机器人识别到箭头——机器人向箭头指向的方向平移——机器人视野脱离箭头——机器人直行,但是直觉与经验都在告诉我这个比赛最困难的地方就是在于怎么样才能去识别到三个箭头的方向。
首先我通过阅读材料以及代码,很容易便对颜色识别代码进行了改写,通过改写ColorDetect.py中113行—133行,实现了识别到红色和蓝色向左平移,识别到黄色向右平移。如未识别到颜色则直行。(红色字体为添加代码,下同)
while True:
if debug:
return
if __isRunning:
if detect_color != 'None':
AGC.runActionGroup('go_forward_fast')
action_finish = False
if detect_color == 'red':
AGC.runActionGroup('left_move')
detect_color = 'None'
draw_color = range_rgb["black"]
time.sleep(1)
elif detect_color == 'yellow':
AGC.runActionGroup('right_move')
detect_color = 'None'
draw_color = range_rgb["black"]
time.sleep(1)
elif detect_color == 'blue':
AGC.runActionGroup('left_move')
detect_color = 'None'
draw_color = range_rgb["black"]
time.sleep(1)
else:
time.sleep(0.01)
action_finish = True
detect_color = 'None'
else:
time.sleep(0.01)
else:
time.sleep(0.01)
在实际测试中发现可能由于机器人腿部舵机或是场地摩擦力问题导致机器人并不能完全的达到平移效果,经过调整,将“平移”动作后加入一个“转体”动作即可修正平移产生的过度偏差。后续的工作便是在箭头识别设计成功之后,将颜色识别替换为箭头识别即可。(蓝色字体为调整代码,下同)
if detect_color == 'red':
AGC.runActionGroup('left_move')
AGC.runActionGroup('turn_left_small_step')
detect_color = 'None'
draw_color = range_rgb["black"]
time.sleep(1)
elif detect_color == 'yellow':
AGC.runActionGroup('right_move')
AGC.runActionGroup('turn_right_small_step')
detect_color = 'None'
draw_color = range_rgb["black"]
time.sleep(1)
然后是对于箭头的识别的设计,我初步的思路是将标准箭头进行对半拆分,拆分成一个包含三角形的部分和只有矩形的部分,由于包含三角形的面积总是大于只有矩形的面积。因此可以把判断条件更改为“左右部分面积谁大”,即如果左半部分的面积更大则向左移动,右半部分的面积更大则向右移动。考虑到可能出现的图像处理问题,初步选择在lab空间中对图像进行处理。(绿色字体为障碍代码,下同)
for i in lab_data:
if i != 'black' and i != 'white':
frame_mask = cv2.inRange(frame_lab,
(lab_data[i]['min'][0],
lab_data[i]['min'][1],
lab_data[i]['min'][2]),
(lab_data[i]['max'][0],
lab_data[i]['max'][1],
lab_data[i]['max'][2])) #对原图像和掩模进行位运算
eroded=cv2.erode(frame_mask,cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))) #腐蚀
dilated=cv2.dilate(eroded,cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))) #膨胀
if debug:
cv2.imshow(i, dilated)
contours=cv2.findContours(dilated,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)[-2] #找出轮廓
areaMaxContour, area_max = getAreaMaxContour(contours) #找出最大轮廓
if areaMaxContour is not None:
if area_max > max_area:#找最大面积
max_area = area_max
color_area_max = i
areaMaxContour_max = areaMaxContour
if max_area > 200: # 有找到最大面积
((centerX, centerY), radius) = cv2.minEnclosingCircle(areaMaxContour_max) # 获取最小外接圆
centerX = int(Misc.map(centerX, 0, size[0], 0, img_w))
centerY = int(Misc.map(centerY, 0, size[1], 0, img_h))
radius = int(Misc.map(radius, 0, size[0], 0, img_w))
根据预设代码的研究发现,如果直接对图像进行分割,它可能会导致对最大图像的识别的误差,即在切割之后的图像可能不是识别到的最大的轮廓,如果在收集并处理过图像之后,对图像的腐蚀膨胀操作可能无法使图像正常识别,猜测因为图像经腐蚀膨胀后两边图像的差异化不够明显。故方案作废。
而后我通过在幻尔科技的官方网站上进行查阅资料,发现在介绍中有着“标签识别”的功能。于是我预想通过重定义标签或是对预设标签进行替换,即将两边箭头的图像编辑成“标签”,通过对新“标签”的识别做出反应,从而达到方向识别的效果。
detector = apriltag.Detector(searchpath=apriltag._get_demo_searchpath())
def apriltagDetect(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
detections = detector.detect(gray, return_image=False)
if len(detections) != 0:
for detection in detections:
corners = np.rint(detection.corners) # 获取四个角点
cv2.drawContours(img, [np.array(corners, np.int)], -1, (0, 255, 255), 2)
tag_family = str(detection.tag_family, encoding='utf-8') # 获取tag_family
tag_id = int(detection.tag_id) # 获取tag_id
object_center_x, object_center_y = int(detection.center[0]), int(detection.center[1]) # 中心点
tag_family, tag_id = apriltagDetect(img) # apriltag检测
if tag_id is not None:
cv2.putText(img, "tag_id: " + str(tag_id), (10, img.shape[0] - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.65, [0, 255, 255], 2)
cv2.putText(img, "tag_family: " + tag_family, (10, img.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65, [0, 255, 255], 2)
else:
cv2.putText(img, "tag_id: None", (10, img.shape[0] - 30), cv2.FONT_HERSHEY_SIMPLEX, 0.65, [0, 255, 255], 2)
cv2.putText(img, "tag_family: None", (10, img.shape[0] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65, [0, 255, 255], 2)
经过对代码和机器人内部文件的查阅,我发现机器人的标签识别功能是建立在四个脚点的定位上,不具备对箭头等不规则图形的识别功能,同时机器人内部不存在标签相关的图像文件,难以对标签识别的代码与文件进行修改。故方案作废。
然后我看到了面部识别的功能介绍和代码,我计划通过修改面部识别的文件,将“箭头”作为“面部”进行识别,但经对面部识别代码以及机器人内部文件的研究,发现机器人的面部识别功能是基于.d6a模型建立起来的。无法针对已经完善的模型文件进行修改,剩余的时间也不足以训练一个新的模型,故方案作废。
至此,无论是剩余的时间还是任务分配都不允许我去设计其他新的思路去比赛,最终迫不得已基于思路一穷举出了可能的箭头情况并在比赛中分别调用,尽管我们及时发现并修补了机器人行动程序上的缺陷,但场地摩擦力不均带来的行动困难、机器人腿部舵机问题引起的不可控因素等场外条件将比赛算法不成熟引起的问题放大到了极致。因此很遗憾没有取得一个令人满意的成绩。