K210寻黑线
代码:
# 导入必要的模块
import sensor, image, lcd
from machine import UART # 串口库函数
from fpioa_manager import fm # GPIO重定向函数
# GPIO引脚重定向为UART1的TX和RX
fm.register(18, fm.fpioa.UART1_TX, force=True)
fm.register(19, fm.fpioa.UART1_RX, force=True)
uart_A = UART(UART.UART1, 115200, 8, 0, 1, timeout=1000, read_buf_len=4096) # 初始化UART1串口
# 设置巡线需要的阈值和敏感区域
green_threshold = ((0, 190)) # 绿色阈值(用于黑色)
roi1 = [0, 100, 320, 16] # 巡线敏感区域
roi2 = [0, 180, 320, 8] # 关键点敏感区域
expectedValue = 160 # 巡线位置期望值
err = 0 # 本次误差
old_err = 0 # 上次误差
Kp = 0.046 # PID比例系数
Kd = 0 # PID微分系数
Speed = 0 # 期望速度
Speed_left = 0 # 左轮速度
Speed_right = 0 # 右轮速度
Flag = 0 # 关键点标志位
# 发送数据的函数定义
def sending_data(x, y, z):
global uart_A
FH = bytearray([0x2C, 0x12, x, y, z, 0x5B]) # 准备发送的数据包
uart_A.write(FH) # 发送数据
# 初始化LCD和摄像头
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QVGA) # 设置图像帧大小为320x240
sensor.skip_frames(time=3000) # 跳过3000张图像帧以便调整传感器
sensor.set_auto_gain(False) # 关闭自动增益
sensor.set_auto_whitebal(False) # 关闭白平衡
sensor.run(1) # 启动摄像头
while True:
img = sensor.snapshot() # 获取一帧图像
# 在roi1区域内寻找绿色区域的统计数据(用于巡线)
statistics1 = img.find_blobs([green_threshold], roi=roi1, area_threshold=200, merge=True)
# 在roi2区域内寻找绿色区域的统计数据(用于关键点检测)
statistics2 = img.find_blobs([green_threshold], roi=roi2, area_threshold=120, merge=True, margin=120)
if statistics1: # 如果在roi1区域找到绿色区域
for b in statistics1:
tmp = img.draw_rectangle(b[0:4]) # 在找到的区域绘制矩形框
tmp = img.draw_cross(b[5], b[6]) # 在找到的区域绘制十字交叉点
# PID控制计算
actualValue = b[5] # 获取当前绿色区域的中心位置
err = actualValue - expectedValue # 计算误差
Speed_left = Speed - (Kp * err + Kd * (err - old_err)) # 计算左轮速度
Speed_right = Speed + (Kp * err + Kd * (err - old_err)) # 计算右轮速度
old_err = err # 更新误差值
print("Speed_left, Speed_right")
print(int(Speed_left), int(Speed_right)) # 打印速度值
if statistics2: # 如果在roi2区域找到绿色区域
for b in statistics2:
tmp = img.draw_rectangle(b[0:4]) # 在找到的区域绘制矩形框
tmp = img.draw_cross(b[5], b[6]) # 在找到的区域绘制十字交叉点
if b[2] > 50:
Flag = 1 # 如果面积大于50,则设置关键点标志位为1
sending_data(int(Speed_left), int(Speed_right), Flag) # 发送控制数据到UART
lcd.display(img) # 在LCD上显示处理后的图像
代码解释:
1、1寻找色块部分
- 其中的 sensor.set_pixformat(sensor.GRAYSCALE) 是将像素模式设置为灰度灰度模式对于只有两种颜色的地图可以起到提高帧数,过滤其它颜色的影响,提高了识别的正确率。
- 其中的 Flag 是用于当检测到如十字路口,起停线等的标志
- 通过find_blobs函数可以找到色块,其中==[green_threshold]==寻找目标是颜色的阈值,阈值可以通过Maixpy IDE工具栏中的工具 -> 机器视觉 -> 阈值编辑器获得。
- 其中的 ==roi1 ==和 ==roi2 ==分别是寻线部分和关键的感兴趣区,就是在要处理的整张图像中
- ==area_threshold ==是面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉,减少干扰提高巡线准确度。
- merge=True 将所有重叠的blob合并为一个。
- margin为边界,如果设置为1,那么两个blobs如果间距1一个像素点,也会被合并。此参数只在检测关键点寻找色块函数中运用,可以更明确的侦测出关键点,如Y型路口。
- ==statistics = img.find_blobs[ ] ==对象返回的是多个blob的列表,列表类似与C语言的数组,一个blobs列表里包含很多blob对象,blobs对象就是色块,每个blobs对象包含一个色块的信息。b就是很多色块。可以用for循环把所有的色块找一遍。
for b in statistics:
返回色块的外框的x坐标(int),也可以通过b[0]来获得
返回色块的外框的y坐标(int),也可以通过b[1]来获得
返回色块的外框的宽度w(int),也可以通过b[2]来获得
返回色块的外框的高度h(int),也可以通过b[3]来获得
返回色块的像素数量(int),也可以通过b[4]来获得
返回色块的外框的中心x坐标(int),也可以通过b[5]来获得
返回色块的外框的中心y坐标(int),也可以通过b[6]来获得
————————————————
- tmp=img.draw_rectangle(b[0:4]) 和tmp=img.draw_cross(b[5], b[6]) 分别为画出找到色块的外框和中心十字
1、2寻线部分
由上我们可得b[5]为返回外框中心x的值,而且拍摄的画面为320x240,X方向的中间为160,所以线与摄像头中心的偏移量err,就为实际值actualValue 也就是b[5]减去期望expectedValue(160)。对应代码为:
actualValue=b[5]
err=actualValue-expectedValue
得到偏差之后计算pid的值:
Speed_left = Speed - (Kperr+Kd(err-old_err))
Speed_right = Speed + (Kperr+Kd(err-old_err))
old_err= err
1、3 关键点部分
当要经过十字路口,y型路口,起停线时,可以关注blob的列表中的b[2],其反回的值为返回色块的外框的宽度w。因为正常巡线时寻找到的色块外框宽度w的值是小于关键点部分的值。
只需要判断是否大于正常色块外框宽度就可以判断关键点
if b[2] >50:
Flag = 1