RoboGame参赛总结-4
树莓派和STM32的串口搭建
本次使用的是树莓派4B,获取物块颜色后通过串口返回给STM32用于决策,树莓派4B的RXD和TXD的默认映射是ttyS0,一般认为这并不适合用于串口传输,需要改为ttyAMA0映射来形成稳定的传输路径。在尝试多次后,发现搞不定(
其实有一个更好的办法,事实上树莓派的USB接口可以直接作为USART的传输接口,使用一条microUSB的充电线把两个板子连接,就可以实现传输。
ser = serial.Serial("/dev/ttyUSB0", 115200)
但需要注意的是,USB的端口映射是动态的,每次连接的话需要确认USB口的Index值,可以通过指令确认
ls -l /sys/class/tty
还有一个要注意的,如果STM32连接了大量器件的话,本身STM32的电流就将达到2A以上,这时候STM32不能通过USB供电,需要外接其他的电源。否则树莓派将会罢工。
树莓派上的主函数:
import visual_capture
import cv2 as cv
import time
import numpy as np
import serial
cap = cv.VideoCapture(0)
ser = serial.Serial("/dev/ttyUSB0", 115200)
recv = ''
while True:
success, image = cap.read()
count = ser.inWaiting()
if success is False:
break
# 参数换成src# 获取测试图像 可能需要调整缩放
ObjectBuf = visual_capture.visual_object_run(image)
# LineBuf = visual_capture.visual_line_run(image)
# cv.imshow("capture", image)
if count != 0:
# read data
recv = ser.read(count)
recv = recv.decode()
print(recv)
# clear buffer
ser.flushInput()
if recv.find('obj') != -1:
if ObjectBuf['object_amount'] is 0:
ser.write(b'n\r\n')
else:
str = ObjectBuf['color']+'\r\n'
# ser.write(b'color\r\n')
ser.write(str.encode('gbk'))
print(str)
time.sleep(0.04)
recv = ''
c = cv.waitKey(40)
# cv.imshow('image', image)
if c == 27: # 判断escape
break
cv.waitKey(0)
cv.destroyAllWindows()
值得注意的是,这种通信方式中,树莓派和STM32的编码格式存在区别,即每个语句需要添加’\r\n’,并执行编码命令
ser.write(str.encode('gbk'))
这样STM32才能正确获取树莓派回传的字符。
视觉识别
这个比赛的要求很简单,只需要读出三种颜色就行,先按照HSV的相关表格创建红黄绿三种颜色的取值范围。
color_data = {'greenUp': np.array([77, 255, 255]), 'greenDown': np.array([35, 43, 46]),
'yellowUp': np.array([34, 255, 255]), 'yellowDown': np.array([26, 43, 36]),
'redUp1': np.array([10, 255, 255]), 'redDown1': np.array([0, 43, 36]),
'redUp2': np.array([180, 255, 255]), 'redDown2': np.array([156, 43, 36])}
deal_image = visual_image_scale(image)[0] # 缩放
deal_image = cv.GaussianBlur(deal_image, (3, 3), 2)
hsv_image = cv.cvtColor(deal_image, cv.COLOR_BGR2HSV)
# 创建颜色对应的掩膜
这里先对图像做了一个小的高斯滤波,除去部分噪声,之后按照创建好的list创建三张掩膜覆盖后的图像。
red_mask1 = cv.inRange(hsv_image, color_data['redDown1'], color_data['redUp1'])
red_mask2 = cv.inRange(hsv_image, color_data['redDown2'], color_data['redUp2'])
red_mask = cv.bitwise_or(red_mask1, red_mask2)
yellow_mask = cv.inRange(hsv_image, color_data['yellowDown'], color_data['yellowUp'])
green_mask = cv.inRange(hsv_image, color_data['greenDown'], color_data['greenUp'])
之后进行图像处理,获取二值图进行开操作,除去小的噪声点
gray = cv.cvtColor(deal_image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
# 形态学处理
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
binary = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel) # 开操作
直接使用OTSU快速二值化,获取较大的图像块,并进行反二值化。
完成之后获取边缘图像边缘:
binary, contours, hireachy = cv.findContours(binary, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
得到相关数据后,可以进一步对图像的特征进行筛选,并在图像上进行标注处理
for index, contour in enumerate(contours):
area = cv.contourArea(contour)
# 面积
rect_x, rect_y, rect_width, rect_height = cv.boundingRect(contour)
# 外接矩形特征
rate = max(rect_width, rect_height) / min(rect_width, rect_height)
# print(str(color) + " rectangle rate-%s: %s" % (index, rate))
mm = cv.moments(contour)
# 重心参数
# print(type(mm))
if area == 0:
continue
cx = int(mm['m10'] / mm['m00'])
cy = int(mm['m01'] / mm['m00'])
# 多边形描绘
approxCurve = cv.approxPolyDP(contour, 4, True)
# print(str(color) + " approxCurve-%s:" % index)
# print(approxCurve.shape)
if approxCurve.shape[0] >= 4 and (area < (height*width-4000) and area > height*width/5):
# 绘制红色外接矩形及其矩心
cv.circle(deal_image, (np.int(cx), np.int(cy)), 2, (0, 0, 255), -1)
cv.rectangle(deal_image, (rect_x, rect_y), (rect_x + rect_width, rect_y + rect_height), (0, 0, 255), 2)
# 选取面积较大的矩形 绘制绿色边缘
cv.drawContours(deal_image, contours, index, (0, 255, 0), 2)
core_location.append((cx, cy))
object_counter = object_counter + 1
# print(str(color) + " contour-%s: area: %s" % (index, area))
pass
# cv.imshow(str(color) + " measure_contours", deal_image)
将数据整理后回传
objectinf_dict = {'color': color, 'core_location': core_location, 'object_amount': object_counter}
效果如图:
STM32串口接收
这个一般的教程都有,很简单,不再赘述,但要回传注意速度不能过快,否则会出问题。
if(counter == 10){
counter = 0;
Usart_SendString(USART1, commond);
delay_ms(1);
if(USART_RX_STA&0x8000){
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
ObjectSearchNode->color = USART_RX_BUF[len-1]; //获取最新的数据
if(ObjectSearchNode->vedio_en == 0) ObjectSearchNode->color = 0;
if(ObjectSearchNode->color == 'r' && red_trigger){
MissionCarryNode->red_index = ObjectSearchNode->line_index +1;
red_trigger = 0;
}
USART_RX_STA = 0;
}
}