Obsah:

Autonómne auto na udržiavanie v jazdnom pruhu s použitím Raspberry Pi a OpenCV: 7 krokov (s obrázkami)
Autonómne auto na udržiavanie v jazdnom pruhu s použitím Raspberry Pi a OpenCV: 7 krokov (s obrázkami)

Video: Autonómne auto na udržiavanie v jazdnom pruhu s použitím Raspberry Pi a OpenCV: 7 krokov (s obrázkami)

Video: Autonómne auto na udržiavanie v jazdnom pruhu s použitím Raspberry Pi a OpenCV: 7 krokov (s obrázkami)
Video: Asistenční systém LKA 2024, November
Anonim
Autonómne auto na udržiavanie v jazdnom pruhu s použitím Raspberry Pi a OpenCV
Autonómne auto na udržiavanie v jazdnom pruhu s použitím Raspberry Pi a OpenCV

V týchto pokynoch bude implementovaný autonómny robot na udržiavanie v jazdnom pruhu, ktorý prejde týmito krokmi:

  • Zhromažďovanie dielov
  • Inštalácia softvérových predpokladov
  • Zostava hardvéru
  • Prvý test
  • Detekcia línií jazdných pruhov a zobrazenie vodiacej čiary pomocou openCV
  • Implementácia PD radiča
  • Výsledky

Krok 1: Zhromažďovanie komponentov

Zhromažďovanie komponentov
Zhromažďovanie komponentov
Zhromažďovanie komponentov
Zhromažďovanie komponentov
Zhromažďovanie komponentov
Zhromažďovanie komponentov
Zhromažďovanie komponentov
Zhromažďovanie komponentov

Vyššie uvedené obrázky zobrazujú všetky komponenty použité v tomto projekte:

  • RC auto: Moje som dostal z miestneho obchodu vo svojej krajine. Je vybavený 3 motormi (2 pre škrtenie a 1 pre riadenie). Hlavnou nevýhodou tohto auta je, že riadenie je obmedzené medzi „žiadne riadenie“a „úplné riadenie“. Inými slovami, nemôže riadiť v určitom uhle, na rozdiel od RC automobilov so servo riadením. Nájdete tu podobnú automobilovú súpravu navrhnutú špeciálne pre malinové pi.
  • Raspberry pi 3 model b+: toto je mozog auta, ktoré zvládne mnoho fáz spracovania. Základom je štvorjadrový 64-bitový procesor s taktom 1,4 GHz. Svoje mám odtiaľto.
  • Modul kamery Raspberry pi 5 mp: Podporuje nahrávanie 1080p @ 30 fps, 720p @ 60 fps a 640x480p 60/90. Podporuje tiež sériové rozhranie, ktoré je možné zapojiť priamo do Raspberry Pi. Nie je to najlepšia voľba pre aplikácie na spracovanie obrazu, ale pre tento projekt je postačujúca, pretože je veľmi lacná. Svoje mám odtiaľto.
  • Motor Driver: Používa sa na ovládanie smerov a rýchlostí jednosmerných motorov. Podporuje ovládanie 2 jednosmerných motorov na 1 doske a vydrží 1,5 A.
  • Power Bank (voliteľné): Na napájanie malinového pi zvlášť som použil energetickú banku (s napätím 5 V, 3 A). Na napájanie malinového pi z 1 zdroja by sa mal použiť prevodník typu down down (prevodník dolára: výstupný prúd 3A).
  • 3s (12 V) batéria LiPo: Lítium -polymérové batérie sú známe svojim vynikajúcim výkonom v oblasti robotiky. Slúži na napájanie vodiča motora. Kúpil som svoje odtiaľto.
  • Prepojovacie vodiče medzi mužmi a mužmi a ženy a ženy.
  • Obojstranná páska: Používa sa na montáž komponentov na RC auto.
  • Modrá páska: Toto je veľmi dôležitý komponent tohto projektu, ktorý sa používa na vytvorenie dvoch pruhov, medzi ktorými bude auto jazdiť. Môžete si vybrať akúkoľvek farbu, ktorú chcete, ale odporúčam vybrať si iné farby, než aké sú v okolitom prostredí.
  • Kravaty na zips a drevené tyče.
  • Skrutkovač.

Krok 2: Inštalácia OpenCV na Raspberry Pi a nastavenie vzdialeného displeja

Inštalácia OpenCV na Raspberry Pi a nastavenie vzdialeného displeja
Inštalácia OpenCV na Raspberry Pi a nastavenie vzdialeného displeja

Tento krok je trochu nepríjemný a bude nejaký čas trvať.

OpenCV (Open source Computer Vision) je open source knižnica softvéru pre počítačové videnie a strojové učenie. Knižnica má viac ako 2 500 optimalizovaných algoritmov. Pri inštalácii openCV na váš Raspberry Pi a pri inštalácii Raspberry Pi OS postupujte podľa TÚTO veľmi jednoduchej príručky (ak ste to ešte neurobili). Upozorňujeme, že proces budovania openCV môže v dobre chladenej miestnosti trvať 1,5 hodiny (pretože teplota procesora bude veľmi vysoká!), Dajte si čaj a trpezlivo čakajte: D.

Pokiaľ ide o vzdialený displej, postupujte podľa TENTOHO sprievodcu nastavením vzdialeného prístupu k vášmu malinovému pi z vášho zariadenia Windows/Mac.

Krok 3: Spojenie dielov dohromady

Spojenie dielov dohromady
Spojenie dielov dohromady
Spojenie dielov dohromady
Spojenie dielov dohromady
Spojenie dielov dohromady
Spojenie dielov dohromady

Vyššie uvedené obrázky zobrazujú prepojenia medzi malinou pi, kamerovým modulom a ovládačom motora. Upozorňujeme, že motory, ktoré som použil, absorbujú 0,35 A pri 9 V, vďaka čomu je pre vodiča motora bezpečné prevádzkovať 3 motory súčasne. A pretože chcem ovládať rýchlosť 2 škrtiacich motorov (1 zadný a 1 predný) úplne rovnakým spôsobom, pripojil som ich k rovnakému portu. Pomocou dvojitej pásky som namontoval ovládač motora na pravú stranu auta. Pokiaľ ide o modul kamery, medzi otvory pre skrutky som vložil zips, ako ukazuje obrázok vyššie. Potom kameru namontujem na drevenú tyč, aby som si mohol prispôsobiť polohu kamery, ako chcem. Skúste kameru čo najviac nainštalovať do stredu auta. Fotoaparát odporúčam umiestniť najmenej 20 cm nad zem, aby sa zlepšilo zorné pole pred autom. Fritzingova schéma je priložená nižšie.

Krok 4: Prvý test

Prvý test
Prvý test
Prvý test
Prvý test

Testovanie fotoaparátu:

Akonáhle je kamera nainštalovaná a postavená knižnica openCV, je čas otestovať náš prvý obrázok! Odfotíme fotografiu z pi cam a uložíme ju ako „original.jpg“. To je možné vykonať dvoma spôsobmi:

1. Použitie terminálových príkazov:

Otvorte nové okno terminálu a zadajte nasledujúci príkaz:

raspistill -o original.jpg

Tým sa nasníma statický obrázok a uloží sa do adresára „/pi/original.jpg“.

2. Použitie ľubovoľného IDE pythonu (používam IDLE):

Otvorte nový náčrt a napíšte nasledujúci kód:

import cv2

video = cv2. VideoCapture (0) while True: ret, frame = video.read () frame = cv2.flip (frame, -1) # used to turn the image vertically cv2.imshow ('original', frame) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Pozrime sa, čo sa stalo v tomto kóde. Prvý riadok je import našej knižnice openCV, aby mohla používať všetky svoje funkcie. funkcia VideoCapture (0) začne streamovať živé video zo zdroja určeného touto funkciou, v tomto prípade je to 0, čo znamená kameru raspi. Ak máte viacero kamier, mali by ste zadať rôzne čísla. Video.read () bude čítať každý snímok pochádzajúci z fotoaparátu a uloží ho do premennej nazývanej „rámček“. funkcia flip () prevráti obrázok vzhľadom na os y (vertikálne), pretože kameru montujem inverzne. imshow () zobrazí naše snímky so slovom „originál“a imwrite () uloží našu fotografiu ako original.jpg. waitKey (1) počká 1 ms na stlačenie ľubovoľného tlačidla klávesnice a vráti jeho kód ASCII. ak stlačíte tlačidlo Esc (esc), vráti sa desatinná hodnota 27 a podľa toho preruší slučku. Video.release () zastaví nahrávanie a zničí AllWindows () zatvorí každý obrázok otvorený pomocou funkcie imshow ().

Odporúčam otestovať vašu fotografiu druhou metódou, aby ste sa zoznámili s funkciami openCV. Obrázok je uložený v adresári "/pi/original.jpg". Pôvodná fotografia, ktorú môj fotoaparát urobil, je uvedená vyššie.

Testovanie motorov:

Tento krok je dôležitý pre určenie smeru otáčania každého motora. Najprv si stručne predstavme pracovný princíp vodiča motora. Vyššie uvedený obrázok zobrazuje pin-out vodiča motora. Aktivácia A, vstup 1 a vstup 2 sú spojené s ovládaním motora A. Povoliť B, vstup 3 a vstup 4 sú spojené s ovládaním motora B. Ovládanie smeru je stanovené v časti „Vstup“a ovládanie rýchlosti je určené v časti „Povoliť“. Ak chcete napríklad ovládať smer motora A, nastavte vstup 1 na HIGH (v tomto prípade 3,3 V, pretože používame malinový pi) a nastavte vstup 2 na LOW, motor sa bude otáčať v konkrétnom smere a nastavením opačných hodnôt. na vstup 1 a vstup 2 sa motor bude otáčať v opačnom smere. Ak je vstup 1 = vstup 2 = (HIGH alebo LOW), motor sa neotáča. Povoliť kolíky odoberajú vstupný signál modulácie šírky impulzu (PWM) z maliny (0 až 3,3 V) a príslušne spustia motory. Napríklad signál 100% PWM znamená, že pracujeme na maximálnych otáčkach a signál 0% PWM znamená, že motor sa neotáča. Nasledujúci kód sa používa na určenie smerov motorov a testovanie ich rýchlostí.

čas importu

importujte RPi. GPIO ako GPIO GPIO.setwarnings (False) # Kolíky motora riadenia riadenia_enable = 22 # Fyzický kolík 15 in1 = 17 # Fyzický kolík 11 in2 = 27 # Fyzický kolík 13 # Motory škrtiacej klapky kolíky throttle_enable = 25 # Fyzický kolík 22 in3 = 23 # fyzický kolík 16 in4 = 24 # fyzický kolík 18 GPIO.setmode (GPIO. BCM) # namiesto fyzického číslovania používajte číslovanie GPIO GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. setup (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (volant_enable, GPIO.out) # Steering Motor Control GPIO.output (in1, GPIO. VYSOKÉ) GPIO.output (in2, GPIO. LOW) riadenie = GPIO. PWM (volant_enable, 1000) # nastavte spínaciu frekvenciu na 1000 Hz riadenie.stop () # Ovládanie škrtiacich motorov GPIO.output (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) throttle = GPIO. PWM (throttle_enable, 1000) # nastavte spínaciu frekvenciu na 1000 Hz throttle.stop () time.sleep (1) throttle.start (25) # naštartuje motor na 25 % Signálu PWM -> (0,25 * napätie batérie) - pre vodiča strata riadenia.start (100) # naštartuje motor na 100% signál PWM -> (1 * napätie batérie) - doba straty vodiča. spánok (3) plyn. stop () riadenie.zastavenie ()

Tento kód spustí škrtiace motory a motor riadenia na 3 sekundy a potom ich zastaví. (Stratu vodiča) je možné určiť pomocou voltmetra. Napríklad vieme, že 100% signál PWM by mal udávať úplné napätie batérie na svorke motora. Ale nastavením PWM na 100%som zistil, že vodič spôsobuje pokles 3 V a motor dostáva 9 V namiesto 12 V (presne to, čo potrebujem!). Strata nie je lineárna, tj strata 100% sa veľmi líši od straty 25%. Po spustení vyššie uvedeného kódu boli moje výsledky nasledujúce:

Výsledky škrtenia: ak in3 = HIGH a in4 = LOW, škrtiace motory budú mať rotáciu Clock-Wise (CW), t.j. auto sa pohne dopredu. V opačnom prípade sa auto pohne dozadu.

Výsledky riadenia: ak je in1 = HIGH a in2 = LOW, motor riadenia sa otočí maximálne doľava, t. J. Auto sa bude otáčať doľava. V opačnom prípade bude auto riadiť vpravo. Po niekoľkých experimentoch som zistil, že motor riadenia sa neotáča, ak signál PWM nebol 100% (t. J. Motor bude riadiť buď úplne doprava, alebo úplne doľava).

Krok 5: Detekcia línií jazdných pruhov a výpočet nadpisovej čiary

Detekcia jazdných pruhov a výpočet nadpisovej čiary
Detekcia jazdných pruhov a výpočet nadpisovej čiary
Detekcia jazdných pruhov a výpočet nadpisovej čiary
Detekcia jazdných pruhov a výpočet nadpisovej čiary
Detekcia jazdných pruhov a výpočet nadpisovej čiary
Detekcia jazdných pruhov a výpočet nadpisovej čiary

V tomto kroku bude vysvetlený algoritmus, ktorý bude ovládať pohyb auta. Prvý obrázok zobrazuje celý proces. Vstupom systému sú obrázky, výstupom je theta (uhol riadenia v stupňoch). Uvedomte si, že spracovanie sa vykoná na 1 obrázku a bude sa opakovať vo všetkých snímkach.

Fotoaparát:

Fotoaparát začne nahrávať video s rozlíšením (320 x 240). Odporúčam znížiť rozlíšenie, aby ste dosiahli lepšiu snímkovú frekvenciu (fps), pretože po použití techník spracovania na každý snímok dôjde k poklesu snímkovej frekvencie. Nasledujúci kód bude hlavnou slučkou programu a pridá každý krok k tomuto kódu.

import cv2

import numpy as np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # nastavte šírku na 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # nastavte výšku na 240 p # Slučka pri True: ret, frame = video.read () frame = cv2.flip (frame, -1) cv2.imshow ("original", frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Kód tu zobrazuje pôvodný obrázok získaný v kroku 4 a je zobrazený na obrázkoch vyššie.

Previesť na farebný priestor HSV:

Potom, čo ste zaznamenali video ako snímky z fotoaparátu, ďalším krokom je previesť každý rámec na farebný priestor Odtieň, Sýtosť a Hodnota (HSV). Hlavnou výhodou je, že je možné rozlíšiť farby podľa úrovne jasu. A tu je dobré vysvetlenie farebného priestoru HSV. Konverzia na HSV sa vykonáva pomocou nasledujúcej funkcie:

def convert_to_HSV (rámec):

hsv = cv2.cvtColor (rám, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) návrat hsv

Táto funkcia sa bude volať z hlavnej slučky a vráti rámec do farebného priestoru HSV. Rám, ktorý som získal vo farebnom priestore HSV, je uvedený vyššie.

Detekcia modrej farby a okrajov:

Po konverzii obrázku do farebného priestoru HSV je načase zistiť iba farbu, ktorá nás zaujíma (t. J. Modrá farba, pretože je to farba čiar pruhu). Na extrahovanie modrej farby z rámca HSV by mal byť zadaný rozsah odtieňa, sýtosti a hodnoty. pozrite sa sem, aby ste mali lepšiu predstavu o hodnotách HSV. Po niekoľkých experimentoch sú v kóde nižšie uvedené horné a dolné limity modrej farby. A aby sa znížilo celkové skreslenie v každom rámci, hrany sa detegujú iba pomocou detektora hrany canny. Viac o canny edge nájdete tu. Pravidlom je zvoliť parametre funkcie Canny () v pomere 1: 2 alebo 1: 3.

def detect_edges (rámec):

lower_blue = np.array ([90, 120, 0], dtype = "uint8") # dolná hranica modrej farby upper_blue = np.array ([150, 255, 255], dtype = "uint8") # horná hranica modrá farba masky = cv2.inRange (hsv, lower_blue, upper_blue) # táto maska odfiltruje všetko okrem modrej # detekuje hrany hrany = cv2. Canny (maska, 50, 100) cv2.imshow („hrany“, hrany) vracia okraje

Táto funkcia sa bude volať aj z hlavnej slučky, ktorá ako parameter vezme rámec farebného priestoru HSV a vráti hranatý rámec. Hranatý rám, ktorý som získal, sa nachádza vyššie.

Vyberte oblasť záujmu (NI):

Výber oblasti záujmu je kľúčové, aby ste sa zamerali iba na 1 oblasť rámca. V tomto prípade nechcem, aby auto videlo v okolí veľa predmetov. Chcem len, aby sa auto zameralo na jazdné pruhy a čokoľvek iné ignorovalo. P. S: súradnicový systém (osi x a y) začína v ľavom hornom rohu. Inými slovami, bod (0, 0) začína v ľavom hornom rohu. os y je výška a os x je šírka. Nasledujúci kód vyberá oblasť záujmu, ktorá sa má zamerať iba na dolnú polovicu rámca.

def region_of_interest (hrany):

výška, šírka = hrany.tvar # extrahovať výšku a šírku okrajov maska rámčeka = np.zeros_like (hrany) # vytvoriť prázdnu maticu s rovnakými rozmermi okrajov rám # zamerať iba dolnú polovicu obrazovky # určiť súradnice 4 body (vľavo dole, vľavo hore, vpravo hore, vpravo dole) polygón = np.array (

Táto funkcia vezme hranatý rámec ako parameter a nakreslí polygón so 4 prednastavenými bodmi. Zameria sa iba na to, čo je v mnohouholníku, a bude ignorovať všetko, čo je mimo neho. Môj rámec záujmu oblasti je uvedený vyššie.

Rozpoznať riadkové segmenty:

Houghova transformácia sa používa na detekciu úsečiek z hranatého rámca. Houghova transformácia je technika na zistenie akéhokoľvek tvaru v matematickej forme. Dokáže detekovať takmer akýkoľvek objekt, aj keď je skreslený podľa určitého počtu hlasov. je tu ukázaný veľký odkaz na Houghovu transformáciu. V tejto aplikácii sa na detekciu čiar v každom rámci používa funkcia cv2. HoughLinesP (). Medzi dôležité parametre, ktoré táto funkcia má, patria:

cv2. HoughLinesP (frame, rho, theta, min_threshold, minLineLength, maxLineGap)

  • Rám: je rámec, v ktorom chceme detekovať čiary.
  • rho: Je to presnosť vzdialenosti v pixeloch (zvyčajne je = 1)
  • theta: uhlová presnosť v radiánoch (vždy = np.pi/180 ~ 1 stupeň)
  • min_threshold: minimálny počet hlasov, ktorý je potrebný na to, aby bol považovaný za riadok
  • minLineLength: minimálna dĺžka riadka v pixeloch. Akýkoľvek riadok kratší ako toto číslo sa nepovažuje za riadok.
  • maxLineGap: maximálna medzera v pixeloch medzi 2 riadkami sa považuje za 1 riadok. (V mojom prípade sa nepoužíva, pretože jazdné pruhy, ktoré používam, nemajú žiadnu medzeru).

Táto funkcia vráti koncové body riadka. Z mojej hlavnej slučky sa volá nasledujúca funkcia na detekciu čiar pomocou Houghovej transformácie:

def detect_line_segments (cropped_edges):

rho = 1 theta = np.pi / 180 min_threshold = 10 riadkov_segmentov = cv2. HoughLinesP (cropped_edges, rho, theta, min_threshold, np.array (), minLineLength = 5, maxLineGap = 0) return line_segments

Priemerný sklon a zachytenie (m, b):

pripomeňme, že rovnicová rovnica je daná y = mx + b. Kde m je sklon priamky a b je priesečník y. V tejto časti bude vypočítaný priemer sklonov a zachytených úsečiek detekovaných pomocou Houghovej transformácie. Než to urobíme, pozrime sa na pôvodnú rámovú fotografiu uvedenú vyššie. Zdá sa, že ľavý pruh smeruje nahor, takže má negatívny sklon (pamätáte si počiatočný bod súradnicového systému?). Inými slovami, čiara ľavého pruhu má x1 <x2 a y2 x1 a y2> y1, čo poskytne kladný sklon. Všetky čiary s pozitívnym sklonom sa teda považujú za body v pravom jazdnom pruhu. V prípade zvislých čiar (x1 = x2) bude sklon nekonečný. V tomto prípade preskočíme všetky zvislé čiary, aby sme predišli chybám. Aby bola táto detekcia ešte presnejšia, je každý snímok rozdelený na dve oblasti (pravú a ľavú) prostredníctvom dvoch hraničných čiar. Všetky body šírky (body osi x) väčšie ako pravá hraničná čiara sú priradené k výpočtu pravého jazdného pruhu. A ak sú všetky body šírky menšie ako ľavá hraničná čiara, sú spojené s výpočtom ľavého pruhu. Nasledujúca funkcia prevezme spracovanie rámu a segmenty pruhu detegované pomocou Houghovej transformácie a vráti priemerný sklon a zachytenie dvoch línií jazdných pruhov.

def average_slope_intercept (frame, line_segments):

lane_lines = ak riadky_segmenty nie sú uvedené: print („nebol detekovaný žiadny riadok“) vrátiť lane_lines výška, šírka, _ = frame.shape left_fit = right_fit = boundary = left_region_boundary = width * (1 - boundary) right_region_boundary = width * border for line_segment in line_segments: for x1, y1, x2, y2 in line_segment: if x1 == x2: print ("skipping vertical lines (lope = infinity)") continue fit = np.polyfit ((x1, x2), (y1, y2), 1) sklon = (y2 - y1) / (x2 - x1) intercept = y1 - (sklon * x1) ak sklon <0: ak x1 <left_region_boundary a x2 right_region_boundary a x2> right_region_boundary: right_fit. append ((sklon, zachytenie)) left_fit_average = np. priemer (left_fit, axis = 0) if len (left_fit)> 0: lane_lines.append (make_points (frame, left_fit_average)) right_fit_average = np.average (right_fit, axis = 0) if len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines je 2-D pole pozostávajúce zo súradníc línií pravého a ľavého pruhu # napríklad: lan e_lines =

make_points () je pomocná funkcia pre funkciu average_slope_intercept (), ktorá vráti ohraničené súradnice čiar jazdného pruhu (zdola do stredu rámca).

def make_points (rám, riadok):

výška, šírka, _ = sklon rámca. tvar, úsečka = priamka y1 = výška # spodná časť rámca y2 = int (y1 / 2) # urobte body od stredu rámca nadol, ak je sklon == 0: sklon = 0,1 x1 = int ((y1 - intercept) / sklon) x2 = int ((y2 - intercept) / sklon) návrat

Aby sa zabránilo deleniu číslom 0, je predložená podmienka. Ak sklon = 0, čo znamená y1 = y2 (vodorovná čiara), dajte sklonu hodnotu blízku 0. To neovplyvní výkonnosť algoritmu, rovnako ako zabráni nemožnému prípadu (delenie 0).

Na zobrazenie línií jazdných pruhov na rámoch sa používa nasledujúca funkcia:

def display_lines (frame, lines, line_color = (0, 255, 0), line_width = 6): # line color (B, G, R)

line_image = np.zeros_like (frame) if lines is not None: for line in lines: for x1, y1, x2, y2 in line: cv2.line (line_image, (x1, y1), (x2, y2), line_color, line_width) line_image = cv2.addWeighted (frame, 0.8, line_image, 1, 1) return line_image

Funkcia cv2.addWeighted () má nasledujúce parametre a používa sa na kombináciu dvoch obrázkov, pričom každému z nich je priradená váha.

cv2.addWeighted (obrázok1, alfa, obrázok2, beta, gama)

A vypočíta výstupný obrázok pomocou nasledujúcej rovnice:

výstup = alpha * image1 + beta * image2 + gama

Tu sú odvodené ďalšie informácie o funkcii cv2.addWeighted ().

Vypočítať a zobraziť riadok nadpisu:

Toto je posledný krok pred tým, ako na naše motory použijeme rýchlosti. Hlavičková čiara je zodpovedná za to, aby dala motoru riadenia smer, v ktorom by sa mal otáčať, a dával škrtiacim motorom rýchlosť, ktorou budú pracovať. Výpočtová čiara je čistá trigonometria, používajú sa goniometrické funkcie tan a atan (tan^-1). Extrémne prípady sú, keď kamera detekuje iba jednu čiaru jazdného pruhu alebo keď nezistí žiadnu čiaru. Všetky tieto prípady sú zobrazené v nasledujúcej funkcii:

def get_steering_angle (frame, lane_lines):

výška, šírka, _ = rám. tvar, ak len (lane_lines) == 2: # ak sú zistené dve čiary pruhu _, _, left_x2, _ = lane_lines [0] [0] # extrahovať x2 z pruhu_pruhy pole _, _, right_x2, _ = lane_lines [1] [0] # extrahovať vpravo x2 z poľa lane_lines mid = int (width / 2) x_offset = (left_x2 + right_x2) / 2 - mid y_offset = int (výška / 2) elif len (lane_lines) == 1: # ak je detekovaný iba jeden riadok x1, _, x2, _ = lane_lines [0] [0] x_offset = x2 - x1 y_offset = int (výška / 2) elif len (lane_lines) == 0: # ak nie je detekovaný žiadny riadok x_offset = 0 y_offset = int (výška / 2) angle_to_mid_radian = math.atan (x_offset / y_offset) angle_to_mid_deg = int (angle_to_mid_radian * 180,0 / math.pi) volant_angle = angle_to_mid_deg + 90 návrat

x_offset v prvom prípade je, o koľko sa priemer ((vpravo x2 + vľavo x2) / 2) líši od stredu obrazovky. y_offset sa vždy považuje za výšku / 2. Posledný obrázok vyššie ukazuje príklad záhlavia. angle_to_mid_radians je rovnaký ako „theta“zobrazený na poslednom obrázku vyššie. Ak riadiaci uhol = 90, znamená to, že auto má smerovú čiaru kolmú na čiaru „výška / 2“a auto sa pohne dopredu bez riadenia. Ak je uhol natočenia volantu> 90, auto by malo smerovať doprava, v opačnom prípade by malo smerovať doľava. Na zobrazenie nadpisového riadku sa používa nasledujúca funkcia:

def display_heading_line (rám, uhol_ riadenia, čiarový_color = (0, 0, 255), šírka_ riadku = 5)

direction_image = np.zeros_like (frame) výška, šírka, _ = frame.shape (uhol_radia_radia)) y2 = int (výška / 2) cv2.line (nadpis_obrázku, (x1, y1), (x2, y2), riadok_farba, šírka_ čiary) nadpis_obraz = cv2.addWeighted (rám, 0,8, nadpis_obraz, 1, 1) vrátiť nadpis_obraz

Vyššie uvedená funkcia berie ako vstup rám, v ktorom bude nakreslená čiara smeru a uhol riadenia. Vráti obrázok nadpisového riadku. Rám záhlavia použitý v mojom prípade je zobrazený na obrázku vyššie.

Kombinácia celého kódu dohromady:

Kód je teraz pripravený na zostavenie. Nasledujúci kód ukazuje hlavnú slučku programu, ktorá volá každú funkciu:

import cv2

import numpy as np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) while True: ret, frame = video.read () frame = cv2.flip (rámec, -1) #Volanie funkcií hsv = convert_to_HSV (frame) edge = detect_edges (hsv) roi = region_of_interest (edge) line_segments = detect_line_segments (roi) lane_lines = average_slope_intercept (frame, line_segments) lane_lines_image = display_lines (frame, lane_) = get_steering_angle (frame, lane_lines) header_image = display_heading_line (lane_lines_image, volant_angle) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Krok 6: Aplikácia PD Control

Aplikácia PD Control
Aplikácia PD Control

Teraz máme uhol riadenia pripravený na napájanie motorov. Ako už bolo spomenuté, ak je uhol riadenia väčší ako 90, auto by malo odbočiť vpravo, inak by malo odbočiť doľava. Použil som jednoduchý kód, ktorý otočí motor riadenia doprava, ak je uhol vyšší ako 90, a otočí ho doľava, ak je uhol riadenia menší ako 90 pri konštantnej rýchlosti škrtenia (10% PWM), ale dostal som veľa chýb. Hlavnou chybou, ktorú som urobil, je, že keď sa auto priblíži k akejkoľvek zákrute, motor riadenia pôsobí priamo, ale škrtiace motory sa zasekli. Pokúsil som sa zvýšiť rýchlosť škrtenia na (20% PWM) v zákrutách, ale skončil som tým, že sa robot dostal z jazdných pruhov. Potreboval som niečo, čo veľmi zvyšuje rýchlosť škrtenia, ak je uhol riadenia veľmi veľký a trochu zvyšuje rýchlosť, ak uhol riadenia nie je taký veľký, potom zníži rýchlosť na počiatočnú hodnotu, keď sa auto priblíži k 90 stupňom (pohybuje sa rovno). Riešením bolo použiť PD regulátor.

PID regulátor znamená proporcionálny, integrálny a derivačný regulátor. Tento typ lineárnych regulátorov je široko používaný v robotických aplikáciách. Obrázok vyššie ukazuje typickú regulačnú slučku spätnej väzby PID. Cieľom tohto regulátora je dosiahnuť "požadovanú hodnotu" najefektívnejším spôsobom na rozdiel od regulátorov "zapnutia - vypnutia", ktoré zapínajú alebo vypínajú zariadenie podľa určitých podmienok. Niektoré kľúčové slová by mali byť známe:

  • Setpoint: je požadovaná hodnota, ktorú má váš systém dosiahnuť.
  • Skutočná hodnota: je skutočná hodnota snímaná snímačom.
  • Chyba: je rozdiel medzi požadovanou a skutočnou hodnotou (chyba = žiadaná hodnota - skutočná hodnota).
  • Riadená premenná: z jej názvu je premenná, ktorú chcete ovládať.
  • Kp: Proporcionálna konštanta.
  • Ki: Integrálna konštanta.
  • Kd: Derivátová konštanta.

Stručne povedané, slučka riadiaceho systému PID funguje nasledovne:

  • Užívateľ definuje požadovanú hodnotu, ktorú musí systém dosiahnuť.
  • Chyba sa vypočíta (chyba = požadovaná hodnota - skutočná).
  • Regulátor P generuje akciu úmernú hodnote chyby. (chyba sa zvyšuje, činnosť P sa tiež zvyšuje)
  • Ovládač integruje chybu v priebehu času, čo eliminuje chybu systému v ustálenom stave, ale zvyšuje jeho prekročenie.
  • Regulátor D je jednoducho časový derivát chyby. Inými slovami, je to sklon chyby. Vykonáva akciu úmernú derivácii chyby. Tento ovládač zvyšuje stabilitu systému.
  • Výstupom regulátora bude súčet troch regulátorov. Ak sa chyba stane 0, výstup regulátora sa zmení na 0.

Skvelé vysvetlenie PID regulátora nájdete tu.

Keď sa vrátim k vozidlu na udržiavanie v jazdnom pruhu, mojou riadenou premennou bola rýchlosť škrtenia (pretože riadenie má iba dva stavy, buď vpravo alebo vľavo). Na tento účel sa používa regulátor PD, pretože akcia D výrazne zvyšuje rýchlosť škrtenia, ak je zmena chýb veľmi veľká (tj. Veľká odchýlka) a spomaľuje auto, ak sa táto zmena chýb blíži k 0. Na implementáciu PD som vykonal nasledujúce kroky ovládač:

  • Nastavte požadovanú hodnotu na 90 stupňov (vždy chcem, aby sa auto pohybovalo rovno)
  • Vypočítal uhol odchýlky od stredu
  • Odchýlka poskytuje dve informácie: Ako veľká je chyba (veľkosť odchýlky) a akým smerom sa musí motor riadenia uberať (znak odchýlky). Ak je odchýlka kladná, auto by malo riadiť vpravo, inak by malo smerovať doľava.
  • Pretože odchýlka je buď negatívna alebo pozitívna, je definovaná premenná "chyba" a vždy sa rovná absolútnej hodnote odchýlky.
  • Chyba sa vynásobí konštantou Kp.
  • Chyba prechádza časovou diferenciáciou a je vynásobená konštantou Kd.
  • Rýchlosť motora sa aktualizuje a slučka sa znova spustí.

Nasledujúci kód sa používa v hlavnej slučke na ovládanie rýchlosti škrtiacich motorov:

rýchlosť = 10 # prevádzková rýchlosť v % PWM

#Proměnné, ktoré sa majú aktualizovať v každej slučke lastTime = 0 lastError = 0 # PD konštanty Kp = 0,4 Kd = Kp * 0,65 While True: now = time.time () # aktuálna časová premenná dt = teraz - odchýlka lastTime = uhol riadenia - 90 # ekvivalent to angle_to_mid_deg variabilná chyba = abs (odchýlka), ak je odchýlka -5: # neriadiť, ak existuje odchýlka rozsahu 10 -stupňovej chyby = 0 chyba = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. LOW) volant.stop () odchýlka odchýlky> 5: # nasmerujte doprava, ak je odchýlka kladná GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) volant.start (100) odchýlka zdvihu < -5: # riadenie vľavo, ak je odchýlka záporná GPIO.output (in1, GPIO. HIGH) GPIO.output (in2, GPIO. LOW) volant.start (100) derivative = kd * (chyba - lastError) / dt proporcionálne = kp * chyba PD = int (rýchlosť + derivácia + proporcionálne) spd = abs (PD), ak spd> 25: spd = 25 throttle.start (spd) lastError = chyba lastTime = time.time ()

Ak je chyba veľmi veľká (odchýlka od stredu je vysoká), proporcionálne a derivačné akcie sú vysoké, čo má za následok vysokú rýchlosť škrtenia. Keď sa chyba priblíži k 0 (odchýlka od stredu je nízka), derivačné pôsobenie pôsobí opačne (sklon je záporný) a rýchlosť škrtenia sa zníži, aby sa zachovala stabilita systému. Celý kód je priložený nižšie.

Krok 7: Výsledky

Videá vyššie ukazujú výsledky, ktoré som dosiahol. Vyžaduje si to ďalšie ladenie a ďalšie úpravy. Pripojil som malinový pi k svojej obrazovke LCD, pretože streamovanie videa cez moju sieť malo vysokú latenciu a práca s ním bola veľmi frustrujúca, a preto sú vo videu k malinovému pí pripojené káble. Na kreslenie stopy som použil penové dosky.

Čakám na vaše odporúčania na zlepšenie tohto projektu! Dúfam, že tieto pokyny boli dostatočne dobré na to, aby vám poskytli nové informácie.

Odporúča: