电赛总结之上位机(树莓派python-opencv实时识别黑线)

前言:

        在准备电赛的这段时间里,我确实学到了很多东西,我会一点一点更新博客,从头到尾总结一下,本篇博客主要写一下树莓派opencv的使用,在这里我不会赘述opencv的安装,这个网上有很多教程。

树莓派小车图片:

这里并不是最终版本,树莓派专用电池还没有到,我就没有装在车上。在调试opencv代码的时候,我是先将摄像头搭载在小车上,再把摄像头接在电脑上,这样进行调试的时候会最贴合小车行进中所识别到的路线

 在调试过程中,我用A4纸贴了一些标识,通过这些标识的移动来模拟小车行进过程,并在电脑上微调出最终代码

 

 

树莓派4B与下位机的通信:

        这里有一个很大很大的坑,我和队友在电赛中吃了大亏,我们采用的是树莓派的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()


  • 14
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值