循线视觉
通过上一篇文章的学习,我们掌握了色块追踪的基本方法,进而可以尝试将方法投入实际应用,如循线视觉,原例的解释就非常不错,因此这里引用原例代码稍作补充:
import sensor
import time
import math
# 追踪黑线阈值,[(128, 255)] 可以追踪白线,若要减少干扰可以利用阈值编辑器进一步缩小区间
GRAYSCALE_THRESHOLD = [(0, 64)]
# 利用算法找到每个ROI内最大斑块的质心。随后,这些质心的x位置以不同权重进行平均计算,其中最大的权重分配给靠近图像底部的ROI(距离摄像头近),次之的权重给下一个ROI,依此类推。
ROIS = [ # [ROI, 权重]
(0, 100, 160, 20, 0.7), # 需要根据应用调整这些权重。
(0, 50, 160, 20, 0.3), # 根据机器人设置方式。
(0, 0, 160, 20, 0.1),
]
# 计算权重除数(权重总和不是一定要等于1,这里是一种比例关系)。
weight_sum = 0
for r in ROIS:
weight_sum += r[4] # r[4]代表ROI权重。
# 摄像头配置中...
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQVGA) # 采用QQVGA分辨率以提升速度。
sensor.skip_frames(time=2000) # 让新设置生效。
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock = time.clock() # 跟踪FPS。
while True:
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # 拍照并返回图像。
centroid_sum = 0
for r in ROIS:
blobs = img.find_blobs(
GRAYSCALE_THRESHOLD, roi=r[0:4], merge=True
) # r[0:4]为ROI区域元组。
if blobs: # 防止屏幕中有roi无满足条件色块,导致largest_blob空值的情况
# 寻找像素最多的色块。
largest_blob = max(blobs, key=lambda b: b.pixels()) #lambda表达式
# 在色块周围绘制矩形框。
img.draw_rectangle(largest_blob.rect())
img.draw_cross(largest_blob.cx(), largest_blob.cy())
centroid_sum += largest_blob.cx() * r[4] # r[4]代表ROI权重。
center_pos = centroid_sum / weight_sum # 确定线的中心。
# 将center_pos转换为偏转角度。我们使用非线性
# 操作,使得响应在我们偏离线越远时
# 越强。非线性操作非常适合用于此类算法的输出
# 以引发“触发”响应。
deflection_angle = 0
c
# 80来自X分辨率的一半,60来自Y分辨率的一半。
# 下面的方程只是计算一个三角形的角度,其中
# 三角形的对边是中心位置与中心的偏差,
# 邻边是Y分辨率的一半。这将角度输出限制在
# 大约-45到45之间。(并不完全是-45和45)。
deflection_angle = -math.atan((center_pos - 80) / 60)
# 将弧度转换为角度。
deflection_angle = math.degrees(deflection_angle)
# 现在你有了一个角度,告诉你需要转动机器人多少度
# 包含离机器人最近的线部分以及离机器人较远的
# 线部分,以获得更好的预测。
print("Turn Angle: %f" % deflection_angle)
print(clock.fps())
如果想要看到目前循线中心可以在centroid_pos = centroid_sum / weight_sum下添加,img.draw_cross(int(centroid_pos),60)
知识补充 :
可以通过这位的博客了解一下lambda表达式:
详细解释:
代码首先划定了三个roi区域并且设置了权重,其大概分布如下:
对于每个设置权重的roi得到最大色块位置后,求出偏转中心,计算偏转角度。
值得注意的是,增加ROIS的个数可以使得循线的判断更加细致精确。