在准备电赛的这段时间里,我确实学到了很多东西,我会一点一点更新博客,从头到尾总结一下,本篇博客主要写一下树莓派opencv的使用,在这里我不会赘述opencv的安装,这个网上有很多教程。
这里并不是最终版本,树莓派专用电池还没有到,我就没有装在车上。在调试opencv代码的时候,我是先将摄像头搭载在小车上,再把摄像头接在电脑上,这样进行调试的时候会最贴合小车行进中所识别到的路线
在调试过程中,我用A4纸贴了一些标识,通过这些标识的移动来模拟小车行进过程,并在电脑上微调出最终代码
这里有一个很大很大的坑,我和队友在电赛中吃了大亏,我们采用的是树莓派的USB接口配合USB转TTL连接STM32F103RCT6下位机,我们之前采用了无线串口,这就不可避免的产生了会有干扰,我们一直以为是代码问题,不停地试不停地找,最后才发现是无线串口的问题,在此奉劝大家一句,能用有线解决的问题就不要用无线,尽可能避开一切干扰的选项。
首先为了能用python体系下的串口,我们首先要进行串口配置,我在树莓派上下载了一个pycharm,我在pycharm的终端直接输入以下命令,等待下载即可,只不过我提前换过了源,速度比较快,这里我就不再给大家赘述如何换源了
pip3 install pyserial
此外,使用USB转TTL也要在python代码里引入serial库
import serial
同时,我们还要在树莓派命令行中输入以下命令,查看我们的串口号是多少
ls -l /dev/tty*
不出意外的话是 /dev/ttyUSB0 但是大家一定要根据自己树莓派的串口来,不要直接复制
ser = serial.Serial('/dev/ttyUSB0',2400)
我在这里给大家罗列一下串口通信中比较常见的函数
下面是摘取自我的代码一段发送数据的命令,所用到的函数是.write函数,只不过要发送给下位机的时候,一定要注意发送的必须是字符串
ser = serial.Serial('/dev/ttyUSB0',2400)
ser.write("r:0000l:0000\r\n".encode())
如果发送的不是字符串该如何处理呢,这里我截取了我代码里的一部分,可以用python中的强制转换str()函数,将其转换为字符串
right_param = 1999 + (direction*4) #这个参数可以后期更改
light_param = 1999 #我这里写为6999是为了给STM32下位机来发送后退
final_param = 'r:' + str(light_param) + 'l:' + str(right_param) + '\r\n'
print(final_param)
ser.write(final_param.encode())
很奇葩的一点是,我们可能以为我们咋写的下位机收发函数,上位机就会接收到我们以为的数据,但结果却不是这样的,假如说我们发一个'ok\r\n',但实际上接收到的是b'ok\r\n',这个小坑大家一定注意
我的代码也是从网上更改过来的,网上的代码当把白色像素点改为黑色像素点的时候,会报一个错
IndexError: index 0 is out of bounds for axis 0 with size 0,稍作更改后,便不会再报这个错了
import cv2
import numpy as np
import serial
import time
# center定义
center = 320
ser = serial.Serial('/dev/ttyUSB0',2400)
#ser = serial.Serial('com5',2400)
# 打开摄像头,图像尺寸640*480(长*高),opencv存储值为480*640(行*列)
cap = cv2.VideoCapture(0)
while (1):
ret, frame = cap.read()
# 转化为灰度图
if ret == False: # 如果是最后一帧这个值为False
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 大津法二值化
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
# 膨胀,白区域变大
dst = cv2.dilate(dst, None, iterations=2)
# # 腐蚀,白区域变小
# dst = cv2.erode(dst, None, iterations=6)
cv2.imshow("dst",dst)
# 单看第400行的像素值,第400行像素就越等于图片的底部
color = dst[400]
# 找到黑色的像素点个数
black_count = np.sum(color == 0)
# 找到黑色的像素点索引
black_count_judge = np.sum(color == 255)#利用这个变量来查找摄像头是否观察到黑色
if black_count_judge == 640:
print("黑色像素点为0")
time.sleep(0.2)
ser.write("r:0000l:0000\r\n".encode())#在这里我加上了串口
pass
else:
black_index = np.where(color == 0)
# 防止white_count=0的报错
if black_count == 0:
black_count = 1
# 找到黑色像素的中心点位置
center = (black_index[0][black_count - 1] + black_index[0][0]) / 2
direction = center - 302
#在实际操作中,我发现当黑线处于小车车体正中央的时候应该减去302
direction = int('%4d'%direction)
print(direction)
# 计算出center与标准中心点的偏移量
'''当黑线处于小车车体右侧的时候,偏移量为正值,黑线处于小车车体左侧的时候,偏移量为负值(处于小车视角)'''
if direction > 0:
right_param = 1999 + (direction*4) #这个参数可以后期更改
light_param = 1999
final_param = 'r:' + str(light_param) + 'l:' + str(right_param) + '\r\n'
print(final_param)
ser.write(final_param.encode())
else:
media = -direction
light_param = 1999 + (media * 4)
right_param = 1999
final_param = 'r:' + str(light_param) + 'l:' + str(right_param) + '\r\n'
print(final_param)
ser.write(final_param.encode())
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放清理
cap.release()
cv2.destroyAllWindows()