樹莓派綜合項目2:智能小車(六)黑線循跡

一、介紹

??閱讀本篇文章前建議先參考前期文章:
??樹莓派基礎實驗34:L298N模塊驅動直流電機實驗,學習了單個電機的簡單驅動。
??樹莓派綜合項目2:智能小車(一)四輪驅動,實現了代碼輸入對四個電機的簡單控制。
??樹莓派綜合項目2:智能小車(二)tkinter圖形界面控制,實現了本地圖形界面控制小車的前進后退、轉向和原地轉圈。
??樹莓派綜合項目2:智能小車(三)無線電遙控,實現了無線電遙控設備控制小車的前進后退、轉向和原地轉圈。
??樹莓派綜合項目2:智能小車(四)超聲波避障,實現了超聲波傳感器實時感知小車前方障礙物的距離。
??樹莓派綜合項目2:智能小車(五)紅外避障,實現了紅外光電傳感器探測前方是否存在障礙物。
??本實驗中將使用HJ-IR1紅外循跡模塊。循跡模塊的紅外發(fā)射二極管不斷發(fā)射紅外線,放射出的紅外線被物體反射后,被紅外接收器接收,并輸出信號給樹莓派處理,再對電機驅動模塊進行控制,實現通過對黑線和小車位置的判斷,控制小車沿黑線行進。
??這樣的循跡小車又稱為簡單的循跡機器人,比如餐廳的機器人服務員、農場的投食機器人、瓜果采摘機器人等等。

??其它基礎內容可以參考文集:樹莓派基礎實驗

二、組件

★Raspberry Pi 3 B+全套*1

★睿思凱Frsky XM+ 迷你接收機*1

★電平反向器模塊*1

★睿思凱Frsky Taranis X9D PLUS SE2019遙控器*1

★L298N擴展板模塊*1

★智能小車底板模塊*1

★減速電機和車輪*4

★HC-SR04超聲波模塊*1

★HJ-IR2紅外光電傳感器*2

★HJ-IR1紅外循跡模塊*2

★跳線若干

三、實驗原理

HJ-IR1紅外循跡模塊
HJ-IR1紅外循跡模塊

常見有三種循跡方法:
紅外對管循跡法:利用黑白色對紅外線的吸收作用不同;
攝像頭循跡法:利用攝像頭讀取賽道信息,分為模擬和數字;
激光對管循跡法:和紅外循跡法原理相似,但是檢測距離遠;
本實驗中使用的是第一種,紅外對管循跡法,白色地面反射的紅外信號強度大于黑色地面的反射。

黑色反射的情況

白色反射的情況

??將兩路探頭布置在智能車前方,探頭朝下,調節(jié)可調電阻,讓白色地面和黑色地面會產生不同的信號。若沒有被任何一個探頭檢測到障礙物,小車直行;左邊探頭檢測到障礙物時小車向右轉,右邊探頭檢測到障礙物時小車向左轉。

安裝循跡模塊

??關于紅外傳感器的基礎知識請參見樹莓派基礎實驗28:紅外避障傳感器實驗

??實際試驗過程中發(fā)現,小車在彎曲的黑線上循跡時,容易沖出黑線,一方面可能是速度過快,另一方面可能是循環(huán)間隙時間太長。速度過快降低速度就好,循跡間隙時間太長可能是由于檢測循跡模塊信號的頻率太慢,當檢測到偏離黑線時,小車已經沖出黑線了。

??這可能是由于檢測循跡模塊信號的循環(huán)語句里包含了其它傳感器的檢測,或者是為了檢測其它傳感器,間隔一定時間才運行檢測循跡模塊信號的循環(huán)語句。所以我使用多線程編程技術,將檢測循跡模塊信號的函數創(chuàng)建為一個單獨的線程,不與其它傳感器或者其它循環(huán)相干擾,或者并行運行,使得循跡控制非常靈敏。

四、實驗步驟

??第1步: 連接電路。這里對黑線循跡外的連線方法不在累述,請參考樹莓派綜合項目2:智能小車(四)超聲波避障。

樹莓派(name) 樹莓派(BOARD) 循跡模塊
GPIO.23 33 左側循跡模塊輸出
GPIO.24 35 右側循跡模塊輸出
5V 5V 兩個模塊的VCC
GND GND 兩個模塊的GND
加裝循跡模塊的小車
底部安裝距離

??第2步: motor_4w.py,moving_control.py,sbus_receiver_pi.py,ultrasonic.py,infrared.py文件的編碼這里不再累述,參考樹莓派綜合項目2:智能小車(四)超聲波避障。

??第3步:編寫循跡模塊,文件名為tracking.py,與樹莓派基礎實驗33:TCRT5000紅外循跡傳感器實驗中的Python程序原理基本相同。Tracking()類中的方法tracking_detect()可以返回兩個值,當檢測到紅外反射時值為0,當沒有檢測到紅外反射時值為1。
tracking.py:

#!/usr/bin/env python3
#-*- coding: utf-8 -*-
#本模塊只含Tracking()一個類,用于塊測出是否有障礙物
#有障礙物時返回值0,無障礙物時返回值1

import RPi.GPIO as GPIO
import time
class Tracking():
    
    trackingPinLeft = 33  #左側模塊的輸出連接樹莓派33號針腳
    trackingPinRight = 35

    def setup(self):
        GPIO.setmode(GPIO.BOARD)
        GPIO.setup(Tracking.trackingPinLeft, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(Tracking.trackingPinRight, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    def tracking_detect(self):
        tracking_left_value = 1
        tracking_right_value = 1
        if (0 == GPIO.input(Tracking.trackingPinLeft)):  # 當檢測到紅外反射,即白色地面時,輸出低電平信號
            tracking_left_value = 0
        if (0 == GPIO.input(Tracking.trackingPinRight)):  #當檢測到紅外反射時,即白色地面時,輸出低電平信號
            tracking_right_value = 0
        return tracking_left_value,tracking_right_value
 
    def destroy(self):
        GPIO.cleanup()

if __name__ == "__main__":
    
    try:
        tracking = Tracking()
        tracking.setup()
        while True:
            tracking_left_value,tracking_right_value = tracking.tracking_detect()
            print('tracking_left_value=%d tracking_right_value=%d'%(tracking_left_value,tracking_right_value) )
            #time.sleep(0.1)
    except KeyboardInterrupt:
        tracking.destroy()

??第4步: 編寫樹莓派智能小車的主程序,文件名為smartcar.py,將這5個Python文件放入一個文件夾后,只運行本文件就可以了。
??主程序中加入了tracking_control()循跡控制函數,實現了沿黑線自動行進。引入了python多線程模塊threading,啟動線程后,tracking_control()函數中的while True:語句會一直運行,但是只有當全局變量exitFlag_tracking為0時,循跡控制才會實際執(zhí)行。即當SA開關打到下檔位置時,exitFlag_tracking為0,不為下檔時,exitFlag_tracking = 1,循跡控制暫停,由超聲波等傳感器和遙控器控制小車行進。通過SA開關可以雙向隨時切換控制。
smartcar.py:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 本模塊為樹莓派小車的主程序,
from motor_4w import Motor_4w
from sbus_receiver_pi import SBUSReceiver
from moving_control import MovingControl
from ultrasonic import Ultrasonic
from infrared import Infrared
from tracking import Tracking
import time
import threading

sbus_receiver = SBUSReceiver('/dev/ttyAMA0') # 初始化SBUS信號接收實例
smp_car = Motor_4w()  # 初始化電機控制實例
smp_car.setGPIO()     # 初始化引腳

ENA_pwm = smp_car.pwm(smp_car.ENA)  # 初始化使能信號PWM,A為左邊車輪
ENB_pwm = smp_car.pwm(smp_car.ENB)  # 初始化使能信號PWM,B為右邊車輪
    
smartcar = MovingControl(smp_car,ENA_pwm,ENB_pwm)  # 初始化車輛運動控制實例

ultr = Ultrasonic()  # 初始化超聲波實例
ultr.setup()         # 初始化超聲波引腳

infrared = Infrared()  # 初始化紅外避障實例
infrared.setup()       # 初始化紅外避障引腳

tracking = Tracking()
tracking.setup()


def ultra_control():
    """超聲波傳感器控制"""
    # 如果測距小于10cm時,先停車再倒車0.3秒
    smartcar.brake()
    smartcar.reverse()
    time.sleep(0.3)

        
def infra_control(infra_left_value,infra_right_value):
    """紅外避障傳感器控制"""
    if infra_left_value == 0:  # 值為0時,表示左側檢測到障礙物
        # smartcar.acc_value=80
        smartcar.accelerator(1, 0.5)  # 控制小車向右偏轉
        time.sleep(0.5)             # 向右偏轉行進0.5秒
    if infra_right_value == 0:
        # smartcar.acc_value=80
        smartcar.accelerator(0.5, 1)
        time.sleep(0.5)
    print('infra_left_value=%d infra_right_value=%d'%(infra_left_value,infra_right_value) )


exitFlag_tracking = 1    # 是否退出循跡while循環(huán)的標志,0為不退出
def tracking_control():
    """紅外循跡傳感器,控制小車沿黑線自動行進"""
    while True:
        time.sleep(0.5)   # 為節(jié)省計算資源,不讓循環(huán)過于太快
        while not exitFlag_tracking:
            smartcar.acc_value = 40
            tracking_left_value,tracking_right_value = tracking.tracking_detect()
            if tracking_left_value == 0 and tracking_right_value == 0:  # 值都為0時,表示都檢測到紅外反射,直行
                smartcar.accelerator()  # 讓油門左右一致
                smartcar.forward()      # 控制小車直線前進

            if tracking_left_value == 1 and tracking_right_value == 0:  # 左側檢測不到反射信號,即遇到黑線,車向左偏轉
                smartcar.accelerator(0, 1)
                smartcar.forward()

            if tracking_right_value == 1 and tracking_left_value == 0:  # 右側檢測不到反射信號,即遇到黑線,車向右偏轉
                smartcar.accelerator(1, 0)
                smartcar.forward()

            if tracking_left_value == 1 and tracking_right_value == 1:  # 值都為1時,表示都沒有檢測到紅外反射,停車
                smartcar.brake()  # 控制小車停車

def sbus_control():
    """無線電控制"""
    aileron_value = sbus_receiver.get_rx_channel(0)   # 1通道為副翼通道,這里控制車原地轉向
    elevator_value = sbus_receiver.get_rx_channel(1)  # 2通道為升降舵通道,這里控制車前進后退
    rudder_value = sbus_receiver.get_rx_channel(3)    # 4通道為方向舵通道,這里控制車左右偏移行進

    acc_value_sbus = 172  # 3通道,即油門的值,最低時為172
    if sbus_receiver.get_rx_channel(2):
        acc_value_sbus = sbus_receiver.get_rx_channel(2)  # 3通道為油門通道,這里控制車速度
    # 將172~1811的油門通道值轉換為0~100的占空比信號,
    smartcar.acc_value = int(100*(acc_value_sbus-172)/(1811-172))
    print('original.acc_value=', smartcar.acc_value)  # 檢測SBUS信號的油門值
    smartcar.accelerator()
    print('disposed.acc_value=', smartcar.acc_value)  # 檢測超聲波函數處理后的油門值
      
    if 970 < rudder_value < 1100:  # 沒有左右偏移時,中間值為992,但遙控器微調時會有上下浮動
        pass                       # 沒有左右偏移時
    elif rudder_value >= 1100:      # 向右偏移行進時
        rate_right = (1811.0 - rudder_value)/(1811-1100) 
        # 最大值一般為1811,這里使用浮點類型,所以一定要使用1811.0
        smartcar.accelerator(1, rate_right)
    elif rudder_value <= 970:       # 向左偏移行進時
        rate_left = (rudder_value - 172.0)/(970-172)  
        # 最小值為172,這里使用浮點類型,所以一定要使用172.0
        smartcar.accelerator(rate_left, 1)
    print('elevator_value=%d,aileron_value=%d,rudder_value=%d'%(elevator_value,aileron_value,rudder_value))#測試數據用
            
    if aileron_value >= 1100:  # 原地左轉彎
        smartcar.leftTurn()
        smartcar.accelerator()
    elif aileron_value <= 970:  # 原地右轉彎
        smartcar.rightTurn()
        smartcar.accelerator()
    else:
        smartcar.brake()       # 停車,這里用了else,后面不需要再停車了
                
    if elevator_value >= 1100:   # 前進
        smartcar.forward()
    elif elevator_value <= 970:  # 后退
        smartcar.reverse()
        # 循環(huán)最后,這里不能再用停車了


try:
    # 創(chuàng)建循跡控制的線程,讓tracking_control()函數始終處于運行狀態(tài)
    myThread_tracking = threading.Thread(target=tracking_control)
    myThread_tracking.start()  # 啟動線程
    while True:
        sbus_receiver.update()  # 獲取一個完整的幀數據
        tracking_value = sbus_receiver.get_rx_channel(4)  # 5通道為是否循跡的開關,打開后則按循跡自動行進
        # print('tracking_value=',tracking_value)
        if tracking_value > 1800:  # 當SA開關打到下檔位置時,get_rx_channel(4)的值為1811
            exitFlag_tracking = 0  # 讓循跡函數線程中的while循環(huán)開始,則由循跡函數線程控制小車行進
            
        else:
            exitFlag_tracking = 1  # 讓循跡函數線程中的while循環(huán)結束,開始由遙控器和其它傳感器控制小車行進
            dis = ultr.distance()
            # print('distance= %.1f cm\n' % dis)
            if dis < 10:
                ultra_control()  # 超聲波避障
        
            infra_left_value,infra_right_value = infrared.infra_detect()    
            if infra_left_value == 0 or infra_right_value == 0:
                infra_control(infra_left_value,infra_right_value)

            sbus_control()  # 遙控器控制小車行進

except KeyboardInterrupt:  
    smp_car.destroy(ENA_pwm,ENB_pwm)
finally:
    smp_car.destroy(ENA_pwm,ENB_pwm)


自動黑線循跡

這個項目的代碼90%是我原創(chuàng)瞎寫的,有需要參考的同學可以下載:
樹莓派智能小車項目python源代碼下載

這個小項目就到這里截止了,本來想再融入舵機,攝像頭,圖像識別,網絡編程控制等等,不過上一次實驗就不小心把L298N擴展板燒了,雖然經過我不專業(yè)的維修后可以繼續(xù)使用,但車身太小,空間有限,決定重新選一款小車項目套件,也看看別的專家是怎么編寫代碼的,謝謝大家的支持點贊!


?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容