Sisällysluettelo:

Itsenäinen kaista-auto Raspberry Pi: n ja OpenCV: n avulla: 7 vaihetta (kuvilla)
Itsenäinen kaista-auto Raspberry Pi: n ja OpenCV: n avulla: 7 vaihetta (kuvilla)

Video: Itsenäinen kaista-auto Raspberry Pi: n ja OpenCV: n avulla: 7 vaihetta (kuvilla)

Video: Itsenäinen kaista-auto Raspberry Pi: n ja OpenCV: n avulla: 7 vaihetta (kuvilla)
Video: 20 товаров для автомобиля с Алиэкспресс, автотовары №32 2024, Marraskuu
Anonim
Itsenäinen kaistaa pitävä auto Raspberry Pi: n ja OpenCV: n avulla
Itsenäinen kaistaa pitävä auto Raspberry Pi: n ja OpenCV: n avulla

Tässä ohjeessa toteutetaan itsenäinen kaistanpitorobotti, joka suorittaa seuraavat vaiheet:

  • Osien kerääminen
  • Ohjelmiston asentamisen edellytykset
  • Laitteiston kokoonpano
  • Ensimmäinen testi
  • Kaistaviivojen havaitseminen ja ohjausviivan näyttäminen openCV: n avulla
  • PD -ohjaimen käyttöönotto
  • Tulokset

Vaihe 1: Osien kerääminen

Osien kerääminen
Osien kerääminen
Osien kerääminen
Osien kerääminen
Osien kerääminen
Osien kerääminen
Osien kerääminen
Osien kerääminen

Yllä olevat kuvat esittävät kaikki tässä projektissa käytetyt komponentit:

  • RC -auto: Sain omani maani paikallisesta kaupasta. Se on varustettu 3 moottorilla (2 kuristusta ja 1 ohjausta varten). Tämän auton suurin haitta on, että ohjaus on rajoitettu "ei ohjausta" ja "täysi ohjaus". Toisin sanoen se ei voi ohjata tietyssä kulmassa, toisin kuin servo-ohjaus RC-autot. Löydät täältä samanlaisen autosarjan, joka on suunniteltu erityisesti vadelmapi: lle.
  • Vadelma pi 3 malli b+: tämä on auton aivot, joka käsittelee monia käsittelyvaiheita. Se perustuu 64-bittiseen neljän ytimen prosessoriin, jonka kellotaajuus on 1,4 GHz. Sain omani täältä.
  • Raspberry pi 5 mp kameramoduuli: Se tukee 1080p @ 30 fps, 720p @ 60 fps ja 640x480p 60/90 tallennusta. Se tukee myös sarjaliitäntää, joka voidaan kytkeä suoraan vadelmapi: hen. Se ei ole paras vaihtoehto kuvankäsittelyohjelmille, mutta se riittää tähän projektiin ja on myös erittäin halpa. Sain omani täältä.
  • Moottorin ohjain: Käytetään tasavirtamoottorien suunnan ja nopeuden säätämiseen. Se tukee kahden tasavirtamoottorin ohjausta 1 kortilla ja kestää 1,5 A.
  • Virtapankki (valinnainen): Käytin virtalähdettä (mitoitettu 5V, 3A) virran kytkemiseksi vadelma pi: hen erikseen. Raspberry pi: n virransyöttöä 1 lähteestä tulisi käyttää askel alaspäin -muunninta (buck -muunnin: 3A lähtövirta).
  • 3s (12 V) LiPo -akku: Litiumpolymeeriakut tunnetaan erinomaisesta suorituskyvystä robotiikan alalla. Sitä käytetään moottorin ohjaimen virtalähteenä. Ostin omani täältä.
  • Uros -uros ja naaras -naaras -hyppyjohdot.
  • Kaksipuolinen teippi: Käytetään komponenttien kiinnittämiseen RC -autoon.
  • Sininen teippi: Tämä on erittäin tärkeä osa tätä projektia, ja sitä käytetään tekemään kaksi kaistaa, joiden välillä auto ajaa. Voit valita haluamasi värin, mutta suosittelen valitsemaan eri värit kuin ympäröivässä ympäristössä.
  • Vetoketjut ja puupalkit.
  • Ruuvimeisseli.

Vaihe 2: OpenCV: n asentaminen Raspberry Pi -laitteeseen ja etänäytön määrittäminen

OpenCV: n asentaminen Raspberry Pi -laitteeseen ja etänäytön määrittäminen
OpenCV: n asentaminen Raspberry Pi -laitteeseen ja etänäytön määrittäminen

Tämä vaihe on hieman ärsyttävä ja kestää jonkin aikaa.

OpenCV (Open source Computer Vision) on avoimen lähdekoodin tietokonevisio- ja koneoppimisohjelmistokirjasto. Kirjastossa on yli 2500 optimoitua algoritmia. Asenna openCV raspberry pi -laitteeseesi ja asenna raspberry pi OS -käyttöjärjestelmä (jos et vieläkään) noudattanut TÄTÄ yksinkertaista opasta. Huomaa, että openCV: n rakentaminen voi kestää noin 1,5 tuntia hyvin jäähdytetyssä huoneessa (koska prosessorin lämpötila nousee erittäin korkeaksi!), Joten juo teetä ja odota kärsivällisesti: D.

Etänäytön osalta seuraa myös TÄTÄ opasta, kun haluat määrittää raspberry pi -laitteen etäkäytön Windows-/Mac -laitteestasi.

Vaihe 3: Osien liittäminen yhteen

Osien yhdistäminen yhteen
Osien yhdistäminen yhteen
Osien yhdistäminen yhteen
Osien yhdistäminen yhteen
Osien yhdistäminen yhteen
Osien yhdistäminen yhteen

Yllä olevat kuvat esittävät yhteydet vadelma pi: n, kameramoduulin ja moottoriajurin välillä. Huomaa, että käyttämäni moottorit absorboivat 0,35 A 9 V: n jännitteellä, mikä tekee moottorin kuljettajan turvalliseksi käyttää 3 moottoria samanaikaisesti. Ja koska haluan ohjata kahden kuristusmoottorin nopeutta (1 takana ja 1 edessä) täsmälleen samalla tavalla, liitin ne samaan porttiin. Asensin moottorin ohjaimen auton oikealle puolelle kaksoisnauhalla. Mitä tulee kameramoduuliin, asetin vetoketjun ruuvinreikien väliin, kuten yllä oleva kuva osoittaa. Sitten sovitan kameran puupalkkiin, jotta voin säätää kameran sijaintia haluamallasi tavalla. Yritä asentaa kamera mahdollisimman paljon auton keskelle. Suosittelen asettamaan kameran vähintään 20 cm maanpinnan yläpuolelle, jotta näkökenttä auton edessä paranee. Fritzing -kaavio on liitteenä alla.

Vaihe 4: Ensimmäinen testi

Ensimmäinen testi
Ensimmäinen testi
Ensimmäinen testi
Ensimmäinen testi

Kameratestaus:

Kun kamera on asennettu ja openCV -kirjasto on rakennettu, on aika testata ensimmäinen kuva! Otamme kuvan pi camista ja tallennamme sen "original.jpg" -muodossa. Se voidaan tehdä kahdella tavalla:

1. Päätelaitteen komentojen käyttäminen:

Avaa uusi pääteikkuna ja kirjoita seuraava komento:

raspistill -o original.jpg

Tämä ottaa still -kuvan ja tallentaa sen "/pi/original.jpg" -hakemistoon.

2. Käyttämällä mitä tahansa python IDE: tä (käytän IDLE: tä):

Avaa uusi luonnos ja kirjoita seuraava koodi:

tuoda cv2

video = cv2. VideoCapture (0) kun True: ret, frame = video.read () frame = cv2.flip (frame, -1) # käytetään kuvan kääntämiseen pystysuunnassa cv2.imshow ('alkuperäinen', kehys) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Katsotaanpa mitä tässä koodissa tapahtui. Ensimmäinen rivi tuo openCV -kirjastomme käyttämään kaikkia sen toimintoja. VideoCapture (0) -toiminto aloittaa suoratoiston suoratoiston tämän toiminnon määrittämästä lähteestä, tässä tapauksessa 0, mikä tarkoittaa raspikameraa. jos sinulla on useita kameroita, sinun on asetettava eri numerot. video.read () lukee, että jokainen kehys tulee kamerasta ja tallentaa sen muuttujaan nimeltä kehys. flip () -toiminto kääntää kuvan y-akselin suhteen (pystysuoraan), koska asennan kamerani käänteisesti. imshow () näyttää kehyksemme sanalla "alkuperäinen" ja imwrite () tallentaa valokuvamme alkuperäisenä.jpg. waitKey (1) odottaa 1 ms minkä tahansa näppäimistön painikkeen painamista ja palauttaa sen ASCII -koodin. jos Esc -painiketta painetaan, desimaaliluku 27 palautetaan ja katkaisee silmukan vastaavasti. video.release () lopettaa tallennuksen ja tuhoaa AllWindows () sulkee kaikki imshow () -toiminnon avaamat kuvat.

Suosittelen testaamaan valokuvaasi toisella menetelmällä, jotta voit tutustua openCV -toimintoihin. Kuva tallennetaan hakemistoon "/pi/original.jpg". Kameran ottama alkuperäinen kuva näkyy yllä.

Moottorien testaus:

Tämä vaihe on välttämätön kunkin moottorin pyörimissuunnan määrittämiseksi. Aluksi esitetään lyhyt johdanto moottoriajurin toimintaperiaatteesta. Yllä olevassa kuvassa näkyy moottorin ohjaimen pin-out. Ota käyttöön A, tulo 1 ja tulo 2 liittyvät moottorin A ohjaukseen. Ota käyttöön B, tulo 3 ja tulo 4 liittyvät moottorin B ohjaukseen. Suuntaohjaus määritetään "Input" -osan avulla ja nopeuden säätö "Enable" -osan avulla. Jos haluat esimerkiksi ohjata moottorin A suuntaa, aseta tulo 1 asentoon KORKEA (3,3 V tässä tapauksessa, koska käytämme vadelmapiä) ja aseta tulo 2 asentoon LOW, moottori pyörii tiettyyn suuntaan ja asettamalla vastakkaiset arvot tuloon 1 ja tuloon 2, moottori pyörii vastakkaiseen suuntaan. Jos tulo 1 = tulo 2 = (HIGH tai LOW), moottori ei pyöri. Ota käyttöön nastat ottavat pulssinleveysmodulaation (PWM) tulosignaalin vadelmasta (0 - 3,3 V) ja käyttävät moottoreita vastaavasti. Esimerkiksi 100% PWM -signaali tarkoittaa, että työskentelemme suurimman nopeuden parissa, ja 0% PWM -signaali tarkoittaa, että moottori ei pyöri. Seuraavaa koodia käytetään määrittämään moottoreiden suunnat ja testaamaan niiden nopeudet.

tuonnin aika

tuoda RPi. GPIO GPIO: na GPIO.setwarnings (False) # Ohjausmoottorin nastat 23 # Fyysinen nasta 16 in4 = 24 # Fyysinen nasta 18 GPIO.setmode (GPIO. BCM) # Käytä GPIO -numerointia fyysisen numeroinnin sijaan GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. asetukset (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (kaasuventtiili käytössä, GPIO.lähtö) GPIO.setup (ohjauksen käyttöönotto, GPIO.lähtö) # Ohjausmoottorin ohjaus GPIO.lähtö (in1, GPIO). KORKEA) GPIO.lähtö (in2, GPIO. LOW) ohjaus = GPIO. PWM (ohjaus_käytettävissä, 1000) # aseta kytkentätaajuudeksi 1000 Hz ohjaus. Stop () # Kaasumoottoreiden ohjaus GPIO.lähtö (in3, GPIO. HIGH) GPIO. output (in4, GPIO. LOW) kaasu = GPIO. PWM (kaasu_käytettävä, 1000) # aseta kytkentätaajuudeksi 1000 Hz kaasu. stop () time.sleep (1) kaasu. start (25) # käynnistää moottorin 25 % PWM -signaali-> (0,25 * akun jännite) - kuljettajan häviöohjaus. käynnistys (100) # käynnistää moottorin 100% PWM -signaalilla-> (1 * akun jännite) - kuljettajan häviöaika. nukkumassa (3) kaasu. pysäytys () ohjaus. pysäytys ()

Tämä koodi käyttää kaasu- ja ohjausmoottoria 3 sekunnin ajan ja pysäyttää ne sitten. (Kuljettajan menetys) voidaan määrittää volttimittarilla. Tiedämme esimerkiksi, että 100% PWM -signaalin pitäisi antaa koko akun jännite moottorin liittimessä. Mutta asettamalla PWM -arvoksi 100%huomasin, että ohjain aiheuttaa 3 V: n pudotuksen ja moottori saa 9 V: n 12 V: n sijasta (juuri mitä tarvitsen!). Tappio ei ole lineaarinen eli 100%: n tappio on hyvin erilainen kuin 25%: n tappio. Yllä olevan koodin suorittamisen jälkeen tulokset olivat seuraavat:

Kaasutulokset: jos in3 = HIGH ja in4 = LOW, kuristusmoottoreissa on kellon viisas (CW) kierros eli auto liikkuu eteenpäin. Muuten auto liikkuu taaksepäin.

Ohjaustulokset: jos in1 = HIGH ja in2 = LOW, ohjausmoottori pyörii suurimmalle vasemmalle eli auto ohjaa vasemmalle. Muuten auto ohjaa oikealle. Muutamien kokeiden jälkeen huomasin, että ohjausmoottori ei pyöri, jos PWM -signaali ei ollut 100% (eli moottori ohjaa joko kokonaan oikealle tai kokonaan vasemmalle).

Vaihe 5: Kaistaviivojen havaitseminen ja suuntaviivan laskeminen

Kaistaviivojen havaitseminen ja suuntaviivan laskeminen
Kaistaviivojen havaitseminen ja suuntaviivan laskeminen
Kaistaviivojen havaitseminen ja suuntaviivan laskeminen
Kaistaviivojen havaitseminen ja suuntaviivan laskeminen
Kaistaviivojen havaitseminen ja suuntaviivan laskeminen
Kaistaviivojen havaitseminen ja suuntaviivan laskeminen

Tässä vaiheessa selitetään algoritmi, joka ohjaa auton liikettä. Ensimmäinen kuva näyttää koko prosessin. Järjestelmän tulo on kuvia, lähtö on theta (ohjauskulma asteina). Huomaa, että käsittely suoritetaan yhdelle kuvalle ja se toistetaan kaikissa kuvissa.

Kamera:

Kamera alkaa tallentaa videota (320 x 240) resoluutiolla. Suosittelen pienentämään resoluutiota, jotta saat paremman kuvataajuuden (fps), koska fps laskee, kun käsittelytekniikoita on sovellettu kuhunkin kehykseen. Alla oleva koodi on ohjelman pääsilmukka ja lisää jokaisen vaiheen tämän koodin päälle.

tuoda cv2

tuo numpy muodossa np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # aseta leveydeksi 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # aseta korkeudeksi 240 p # Silmukka Totta: 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 ()

Tässä oleva koodi näyttää vaiheessa 4 saadun alkuperäisen kuvan ja näkyy yllä olevissa kuvissa.

Muunna HSV -väritilaksi:

Nyt kun olet ottanut videotallennuksen kuvina kamerasta, seuraava askel on muuntaa jokainen kehys värisävyksi, värikylläisyydeksi ja arvoksi (HSV). Suurin etu tässä on, että pystyy erottamaan värit niiden kirkkauden mukaan. Ja tässä on hyvä selitys HSV -väriavaruudelle. Muuntaminen HSV -muotoon tapahtuu seuraavan toiminnon avulla:

def convert_to_HSV (kehys):

hsv = cv2.cvtColor (kehys, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) palauta hsv

Tämä toiminto kutsutaan pääsilmukasta ja palauttaa kehyksen HSV -väriavaruudessa. HSV -väriavaruudessa hankkimani kehys on esitetty yllä.

Tunnista sininen väri ja reunat:

Kun kuva on muutettu HSV -väritilaksi, on aika tunnistaa vain haluamamme väri (eli sininen väri, koska se on kaistaviivojen väri). Sinisen värin saamiseksi HSV -kehyksestä on määritettävä värisävy, värikylläisyys ja arvo. katso täältä saadaksesi paremman käsityksen HSV -arvoista. Joidenkin kokeiden jälkeen sinisen värin ylä- ja alarajat näkyvät alla olevassa koodissa. Ja jokaisen kehyksen yleisen vääristymän vähentämiseksi reunat tunnistetaan vain käyttämällä karkeaa reunatunnistinta. Lisää canny reunasta löytyy täältä. Nyrkkisääntönä on valita Canny () -funktion parametrit suhteella 1: 2 tai 1: 3.

def detect_edges (kehys):

alempi_sininen = np.array ([90, 120, 0], dtype = "uint8") # sinisen värin alaraja ylä_sininen = np.array ([150, 255, 255], dtype = "uint8") # yläraja sininen värimaski = cv2.inRange (hsv, alempi_sininen, ylempi_sininen) # tämä maski suodattaa kaiken paitsi sinisen # havaitsee reunat reunat = cv2. Canny (maski, 50, 100) cv2.imshow ("reunat", reunat) paluureunat

Tätä toimintoa kutsutaan myös pääsilmukasta, joka ottaa parametriksi HSV -väriavaruuden kehyksen ja palauttaa reunan. Saamani terävä kehys löytyy yllä.

Valitse kiinnostava alue (ROI):

Kiinnostavan alueen valitseminen on tärkeää, jotta voit keskittyä vain yhteen kehyksen alueeseen. Tässä tapauksessa en halua, että auto näkee paljon esineitä ympäristössä. Haluan vain, että auto keskittyy kaistaviivoihin ja sivuuttaa kaiken muun. P. S: Koordinaattijärjestelmä (x- ja y -akselit) alkaa vasemmasta yläkulmasta. Toisin sanoen piste (0, 0) alkaa vasemmasta yläkulmasta. y-akseli on korkeus ja x-akseli leveys. Alla oleva koodi valitsee kiinnostavan alueen keskittyäkseen vain kehyksen alaosaan.

def region_of_interest (reunat):

korkeus, leveys = reunat. muoto # poimi reunojen korkeus ja leveys kehyksen naamio = np.zeros_like (reunat) # tee tyhjä matriisi, jonka reunat ovat samankokoiset # tarkenna vain näytön alaosa # määritä 4 pistettä (vasen alakulma, vasen yläkulma, oikea ylä, oikea alaosa),], np.int32) cv2.fillPoly (maski, monikulmio, 255) # täytä monikulmio sinisellä värillä cropped_edges = cv2.bitwise_and (reunat, maski) cv2.imshow ("roi", cropped_edges) return cropped_edges

Tämä toiminto ottaa reunaisen kehyksen parametriksi ja piirtää monikulmion, jossa on 4 esiasetuspistettä. Se keskittyy vain monikulmion sisälle ja jättää huomiotta kaiken sen ulkopuolella. Kiinnostusalueeni kehys näkyy yllä.

Tunnista rivisegmentit:

Hough -muunnosta käytetään viivaosien havaitsemiseen terävästä kehyksestä. Hough -muunnos on tekniikka minkä tahansa muodon havaitsemiseksi matemaattisessa muodossa. Se voi tunnistaa melkein minkä tahansa kohteen, vaikka se olisi vääristynyt jonkin äänimäärän mukaan. Tässä on hyvä viittaus Hough -muunnokseen. Tässä sovelluksessa cv2. HoughLinesP () -toimintoa käytetään havaitsemaan viivat kussakin kehyksessä. Tämän toiminnon tärkeät parametrit ovat:

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

  • Kehys: on kehys, jossa haluamme havaita viivoja.
  • rho: Se on etäisyyden tarkkuus pikseleinä (yleensä se on = 1)
  • teeta: kulman tarkkuus radiaaneina (aina = np.pi/180 ~ 1 aste)
  • min_threshold: vähimmäisäänimäärä, jonka sen pitäisi saada, jotta sitä voidaan pitää rivinä
  • minLineLength: rivin vähimmäispituus pikseleinä. Mitään tätä numeroa lyhyempää riviä ei pidetä rivinä.
  • maxLineGap: kahden rivin välinen pikseliväli, joka käsitellään yhtenä rivinä. (Sitä ei käytetä minun tapauksessani, koska käyttämilläni kaistaviivoilla ei ole aukkoa).

Tämä funktio palauttaa rivin päätepisteet. Pääsilmukastani kutsutaan seuraavaa funktiota linjojen havaitsemiseksi Hough -muunnoksen avulla:

def detect_line_segments (cropped_edges):

rho = 1 theta = 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

Keskimääräinen kaltevuus ja leikkaus (m, b):

Muista, että suoran yhtälö on y = mx + b. Missä suoran kaltevuus ja b y-leikkauspiste. Tässä osassa lasketaan Hough -muunnoksen avulla havaittujen linjojen segmenttien rinteiden ja leikkausten keskiarvo. Ennen kuin teemme niin, katsotaan yllä olevaa alkuperäistä kehyskuvaa. Vasen kaista näyttää nousevan ylöspäin, joten sillä on negatiivinen kaltevuus (muistatko koordinaatiston alkupisteen?). Toisin sanoen vasemmanpuoleisella kaistalla on x1 <x2 ja y2 x1 ja y2> y1, mikä antaa positiivisen kaltevuuden. Joten kaikkia viivoja, joilla on positiivinen kaltevuus, pidetään oikean kaistan pisteinä. Pystysuorien viivojen (x1 = x2) kaltevuus on ääretön. Tässä tapauksessa ohitamme kaikki pystysuorat viivat virheen saamisen estämiseksi. Jotta tämä tunnistus olisi tarkempi, jokainen kehys on jaettu kahteen alueeseen (oikea ja vasen) kahden rajaviivan kautta. Kaikki leveyspisteet (x-akselipisteet), jotka ovat suurempia kuin oikea rajaviiva, liittyvät oikean kaistan laskentaan. Ja jos kaikki leveyspisteet ovat pienempiä kuin vasen rajaviiva, ne liitetään vasemman kaistan laskentaan. Seuraava toiminto ottaa kehyksen käsiteltäväksi ja Hough -muunnoksen avulla havaitut kaistasegmentit ja palauttaa kahden kaistaviivan keskimääräisen kaltevuuden ja leikkauksen.

def keskimääräinen_kulman_intercept (kehys, rivin_segmentit):

lane_lines = jos line_segments on None: print ("linjasegmenttiä ei havaittu") palaa lane_lines korkeus, leveys, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - border) right_region_boundary = leveys * raja rivin_segmentille rivin_segmentit: x1, y1, x2, y2 rivisegmentissä: jos x1 == x2: tulosta ("ohitetaan pystysuorat viivat (kaltevuus = ääretön)") jatka sovitusta = np.polyfit ((x1, x2)) liitä ((kaltevuus, sieppaus)) 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)) if len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines on 2-D-matriisi, joka koostuu oikean ja vasemman kaistan viivojen koordinaateista # esimerkiksi: lan e_lines =

make_points () on aputoiminto keskiarvon_silope_intercept () -funktiolle, joka palauttaa kaistaviivojen rajatut koordinaatit (kehyksen alareunasta keskelle).

def make_points (kehys, viiva):

korkeus, leveys, _ = runko. muodon kaltevuus, leikkaus = viiva y1 = korkeus # kehyksen alaosa y2 = int (y1 / 2) # tee pisteitä kehyksen keskeltä alaspäin, jos kaltevuus == 0: kaltevuus = 0,1 x1 = int ((y1 - sieppaus) / kaltevuus) x2 = int ((y2 - sieppaus) / kaltevuus) paluu

Jotta estetään jakaminen 0: lla, esitetään ehto. Jos kaltevuus = 0, mikä tarkoittaa y1 = y2 (vaakasuora viiva), anna kaltevuudelle lähellä arvoa. Tämä ei vaikuta algoritmin suorituskykyyn eikä estä mahdottomia tapauksia (jakaminen 0: lla).

Kaistaviivojen näyttämiseksi kehyksissä käytetään seuraavaa toimintoa:

def display_lines (kehys, viivat, rivin väri = (0, 255, 0), rivin leveys = 6): # rivin väri (B, G, R)

line_image = np.zeros_like (frame), jos rivit eivät ole Ei mitään: rivin rivit: x1, y1, x2, y2 rivillä: 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

cv2.addWeighted () -funktio ottaa seuraavat parametrit ja sitä käytetään kahden kuvan yhdistämiseen, mutta jokaiselle annetaan paino.

cv2.add Painotettu (kuva1, alfa, kuva2, beta, gamma)

Ja laskee tulostettavan kuvan seuraavan yhtälön avulla:

tuotos = alfa * kuva1 + beta * kuva2 + gamma

Lisätietoja cv2.addWeighted () -funktiosta saadaan täältä.

Laske ja näytä otsikkorivi:

Tämä on viimeinen vaihe ennen kuin käytämme nopeuksia moottoreihimme. Suuntaviiva on velvollinen antamaan ohjausmoottorille suunnan, johon sen pitäisi pyöriä, ja antaa kuristusmoottoreille nopeuden, jolla ne toimivat. Otsikkorivin laskeminen on puhdasta trigonometriaa, käytetään tan ja atan (tan^-1) trigonometrisiä funktioita. Joitakin ääritapauksia ovat esimerkiksi silloin, kun kamera tunnistaa vain yhden kaistan linjan tai kun se ei havaitse yhtään linjaa. Kaikki nämä tapaukset näkyvät seuraavassa toiminnossa:

def get_steering_angle (kehys, kaista_viivat):

korkeus, leveys, _ = runko.muoto, jos len (kaista_viivat) == 2: # jos havaitaan kaksi kaistaista viivaa _, _, vasen_x2, _ = kaista_viivat [0] [0] # poimi vasemmalta x2 kaista_viivojen matriisista _, _, oikea_x2, _ = kaista_viivat [1] [0] # poimi oikea x2 kaista_viivojen matriisista mid = int (leveys / 2) x_offset = (vasen_x2 + oikea_x2) / 2 - y yoffoff = int (korkeus / 2) elif len (kaista_viivat)) == 1: # jos vain yksi viiva havaitaan x1, _, x2, _ = kaista_viivat [0] [0] x_offset = x2 - x1 y_offset = int (korkeus / 2) elif len (kaista_viivat) == 0: # jos viivaa ei havaita

x_siirto ensimmäisessä tapauksessa on se, kuinka paljon keskiarvo ((oikea x2 + vasen x2) / 2) eroaa näytön keskikohdasta. y_siirtymä on aina korkeus / 2. Viimeinen kuva yllä näyttää esimerkin otsikkorivistä. Angle_to_mid_radians on sama kuin "theta", joka näkyy yllä olevassa viimeisessä kuvassa. Jos ohjauskulma = 90, se tarkoittaa, että autossa on kulkusuunta kohtisuorassa "korkeus / 2" -viivaan nähden ja auto liikkuu eteenpäin ilman ohjausta. Jos ohjauskulma> 90, auton on ohjattava oikealle, muuten sen on ohjattava vasemmalle. Otsikkorivin näyttämiseen käytetään seuraavaa toimintoa:

def display_heading_line (kehys, ohjauskulma, rivin väri = (0, 0, 255), rivin leveys = 5)

otsikko_kuva = np.zeros_like (runko) korkeus, leveys, _ = runko.muoto ohjaus_kulma_radiaani = ohjauskulma / 180,0 * matematiikka.pi x1 = int (leveys / 2) y1 = korkeus x2 = int (x1 - korkeus / 2 / matematiikka) (ohjaus_kulma_radiaani)) y2 = int (korkeus / 2) cv2.line (otsikon_kuva, (x1, y1), (x2, y2), line_color, line_width) head_image = cv2.addWeighted (frame, 0.8, head_image, 1, 1) palaa otsikon_kuva

Yllä oleva toiminto ottaa syötteenä kehyksen, johon suuntaviiva vedetään, ja ohjauskulman. Se palauttaa otsikkorivin kuvan. Minun tapauksessani otettu otsikkorivikehys näkyy yllä olevassa kuvassa.

Koko koodin yhdistäminen yhteen:

Koodi on nyt valmis koottavaksi. Seuraava koodi näyttää jokaisen toiminnon kutsuvan ohjelman pääsilmukan:

tuoda cv2

tuo numpy muodossa np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) kun taas True: ret, frame = video.read () frame = cv2.flip (runko, -1) #Funktioiden kutsuminen = get_steering_angle (frame, lane_lines) head_image = display_heading_line (lane_lines_image, ohjaus_kulma) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Vaihe 6: PD -ohjauksen käyttäminen

PD -ohjauksen käyttäminen
PD -ohjauksen käyttäminen

Nyt ohjauskulma on valmis syötettäväksi moottoreille. Kuten aiemmin mainittiin, jos ohjauskulma on suurempi kuin 90, auton tulee kääntyä oikealle, muuten sen pitäisi kääntyä vasemmalle. Käytin yksinkertaista koodia, joka kääntää ohjausmoottorin oikealle, jos kulma on yli 90, ja kääntää sen vasemmalle, jos ohjauskulma on alle 90 vakiokaasunopeudella (10% PWM), mutta sain paljon virheitä. Suurin virhe, jonka sain, kun auto lähestyy mitä tahansa käännöstä, ohjausmoottori toimii suoraan, mutta kuristusmoottorit jäävät jumiin. Yritin lisätä kaasun nopeutta (20% PWM) käännöksissä, mutta päättyin siihen, että robotti nousi kaistoilta. Tarvitsin jotain, joka lisää kaasun nopeutta paljon, jos ohjauskulma on erittäin suuri, ja lisää nopeutta hieman, jos ohjauskulma ei ole niin suuri, pienentää nopeuden alkuarvoon, kun auto lähestyy 90 astetta (liikkuu suoraan). Ratkaisu oli käyttää PD -ohjainta.

PID -säädin tarkoittaa suhteellista, integroitua ja johdannaissäädintä. Tämän tyyppisiä lineaarisia ohjaimia käytetään laajalti robotiikkasovelluksissa. Yllä oleva kuva näyttää tyypillisen PID -takaisinkytkennän ohjaussilmukan. Tämän säätimen tavoitteena on saavuttaa "asetuspiste" tehokkaimmin, toisin kuin "päälle - pois" -ohjaimet, jotka kytkevät tai sammuttavat laitoksen tietyissä olosuhteissa. Jotkut avainsanat on tiedettävä:

  • Asetusarvo: on haluttu arvo, jonka haluat järjestelmän saavuttavan.
  • Todellinen arvo: on anturin havaitsema todellinen arvo.
  • Virhe: on ohjearvon ja todellisen arvon välinen ero (virhe = asetuspiste - todellinen arvo).
  • Hallittu muuttuja: sen nimen perusteella muuttuja, jota haluat hallita.
  • Kp: Suhteellinen vakio.
  • Ki: Integraalivakio.
  • Kd: Johdannaisvakio.

Lyhyesti sanottuna PID -ohjausjärjestelmän silmukka toimii seuraavasti:

  • Käyttäjä määrittelee järjestelmän saavuttamaan tarvittavan asetusarvon.
  • Virhe lasketaan (virhe = ohjearvo - todellinen).
  • P -ohjain tuottaa virheen arvoon verrannollisen toiminnan. (virhe kasvaa, myös P -toiminta kasvaa)
  • I -ohjain integroi virheen ajan myötä, mikä poistaa järjestelmän vakaan tilan virheen, mutta lisää sen ylitystä.
  • D -ohjain on yksinkertaisesti virheen aikajohdannainen. Toisin sanoen se on virheen kaltevuus. Se suorittaa virheen johdannaiseen verrannollisen toiminnan. Tämä ohjain lisää järjestelmän vakautta.
  • Ohjaimen lähtö on kolmen ohjaimen summa. Ohjaimen lähdöstä tulee 0, jos virheestä tulee 0.

Suuri selitys PID -säätimelle löytyy täältä.

Palatessani takaisin kaistaa pitävälle autolle, hallittu muuttujani oli kaasun nopeus (koska ohjauksessa on vain kaksi tilaa joko oikealle tai vasemmalle). Tähän tarkoitukseen käytetään PD -ohjainta, koska D -toiminto lisää kaasun nopeutta paljon, jos virheen muutos on erittäin suuri (eli suuri poikkeama), ja hidastaa autoa, jos tämä virhemuutos lähestyy 0. Tein seuraavat vaiheet PD: n toteuttamiseksi ohjain:

  • Aseta asetusarvo 90 asteeseen (haluan aina, että auto liikkuu suoraan)
  • Laskettu poikkeamakulma keskeltä
  • Poikkeama antaa kaksi tietoa: kuinka suuri virhe on (poikkeaman suuruus) ja mihin suuntaan ohjausmoottorin on otettava (poikkeaman merkki). Jos poikkeama on positiivinen, auton on ohjattava oikealle, muuten sen on ohjattava vasemmalle.
  • Koska poikkeama on joko negatiivinen tai positiivinen, määritetään "virhe" -muuttuja, joka on aina yhtä suuri kuin poikkeaman absoluuttinen arvo.
  • Virhe kerrotaan vakiona Kp.
  • Virhe läpikäy ajanerotuksen ja kerrotaan vakiona Kd.
  • Moottorin nopeus päivitetään ja silmukka alkaa uudelleen.

Pääsilmukassa käytetään seuraavaa koodia kuristusmoottorien nopeuden säätämiseen:

nopeus = 10 # toimintanopeus % PWM

# Muuttujat päivitettävä jokaisessa silmukassa lastTime = 0 lastError = 0 # PD -vakioita Kp = 0,4 Kd = Kp * 0,65 Vaikka True: now = time.time () # nykyinen aikamuuttuja dt = now - lastTime deviation = ohjaus_kulma - 90 # vastaava kulmaan_kulma_milj. asteeseen muuttuva virhe = abs (poikkeama), jos poikkeama -5: # älä ohjaa, jos poikkeama on 10 astetta = 0 virhe = 0 GPIO. output (in1, GPIO. LOW) GPIO. output (in2, GPIO. LOW) ohjaus.stop () elif -poikkeama> 5: # ohjaa oikealle, jos poikkeama on positiivinen GPIO. output (in1, GPIO. LOW) GPIO. output (in2, GPIO. HIGH) ohjaus. Start (100) elif poikkeama < -5: # ohjata vasemmalle, jos poikkeama on negatiivinen * virhe PD = int (nopeus + johdannainen + suhteellinen) spd = abs (PD), jos spd> 25: spd = 25 kaasu.start (spd) lastError = virhe lastTime = time.time ()

Jos virhe on erittäin suuri (poikkeama keskeltä on suuri), suhteelliset ja johdannaistoiminnot ovat suuria, mikä johtaa suureen kuristusnopeuteen. Kun virhe lähestyy 0: ta (poikkeama keskikohdasta on pieni), johdannaistoiminto toimii päinvastoin (kaltevuus on negatiivinen) ja kuristusnopeus pienenee järjestelmän vakauden ylläpitämiseksi. Koko koodi on alla.

Vaihe 7: Tulokset

Yllä olevat videot osoittavat saamani tulokset. Se vaatii enemmän viritystä ja lisäsäätöjä. Yhdistin vadelma pi LCD -näyttöön, koska verkon kautta suoratoistettavan videon latenssi oli korkea ja sen kanssa työskentely oli erittäin turhauttavaa, minkä vuoksi videossa on kytketty vadelmia pi. Käytin vaahtolevyjä radan piirtämiseen.

Odotan kuulevani suosituksiasi projektin parantamiseksi! Toivon, että nämä ohjeet ovat tarpeeksi hyviä antamaan sinulle uutta tietoa.

Suositeltava: