目录
目录
OpenMV简介
1.简介
OpenMV摄像头通过高级语言Python脚本( MicroPython )。可以很容易的使用外部终端触发拍摄或者或者执行算法,也可以把算法的结果用来控制IO引脚。通过额外的ttl-rs232或者ttl-rs485模块和PLC通信。
2.具体参数
STM32H743II ARM Cortex M7 处理器,480 MHz ,1MB RAM,2 MB flash.
所有引脚均可承受5V电压,输出电压为3.3V。所有引脚都可以提供最高25mA的拉电流(source)或灌电流(sink)。在ADC或DAC模式下,P6不能承受5V电压。引脚总共可提供最高120mA的拉电流(source)或灌电流(sink)。VIN可以在3.6V和5V之间。不要从OpenMV Cam的3.3V引脚输出超过250mA的电流。
他的镜头是M12通用的镜头,在不同的场景应用不同的镜头:
-
标配镜头视角大概120度,长焦镜头30度,广角185度,无畸变镜头90度。
-
标配镜头焦距2.8mm,长焦12mm,广角1.7mm,无畸变镜头3.6mm。
这个处理器有以下的IO接口:
-
全速 USB (12Mbs) 接口,连接到电脑。当插入OpenMV摄像头后,电脑会出现一个虚拟COM端口和一个“U盘”。
-
μSD卡槽拥有100Mbs读写,这允许OpenMV摄像头录制视频,和把机器视觉的素材从SD卡提取出来。
-
一个SPI总线高达100Mbs速度,允许简单的把图像流数据传给LCD扩展板,WiFi扩展板,或者其他控制器。
-
一个 I2C总线,CAN总线, 和2两个异步串口总线 (TX/RX) ,用来链接其他控制器或者传感器。
-
一个12-bit ADC 和一个12-bit DAC。
-
2个 I/O 引脚用于舵机控制.
-
所有的IO口都可以用于,中断和PWM(板子上有10个I/O引脚)。
-
一个RGB LED(三色), 两个高亮的 850nm IR LED(红外)。
-
32 MB 外置的 32-bit SDRAM ,100 MHz的时钟,达到 400 MB/s 的带宽。
-
32 MB 外置的 quadspi flash, 100 MHz的时钟,4-bit DDR模式达到 100 MB/s 的带宽。
-
可拆卸的摄像头模块系统,允许OpenMV Cam H7 Plus与不同的感光元件模组连接:
-
OpenMV4 H7 Plus默认配置的OV5640 感光元件处理2592×1944 (5MP)图像。在QVGA (320×240)及以下的分辨率时,大多数简单的算法可以运行(25~50)FPS。OpenMV 摄像头有一个2.8mm焦距镜头在一个标准M12镜头底座上。
-
3.7V 锂离子电池接口
3.功能
-
滤波
-
颜色追踪
-
AptilTag
-
二维码
-
条形码
-
人脸识别
-
人眼追踪(瞳孔识别)
-
直线识别
-
圆形识别
-
矩形识别
-
数字识别
-
线性回归-巡线
-
模板匹配
-
特征点追踪
-
光流
-
边缘检测
-
录制视频
-
mavlink
使用
供电:USB口供电或者外部电源供电(3.7-5V)。注意,不要接3.3V
图像处理
1.LAB亮度-对比度
Lab颜色空间中,L亮度;a的正数代表红色,负端代表绿色;b的正数代表黄色,负端代表兰色。不像RGB和CMYK色彩空间,Lab颜色被设计来接近人类视觉。
因此L分量可以调整亮度对,修改a和b分量的输出色阶来做精确的颜色平衡。
2.镜头的畸变:OpenMV中使用image.lens_corr(1.8)来矫正2.8mm焦距的镜头
3.滤光片:通过波长650nm以内的滤光片,把红外光截止了
python
REPL和串口
OpenMV的IDE自带一个串口助手,用于连接OpenMV,也可以连接其他的串口,比如Arduino,pyboard,esp8266。
首先,断开OpenMV与IDE的连接,否则串口会冲突!
基础程序
首先 import 是导入此代码所依赖的模块
进入一个while大循环,在这里 image 在不断的截图保存图像,而截取的图像我们就可以在右上角看到
最后一句 也就是打印的帧率,我们可以在下面的框Terminal看到
图像处理
感光元件
初始化
- sensor.reset() 初始化感光元件
自动增益/白平衡/曝光
- sensor.set_auto_gain()
自动增益开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动增益。
- sensor.set_auto_whitebal()
自动白平衡开启(True)或者关闭(False)。在使用颜色追踪时,需要关闭自动白平衡。
- sensor.set_auto_exposure(enable[, exposure_us])
enable 打开(True)或关闭(False)自动曝光。默认打开。
如果 enable 为 False,则可以用 exposure_us 设置一个固定的曝光时间(以微秒为单位)。
窗口ROI
ROI:Region Of Interest,图像处理中的术语“感兴趣区”。就是在要处理的图像中提取出的要处理的区域。
sensor.set_windowing(roi)
roi的格式是(x, y, w, h)
x:ROI区域中左上角的x坐标
y:ROI区域中左上角的y坐标
w:ROI的宽度
h:ROI的高度
画图
色彩
-
颜色可以是灰度值(0-255),或者是彩色值(r, g, b)的tupple。默认是白色。
-
其中的color关键字必须显示的标明color=。
画线
-
image.draw_line(line_tuple, color=White) 在图像中画一条直线。
-
line_tuple的格式是(x0, y0, x1, y1),意思是(x0, y0)到(x1, y1)的直线。
-
颜色可以是灰度值(0-255),或者是彩色值(r, g, b)的tupple。默认是白色
-
画框
-
image.draw_rectangle(rect_tuple, color=White) 在图像中画一个矩形框。
-
rect_tuple 的格式是 (x, y, w, h)。
-
画圆
-
image.draw_circle(x, y, radius, color=White) 在图像中画一个圆。
-
x,y是圆心坐标
-
radius是圆的半径
-
画十字
-
image.draw_cross(x, y, size=5, color=White) 在图像中画一个十字
-
x,y是坐标
-
size是两侧的尺寸
-
写字
-
image.draw_string(x, y, text, color=White) 在图像中写字 8x10的像素
-
x,y是坐标。使用\n, \r, and \r\n会使光标移动到下一行。
-
text是要写的字符串。
-
颜色识别
find_blobs函数
通过find_blobs函数可以找到色块
image.find_blobs(thresholds, roi=Auto, x_stride=2, y_stride=1, invert=False, area_threshold=10, pixels_threshold=10, merge=False, margin=0, threshold_cb=None, merge_cb=None)
参数:
-
thresholds是颜色的阈值,注意:这个参数是一个列表,可以包含多个颜色。如果你只需要一个颜色,那么在这个列表中只需要有一个颜色值,如果你想要多个颜色阈值,那这个列表就需要多个颜色阈值。注意:在返回的色块对象blob可以调用code方法,来判断是什么颜色的色块。
-
x_stride 就是查找的色块的x方向上最小宽度的像素,默认为2,如果你只想查找宽度10个像素以上的色块,那么就设置这个参数为10:
blobs = img.find_blobs([red],x_stride=10)
-
y_stride 就是查找的色块的y方向上最小宽度的像素,默认为1
-
invert 反转阈值,把阈值以外的颜色作为阈值进行查找
-
area_threshold 面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉
-
pixels_threshold 像素个数阈值,如果色块像素数量小于这个值,会被过滤掉
-
merge 合并,如果设置为True,那么合并所有重叠的blob为一个。
注意:这会合并所有的 -
roi的格式是(x, y, w, h)的tupple.
x:ROI区域中左上角的x坐标
y:ROI区域中左上角的y坐标
w:ROI的宽度
h:ROI的高度
颜色阈值选择工具
借助颜色阈值选择工具实时的看到阈值的结果
blobs列表
一个blobs列表里包含很多blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。
find_blobs对象返回的是多个blob的列表.
blob的多个方法:
-
blob.rect() 返回这个色块的外框——矩形元组(x, y, w, h),可以直接在image.draw_rectangle中使用。
-
blob.x() 返回色块的外框的x坐标(int),也可以通过blob[0]来获取。
-
blob.y() 返回色块的外框的y坐标(int),也可以通过blob[1]来获取。
-
blob.w() 返回色块的外框的宽度w(int),也可以通过blob[2]来获取。
-
blob.h() 返回色块的外框的高度h(int),也可以通过blob[3]来获取。
-
blob.pixels() 返回色块的像素数量(int),也可以通过blob[4]来获取。
-
blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取。
-
blob.cy() 返回色块的外框的中心y坐标(int),也可以通过blob[6]来获取。
-
blob.rotation() 返回色块的旋转角度(单位为弧度)(float)。如果色块类似一个铅笔,那么这个值为0~180°。如果色块是一个圆,那么这个值是无用的。如果色块完全没有对称性,那么你会得到0~360°,也可以通过blob[7]来获取。
-
blob.code() 返回一个16bit数字,每一个bit会对应每一个阈值。举个例子:
blobs = img.find_blobs([red, blue, yellow], merge=True)
如果这个色块是红色,那么它的code就是0001,如果是蓝色,那么它的code就是0010。注意:一个blob可能是合并的,如果是红色和蓝色的blob,那么这个blob就是0011。这个功能可以用于查找颜色代码。也可以通过blob[8]来获取。
-
blob.count() 如果merge=True,那么就会有多个blob被合并到一个blob,这个函数返回的就是这个的数量。如果merge=False,那么返回值总是1。也可以通过blob[9]来获取。
-
blob.area() 返回色块的外框的面积。应该等于(w * h)
-
blob.density() 返回色块的密度。这等于色块的像素数除以外框的区域。如果密度较低,那么说明目标锁定的不是很好。
比如,识别一个红色的圆,返回的blob.pixels()是目标圆的像素点数,blob.area()是圆的外接正方形的面积。
标记跟踪
AprilTag简介
AprilTag是一个视觉基准系统,可用于各种任务,包括AR,机器人和相机校准。这个tag可以直接用打印机打印出来,而AprilTag检测程序可以计算相对于相机的精确3D位置,方向和id,只要把这个tag贴到目标上,就可以在OpenMV上识别出这个标签的3D位置,id。
制作AprilTag
从OpenMV的IDE里生成。 在工具——Machine Vision——AprilTag Generate中选择family,推荐使用TAG36H11。
一个检测程序示例
# AprilTags Example
#
# This example shows the power of the OpenMV Cam to detect April Tags
# on the OpenMV Cam M7. The M4 versions cannot detect April Tags.
import sensor, image, time, math
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger...
sensor.skip_frames(30)
sensor.set_auto_gain(False) # must turn this off to prevent image washout...
sensor.set_auto_whitebal(False) # must turn this off to prevent image washout...
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot()
for tag in img.find_apriltags(): # defaults to TAG36H11 without "families".
img.draw_rectangle(tag.rect(), color = (255, 0, 0))
img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
degress = 180 * tag.rotation() / math.pi
print(tag.id(),degress)
3D定位示例
Tag的空间位置,一共有6个自由度,三个位置,三个角度。
# AprilTags Example
#
# This example shows the power of the OpenMV Cam to detect April Tags
# on the OpenMV Cam M7. The M4 versions cannot detect April Tags.
import sensor, image, time, math
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA) # we run out of memory if the resolution is much bigger...
sensor.skip_frames(30)
sensor.set_auto_gain(False) # must turn this off to prevent image washout...
sensor.set_auto_whitebal(False) # must turn this off to prevent image washout...
clock = time.clock()
# 注意!与find_qrcodes不同,find_apriltags 不需要软件矫正畸变就可以工作。
# 注意,输出的姿态的单位是弧度,可以转换成角度,但是位置的单位是和你的大小有关,需要等比例换算
# f_x 是x的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/3.984*656,这个值是用毫米为单位的焦距除以x方向的感光元件的长度,乘以x方向的感光元件的像素(OV7725)
# f_y 是y的像素为单位的焦距。对于标准的OpenMV,应该等于2.8/2.952*488,这个值是用毫米为单位的焦距除以y方向的感光元件的长度,乘以y方向的感光元件的像素(OV7725)
# c_x 是图像的x中心位置
# c_y 是图像的y中心位置
f_x = (2.8 / 3.984) * 160 # 默认值
f_y = (2.8 / 2.952) * 120 # 默认值
c_x = 160 * 0.5 # 默认值(image.w * 0.5)
c_y = 120 * 0.5 # 默认值(image.h * 0.5)
def degrees(radians):
return (180 * radians) / math.pi
while(True):
clock.tick()
img = sensor.snapshot()
for tag in img.find_apriltags(fx=f_x, fy=f_y, cx=c_x, cy=c_y): # 默认为TAG36H11
img.draw_rectangle(tag.rect(), color = (255, 0, 0))
img.draw_cross(tag.cx(), tag.cy(), color = (0, 255, 0))
print_args = (tag.x_translation(), tag.y_translation(), tag.z_translation(), \
degrees(tag.x_rotation()), degrees(tag.y_rotation()), degrees(tag.z_rotation()))
# 位置的单位是未知的,旋转的单位是角度
print("Tx: %f, Ty %f, Tz %f, Rx %f, Ry %f, Rz %f" % print_args)
print(clock.fps())
测距
方法一:
利用Apriltag进行3D定位
方法二:
选参照物,利用参照物的大小比例来计算距离
距离 = 一个常数/直径的像素
这个常数的值,就是先让球距离摄像头10cm,打印出摄像头里直径的像素值,然后相乘,就得到了k的值!
# Measure the distance
#
# This example shows off how to measure the distance through the size in imgage
# This example in particular looks for yellow pingpong ball.
import sensor, image, time
# For color tracking to work really well you should ideally be in a very, very,
# very, controlled enviroment where the lighting is constant...
yellow_threshold = ( 56, 83, 5, 57, 63, 80)
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False) # turn this off.
clock = time.clock() # Tracks FPS.
K=5000#the value should be measured
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
blobs = img.find_blobs([yellow_threshold])
if len(blobs) == 1:
# Draw a rect around the blob.
b = blobs[0]
img.draw_rectangle(b[0:4]) # rect
img.draw_cross(b[5], b[6]) # cx, cy
Lm = (b[2]+b[3])/2
length = K/Lm
print(length)
#print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.
颜色形状同时识别
首先我们进行形状识别,然后在识别到的区域内进行颜色统计
import sensor, image, time
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False) # must be turned off for color tracking
sensor.set_auto_whitebal(False) # must be turned off for color tracking
clock = time.clock()
while(True):
clock.tick()
img = sensor.snapshot().lens_corr(1.8)
for c in img.find_circles(threshold = 3500, x_margin = 10, y_margin = 10, r_margin = 10,
r_min = 2, r_max = 100, r_step = 2):
area = (c.x()-c.r(), c.y()-c.r(), 2*c.r(), 2*c.r())
#area为识别到的圆的区域,即圆的外接矩形框
statistics = img.get_statistics(roi=area)#像素颜色统计
print(statistics)
#(0,100,0,120,0,120)是红色的阈值,所以当区域内的众数(也就是最多的颜色),范围在这个阈值内,就说明是红色的圆。
#l_mode(),a_mode(),b_mode()是L通道,A通道,B通道的众数。
if 0<statistics.l_mode()<100 and 0<statistics.a_mode()<127 and 0<statistics.b_mode()<127:#if the circle is red
img.draw_circle(c.x(), c.y(), c.r(), color = (255, 0, 0))#识别到的红色圆形用红色的圆框出来
else:
img.draw_rectangle(area, color = (255, 255, 255))
#将非红色的圆用白色的矩形框出来
print("FPS %f" % clock.fps())
颜色与模板匹配同时识别
首先我们进行颜色识别,然后在识别到的颜色区域内进行模板识别。
视觉小车
pyb各种外设
详情看这里,pyb各种外设 · OpenMV中文入门教程
json
OpenMV有json的模块,json.dumps(obj)和ujson.loads(str)可以很容的生成json字符串和解析json字符串。
串口通信
串口连接
from machine import UART
uart = UART(1, baudrate=115200) # 初始化串口 波特率设置为115200 TX是B12 RX是B13
uart.write("uart test\r\n") # 发送字符串
uart_num = 0 # 定义一个变量
uart_array = [48,49,50,51,52,53,54,55,56,57] # 定义一个列表 保存数字
uart.write(bytearray(uart_array)) # 发送列表
uart.write(bytearray([0x41])) # 发送一个十六进制数据
while(True):
uart_num = uart.any() # 获取当前串口数据数量
if(uart_num):
uart_str = uart.read(uart_num) # 读取串口数据
uart.write(uart_str) # 将读取到的串口数据发回
# uart.read会自动在数据末尾添加\n的操作,如果想去掉可以用strip()去掉
# 例如 uart_str = uart.read(uart_num).strip()
# 这样 uart_str得到的数据就不会被自动添加\n