今日学习K210视觉识别模块: 三ROI区域识别最大色块及中点
固件库: canmv_yahboom_v2.1.1.bin
本文只简单编写记录一下如何编程使用3个ROI区域识别最大黑色块及其中点,可用于巡线
文章提供测试代码讲解、完整代码贴出、测试效果图
目录
代码展示:
这个代码展示了先将摄像头转化为灰度模式,当然也可注释掉while True 循环之前打开灰度的语句,只是灰度模式更适合寻迹黑线
摄像头采集像素设置为 320*240
设置了三个ROI区域检测黑色色块,它们X轴边长都是(0~320)全范围,就Y轴起点不同
分别为(60,120,180),然后它们框的区域都是16个像素的Y轴区域
其中每个识别到的中点都能输出(但语句被注释)
目前如果你也是亚博智能的K210并且固件库与我一致的话,这个程序可以直接运行的,不需要更改,但我建议接着往下看,复制使用文章靠后部分改进后的程序
import sensor, image, time, lcd
from machine import UART
from fpioa_manager import fm
# 初始化摄像头和串口
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.HQVGA)
sensor.skip_frames(time=2000)
sensor.set_vflip(0)#设置摄像头上下翻转模式
#lcd.init(freq=15000000)
sensor.set_pixformat(sensor.GRAYSCALE) #灰度模式(方便循黑线),可以注释掉方便以后用来循其余颜色的线..
sensor.set_framesize(sensor.QVGA) # 设置图像帧大小为320x240
fm.register(6, fm.fpioa.UART1_RX, force=True)
fm.register(7, fm.fpioa.UART1_TX, force=True)
uart = UART(UART.UART1, 115200, read_buf_len=4096)
# 定义阈值和颜色范围
thresholds_1 = [(0, 38, -60, 111, -76, 97)] #黑色
roi1 = [0, 60, 320, 16] # 巡线敏感区域1
roi2 = [0, 120, 320, 16] # 巡线敏感区域2
roi3 = [0, 180, 320, 16] # 巡线敏感区域3
while True:
img = sensor.snapshot()#使用摄像机拍摄一张图片并返回给img后续进行操作
lcd.display(img) # 将图像用LCD显示
# 查找所有黑色色块 area_threshold是过滤小的,自己可以改 pixels_threshold像素的最小数量
#blobs_1 = img.find_blobs(thresholds_1, pixels_threshold=100, area_threshold=100)
# 在roi1区域内寻找绿色区域的统计数据(用于巡线1)
statistics1 = img.find_blobs(thresholds_1, roi=roi1, area_threshold=200, merge=True)
# 在roi2区域内寻找绿色区域的统计数据(用于巡线2), margin=120
statistics2 = img.find_blobs(thresholds_1, roi=roi2, area_threshold=200, merge=True)
# 在roi3区域内寻找绿色区域的统计数据(用于巡线2), margin=120
statistics3 = img.find_blobs(thresholds_1, roi=roi3, area_threshold=200, merge=True)
if statistics1: # 如果在roi1区域找到黑色区域
for b in statistics1:
#绘制框出黑色区域的矩形框
actualValue = b[5] # 获取当前黑区域的中心位置
max_blob1 = max(statistics1, key=lambda b: b.pixels())
# 绘制矩形框
img.draw_rectangle(max_blob1.rect(),color=(0,255,0),scale=2)
#中心点XY 并 画十字
img.draw_cross(max_blob1.cx(), max_blob1.cy())
# 计算中心位置并向串口发送数据
#x1 = max_blob1.cx()
#y1 = max_blob1.cy()
#data = bytearray([0xb3,0xb3,x1,y1,0x0c,0x0d,0x0a]) # 要发送的数据,对应串口接收中断需要设置头尾判断
#uart.write(data) # 发送数据
#print(x1, y1)
if statistics2: # 如果在roi2区域找到黑色区域
for b in statistics2:
actualValue = b[5] # 获取当前黑区域的中心位置
max_blob2 = max(statistics2, key=lambda b: b.pixels())
# 绘制矩形框
img.draw_rectangle(max_blob2.rect(),color=(0,255,255),scale=2)
#中心点XY
img.draw_cross(max_blob2.cx(), max_blob2.cy())
# 计算中心位置并向串口发送数据
#x2 = max_blob2.cx()
#y2 = max_blob2.cy()
#data = bytearray([0xb3,0xb3,x2,y2,0x0c,0x0d,0x0a]) # 要发送的数据,对应串口接收中断需要设置头尾判断
#uart.write(data) # 发送数据
#print(x2, y2)
if statistics3: # 如果在roi3区域找到黑色区域
for b in statistics3:
actualValue = b[5] # 获取当前黑区域的中心位置
max_blob3 = max(statistics3, key=lambda b: b.pixels())
# 绘制矩形框
img.draw_rectangle(max_blob3.rect(),color=(0,0,255),scale=2)
#中心点XY
img.draw_cross(max_blob3.cx(), max_blob3.cy())
# 计算中心位置并向串口发送数据
#x3 = max_blob3.cx()
#y3 = max_blob3.cy()
#data = bytearray([0xb3,0xb3,x3,y3,0x0c,0x0d,0x0a]) # 要发送的数据,对应串口接收中断需要设置头尾判断
#uart.write(data) # 发送数据
#print(x3, y3)
# lcd.rotation(2)
lcd.display(img) # 将img,也就是那帧图像用lcd显示
几种巡线情况的测试效果图与建议:
直线
三点坐标连线斜率为0,小车俩边轮子差速建议一致
斜线:
三点斜率不为0,但中间的X2满足落在X1与X3组成的直线一次函数关系
转弯:
三点斜率不为0,中间的X2也不满足落在X1与X3组成的直线一次函数关系
路口:
X1\X2\X3在一条直线,但X1 的ROI区域识别到的色块已经占满ROI区域(>75%)
其余ROI区域对黑线的识别 还未发生这种突变
程序改进:
如果你也是亚博智能的K210并且固件库与我一致的话 :也是直接复制就能使用的程序,新增一些功能,以及改进串口程序\
界面显示:
三个Roi同步在屏幕上打印识别到色块中心点的坐标
串口程序:
之前的程序串口没有测试,其实是没法用的,这次改进后就能用了
LCD程序:
之前忘记添加初始化LCD的程序了,导致在canmv测试没啥问题,但将main.py程序放在SD卡让其执行时能正常运行返回坐标,但LCD不显示,现已解决
import sensor, image, time, lcd
from machine import UART
from fpioa_manager import fm
from modules import ybserial #从modules导入ybserial
# 初始化摄像头和串口
lcd.init() # 初始化LCD显示屏
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.HQVGA)
sensor.skip_frames(time=2000)
sensor.set_vflip(0)#设置摄像头上下翻转模式
#lcd.init(freq=15000000)
sensor.set_pixformat(sensor.GRAYSCALE) #灰度模式(方便循黑线),可以注释掉方便以后用来循其余颜色的线..
sensor.set_framesize(sensor.QVGA) # 设置图像帧大小为320x240
#fm.register(6, fm.fpioa.UART1_RX, force=True)
#fm.register(7, fm.fpioa.UART1_TX, force=True)
#uart = UART(UART.UART1, 115200, read_buf_len=4096)
serial = ybserial() #创建ybserial的对象,并命名为serial
# 定义阈值和颜色范围
thresholds_1 = [(0, 38, -60, 111, -76, 97)] #黑色
roi1 = [0, 60, 320, 16] # 巡线敏感区域1
roi2 = [0, 120, 320, 16] # 巡线敏感区域2
roi3 = [0, 180, 320, 16] # 巡线敏感区域3
while True:
img = sensor.snapshot()#使用摄像机拍摄一张图片并返回给img后续进行操作
lcd.display(img) # 将图像用LCD显示
# 查找所有黑色色块 area_threshold是过滤小的,自己可以改 pixels_threshold像素的最小数量
#blobs_1 = img.find_blobs(thresholds_1, pixels_threshold=100, area_threshold=100)
# 在roi1区域内寻找绿色区域的统计数据(用于巡线1)
statistics1 = img.find_blobs(thresholds_1, roi=roi1, area_threshold=200, merge=True)
# 在roi2区域内寻找绿色区域的统计数据(用于巡线2), margin=120
statistics2 = img.find_blobs(thresholds_1, roi=roi2, area_threshold=200, merge=True)
# 在roi3区域内寻找绿色区域的统计数据(用于巡线2), margin=120
statistics3 = img.find_blobs(thresholds_1, roi=roi3, area_threshold=200, merge=True)
if statistics1: # 如果在roi1区域找到黑色区域
for b in statistics1:
#绘制框出黑色区域的矩形框
actualValue = b[5] # 获取当前黑区域的中心位置
max_blob1 = max(statistics1, key=lambda b: b.pixels())
# 绘制矩形框
img.draw_rectangle(max_blob1.rect(),color=(0,255,0),scale=2)
#中心点XY 并 画十字
img.draw_cross(max_blob1.cx(), max_blob1.cy())
# 计算中心位置
x1 = max_blob1.cx()
y1 = max_blob1.cy()
#屏幕上标出坐标
string = "({},{})".format(x1, y1)
img.draw_string(max_blob1.cx(), max_blob1.cy(),string,color=(0,255,0),scale=2)
#向串口发送数据
data = [0xb1,0xb1,x1,y1,0x00,0x00] # 要发送的数据,对应串口接收中断最好需要设置头尾判断
serial.send_bytearray(data) # 外置串口发送数据
#print(x1, y1) # USB串口发送数据
if statistics2: # 如果在roi2区域找到黑色区域
for b in statistics2:
actualValue = b[5] # 获取当前黑区域的中心位置
max_blob2 = max(statistics2, key=lambda b: b.pixels())
# 绘制矩形框
img.draw_rectangle(max_blob2.rect(),color=(0,255,255),scale=2)
#中心点XY
img.draw_cross(max_blob2.cx(), max_blob2.cy())
# 计算中心位置并向串口发送数据
x2 = max_blob2.cx()
y2 = max_blob2.cy()
#屏幕上标出坐标
string = "({},{})".format(x2, y2)
img.draw_string(max_blob2.cx(), max_blob2.cy(),string,color=(0,255,0),scale=2)
data = [0xb2,0xb2,x2,y2,0x00,0x00] # 要发送的数据,对应串口接收中断最好需要设置头尾判断
serial.send_bytearray(data) # 发送数据
#print(x2, y2)
if statistics3: # 如果在roi3区域找到黑色区域
for b in statistics3:
actualValue = b[5] # 获取当前黑区域的中心位置
max_blob3 = max(statistics3, key=lambda b: b.pixels())
# 绘制矩形框
img.draw_rectangle(max_blob3.rect(),color=(0,0,255),scale=2)
#中心点XY
img.draw_cross(max_blob3.cx(), max_blob3.cy())
# 计算中心位置并向串口发送数据
x3 = max_blob3.cx()
y3 = max_blob3.cy()
#屏幕上标出坐标
string = "({},{})".format(x3, y3)
img.draw_string(max_blob3.cx(), max_blob3.cy(),string,color=(0,255,0),scale=2)
data = [0xb3,0xb3,x3,y3,0x00,0x00] # 要发送的数据,对应串口接收中断最好需要设置头尾判断
serial.send_bytearray(data) # 发送数据
#print(x3, y3)
# lcd.rotation(2)
lcd.display(img) # 将img,也就是那帧图像用lcd显示
测试效果图:
三个Roi同步在屏幕上打印识别到色块中心点的坐标,同时外置串口发送相关数据