Sisukord:

Autonoomne sõidurada hoidv auto Raspberry Pi ja OpenCV abil: 7 sammu (koos piltidega)
Autonoomne sõidurada hoidv auto Raspberry Pi ja OpenCV abil: 7 sammu (koos piltidega)

Video: Autonoomne sõidurada hoidv auto Raspberry Pi ja OpenCV abil: 7 sammu (koos piltidega)

Video: Autonoomne sõidurada hoidv auto Raspberry Pi ja OpenCV abil: 7 sammu (koos piltidega)
Video: "🔥 Breaking News: Unleashing the Future of Driving with the 2024 Kia EV9! #KiaEV9 #2024ElectricSUV" 2024, November
Anonim
Autonoomne sõidurada hoidv auto, kasutades Raspberry Pi ja OpenCV
Autonoomne sõidurada hoidv auto, kasutades Raspberry Pi ja OpenCV

Selles juhendis rakendatakse autonoomne sõiduraja hoidmise robot, mis läbib järgmised sammud:

  • Osade kogumine
  • Tarkvara installimise eeldused
  • Riistvara kokkupanek
  • Esimene test
  • Rajajoonte tuvastamine ja juhtjoone kuvamine openCV abil
  • PD -kontrolleri rakendamine
  • Tulemused

Samm: komponentide kogumine

Komponentide kogumine
Komponentide kogumine
Komponentide kogumine
Komponentide kogumine
Komponentide kogumine
Komponentide kogumine
Komponentide kogumine
Komponentide kogumine

Ülaltoodud piltidel on näha kõik selles projektis kasutatud komponendid:

  • RC auto: Mina sain oma riigi kohalikust poest. See on varustatud 3 mootoriga (2 gaasihoovastiku ja 1 roolimise jaoks). Selle auto peamine puudus on see, et roolimine on piiratud roolimiseta ja täieliku roolimise vahel. Teisisõnu, erinevalt servojuhtimisega RC-autodest ei saa see kindla nurga all rooli juhtida. Siit leiate sarnase autokomplekti, mis on loodud spetsiaalselt vaarika pi jaoks.
  • Vaarika pi 3 mudel b+: see on auto aju, mis saab hakkama paljude töötlemisetappidega. See põhineb neljatuumalisel 64-bitisel protsessoril, mille sagedus on 1,4 GHz. Mina sain oma siit.
  • Vaarika pi 5 mp kaameramoodul: toetab 1080p @ 30 fps, 720p @ 60 fps ja 640x480p 60/90 salvestust. Samuti toetab see jadaliidest, mille saab ühendada otse vaarika pi -ga. See ei ole parim valik pilditöötlusrakenduste jaoks, kuid sellest projektist piisab ja see on väga odav. Mina sain oma siit.
  • Mootori juht: kasutatakse alalisvoolumootorite suundade ja kiiruste juhtimiseks. See toetab 2 alalisvoolumootori juhtimist ühel plaadil ja talub 1,5 A.
  • Toitepank (valikuline): ma kasutasin vaarika pi eraldi sisselülitamiseks toitepanka (5 V, 3 A). Vaarika pi toiteks ühest allikast tuleks kasutada astmelist muundurit (buck converter: 3A väljundvool).
  • 3s (12 V) LiPo aku: liitiumpolümeerpatareid on tuntud oma suurepärase jõudluse poolest robootika valdkonnas. Seda kasutatakse mootori juhi toiteks. Ostsin siit oma.
  • Isast meessoost ja naissoost naissoost hüppajajuhtmed.
  • Kahepoolne teip: kasutatakse komponentide kinnitamiseks RC -autole.
  • Sinine lint: see on selle projekti väga oluline komponent, seda kasutatakse kahe sõiduraja tegemiseks, mille vahel auto hakkab sõitma. Saate valida mis tahes värvi, mida soovite, kuid soovitan valida värvid, mis erinevad ümbritsevast.
  • Tõmblukud ja puidust vardad.
  • Kruvikeeraja.

2. samm: OpenCV installimine Raspberry Pi -sse ja kaugnäidiku seadistamine

OpenCV installimine Raspberry Pi -le ja kaugnäidiku seadistamine
OpenCV installimine Raspberry Pi -le ja kaugnäidiku seadistamine

See samm on natuke tüütu ja võtab natuke aega.

OpenCV (Open source Computer Vision) on avatud lähtekoodiga arvutite nägemise ja masinõppe tarkvara kogu. Raamatukogus on üle 2500 optimeeritud algoritmi. Järgige seda väga lihtsat juhendit, et installida openCV oma vaarika pi ja ka vaarika pi operatsioonisüsteemi (kui te seda veel ei teinud). Pange tähele, et openCV ehitamise protsess võib hästi jahutatud ruumis kesta umbes 1,5 tundi (kuna protsessori temperatuur tõuseb väga kõrgele!), Nii et jooge teed ja oodake kannatlikult: D.

Kaugkuva jaoks järgige ka seda juhendit, et seadistada oma Windowsi/Maci seadmest oma raspberry pi kaugjuurdepääs.

Samm: osade ühendamine

Osade ühendamine
Osade ühendamine
Osade ühendamine
Osade ühendamine
Osade ühendamine
Osade ühendamine

Ülaltoodud piltidel on näha vaarika pi, kaamera mooduli ja mootori draiveri vahelised ühendused. Pange tähele, et minu kasutatavad mootorid neelavad 0,35 A pinget 9 V juures, mis teeb mootorsõidukijuhile kolme mootori korraga töötamise ohutuks. Ja kuna ma tahan juhtida 2 gaasimootori kiirust (1 taga ja 1 ees) täpselt samamoodi, ühendasin need sama pordiga. Mootorijuhi paigaldasin topeltlindi abil auto paremale küljele. Kaameramooduli osas sisestasin kruviaukude vahele tõmbluku, nagu ülaltoodud pilt näitab. Seejärel sobitan kaamera puitvardale, et saaksin kaamera asendit vastavalt soovile reguleerida. Proovige paigaldada kaamera nii palju kui võimalik auto keskele. Soovitan paigutada kaamera vähemalt 20 cm maapinnast kõrgemale, nii et vaateväli auto ees paraneb. Fritzingu skeem on lisatud allpool.

Samm: esimene test

Esimene test
Esimene test
Esimene test
Esimene test

Kaamera testimine:

Kui kaamera on installitud ja openCV raamatukogu ehitatud, on aeg testida meie esimest pilti! Teeme foto pi cam'ist ja salvestame selle "original.jpg". Seda saab teha kahel viisil:

1. Terminali käskude kasutamine:

Avage uus terminaliaken ja tippige järgmine käsk:

raspistill -o originaal.jpg

See võtab pildi ja salvestab selle kataloogi "/pi/original.jpg".

2. Mis tahes pythoni IDE kasutamine (ma kasutan IDLE -d):

Avage uus visand ja kirjutage järgmine kood:

import cv2

video = cv2. VideoCapture (0) samas True: ret, frame = video.read () frame = cv2.flip (frame, -1) # kasutatakse pildi vertikaalseks pööramiseks cv2.imshow ('originaal', raam) cv2. imwrite ('original.jpg', frame) võti = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Vaatame, mis selles koodis juhtus. Esimene rida impordib meie openCV raamatukogu, et kasutada kõiki selle funktsioone. funktsioon VideoCapture (0) alustab otseülekannet selle funktsiooni poolt määratud allikast, antud juhul on see 0, mis tähendab raspi -kaamerat. kui teil on mitu kaamerat, tuleks panna erinevad numbrid. video.read () loeb iga kaadri kaamerast ja salvestab selle muutuja nimega "frame". Funktsioon flip () pöörab pilti y-telje suhtes (vertikaalselt), kuna paigaldan kaamera vastupidi. imshow () kuvab meie raamid eesotsas sõnaga "originaal" ja imwrite () salvestab meie foto originaalina. jpg. waitKey (1) ootab 1 ms mis tahes klaviatuuri nupu vajutamist ja tagastab selle ASCII koodi. kui vajutate nuppu Esc (Esc), tagastatakse kümnendväärtus 27 ja katkestab vastavalt silmuse. video.release () lõpetab salvestamise ja hävitab AllWindows () sulgeb kõik pildid, mis on avatud funktsiooni imshow () abil.

Soovitan testida oma fotot teise meetodiga, et tutvuda openCV funktsioonidega. Pilt salvestatakse kataloogi "/pi/original.jpg". Minu kaamera tehtud originaalfoto on näidatud ülal.

Mootorite testimine:

See samm on oluline iga mootori pöörlemissuuna määramiseks. Kõigepealt tutvustame lühidalt mootorsõidukijuhi tööpõhimõtet. Ülaltoodud pilt näitab mootori juhi pistikut. Luba A, sisend 1 ja sisend 2 on seotud mootori A juhtimisega. Luba B, sisend 3 ja sisend 4 on seotud mootori B juhtimisega. Suuna juhtimine on sisse lülitatud osaga "Sisend" ja kiiruse reguleerimisega "Luba". Näiteks mootori A suuna juhtimiseks seadke sisend 1 väärtusele HIGH (sel juhul 3,3 V, kuna kasutame vaarika pi) ja sisend 2 väärtusele LOW, pöörleb mootor kindlas suunas ja määrab vastupidised väärtused sisendisse 1 ja sisendisse 2 pöörleb mootor vastupidises suunas. Kui sisend 1 = sisend 2 = (HIGH või LOW), mootor ei pöörle. Lubatud tihvtid võtavad vaarikast (0 kuni 3,3 V) impulsslaiusmodulatsiooni (PWM) sisendsignaali ja käivitavad vastavalt mootorid. Näiteks 100% PWM signaal tähendab, et töötame maksimaalse kiiruse kallal ja 0% PWM signaal tähendab, et mootor ei pöörle. Järgmist koodi kasutatakse mootorite suundade määramiseks ja nende kiiruste testimiseks.

impordi aeg

RPi. GPIO importimine GPIO -na GPIO.setwarnings (vale) # Roolimootori nööbid roolisamm 23 # Füüsiline tihvt 16 in4 = 24 # Füüsiline tihvt 18 GPIO.setmode (GPIO. BCM) # Kasutage GPIO numeratsiooni füüsilise numeratsiooni asemel GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. seadistamine (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (drossel_enable, GPIO.out) GPIO.setup (roolimise lubatav, GPIO.väljaanne) # Roolimootori juhtimine GPIO.väljund (in1, GPIO. HIGH) GPIO.väljund (in2, GPIO. LOW) juhtimine = GPIO. PWM (roolimine_lubatav, 1000) # määrake lülitussageduseks 1000 Hz rool. Stop () # Throttle Motors Control GPIO.output (in3, GPIO. HIGH) GPIO. Output (in4, GPIO. LOW) gaasipedaal = GPIO. PWM (throttle_enable, 1000) # määrake lülitussageduseks 1000 Hz drossel. stop () time.sleep (1) drossel. start (25) # käivitab mootori 25 juures % PWM signaal-> (0,25 * aku pinge) - juhi oma kadujuhtimine. käivitamine (100) # käivitab mootori 100% PWM signaalil -> (1 * aku pinge) - juhi kadumisaeg. unerežiim (3) gaasipedaal. seiskamine () juhtimine. seiskamine ()

See kood käivitab gaasimootoreid ja roolimootorit 3 sekundit ning seejärel peatab need. (Juhi kadu) saab määrata voltmeetri abil. Näiteks teame, et 100% PWM signaal peaks andma aku täispinge mootori klemmile. Kuid seadistades PWM väärtuseks 100%, leidsin, et draiver põhjustab 3 V languse ja mootor saab 12 V asemel 9 V (täpselt see, mida ma vajan!). Kahjum ei ole lineaarne, st 100% kahjum erineb oluliselt 25% kahjumist. Pärast ülaltoodud koodi käivitamist olid minu tulemused järgmised:

Gaasihoovastiku tulemused: kui in3 = HIGH ja in4 = LOW, on drosselmootoritel Clock-Wise (CW) pööre, st auto liigub edasi. Vastasel juhul liigub auto tagurpidi.

Roolitulemused: kui in1 = HIGH ja in2 = LOW, pöördub roolimootor maksimaalselt vasakule, st auto roolib vasakule. Vastasel juhul pöörab auto paremale. Pärast mõningaid katseid leidsin, et roolimootor ei pöörle, kui PWM -signaal ei ole 100% (st mootor juhib kas täielikult paremale või täielikult vasakule).

5. samm: sõidurajajoonte tuvastamine ja suunajoone arvutamine

Rajajoonte tuvastamine ja suunajoone arvutamine
Rajajoonte tuvastamine ja suunajoone arvutamine
Rajajoonte tuvastamine ja suunajoone arvutamine
Rajajoonte tuvastamine ja suunajoone arvutamine
Rajajoonte tuvastamine ja suunajoone arvutamine
Rajajoonte tuvastamine ja suunajoone arvutamine

Selles etapis selgitatakse auto liikumist juhtivat algoritmi. Esimene pilt näitab kogu protsessi. Süsteemi sisendiks on kujutised, väljundiks on teeta (pöördenurk kraadides). Pange tähele, et töötlemine toimub 1 pildil ja seda korratakse kõigil kaadritel.

Kaamera:

Kaamera alustab (320 x 240) eraldusvõimega video salvestamist. Soovitan vähendada eraldusvõimet, et saaksite paremat kaadrisagedust (fps), kuna kaadrisagedus langeb pärast töötlemistehnikate rakendamist igale kaadrile. Allpool olev kood on programmi peamine silmus ja lisab selle koodi iga sammu.

import cv2

import numpy kui np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # määrake laiuseks 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # määrake kõrgus 240 p # Ahel Tõsi: 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 ()

Siin olev kood näitab 4. sammus saadud originaalpilti ja on näidatud ülaltoodud piltidel.

Teisenda HSV värviruumiks:

Nüüd, pärast videosalvestuse võtmist kaamerana raamidena, on järgmine samm iga kaadri teisendamine tooni, küllastuse ja väärtuse (HSV) värviruumiks. Selle peamine eelis on võimalus eristada värve nende heleduse taseme järgi. Ja siin on hea selgitus HSV värviruumi kohta. Teisendamine HSV -ks toimub järgmise funktsiooni abil:

def convert_to_HSV (kaader):

hsv = cv2.cvtColor (raam, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) tagasta hsv

Seda funktsiooni kutsutakse põhiahelast ja see tagastab kaadri HSV värviruumis. Minu poolt HSV värviruumis saadud raam on näidatud ülal.

Tuvastage sinine värv ja servad:

Pärast pildi teisendamist HSV värviruumi on aeg tuvastada ainult see värv, mis meid huvitab (st sinine värv, kuna see on sõiduraja värv). Sinise värvuse eraldamiseks HSV -raamilt tuleks määrata toonide, küllastuse ja väärtuste vahemik. Siit leiate HSV väärtustest parema ettekujutuse. Pärast mõningaid katseid on sinise värvi ülemine ja alumine piir näidatud allolevas koodis. Ja et vähendada iga kaadri üldist moonutust, tuvastatakse servad ainult nõtke servaanduri abil. Lisateavet canny edge kohta leiate siit. Rusikareegel on valida funktsiooni Canny () parameetrid suhtega 1: 2 või 1: 3.

def detect_edges (raam):

alumine_sinine = np.massiiv ([90, 120, 0], dtype = "uint8") # sinise värvi alumine piir ülemine_sinine = np.massiiv ([150, 255, 255], dtype = "uint8") # ülemine piir sinine värvimask = cv2.inRange (hsv, alumine_sinine, ülemine_sinine) # see mask filtreerib välja kõik peale sinise # tuvastab servad servad = cv2. Canny (mask, 50, 100) cv2.imshow ("servad", servad) tagasiservad

Seda funktsiooni kutsutakse ka põhiahelast, mis võtab parameetrina kasutusele HSV värviruumi raami ja tagastab servakaadri. Servitud raam, mille ma sain, on ülal.

Valige huvipakkuv piirkond (ROI):

Huvipakkuva piirkonna valimine on ülioluline, et keskenduda ainult kaadri ühele piirkonnale. Sel juhul ma ei taha, et auto näeks keskkonnas palju esemeid. Ma lihtsalt tahan, et auto keskenduks sõiduradadele ja ignoreeriks kõike muud. P. S: koordinaatsüsteem (x- ja y -teljed) algab vasakust ülanurgast. Teisisõnu, punkt (0, 0) algab vasakust ülanurgast. y-telg on kõrgus ja x-telg on laius. Allolev kood valib huvipakkuva piirkonna, mis keskendub ainult kaadri alumisele poolele.

def region_of_interest (servad):

kõrgus, laius = servad. kuju # väljavõtte servade kõrgus ja laius raami mask = np.zeros_like (servad) # tehke tühi maatriks, millel on samad mõõtmed raamiga # keskenduge ainult ekraani alumisele poolele # määrake koordinaadid 4 punkti (alumine vasak, ülemine vasakpoolne, parempoolne ülemine, alumine parem) hulknurk = np.massiiv (

See funktsioon võtab parameetrina terava raami ja joonistab 4 eelseadistatud punktiga hulknurga. See keskendub ainult sellele, mis on hulknurga sees, ja ignoreerib kõike väljaspool seda. Minu huvipiirkonna raam on näidatud ülal.

Tuvastage segmendid:

Hough teisendust kasutatakse servaraamide joonelõikude tuvastamiseks. Hough teisendus on tehnika mis tahes kuju tuvastamiseks matemaatilisel kujul. See suudab tuvastada peaaegu iga objekti, isegi kui see on mõne häälte arvu järgi moonutatud. siin on toodud suurepärane viide Houghi teisendusele. Selle rakenduse jaoks kasutatakse iga kaadri ridade tuvastamiseks funktsiooni cv2. HoughLinesP (). Selle funktsiooni olulised parameetrid on järgmised:

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

  • Raam: on raam, milles tahame jooni tuvastada.
  • rho: see on kauguse täpsus pikslites (tavaliselt on see = 1)
  • teeta: nurga täpsus radiaanides (alati = np.pi/180 ~ 1 kraad)
  • min_threshold: miinimumhääl, mida ta peaks saama, et seda loetaks jooneks
  • minLineLength: rea minimaalne pikkus pikslites. Sellest numbrist lühemat rida ei loeta jooneks.
  • maxLineGap: maksimaalne pikslivahe kahe rea vahel, mida käsitletakse kui 1 rida. (Minu puhul seda ei kasutata, kuna kasutatavatel sõidurajadel pole tühikuid).

See funktsioon tagastab rea lõpp -punktid. Hough -teisenduse abil liinide tuvastamiseks kutsutakse minu põhiahelast järgmine funktsioon:

def detect_line_segments (kärbitud servad):

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

Keskmine kalle ja lõikumine (m, b):

tuletame meelde, et sirge võrrand on antud y = mx + b. Kus m on sirge kalle ja b on y-lõikepunkt. Selles osas arvutatakse Houghi teisenduse abil tuvastatud joonte segmentide nõlvade ja lõikumiste keskmine. Enne seda vaatame ülaltoodud originaalraami fotot. Tundub, et vasakpoolne rada liigub ülespoole, nii et sellel on negatiivne kalle (mäletate koordinaatsüsteemi alguspunkti?). Teisisõnu, vasakpoolsel sõidurajal on x1 <x2 ja y2 x1 ja y2> y1, mis annavad positiivse kalde. Niisiis, kõiki positiivse kaldega jooni loetakse parempoolse raja punktideks. Vertikaalsete joonte korral (x1 = x2) on kalle lõpmatus. Sellisel juhul jätame kõik vertikaalsed jooned vahele, et vältida vea saamist. Selle tuvastamise täpsuse suurendamiseks jagatakse iga kaader kaheks piirjooneks kaheks piirkonnaks (paremale ja vasakule). Kõik laiuspunktid (x-telje punktid) paremast piirjoonest suuremad on seotud parema sõiduraja arvutamisega. Ja kui kõik laiusepunktid on vasakust piirjoonest väiksemad, seostatakse need vasakpoolse raja arvutamisega. Järgmine funktsioon võtab raami töötlemise alla ja Hough teisenduse abil tuvastatud sõidurajad ja tagastab kahe sõiduraja keskmise kalde ja lõikepunkti.

def keskmine_kalde_intercept (kaader, rea_segmendid):

lane_lines = kui line_segments on None: print ("rea segmenti ei tuvastatud") tagastab lane_lines kõrgus, laius, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - border) right_region_boundary = laius * piir rea_lõike jaoks reasegmentides: x1, y1, x2, y2 reasegmendis: kui x1 == x2: print ("vertikaalsete joonte vahelejätmine (kalle = lõpmatus)") jätka sobivust = np.polyfit ((x1, x2), (y1, y2), 1) kalle = (y2 - y1) / (x2 - x1) lõikepunkt = y1 - (kalle * x1) kui kalle <0: kui x1 <vasak_piirkonna_piir ja x2 parempoolne_piirkond ja x2> parempoolne_piirkonna_piir: parem_filtr. lisama ((kallak, pealtkuulamine)) left_fit_average = np.average (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)) kui len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines on 2-D massiiv, mis koosneb parema ja vasaku sõiduraja koordinaatidest # näiteks: lan e_lines =

make_points () on funktsiooni keskmine_slope_intercept () abifunktsioon, mis tagastab rajajoonte piiratud koordinaadid (kaadri alt keskelt).

def make_points (raam, rida):

kõrgus, laius, _ = raam. kuju kallak, lõikepunkt = joon y1 = kõrgus # raami põhi y2 = int (y1 / 2) # tehke punkte kaadri keskelt allapoole, kui kalle == 0: kalle = 0,1 x1 = int ((y1 - pealtkuulamine) / kalle) x2 = int ((y2 - pealtkuulamine) / kallak) return

0 -ga jagamise vältimiseks esitatakse tingimus. Kui kalle = 0, mis tähendab y1 = y2 (horisontaaljoon), andke kalle väärtusele 0 lähedal. See ei mõjuta algoritmi toimivust ning väldib võimatut juhtumit (jagades 0 -ga).

Rajajoonte kuvamiseks raamidel kasutatakse järgmist funktsiooni:

def display_lines (raam, read, line_color = (0, 255, 0), line_width = 6): # rea värv (B, G, R)

line_image = np.zeros_like (raam), kui read ei ole Puuduvad: ridade ridade puhul: x1, y1, x2, y2 reas: 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

Funktsioon cv2.addWeighted () kasutab järgmisi parameetreid ja seda kasutatakse kahe pildi kombineerimiseks, kuid igaühele kaalu andmisega.

cv2.add Kaalutud (pilt1, alfa, pilt2, beeta, gamma)

Ja arvutab väljundpildi järgmise võrrandi abil:

väljund = alfa * pilt1 + beeta * pilt2 + gamma

Lisateavet funktsiooni cv2.addWeighted () kohta leiate siit.

Pealkirja rea arvutamine ja kuvamine:

See on viimane samm enne mootoritele kiiruste rakendamist. Suunajoon on kohustatud andma roolimootorile suuna, milles see peaks pöörlema, ja andma drosselmootoritele kiiruse, millega need töötavad. Pealkirja joone arvutamine on puhas trigonomeetria, kasutatakse tan ja atan (tan^-1) trigonomeetrilisi funktsioone. Mõned äärmuslikud juhtumid on siis, kui kaamera tuvastab ainult ühe sõiduraja rea või kui see ei tuvasta ühtegi joont. Kõik need juhtumid on näidatud järgmises funktsioonis:

def get_steering_angle (raam, sõidurea read):

kõrgus, laius, _ = raam.kuju, kui len (sõiduraja jooned) == 2: # kui tuvastatakse kaks sõiduraja joont _, _, vasak_x2, _ = sõiduraja jooned [0] [0] # ekstrakt vasakult x2 lane_lines massiivist _, _;) == 1: # kui tuvastatakse ainult üks rida x1, _, x2, _ = sõiduraja_liinid [0] [0] x_nihe = x2 - x1 y_nihe = int (kõrgus / 2) elif len (lane_lines) == 0: # kui joont ei tuvastata

x_nihe esimesel juhul on see, kui palju keskmine ((parem x2 + vasak x2) / 2) erineb ekraani keskelt. y_nihe võetakse alati kõrguseks / 2. Viimane ülaltoodud pilt näitab pealkirja rea näidet. ang_to_mid_radians on sama, mis ülaltoodud viimasel pildil näidatud teeta. Kui roolimisnurk = 90, tähendab see, et autol on rida "kõrgus / 2" risti ja auto liigub edasi ilma roolita. Kui roolimisnurk> 90, peaks auto juhtima paremale, vastasel juhul peaks juhtima vasakule. Pealkirja rea kuvamiseks kasutatakse järgmist funktsiooni:

def display_heading_line (raam, juhtnurk, line_color = (0, 0, 255), line_width = 5)

rubriigi_kujutis = np.zeros_like (raam) kõrgus, laius, _ = raam.kuju roolimine_nurk_radiaan = roolimisnurk / 180,0 * matemaatika.pi x1 = int (laius / 2) y1 = kõrgus x2 = int (x1 - kõrgus / 2 / math.tan [roolimisnurga_radiaan]) y2 = int (kõrgus / 2) cv2.line (rubriigi_pilt, (x1, y1), (x2, y2), line_color, line_width) head_image = cv2.addWeighted (raam, 0,8, rubriigi_pilt, 1, 1) tagasi pealkiri_pilt

Ülaltoodud funktsioon võtab sisendiks raami, millesse suundjoon joonistatakse, ja juhtnurga. See tagastab pealkirja rea pildi. Minu puhul võetud pealkirja raam on näidatud ülaltoodud pildil.

Kogu koodi ühendamine:

Kood on nüüd kokkupanemiseks valmis. Järgmine kood näitab iga funktsiooni kutsuva programmi põhiahelat:

import cv2

import numpy kui np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240), samas kui True: ret, frame = video.read () frame = cv2.flip (raam, -1) #Funktsioonide kutsumine hsv = convert_to_HSV (raam) servad = detekt_servad (hsv) roi = region_of_interest (servad) line_segments = detect_line_segments (roi) lane_lines = medium_slope_intercept (frame, line_segments) lane_lines_image = display_lines (frame) = get_steering_angle (frame, lane_lines) head_image = display_heading_line (lane_lines_image, roolimisnurk) võti = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

6. samm: PD -kontrolli rakendamine

PD juhtimise rakendamine
PD juhtimise rakendamine

Nüüd on roolimisnurk mootoritele etteandmiseks valmis. Nagu varem mainitud, peaks auto pöördenurk olema suurem kui 90, pöörama paremale, vastasel juhul vasakule. Rakendasin lihtsat koodi, mis pöörab roolimootori paremale, kui nurk on üle 90 ja pöörab vasakule, kui pöördenurk on alla 90 püsiva drosselkiirusega (10% PWM), kuid sain palju vigu. Peamine viga, mille sain, on see, kui auto läheneb igale pöördele, roolimootor toimib otse, kuid gaasimootorid jäävad kinni. Üritasin suurendada kurvikäiku (20% PWM) kurvides, kuid lõppes sellega, et robot väljus sõidurajadelt. Mul oli vaja midagi, mis suurendab drosselkiirust palju, kui juhtnurk on väga suur, ja suurendab kiirust veidi, kui roolinurk ei ole nii suur, vähendab seejärel kiirust algväärtuseni, kui auto läheneb 90 kraadile (otse liikudes). Lahenduseks oli PD -kontrolleri kasutamine.

PID -regulaator tähistab proportsionaalset, integreeritud ja tuletatud kontrollerit. Seda tüüpi lineaarseid kontrollereid kasutatakse robootikarakendustes laialdaselt. Ülaltoodud pilt näitab tüüpilist PID -tagasiside juhtimisahelat. Selle kontrolleri eesmärk on jõuda seadistuspunktini kõige tõhusamal viisil, erinevalt sisse- ja väljalülitusseadmetest, mis lülitavad seadme teatud tingimustel sisse või välja. Mõned märksõnad peaksid olema teada:

  • Seadeväärtus: on soovitud väärtus, milleni teie süsteem soovib jõuda.
  • Tegelik väärtus: on anduri poolt tuvastatud tegelik väärtus.
  • Viga: on erinevus seadeväärtuse ja tegeliku väärtuse vahel (viga = seadeväärtus - tegelik väärtus).
  • Kontrollitav muutuja: selle nime järgi on muutuja, mida soovite juhtida.
  • Kp: Proportsionaalne konstant.
  • Ki: Integraalkonstant.
  • Kd: tuletiskonstant.

Lühidalt, PID -juhtimissüsteemi silmus töötab järgmiselt.

  • Kasutaja määrab süsteemi saavutamiseks vajaliku seadepunkti.
  • Viga arvutatakse (viga = seadeväärtus - tegelik).
  • P -kontroller genereerib vea väärtusega proportsionaalse toimingu. (viga suureneb, ka P -tegevus suureneb)
  • I kontroller integreerib vea aja jooksul, mis kõrvaldab süsteemi püsiseisundi vea, kuid suurendab selle ületamist.
  • D -kontroller on lihtsalt vea ajatuletis. Teisisõnu, see on vea kalle. See teeb vea tuletisega proportsionaalse toimingu. See kontroller suurendab süsteemi stabiilsust.
  • Kontrolleri väljund on kolme kontrolleri summa. Kontrolleri väljundiks saab 0, kui veaks saab 0.

PID -regulaatori suurepärase selgituse leiate siit.

Tulles tagasi sõidurada hoidva auto juurde, oli minu juhitav muutuja gaasipedaal (kuna roolil on ainult kaks olekut kas paremale või vasakule). Sel eesmärgil kasutatakse PD -kontrollerit, kuna D -toiming suurendab gaasihoovastuse kiirust palju, kui vea muutus on väga suur (st suur kõrvalekalle) ja aeglustab autot, kui see veamuudatus läheneb 0. Tegin PD -i rakendamiseks järgmised sammud kontroller:

  • Seadistage seadeväärtuseks 90 kraadi (ma tahan alati, et auto liiguks otse)
  • Arvutatud kõrvalekalde nurk keskelt
  • Kõrvalekalle annab kaks teavet: kui suur on viga (kõrvalekalde suurus) ja mis suunas roolimootor peab võtma (kõrvalekalde märk). Kui kõrvalekalle on positiivne, peaks auto juhtima paremale, vastasel juhul peaks juhtima vasakule.
  • Kuna kõrvalekalle on negatiivne või positiivne, määratletakse muutuja "viga" ja see on alati võrdne kõrvalekalde absoluutväärtusega.
  • Viga korrutatakse konstantse Kp -ga.
  • Viga diferentseerub ajaliselt ja korrutatakse konstandiga Kd.
  • Mootorite kiirust uuendatakse ja tsükkel algab uuesti.

Drosselmootorite kiiruse reguleerimiseks kasutatakse põhiahelas järgmist koodi:

kiirus = 10 # töökiirus % PWM

# Muutujad, mida tuleb iga tsükli korral ajakohastada kuni nurga_vahemikuni_deg muutuva vea = abs (kõrvalekalle), kui kõrvalekalle -5: # ei tüürita, kui kõrvalekalle vahemikus 10 kraadi = 0 viga = 0 GPIO.väljund (in1, GPIO. LOW) GPIO.putput (in2, GPIO. LOW) roolimine.seiskamine () elif kõrvalekalle> 5: # juhtida paremale, kui kõrvalekalle on positiivne GPIO.väljund (in1, GPIO. LOW) GPIO.väljund (in2, GPIO. HIGH) rool. Algus (100) elifi kõrvalekalle < -5: # tüür vasakule, kui kõrvalekalle on negatiivne GPIO. väljund (in1, GPIO. HIGH) GPIO.väljund (in2, GPIO. LOW) * viga PD = int (kiirus + tuletis + proportsionaalne) spd = abs (PD), kui spd> 25: spd = 25 drossel.start (spd) lastError = viga lastTime = time.time ()

Kui viga on väga suur (kõrvalekalle keskelt on suur), on proportsionaalsed ja tuletatud toimingud suured, mille tulemuseks on suur drosselkiirus. Kui viga läheneb 0 -le (kõrvalekalle keskelt on madal), toimib tuletistegevus vastupidiselt (kalle on negatiivne) ja gaasihoovastuse kiirus väheneb, et säilitada süsteemi stabiilsus. Täielik kood on lisatud allpool.

Samm 7: Tulemused

Ülaltoodud videod näitavad minu saadud tulemusi. See vajab rohkem häälestamist ja täiendavaid kohandusi. Ühendasin vaarika pi oma LCD -ekraaniga, kuna minu võrgu kaudu voogedastatav video oli kõrge latentsusega ja sellega töötamine oli väga masendav, seetõttu on videos vaarika pi -ga ühendatud juhtmed. Raja joonistamiseks kasutasin vahtplaate.

Ootan teie soovitusi selle projekti paremaks muutmiseks! Loodan, et see juhend oli piisavalt hea, et anda teile uut teavet.

Soovitan: