为参加今年的工训大赛,本人自学32和openmv,虽不深不才但学有所获,虽结果不尽人意,但能思其所以,再之csdn教会了我很多,也想发表一下自己的芝麻设计,希望能给读者带来一些思考和灵感,水平有限希望理解,备注:侵立删!
以下是openmv的巡线和识别一体化程序设计:
# 基于openmv与stm32串口通信的巡线+识别 - By:水下管道机器人智能控制设计者付智辉
#1 - 周四 12月 17 2020
import sensor, image, time, math
from pyb import UART
sensor.reset()
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQVGA)
# _____________________________________________________________________________
#规定串行通信组合为
#turn_right=bytearray([0xb2,0xb3,0xb1,0x0d,0x0a])
#turn_left=bytearray([0xb2,0xb3,0xb2,0x0d,0x0a])
#find_red=bytearray([0xb2,0xb3,0xb3,0x0d,0x0a])
#find_green=bytearray([0xb2,0xb3,0xb4,0x0d,0x0a])
#find_black=bytearray([0xb2,0xb3,0xb5,0x0d,0x0a])
#find_blue=bytearray([0xb2,0xb3,0xb6,0x0d,0x0a])
#find_circle=bytearray([0xb2,0xb3,0xb7,0x0d,0x0a])
#find_rectangle=bytearray([0xb2,0xb3,0xb8,0x0d,0x0a])
#
#_____________________________________________________________________________
uart = UART(3,115200)
uart.init(115200,bits=8,parity=None,stop=1)
turn_right=bytearray([0xb2,0xb3,0xb1,0x0d,0x0a])
turn_left=bytearray([0xb2,0xb3,0xb2,0x0d,0x0a])
find_red=bytearray([0xb2,0xb3,0xb3,0x0d,0x0a])
find_green=bytearray([0xb2,0xb3,0xb4,0x0d,0x0a])
find_black=bytearray([0xb2,0xb3,0xb5,0x0d,0x0a])
find_blue=bytearray([0xb2,0xb3,0xb6,0x0d,0x0a])
find_circle=bytearray([0xb2,0xb3,0xb7,0x0d,0x0a])
find_rectangle=bytearray([0xb2,0xb3,0xb8,0x0d,0x0a])
white_threshold_01 = ((95, 100, -18, 3, -8, 4)); #白色阈值
black_threshold_01 = ((0, 15, -24, -1, -18, 6)); #黑色阈值
red_threshold_01 = ((35, 100, 41, 77, 24, 59));#红色阈值
green_threshold_01 = ((50, 100, -80, -20, 8, 20));#绿色阈值
blue_threshold_01 = ((20, 100, -18, 18, -80, -30));#蓝色阈值
#设置是否使用img.binary()函数进行图像分割
BINARY_VISIBLE = True
# _________________________________________________________________________________
def find_max(blobs): #定义寻找色块面积最大的函数
max_size=0
for blob in blobs:
if blob.pixels() > max_size:
max_blob=blob
max_size = blob.pixels()
return max_blob
sensor.skip_frames(time = 2000)
clock = time.clock()
while(True):
THRESHOLD = (190, 255)
sensor.set_auto_whitebal(False)#关闭白平衡
clock.tick()
img = sensor.snapshot().binary([THRESHOLD]) if BINARY_VISIBLE else sensor.snapshot()
img.draw_rectangle(130, 85, 30, 35, color = (0, 0, 0), thickness = 2, fill = True)
line = img.get_regression([(255,255) if BINARY_VISIBLE else THRESHOLD])
if (line):
img.draw_line(line.line(), color = 127)
theta = int(line.theta())
x = int ( (line.x1()+line.x2())/2)
print("find line")
if line.magnitude() >= 1:
print(theta,x) #x的中值大概是60
if theta>100:
print("turn_right!")
uart.write(turn_right)
elif theta<80:
print("turn_left!")
uart.write(turn_left)
else:
print("go straight!")
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False)
clock = time.clock()
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
# pixels_threshold=100, area_threshold=100
blobs = img.find_blobs([red_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10);
blobs1 = img.find_blobs([green_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10);
blobs2 = img.find_blobs([blue_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10);
blobs3 = img.find_blobs([black_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10);
cx=0;cy=0;cx1=0;cy1=0;cx2=0;cy2=0;
if blobs:
#如果找到了red
max_b = find_max(blobs);
# Draw a rect around the blob.
img.draw_rectangle(max_b[0:4]) # rect
#用矩形标记出目标颜色区域
img.draw_cross(max_b[5], max_b[6]) # cx, cy
img.draw_cross(160, 120) # 在中心点画标记
#在目标颜色区域的中心画十字形标记
cx=max_b[5];
cy=max_b[6];
img.draw_line((160,120,cx,cy), color=(127));
#img.draw_string(160,120, "(%d, %d)"%(160,120), color=(127));
img.draw_string(cx, cy, "(%d, %d)"%(cx,cy), color=(127));
print("找到红色!")
uart.write(find_red)
sensor.reset() #初始化设置
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)#设置为彩色设置清晰度sensor.set_framesize(sensor.SVGA)
sensor.skip_frames(time = 2000) #跳过前2000ms的图像
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() #创建一个clock便于计算FPS,看看到底卡不卡
sensor.set_auto_gain(False) # 关闭自动自动增益。默认开启的。
sensor.set_auto_whitebal(False) #关闭白平衡。在颜色识别中,一定要关闭白平衡。
clock.tick()
img = sensor.snapshot().lens_corr(1.8) #拍摄一张照片,lens_corr函数用于非鱼眼畸变矫正,默认设置参数为1.8,
for blob in img.find_blobs([black_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
#openmv自带的寻找色块函数。img.find_blobs([red_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10);
#pixels_threshold是像素阈值,面积小于这个值的色块就忽略blobs4 = img.find_blobs([thresholds], pixels_threshold=100, area_threshold=100, merge=True, margin=10);
#roi是感兴趣区域,只在这个区域内寻找色块 if blobs4:
#are_threshold是面积阈值,如果色块被框起来的面积小于这个值,会被过滤掉
print('该形状占空比为',blob.density())
#density函数可以自动返回色块面积/外接矩形面积这个值
if blob.density()>0.805:#理论上矩形和他的外接矩形应该是完全重合
#但是测试时候发现总会有偏差,多次试验取的这个值。下面圆形和三角形亦然
print("检测为长方形 ",end='')
uart.write(find_rectangle)
elif blob.density()>0.65:
print("检测为圆 ",end='')
uart.write(find_circle)
else:
print("no dectedtion")
elif blobs2:
#如果找到了blue
max_b = find_max(blobs2);
# Draw a rect around the blob.
img.draw_rectangle(max_b[0:4])
img.draw_cross(max_b[5], max_b[6])
img.draw_cross(160, 120)
cx2=max_b[5];
cy2=max_b[6];
img.draw_line((160,120,cx2,cy2), color=(127));
#img.draw_string(160,120, "(%d, %d)"%(160,120), color=(127));
img.draw_string(cx2, cy2, "(%d, %d)"%(cx2,cy2), color=(127));
print("找到蓝色!")
uart.write(find_blue)
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock = time.clock()
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock.tick()
img = sensor.snapshot().lens_corr(1.8)
for blob in img.find_blobs([black_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
print('该形状占空比为',blob.density())
if blob.density()>0.805:
print("检测为长方形 ",end='')
uart.write(find_rectangle)
elif blob.density()>0.65:
print("检测为圆 ",end='')
uart.write(find_circle)
print('圆形半径',(blob.w()+blob.h())/4)
else:
print("no dectedtion")
elif blobs3:
#如果找到了black
max_b = find_max(blobs3);
# Draw a rect around the blob.
img.draw_rectangle(max_b[0:4])
img.draw_cross(max_b[5], max_b[6])
img.draw_cross(160, 120)
cx2=max_b[5];
cy2=max_b[6];
img.draw_line((160,120,cx2,cy2), color=(127));
#img.draw_string(160,120, "(%d, %d)"%(160,120), color=(127));
img.draw_string(cx2, cy2, "(%d, %d)"%(cx2,cy2), color=(127));
print("找到黑色!")
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock = time.clock()
sensor.set_auto_gain(False)
sensor.set_auto_whitebal(False)
clock.tick()
img = sensor.snapshot().lens_corr(1.8)
for blob in img.find_blobs([black_threshold_01], pixels_threshold=100, area_threshold=100, merge=True, margin=10):
print('该形状占空比为',blob.density())
if blob.density()>0.805:
print("检测为长方形 ",end='')
uart.write(find_rectangle)
elif blob.density()>0.65:
print("检测为圆 ",end='')
uart.write(find_circle)
else:
print("no dectedtion")
else:
print("没找到!")
print(clock.fps())
sensor.reset() #初始化设置
sensor.set_pixformat(sensor.GRAYSCALE)
sensor.set_framesize(sensor.QQVGA)
sensor.set_auto_whitebal(False)#关闭白平衡
sensor.set_auto_gain(True)
else:
print("not line!")
以下是stm32串口通信服务程序:
#include "sys.h"
#include "usart.h"
#include "led.h"
#include "delay.h"
#include "stdio.h"
#include "motor.h"
//
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos 使用
#endif
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0x40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
u8 t;
u8 len;
u16 times=0;
u8 date[4];
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0x0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0x3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
if(USART_RX_STA&0x8000)
{
MOTOR_FORWARD_OFF();
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
for(t=0;t<len;t++)
{
date[t]=USART_RX_BUF[t];
}
if(date[2]==0xb1)//需右转//
{
MOTOR_FORWARD_OFF();
MOTOR_RIGHT_ON();
delay_ms(50000);
MOTOR_RIGHT_OFF();
}
else if(date[2]==0xb2)//需左转//
{
MOTOR_FORWARD_OFF();
MOTOR_LEFT_ON();
delay_ms(50000);
MOTOR_LEFT_OFF();
}
else if(date[2]==0xb3)//找到红色//
{
LEDR1_ON;
delay_ms(50000);
LEDR1_OFF;
}
else if(date[2]==0xb6)//找到蓝色//
{
LEDB1_ON;
delay_ms(50000);
LEDB1_OFF;
}
else if(date[2]==0xb7)//找到圆//
{
LEDR2_ON;
delay_ms(50000);
LEDR2_OFF;
}
else if(date[2]==0xb8)//找到方//
{
LEDG2_ON;
delay_ms(50000);
LEDG2_OFF;
}
USART_RX_STA=0;//消除中断标志位
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
#endif
关于船体转弯是通过触碰开关引发单片机外部中端,进入中断程序,引起左右电机转动转弯,时间可调,如何归位管道,通过openmv的直线识别,如以上程序,船体上升下浅可通过针筒排水进水改变船体重量实现。
最后感谢符同学和王同学的辛苦付出,为青春干杯,走好正确的路,自己的世界才是真正光明的,才能真正谈自己有世界。20.12.26