【方法】树莓派小车自动循迹(摄像头)

本教程介绍如何使用树莓派与L298N电机控制器制作一个能够自动循迹的小车。通过摄像头捕获图像,利用OpenCV进行二值化处理,识别白色赛道并调整小车方向。代码详细展示了GPIO控制及摄像头图像处理流程。

Github上有更多代码,按需自取

文章目录


今天我们来介绍一下树莓派小车的循迹教程
首先看一个效果视频
在这里插入图片描述

说明

该小车的硬件是:树莓派+L298N,其实用Arduino也是一样的,下位机只提供一个车轮的控制,视觉识别都是通过树莓派完成的

视觉利用Opencv来实现,关于如何安装Opencv以及使用摄像头请参看我另一篇博客

算法逻辑:看到这样一个赛道,赛道是白色的,其余部分是我们都看成非白色,这样很自然而然地就想到了二值化,将赛道的白色单独显示出来,通过一个二值化就能够很好的区分开赛道与背景。(二值化参考博客
我们不需要分析整个摄像头范围,其实只需要选取图像中的一行像素,类似于一个线阵摄像头,中间部分是白色的,左右是黑色,所以只需要找到白色部分的中点,然后让小车一直朝着这个中点矫正就好了,如果偏左了,就让左轮加快速度,另一边同理。重点是过弯的时候,需要仔细调整这个速度,否则容易转不过去。

主要是GPIO口的一些操控,可以看我的另一篇博客

注意

尽量自己学会调试,查找问题,我的代码不是万能的,但你是万能的

一、腐蚀膨胀问题,原理我不在这里多解释了,自己选择是否腐蚀膨胀/先后顺序/迭代次数,会满足你不同的使用场景

二、转弯速度问题
30 + direction …
这些速度参数设置自己调节,甚至都可以采用缩放倍率

三、检查摄像头拍的图片
有发现拍出来的图最右边多了一列全白,解决办法有俩:1、先腐蚀,把白色去掉。2、别膨胀,把最后一列去掉,怎么去掉呢?white_index[0][white_count - 1] 把这个1改为2,相当于不去考虑最后一列

代码

# coding:utf-8

# 加入摄像头模块,让小车实现自动循迹行驶
# 思路为:摄像头读取图像,进行二值化,将白色的赛道凸显出来
# 选择下方的一行像素,黑色为0,白色为255
# 找到白色值的中点
# 目标中点与标准中点(320)进行比较得出偏移量
# 根据偏移量来控制小车左右轮的转速
# 考虑了偏移过多失控->停止;偏移量在一定范围内->高速直行(这样会速度不稳定,已删)

import RPi.GPIO as gpio
import time
import cv2
import numpy as np

# 定义引脚
pin1 = 12
pin2 = 16
pin3 = 18
pin4 = 22

# 设置GPIO口为BOARD编号规范
gpio.setmode(gpio.BOARD)

# 设置GPIO口为输出
gpio.setup(pin1, gpio.OUT)
gpio.setup(pin2, gpio.OUT)
gpio.setup(pin3, gpio.OUT)
gpio.setup(pin4, gpio.OUT)

# 设置PWM波,频率为500Hz
pwm1 = gpio.PWM(pin1, 500)
pwm2 = gpio.PWM(pin2, 500)
pwm3 = gpio.PWM(pin3, 500)
pwm4 = gpio.PWM(pin4, 500)

# pwm波控制初始化
pwm1.start(0)
pwm2.start(0)
pwm3.start(0)
pwm4.start(0)

# center定义
center = 320
# 打开摄像头,图像尺寸640*480(长*高),opencv存储值为480*640(行*列)
cap = cv2.VideoCapture(0)
while (1):
    ret, frame = cap.read()
    # 转化为灰度图
    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)

    # 单看第400行的像素值
    color = dst[400]
    # 找到白色的像素点个数
    white_count = np.sum(color == 255)
    # 找到白色的像素点索引
    white_index = np.where(color == 255)

    # 防止white_count=0的报错
    if white_count == 0:
        white_count = 1

    # 找到白色像素的中心点位置
    center = (white_index[0][white_count - 1] + white_index[0][0]) / 2

    # 计算出center与标准中心点的偏移量
    direction = center - 320

    print(direction)

    # 停止
    if abs(direction) > 250:
        pwm1.ChangeDutyCycle(0)
        pwm2.ChangeDutyCycle(0)
        pwm3.ChangeDutyCycle(0)
        pwm4.ChangeDutyCycle(0)

    # 右转
    elif direction >= 0:
        # 限制在70以内
        if direction > 70:
            direction = 70
        pwm1.ChangeDutyCycle(30 + direction)
        pwm2.ChangeDutyCycle(0)
        pwm3.ChangeDutyCycle(30)
        pwm4.ChangeDutyCycle(0)

    # 左转
    elif direction < 0:
        if direction < -70:
            direction = -70
        pwm1.ChangeDutyCycle(30)
        pwm2.ChangeDutyCycle(0)
        pwm3.ChangeDutyCycle(30 - direction)
        pwm4.ChangeDutyCycle(0)

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

# 释放清理
cap.release()
cv2.destroyAllWindows()
pwm1.stop()
pwm2.stop()
pwm3.stop()
pwm4.stop()
gpio.cleanup()

实现基于 Raspberry Pi 的循迹功能,通常需要结合传感器(如红外或摄像头)来检测路径,再通过 Raspberry Pi 控制电机驱动小车沿路径行驶。以下是具体的实现步骤和技术要点: ### 1. 硬件需求 - **Raspberry Pi**:作为主控单元,建议使用 Raspberry Pi 4 或更高版本以获得更好的性能。 - **循迹传感器**:红外线传感器模块(如 TCRT5000)或摄像头模块(如 Raspberry Pi Camera Module)。 - **电机驱动模块**:如 L298N 或 TB6612FNG,用于控制直流电机。 - **直流电机和轮子**:用于小车的移动。 - **电源模块**:为 Raspberry Pi 和电机供电。 - **连接线和面包板**:用于硬件连接。 ### 2. 软件环境 - **操作系统**:Raspberry Pi OS(基于 Debian 的 Linux 系统)。 - **编程语言**:Python 是主流选择,适合快速开发。 - **库支持**: - `RPi.GPIO`:用于 GPIO 引脚操作。 - `opencv-python`:如果使用摄像头进行图像处理。 - `numpy`:用于数值计算。 ### 3. 传感器选择与数据采集 #### 红外循迹传感器 红外传感器通常包含一个红外发射器和接收器,当传感器位于黑线(低反射率)上时,反射的红外光较少,接收器输出低电平;当位于白线(高反射率)上时,反射较多,输出高电平。多个传感器可组成传感器阵列,用于检测偏移方向[^3]。 示例代码(使用 `RPi.GPIO` 读取传感器状态): ```python import RPi.GPIO as GPIO import time # 设置 GPIO 模式 GPIO.setmode(GPIO.BCM) # 定义传感器引脚 sensor_pin = 17 GPIO.setup(sensor_pin, GPIO.IN) try: while True: # 读取传感器状态 sensor_value = GPIO.input(sensor_pin) print("Sensor Value:", sensor_value) time.sleep(0.1) except KeyboardInterrupt: GPIO.cleanup() ``` #### 摄像头循迹 使用 OpenCV 进行图像处理,可以识别特定颜色或形状的路径。摄像头提供更高的灵活性,但计算量较大。 示例代码(使用 OpenCV 识别黑线): ```python import cv2 import numpy as np # 打开摄像头 cap = cv2.VideoCapture(0) while True: ret, frame = cap.read() # 转换为灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 二值化处理 _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV) # 查找轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if len(contours) > 0: # 找到最大轮廓 largest_contour = max(contours, key=cv2.contourArea) # 计算轮廓的矩 M = cv2.moments(largest_contour) if M["m00"] != 0: # 计算轮廓的中心点 cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) print("Center:", (cx, cy)) # 显示图像 cv2.imshow('Frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ``` ### 4. 控制电机 通过电机驱动模块控制小车的前进、转向。根据传感器数据调整左右电机的速度,使小车保持在路径上。 示例代码(使用 `RPi.GPIO` 控制 L298N 电机驱动模块): ```python import RPi.GPIO as GPIO import time # 设置 GPIO 模式 GPIO.setmode(GPIO.BCM) # 定义电机引脚 in1 = 24 in2 = 25 ena = 23 GPIO.setup(in1, GPIO.OUT) GPIO.setup(in2, GPIO.OUT) GPIO.setup(ena, GPIO.OUT) # 创建 PWM 对象 pwm = GPIO.PWM(ena, 100) # 频率为 100Hz pwm.start(0) def move_forward(speed): GPIO.output(in1, GPIO.HIGH) GPIO.output(in2, GPIO.LOW) pwm.ChangeDutyCycle(speed) def turn_left(speed): GPIO.output(in1, GPIO.LOW) GPIO.output(in2, GPIO.HIGH) pwm.ChangeDutyCycle(speed) def stop(): GPIO.output(in1, GPIO.LOW) GPIO.output(in2, GPIO.LOW) pwm.ChangeDutyCycle(0) try: while True: # 根据传感器数据调整方向 sensor_value = GPIO.input(sensor_pin) if sensor_value == 0: move_forward(50) else: turn_left(30) time.sleep(0.1) except KeyboardInterrupt: stop() GPIO.cleanup() ``` ### 5. 系统集成与调试 将传感器数据采集、图像处理和电机控制模块整合,形成完整的循迹系统。通过调试 PID 控制算法,优化小车的响应速度和稳定性。 ### 6. 可选功能 - **无线控制**:通过 Wi-Fi 或蓝牙实现远程监控和控制。 - **避障功能**:结合超声波传感器或激光雷达,避免小车撞到障碍物。 - **路径规划**:使用 SLAM(同步定位与地图构建)技术,实现更复杂的导航任务[^2]。
评论 131
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yzy_1996

买杯咖啡,再接再厉

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值