视觉循迹小车(旭日x3派、摄像头、循迹)

1、旭日x3派(烧录好系统镜像)

2、USB摄像头

3、TB6612

4、小车底盘(直流电机或直流减速电机)

 

视觉循迹原理

x3派读取摄像头图像,转换成灰度图像,从灰度图像中选择第 120 行(图像的一个水平线),遍历第120行的全部320列,根据像素值小于或大于阈值,将相应的值(0 或 1)添加到 date 列表中。最后根据小于阈值的像素个数和它们的总和来判断黑色赛道的位置,以此调节左右电机的转速实现循迹。

 

python代码

import Hobot.GPIO as GPIO

import time

import cv2

 

class EYE():

    def __init__(self):

        self.video = cv2.VideoCapture(8)  #打开索引为8的摄像头

        ret = self.video.isOpened()  #判断摄像头是否打开成功

        if ret:

            print("The video is opened.")

        else:

            print("No video.")

 

        codec = cv2.VideoWriter_fourcc( 'M', 'J', 'P', 'G' )   #设置参数

        self.video.set(cv2.CAP_PROP_FOURCC, codec)

        self.video.set(cv2.CAP_PROP_FPS, 30)

        self.video.set(cv2.CAP_PROP_FRAME_WIDTH, 672)

        self.video.set(cv2.CAP_PROP_FRAME_HEIGHT, 672)

 

        # 创建全屏窗口

        #cv2.namedWindow("Camera Feed", cv2.WND_PROP_FULLSCREEN)

        #cv2.setWindowProperty("Camera Feed", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

 

    def outmiss(self):

        _, img = self.video.read()  #从摄像头读取一帧图像

        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  #将图像转为灰度

        img = img[120]   #选择图像的第120行,一共240行。

 

        date = []

        for i in range(320):    #遍历每一列,一共320列

            if img[i] <= 64:   #如果当前列的像素值小于等于 64,将 1 添加到 date 列表,表示该像素是感兴趣的。

                date.append(1)

            elif img[i] > 64:  #如果当前列的像素值大于 64,将 0 添加到 date 列表,表示该像素不感兴趣。

                date.append(0)

 

        n = 0   #用于计算感兴趣的像素数量。  

        sum = 0   #用于计算感兴趣像素的列索引总和。

        for i in range(320):

            if date[i] == 1:

                sum += i   #如果该列的像素是感兴趣的(即 date[i] 为 1),则更新 sum 和 n。

                n += 1

        if n >= 18:

            return sum / n - 159.5

        else:

            return None

 

    def off(self):

 

        self.video.release()

 

class CTRL():

    def __init__(self, in1, in2, in3, in4, pa, pb):

        GPIO.setmode(GPIO.BOARD)

        GPIO.setwarnings(False)

 

        GPIO.setup(in1, GPIO.OUT)

        GPIO.setup(in2, GPIO.OUT)

        GPIO.setup(in3, GPIO.OUT)

        GPIO.setup(in4, GPIO.OUT)

 

        self.in1 = in1

        self.in2 = in2

        self.in3 = in3

        self.in4 = in4

 

        self.PWMA = GPIO.PWM(pa, 48000)

        self.PWMB = GPIO.PWM(pb, 48000)

 

    def drive(self, FL, FR):

        if FL >= 0:

            GPIO.output(self.in3, GPIO.HIGH)

            GPIO.output(self.in4, GPIO.LOW)

        elif FL < 0:

            GPIO.output(self.in4, GPIO.HIGH)

            GPIO.output(self.in3, GPIO.LOW)

 

        if FR >= 0:

            GPIO.output(self.in1, GPIO.HIGH)

            GPIO.output(self.in2, GPIO.LOW)

        elif FR < 0:

            GPIO.output(self.in2, GPIO.HIGH)

            GPIO.output(self.in1, GPIO.LOW)

 

        self.PWMA.ChangeDutyCycle(abs(FR))

        self.PWMB.ChangeDutyCycle(abs(FL))

        self.PWMA.start(abs(FR))

        self.PWMB.start(abs(FL))

 

    def stop(self):

        GPIO.output(self.in1, GPIO.LOW)

        GPIO.output(self.in2, GPIO.LOW)

        GPIO.output(self.in3, GPIO.LOW)

        GPIO.output(self.in4, GPIO.LOW)

 

        self.PWMA.ChangeDutyCycle(0)

        self.PWMB.ChangeDutyCycle(0)

        self.PWMA.start(0)

        self.PWMB.start(0)

 

    def clean(self):

        self.PWMB.stop()

        self.PWMA.stop()

        GPIO.cleanup()

        

class PID():

    def __init__(self,KP,KI,KD):

        self.KP = KP

        self.KI = KI

        self.KD = KD

        self.p1 , self.p2 = 0 , 0#保留一个帧的误差

        self.i = 0#积累误差初值

        

    def naosu(self,miss):

        if miss != None:

            self.p1 , self.p2 = self.p2 , miss #替换缓存的误差

            self.i += miss

            if self.i > 1000:

                self.i -= 800

            if self.i < -1000:

                self.i += 800#积累误差的限制

            naosu = self.KP * miss + self.KI * self.i + self.KD * (self.p2 - self.p1)

            #按照公式输出

            return naosu

            

        elif miss == None:

        #摄像头读空时,根据上一帧的缓存误差正负,来判断现在应该原地左转还是右转

            if self.p2 >= 0:

                self.p1 , self.p2 = self.p2 , 1

                return "r"

                

            elif self.p2 < 0:

                self.p1 , self.p2 = self.p2 , -1

                return "l"

 

if __name__ == '__main__':

    try:

        Ctrl = CTRL(11, 13, 16, 15, 32, 33)  # 设置管脚

        Eye = EYE()  # 调用视觉模块

        Pid = PID(0.095,0.001,0.52)#调用PID,传入参数

        Ctrl.drive(25, 25)  # 小车的始发运动

        time.sleep(0.5)

        while True:

            ms = Eye.outmiss()  # 获取误差

            ns = Pid.naosu(ms)#获取修正值

            if ns == "r":#原地转弯的情况

                Ctrl.drive(20,-20)

            elif ns == "l":

                Ctrl.drive(-20,20)

            else:#限制修正值,保证不超过PWM上下限

                if ns > 18:

                    ns = 18

                if ns < -18:

                    ns = -18

                    

                Ctrl.drive(25+ns, 25-ns)  # 小车的始发运动

         

            # 添加代码来显示摄像头捕获的图像

            _, frame = Eye.video.read()

            cv2.imshow("Camera Feed", frame)

            time.sleep(0.2)

            if cv2.waitKey(1) & 0xFF == ord('q'):

                break

                

                

    finally:

        Ctrl.stop()

        Ctrl.clean()

        Eye.off()

        cv2.destroyAllWindows()

相关推荐

  1. 视觉小车旭日x3摄像头

    2024-03-28 09:56:02       44 阅读
  2. 视觉小车旭日x3、opencv)

    2024-03-28 09:56:02       38 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-28 09:56:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-28 09:56:02       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-28 09:56:02       82 阅读
  4. Python语言-面向对象

    2024-03-28 09:56:02       91 阅读

热门阅读

  1. 2023.03.28

    2024-03-28 09:56:02       45 阅读
  2. 软考 - 软件架构设计师 - 架构风格

    2024-03-28 09:56:02       43 阅读
  3. 【React】React 组件 API

    2024-03-28 09:56:02       46 阅读
  4. 深入理解 React 中的 children props 和 render props

    2024-03-28 09:56:02       51 阅读
  5. 11 React 组件通信 父传子

    2024-03-28 09:56:02       40 阅读
  6. React系列之常用ReactHook

    2024-03-28 09:56:02       38 阅读
  7. MySQL 8.0 支持对单个数据库设置只读!

    2024-03-28 09:56:02       40 阅读
  8. MySQL数据库基础篇-SQL

    2024-03-28 09:56:02       42 阅读
  9. 影视接口支持资源站去插播,官解官替,切片

    2024-03-28 09:56:02       41 阅读
  10. log4js里numBackups设置存在无效的情况

    2024-03-28 09:56:02       41 阅读
  11. Linux常用命令详细讲解

    2024-03-28 09:56:02       37 阅读
  12. RabbitMQ相关总结

    2024-03-28 09:56:02       40 阅读
  13. linux动态库的加载

    2024-03-28 09:56:02       43 阅读
  14. linux命令ab测试你 Apache http 服务器的工具

    2024-03-28 09:56:02       37 阅读