Sadržaj:
- Korak 1: Skupljanje komponenti
- Korak 2: Instaliranje OpenCV -a na Raspberry Pi i postavljanje udaljenog ekrana
- Korak 3: Povežite dijelove zajedno
- Korak 4: Prvi test
- Korak 5: Otkrivanje linija trake i izračunavanje linije smjera
- Korak 6: Primjena PD kontrole
- Korak 7: Rezultati
Video: Autonomni automobil za održavanje saobraćajne trake koji koristi Raspberry Pi i OpenCV: 7 koraka (sa slikama)
2024 Autor: John Day | [email protected]. Zadnja izmjena: 2024-01-30 08:05
U ovim uputama će se implementirati autonomni robot za održavanje trake koji će proći kroz sljedeće korake:
- Skupljanje delova
- Preduvjeti za instaliranje softvera
- Sklapanje hardvera
- Prvi test
- Otkrivanje linija trake i prikazivanje linije vođenja pomoću openCV -a
- Implementacija PD kontrolera
- Rezultati
Korak 1: Skupljanje komponenti
Gornje slike prikazuju sve komponente korištene u ovom projektu:
- RC auto: Moj sam nabavio u lokalnoj trgovini u mojoj zemlji. Opremljen je sa 3 motora (2 za prigušivanje i 1 za upravljanje). Glavni nedostatak ovog automobila je to što je upravljanje ograničeno između "bez upravljanja" i "potpunog upravljanja". Drugim riječima, ne može se upravljati pod određenim kutom, za razliku od RC automobila sa servo upravljačem. Odavde možete pronaći slične komplete za automobile dizajnirane posebno za maline pi.
- Raspberry pi 3 model b+: ovo je mozak automobila koji će obraditi mnoge faze obrade. Zasnovan je na četverojezgrenom 64-bitnom procesoru takta 1,4 GHz. Ja sam uzeo svoje odavde.
- Modul kamere Raspberry pi 5 mp: Podržava 1080p @ 30 fps, 720p @ 60 fps i 640x480p 60/90 snimanje. Podržava i serijsko sučelje koje se može priključiti direktno na malinu pi. To nije najbolja opcija za aplikacije za obradu slika, ali je dovoljna za ovaj projekt, jer je i vrlo jeftina. Ja sam uzeo svoje odavde.
- Pogonitelj motora: Koristi se za kontrolu smjera i brzine istosmjernih motora. Podržava upravljanje 2 dc motora u 1 ploči i može izdržati 1,5 A.
- Power Bank (opcionalno): Koristio sam power bank (ocijenjen na 5V, 3A) za zasebno napajanje maline pi. Pretvarač stepenastog pretvarača (buck pretvarač: 3A izlazna struja) treba koristiti za napajanje maline pi iz 1 izvora.
- 3s (12 V) LiPo baterija: Litijum -polimerske baterije poznate su po odličnim performansama na polju robotike. Koristi se za napajanje upravljačkog programa motora. Ja sam svoj kupio odavde.
- Muški na muški i ženski na ženski kratkospojnik.
- Dvostrana traka: Koristi se za montažu komponenti na RC automobil.
- Plava traka: Ovo je vrlo važna komponenta ovog projekta, koristi se za izradu dvije trake između kojih će automobil voziti. Možete odabrati bilo koju boju koju želite, ali preporučujem da odaberete boje drugačije od onih u okolini.
- Zip kravate i drvene šipke.
- Šrafciger.
Korak 2: Instaliranje OpenCV -a na Raspberry Pi i postavljanje udaljenog ekrana
Ovaj korak je pomalo dosadan i trajat će neko vrijeme.
OpenCV (Open Source Computer Vision) je biblioteka računarskog vida i softvera za mašinsko učenje otvorenog koda. Biblioteka ima više od 2500 optimizovanih algoritama. Slijedite OVAJ vrlo jednostavan vodič za instalaciju openCV -a na vašem maline pi, kao i instaliranje malina pi OS -a (ako još niste). Imajte na umu da proces izgradnje openCV-a može potrajati oko 1,5 sati u dobro rashlađenoj prostoriji (jer će temperatura procesora postati vrlo visoka!) Pa popijte čaj i strpljivo sačekajte: D.
Za daljinski prikaz, također slijedite OVAJ vodič za postavljanje daljinskog pristupa vašem maline pi sa vašeg Windows/Mac uređaja.
Korak 3: Povežite dijelove zajedno
Gornje slike prikazuju veze između maline pi, modula kamere i upravljačkog programa motora. Imajte na umu da motori koje sam koristio apsorbiraju 0,35 A na 9 V svaki, što vozaču motora omogućuje sigurno pokretanje 3 motora u isto vrijeme. A budući da želim kontrolirati brzinu 2 motora za prigušivanje (1 straga i 1 sprijeda) na isti način, spojio sam ih na isti priključak. Montirao sam vozača motora na desnu stranu automobila pomoću dvostruke trake. Što se tiče modula kamere, ja sam umetnuo patentni zatvarač između rupa za vijke kao što prikazuje gornja slika. Zatim pričvršćujem kameru na drvenu šipku kako bih mogao prilagoditi položaj kamere kako želim. Pokušajte postaviti kameru što je više moguće u sredinu automobila. Preporučujem da kameru postavite najmanje 20 cm iznad zemlje kako bi se vidno polje ispred automobila poboljšalo. Shema Fritzinga nalazi se u nastavku.
Korak 4: Prvi test
Testiranje kamere:
Nakon što je kamera instalirana i otvorena biblioteka openCV, vrijeme je da testiramo našu prvu sliku! Uzećemo fotografiju sa pi cama i sačuvati je kao "original.jpg". To se može učiniti na 2 načina:
1. Korištenje naredbi terminala:
Otvorite novi prozor terminala i upišite sljedeću naredbu:
raspistill -o original.jpg
Ovo će snimiti fotografiju i sačuvati je u direktoriju "/pi/original.jpg".
2. Koristeći bilo koji python IDE (ja koristim IDLE):
Otvorite novu skicu i napišite sljedeći kod:
import cv2
video = cv2. VideoCapture (0) dok je True: ret, frame = video.read () frame = cv2.flip (frame, -1) # koristi se za okretanje slike okomito cv2.imshow ('original', frame) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()
Pogledajmo šta se dogodilo u ovom kodu. Prva linija je uvoz naše openCV biblioteke za korištenje svih njenih funkcija. funkcija VideoCapture (0) započinje streaming videozapisa uživo s izvora određenog ovom funkcijom, u ovom slučaju to je 0 što znači raspi kamera. ako imate više kamera, potrebno je postaviti različite brojeve. video.read () će pročitati svaki kadar koji dolazi sa kamere i sačuvati ga u promenljivoj koja se zove "frame". funkcija flip () će preokrenuti sliku u odnosu na osu y (okomito) budući da kameru montiram obrnuto. imshow () će prikazati naše okvire na kojima stoji riječ "original", a imwrite () će našu fotografiju spremiti kao original.jpg. waitKey (1) će čekati 1 ms da se pritisne bilo koje dugme na tastaturi i vraća svoj ASCII kod. ako se pritisne tipka escape (esc), vraća se decimalna vrijednost 27 i prema tome će se prekinuti petlja. video.release () će zaustaviti snimanje i uništiti AllWindows () će zatvoriti svaku sliku koju otvori funkcija imshow ().
Preporučujem da testirate svoju fotografiju drugom metodom da biste se upoznali s funkcijama openCV -a. Slika je spremljena u direktorij "/pi/original.jpg". Originalna fotografija koju je moja kamera snimila prikazana je gore.
Testiranje motora:
Ovaj korak je bitan za određivanje smjera rotacije svakog motora. Prvo, kratki uvod o principu rada vozača motora. Gornja slika prikazuje pin-out upravljačkog programa motora. Omogući A, ulaz 1 i ulaz 2 povezani su s upravljanjem motorom A. Omogući B, ulaz 3 i ulaz 4 povezani su s upravljanjem motorom B. Kontrola smjera je uspostavljena dijelom "Input", a kontrola brzine je uspostavljena dijelom "Enable". Na primjer, za kontrolu smjera motora A, postavite ulaz 1 na VISOKO (3,3 V u ovom slučaju budući da koristimo malinu pi) i postavite ulaz 2 na LOW, motor će se vrtjeti u određenom smjeru i postavljanjem suprotnih vrijednosti do ulaza 1 i ulaza 2, motor će se vrtjeti u suprotnom smjeru. Ako je ulaz 1 = ulaz 2 = (VISOKO ili NISKO), motor se neće okretati. Pinovi za omogućavanje uzimaju ulazni signal PWM (Pulse Width Modulation) sa maline (0 do 3,3 V) i pokreću motore u skladu s tim. Na primjer, 100% PWM signal znači da radimo na maksimalnoj brzini, a 0% PWM signal znači da se motor ne okreće. Sljedeći kôd koristi se za određivanje smjerova motora i testiranje njihovih brzina.
vreme uvoza
uvesti RPi. GPIO kao GPIO GPIO.setwarnings (False) # Igle upravljačkog motora upravljiva_moguća = 22 # Fizička iglica 15 in1 = 17 # Fizička iglica 11 in2 = 27 # Fizička iglica 13 # Dugmad motora Igle throttle_enable = 25 # Fizička iglica 22 in3 = 23 # Fizički pin 16 in4 = 24 # Fizički pin 18 GPIO.setmod (GPIO. BCM) # Koristite GPIO numeriranje umjesto fizičkog numeriranja GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. podešavanje (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (enable_nable, GPIO.out) # Upravljanje motorom upravljača GPIO.output (in1, GPIO. VISOKO) GPIO.izlazni izlaz (in2, GPIO. LOW) upravljanje = GPIO. PWM (upravljanje_mogući, 1000) # postavite frekvenciju prebacivanja na 1000 Hz upravljanje.stop () # Motori za gas Kontrolirajte GPIO.izlaz (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) gas = GPIO. PWM (throttle_enable, 1000) # postavite frekvenciju uključivanja na 1000 Hz throttle.stop () time.sleep (1) throttle.start (25) # pokreće motor na 25 % PWM signal-> (0,25 * napon baterije) - vozačev gubitak upravljanja.pokretanje (100) # pokreće motor na 100% PWM signala-> (1 * Napon baterije) - vrijeme gubitka vozača.spavanje (3) gasa.stop () upravljanje.stop ()
Ovaj kôd će pokretati prigušne motore i motor upravljača 3 sekunde, a zatim će ih zaustaviti. (Gubitak vozača) može se odrediti pomoću voltmetra. Na primjer, znamo da 100% PWM signal treba dati puni napon baterije na priključku motora. Ali, postavljanjem PWM -a na 100%, otkrio sam da upravljački program uzrokuje pad od 3 V, a motor dobiva 9 V umjesto 12 V (upravo ono što mi treba!). Gubitak nije linearan, odnosno gubitak od 100% se jako razlikuje od gubitka od 25%. Nakon pokretanja gornjeg koda, moji rezultati su sljedeći:
Rezultati prigušivanja: ako je in3 = VISOKO, a in4 = NIZO, motori prigušivanja će imati rotaciju prema satu (CW), tj. Automobil će se kretati naprijed. U suprotnom, automobil će se kretati unazad.
Rezultati upravljanja: ako je in1 = VISOKO i in2 = NISKO, motor upravljača će se okrenuti maksimalno ulijevo, tj. Automobil će krenuti ulijevo. U suprotnom, automobil će skrenuti desno. Nakon nekih eksperimenata, otkrio sam da se motor upravljača neće okrenuti ako PWM signal nije 100% (tj. Motor će se usmjeriti ili udesno ili potpuno ulijevo).
Korak 5: Otkrivanje linija trake i izračunavanje linije smjera
U ovom koraku bit će objašnjen algoritam koji će kontrolirati kretanje automobila. Prva slika prikazuje cijeli proces. Unos sistema su slike, izlaz je theta (kut upravljanja u stupnjevima). Imajte na umu da se obrada vrši na 1 slici i da će se ponoviti na svim okvirima.
Kamera:
Kamera će početi snimati video zapis rezolucije (320 x 240). Preporučujem smanjenje rezolucije kako biste dobili bolju brzinu kadrova (fps) jer će do pada fps doći nakon primjene tehnika obrade na svaki kadar. Kod ispod bit će glavna petlja programa i dodavat će svaki korak preko ovog koda.
import cv2
uvoz numpy kao np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # postavi širinu na 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # postavi visinu na 240 p # Petlja dok Tačno: 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 ()
Kod ovdje prikazuje originalnu sliku dobivenu u koraku 4 i prikazan je na gornjim slikama.
Pretvori u HSV prostor boja:
Sada, nakon snimanja video zapisa kao okvira s fotoaparata, sljedeći korak je pretvaranje svakog kadra u prostor boje Hue, Saturation i Value (HSV). Glavna prednost toga je mogućnost razlikovanja boja prema stupnju osvjetljenja. Evo dobrog objašnjenja HSV prostora boja. Pretvaranje u HSV vrši se pomoću sljedeće funkcije:
def convert_to_HSV (okvir):
hsv = cv2.cvtBoja (okvir, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) vrati hsv
Ova funkcija će se pozvati iz glavne petlje i vratit će okvir u HSV prostor boja. Okvir koji sam dobio u HSV prostoru boja prikazan je gore.
Otkrijte plavu boju i rubove:
Nakon pretvaranja slike u HSV prostor boja, vrijeme je da otkrijemo samo onu boju koja nas zanima (tj. Plavu boju jer je to boja linija trake). Za izdvajanje plave boje iz HSV okvira, potrebno je navesti raspon nijansi, zasićenja i vrijednosti. pogledajte ovdje kako biste bolje razumjeli vrijednosti HSV -a. Nakon nekih eksperimenata, gornja i donja granica plave boje prikazane su u donjem kodu. A kako bi se smanjilo ukupno izobličenje u svakom kadru, rubovi se detektiraju samo pomoću detektora ivica. Više o canny edge -u možete pronaći ovdje. Opšte pravilo je odabir parametara funkcije Canny () u omjeru 1: 2 ili 1: 3.
def detektiranje_rubova (okvir):
lower_blue = np.array ([90, 120, 0], dtype = "uint8") # donja granica plave boje upper_blue = np.array ([150, 255, 255], dtype = "uint8") # gornja granica maska plave boje = cv2.inRange (hsv, lower_blue, upper_blue) # ova maska će filtrirati sve osim plave # otkriti rubove rubova = cv2. Canny (maska, 50, 100) cv2.imshow ("rubovi", rubovi) vratiti rubove
Ova funkcija će se također pozivati iz glavne petlje koja uzima kao parametar okvir prostora boje HSV -a i vraća okvir s ivicom. Okvir sa ivicom koji sam dobio nalazi se gore.
Odaberite regiju interesa (ROI):
Odabir područja interesa od presudne je važnosti za fokusiranje samo na 1 područje okvira. U ovom slučaju ne želim da automobil vidi mnogo predmeta u okruženju. Samo želim da se automobil usredotoči na trake i zanemari bilo što drugo. P. S: koordinatni sistem (osi x i y) počinje iz gornjeg lijevog ugla. Drugim riječima, točka (0, 0) počinje iz gornjeg lijevog kuta. y-osa je visina, a x-osi širina. Donji kôd odabire područje interesa koje se fokusira samo na donju polovicu kadra.
def region_of_interest (rubovi):
height, width = edge.shape # izdvojite visinu i širinu ivica frame frame = np.zeros_like (bridovi) # napravite praznu matricu sa istim dimenzijama okvira # samo fokus donju polovinu ekrana # navedite koordinate 4 tačke (donji lijevi, gornji lijevi, gornji desni, donji desni) poligon = np.masa (
Ova funkcija će uzeti rubni okvir kao parametar i nacrtati poligon s 4 unaprijed postavljene točke. Fokusirat će se samo na ono što je unutar poligona i zanemariti sve izvan njega. Okvir mog područja interesa prikazan je gore.
Otkrivanje segmenata linije:
Hough transformacija se koristi za otkrivanje segmenata linije iz okvira. Hough transformacija je tehnika za otkrivanje bilo kojeg oblika u matematičkom obliku. Može otkriti gotovo svaki objekt, čak i ako je iskrivljen prema određenom broju glasova. ovdje je prikazana odlična referenca za Hough transformaciju. Za ovu aplikaciju, funkcija cv2. HoughLinesP () se koristi za otkrivanje linija u svakom okviru. Važni parametri koje ova funkcija uzima su:
cv2. HoughLinesP (okvir, rho, theta, min_threshold, minLineLength, maxLineGap)
- Okvir: je okvir u kojem želimo otkriti linije.
- rho: To je preciznost udaljenosti u pikselima (obično je = 1)
- theta: kutna preciznost u radijanima (uvijek = np.pi/180 ~ 1 stepen)
- min_threshold: minimum glasova koji bi trebao dobiti da bi se smatrao linijom
- minLineLength: minimalna dužina linije u pikselima. Svaka linija kraća od ovog broja ne smatra se linijom.
- maxLineGap: maksimalni jaz u pikselima između 2 retka tretira se kao 1 red. (U mom slučaju se ne koristi jer linije traka koje koristim nemaju praznine).
Ova funkcija vraća krajnje točke linije. Sljedeća funkcija se poziva iz moje glavne petlje za otkrivanje linija pomoću Hough transformacije:
def detektiraj_segmente_line (izrezane_rubove):
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
Prosječni nagib i presjek (m, b):
podsjetimo se da je jednadžba prave dana sa y = mx + b. Gdje je m nagib prave, a b y-presjek. U ovom dijelu će se izračunati prosjek nagiba i presjeka segmenata linije otkrivenih Hough -ovom transformacijom. Prije nego što to učinimo, pogledajmo originalnu fotografiju okvira prikazanu gore. Čini se da lijeva traka ide prema gore pa ima negativan nagib (sjećate se početne tačke koordinatnog sistema?). Drugim riječima, lijeva traka ima x1 <x2 i y2 x1 i y2> y1 što će dati pozitivan nagib. Dakle, sve linije s pozitivnim nagibom smatraju se tačkama desne trake. U slučaju okomitih linija (x1 = x2), nagib će biti beskonačan. U ovom slučaju preskočit ćemo sve okomite crte kako bismo spriječili grešku. Kako bi se ovoj detekciji dodala veća preciznost, svaki okvir je podijeljen u dvije regije (desnu i lijevu) kroz 2 granične linije. Sve točke širine (točke osi x) veće od desne granične linije povezane su s proračunom desne trake. A ako su sve točke širine manje od lijeve granične linije, one su povezane s proračunom lijeve trake. Sljedeća funkcija uzima okvir u procesu obrade i segmente trake otkrivene pomoću Houghove transformacije i vraća prosječni nagib i presijecanje dviju traka.
def average_slope_intercept (frame, line_segments):
lane_lines = ako je line_segments nijedan: print ("nije otkriven segment linije") vraća lane_lines visinu, širinu, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - border) right_region_boundary = širina * granica za segment_ linije u segmentima linija: za x1, y1, x2, y2 u segmentu_ linije: ako je x1 == x2: ispis ("preskakanje okomitih linija (nagib = beskonačnost)") nastavi fit = np.polyfit ((x1, x2), (y1, y2), 1) nagib = (y2 - y1) / (x2 - x1) presretanje = y1 - (nagib * x1) ako je nagib <0: ako je x1 <lijeva_regija_granična i x2 desna_region_boundary i x2> desna_region_boundary: right_fit. dodaj ((nagib, presretanje)) 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) ako je len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines je 2-D niz koji se sastoji od koordinata desne i lijeve linije trake # na primjer: lan e_lines =
make_points () je pomoćna funkcija za average_slope_intercept () funkciju koja će vratiti ograničene koordinate linija trake (od dna do sredine okvira).
def make_points (okvir, linija):
visina, širina, _ = okvir.nagib oblika, presjek = linija y1 = visina # dno okvira y2 = int (y1 / 2) # napravite točke od sredine okvira prema dolje ako je nagib == 0: nagib = 0,1 x1 = int ((y1 - presretanje) / nagib) x2 = int ((y2 - presretanje) / nagib) return
Da bi se spriječilo dijeljenje s 0, predstavljen je uvjet. Ako je nagib = 0, što znači y1 = y2 (vodoravna linija), dajte nagibu vrijednost blizu 0. To neće utjecati na performanse algoritma, kao i spriječiti nemoguće slučajeve (dijeljenje s 0).
Za prikaz linija trake na okvirima koristi se sljedeća funkcija:
def display_lines (frame, lines, line_color = (0, 255, 0), line_width = 6): # boja linije (B, G, R)
line_image = np.zeros_like (okvir) ako linije nisu Ništa: za liniju u redovima: za x1, y1, x2, y2 u redu: cv2.line (linija_slika, (x1, y1), (x2, y2), linija_boja, line_width) line_image = cv2.addWeighted (frame, 0.8, line_image, 1, 1) return line_image
cv2.addWeighted () funkcija uzima sljedeće parametre i koristi se za kombiniranje dviju slika, ali svakoj daje težinu.
cv2.addWeighted (image1, alpha, image2, beta, gama)
I izračunava izlaznu sliku pomoću sljedeće jednadžbe:
izlaz = alfa * slika1 + beta * slika2 + gama
Više informacija o funkciji cv2.addWeighted () je izvedeno ovdje.
Izračunajte i prikažite liniju zaglavlja:
Ovo je posljednji korak prije nego što primijenimo brzine na naše motore. Linija smjera odgovorna je da motoru upravljača da smjer u kojem se treba okretati i daje motorima za prigušivanje brzinu kojom će raditi. Izračunavanje linije naslova je čista trigonometrija, koriste se tan i atan (tan^-1) trigonometrijske funkcije. Neki ekstremni slučajevi su kada kamera detektira samo jednu liniju trake ili kada ne detektira nijednu liniju. Svi ovi slučajevi prikazani su u sljedećoj funkciji:
def get_steering_angle (frame, lane_lines):
visina, širina, _ = okvir.oblik ako je len (lane_lines) == 2: # ako se otkriju dvije linije trake _, _, left_x2, _ = lane_lines [0] [0] # ekstrakt lijevo x2 iz niza lane_lines _, _, right_x2, _ = lane_lines [1] [0] # ekstrakt desno x2 iz niza lane_lines mid = int (width / 2) x_offset = (left_x2 + right_x2) / 2 - mid y_offset = int (height / 2) elif len (lane_lines) == 1: # ako je otkrivena samo jedna linija x1, _, x2, _ = lane_lines [0] [0] x_offset = x2 - x1 y_offset = int (height / 2) elif len (lane_lines) == 0: # ako linija nije otkrivena x_offset = 0 y_offset = int (height / 2) angle_to_mid_radian = math.atan (x_offset / y_offset) angle_to_mid_deg = int (angle_to_mid_radian * 180.0 / math.pi) upravljački_ugao = ugao_do_mid_deg + 90 povratak
x_offset u prvom slučaju je koliko se prosjek ((desno x2 + lijevo x2) / 2) razlikuje od sredine ekrana. y_offset se uvijek uzima kao visina / 2. Zadnja gornja slika prikazuje primjer linije zaglavlja. angle_to_mid_radians je isto što i "theta" prikazano na zadnjoj slici iznad. Ako je upravljački kut = 90, to znači da automobil ima liniju smjera okomitu na liniju "visina / 2" i da će se automobil kretati naprijed bez upravljanja. Ako je upravljački ugao> 90, automobil bi trebao krenuti udesno, u suprotnom bi trebao krenuti ulijevo. Za prikaz linije naslova koristi se sljedeća funkcija:
def display_heading_line (okvir, ugao_upravljanja, linija_boja = (0, 0, 255), širina_ linije = 5)
heading_image = np.zeros_like (okvir) visina, širina, _ = frame.shape upravljački_ugao_radian = ugaoni_ugao / 180,0 * math.pi x1 = int (širina / 2) y1 = visina x2 = int (x1 - visina / 2 / math.tan (upravljački_ugaoni_radijan)) y2 = int (visina / 2) cv2.line (slika_zaglavlja, (x1, y1), (x2, y2), linija_boja, širina_ linije) zaglavlje_image = cv2.addOdmjereno (okvir, 0,8, zaglavlje_slika, 1, 1) return heading_image
Gornja funkcija uzima okvir u kojem će biti iscrtana linija smjera i kut upravljanja kao ulaz. Vraća sliku linije naslova. Okvir linije naslova snimljen u mom slučaju prikazan je na gornjoj slici.
Kombinovanje svih kodova zajedno:
Kôd je sada spreman za sastavljanje. Sljedeći kod prikazuje glavnu petlju programa koja poziva svaku funkciju:
import cv2
uvoz numpy kao np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) dok je True: ret, frame = video.read () frame = cv2.flip (frame, -1) #Pozivanje funkcija 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 = get_steering_angle (frame, lane_lines) heading_image = display_heading_line (lane_lines_image, upravljački_ugao) ključ = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()
Korak 6: Primjena PD kontrole
Sada imamo ugao upravljanja spreman za napajanje motorima. Kao što je ranije spomenuto, ako je kut upravljanja veći od 90, automobil bi trebao skrenuti desno, u suprotnom bi trebao skrenuti lijevo. Primijenio sam jednostavan kod koji okreće motor upravljača udesno ako je kut veći od 90 i okreće ga ulijevo ako je kut upravljanja manji od 90 pri konstantnoj brzini prigušivanja (10% PWM), ali dobio sam mnogo grešaka. Glavna greška koju sam dobio je kad se automobil približi bilo kojem zavoju, motor upravljača djeluje izravno, ali se motori za prigušivanje zaglave. Pokušao sam povećati brzinu prigušivanja na (20% PWM) u zavojima, ali završio sam tako što je robot izašao iz traka. Trebalo mi je nešto što uvelike povećava brzinu prigušivanja ako je kut upravljanja vrlo velik i malo povećava brzinu ako kut upravljanja nije tako veliki, tada smanjuje brzinu na početnu vrijednost dok se automobil približava 90 stupnjeva (kreće se ravno). Rješenje je bilo korištenje PD kontrolera.
PID kontroler označava proporcionalni, integralni i izvedeni kontroler. Ova vrsta linearnih kontrolera široko se koristi u aplikacijama robotike. Gornja slika prikazuje tipičnu PID povratnu upravljačku petlju. Cilj ovog kontrolera je postići "zadanu vrijednost" na najefikasniji način za razliku od "on -off" regulatora koji uključuju ili isključuju postrojenje prema nekim uvjetima. Neke ključne riječi trebaju biti poznate:
- Zadana vrijednost: je željena vrijednost koju želite da vaš sistem dosegne.
- Stvarna vrijednost: je stvarna vrijednost koju je senzor osjetio.
- Greška: je razlika između zadane vrijednosti i stvarne vrijednosti (greška = zadana vrijednost - stvarna vrijednost).
- Kontrolisana promenljiva: iz njenog imena, promenljiva koju želite da kontrolišete.
- Kp: Proporcionalna konstanta.
- Ki: Integralna konstanta.
- Kd: Derivativna konstanta.
Ukratko, petlja PID kontrolnog sistema radi na sljedeći način:
- Korisnik definira zadanu vrijednost koja je potrebna sistemu da dosegne.
- Greška se izračunava (greška = zadata vrijednost - stvarna).
- P kontroler generira radnju proporcionalnu vrijednosti greške. (greška se povećava, akcija P se takođe povećava)
- I kontroler će s vremenom integrirati grešku koja eliminira grešku u stanju mirovanja sistema, ali povećava njeno prekoračenje.
- D regulator je jednostavno vremenski derivat greške. Drugim riječima, to je nagib greške. Čini radnju proporcionalnu izvedenici greške. Ovaj regulator povećava stabilnost sistema.
- Izlaz kontrolera će biti zbir tri kontrolera. Izlaz kontrolera će postati 0 ako greška postane 0.
Odlično objašnjenje PID kontrolera možete pronaći ovdje.
Vraćajući se do automobila za zadržavanje trake, moja kontrolirana varijabla je smanjivala brzinu (budući da upravljanje ima samo dva stanja, desno ili lijevo). U tu svrhu se koristi PD kontroler jer D akcija uvelike povećava brzinu prigušivanja ako je promjena greške vrlo velika (tj. Veliko odstupanje) i usporava automobil ako se ova promjena greške približi 0. Uradio sam sljedeće korake za implementaciju PD -a kontroler:
- Postavite zadanu vrijednost na 90 stupnjeva (uvijek želim da se automobil kreće ravno)
- Izračunati ugao odstupanja od sredine
- Odstupanje daje dvije informacije: Kolika je greška (veličina odstupanja) i u kojem smjeru motor upravljača mora ići (znak odstupanja). Ako je odstupanje pozitivno, automobil bi trebao upravljati desno, u suprotnom bi trebao krenuti lijevo.
- Budući da je odstupanje negativno ili pozitivno, varijabla "greške" je definirana i uvijek jednaka apsolutnoj vrijednosti odstupanja.
- Greška se množi s konstantom Kp.
- Greška prolazi kroz vremensku diferencijaciju i množi se s konstantom Kd.
- Brzina motora se ažurira i petlja počinje ponovo.
Sljedeći kôd se koristi u glavnoj petlji za kontrolu brzine motora za prigušivanje:
brzina = 10 # radna brzina u % PWM
# Promenljive koje se ažuriraju u svakoj petlji lastTime = 0 lastError = 0 # PD konstante Kp = 0.4 Kd = Kp * 0.65 While True: now = time.time () # trenutna vremenska promenljiva dt = now - lastTime devijacija = upravljački_ugao - 90 # ekvivalent na angle_to_mid_deg varijabla greška = abs (odstupanje) ako je odstupanje -5: # ne usmjeravajte ako postoji odstupanje od 10 stupnjeva greške odstupanje = 0 greška = 0 GPIO.izlaz (in1, GPIO. LOW) GPIO.output (in2, GPIO. LOW) upravljanje.stop () elif odstupanje> 5: # upravljajte desno ako je odstupanje pozitivno GPIO.izlaz (in1, GPIO. LOW) GPIO.izlaz (in2, GPIO. HIGH) upravljanje.pokretanje (100) elif odstupanje < -5: # upravljač lijevo ako je odstupanje negativno GPIO.izlaz (in1, GPIO. HIGH) GPIO.izlaz (in2, GPIO. LOW) upravljanje.start (100) derivacija = kd * (greška - lastError) / dt proporcionalno = kp * greška PD = int (brzina + derivacija + proporcionalna) spd = abs (PD) ako je spd> 25: spd = 25 gas. start (spd) lastError = greška lastTime = time.time ()
Ako je greška vrlo velika (odstupanje od sredine je veliko), proporcionalne i izvedene radnje su velike što rezultira velikom brzinom prigušivanja. Kada se greška približi 0 (odstupanje od sredine je malo), izvedenica djeluje obrnuto (nagib je negativan), a brzina prigušivanja postaje mala kako bi se održala stabilnost sistema. Potpuni kôd je u prilogu ispod.
Korak 7: Rezultati
Gornji videozapisi prikazuju rezultate koje sam postigao. Potrebno je više ugađanja i daljnjih prilagodbi. Povezivao sam malinu pi sa svojim LCD ekranom jer je video streaming preko moje mreže imao veliku latenciju i bio je vrlo frustrirajući za rad, zato u video zapisu postoje žice povezane s malinom pi. Koristio sam pjenaste ploče za crtanje staze.
Čekam vaše preporuke za poboljšanje ovog projekta! Nadam se da je ovo uputstvo bilo dovoljno dobro da vam da neke nove informacije.
Preporučuje se:
LED oblaci koji koriste Fadecandy, PI i LED trake: 4 koraka (sa slikama)
LED oblaci koristeći Fadecandy, PI i LED trake: Napravio sam neke LED oblake kako bih stvorio eteričnu atmosferu u svojoj kući. Oni su u početku trebali biti korišteni za festival koji je otkazan zbog trenutne pandemije. Koristio sam fade bombon čip kako bih postigao glatke animacije i
Hodajući robot koji koristi 1 servo motor: 13 koraka (sa slikama)
Hodajući robot s jednim servo motorom: Želio sam izgraditi ovog hodačkog robota otkad sam ga vidio na YouTubeu. Nakon kratkog pretraživanja pronašao sam još informacija o njemu i odlučio napraviti svoju. Cilj koji sam imao izgradnjom ove hodalice bio je pokušati je učiniti što manjom
Jednostavne LED trake (nadogradite svoje LED trake): 4 koraka (sa slikama)
Jednostavne LED trake (nadogradite svoje LED trake): Koristim LED trake već neko vrijeme i oduvijek sam volio njihovu jednostavnost. Samo odrežete komad uloge, lemite neke žice na njega, priključite napajanje i imate izvor svjetlosti. Tokom godina pronašao sam c
Raspberry Pi - Autonomni Mars Rover sa OpenCV praćenjem objekata: 7 koraka (sa slikama)
Raspberry Pi - Autonomni Mars Rover sa OpenCV praćenjem objekata: Pokreće ga Raspberry Pi 3, Open CV prepoznavanje objekata, ultrazvučni senzori i motori sa istosmjernim pogonom. Ovaj rover može pratiti bilo koji objekt za koji je obučen i kretati se po bilo kojem terenu
Uređaj za mjerenje UV-indeksa koji govori, koji koristi senzor VEML6075 i mali drugar: 5 koraka
Uređaj za mjerenje UV-indeksa koji govori, pomoću senzora VEML6075 i Malog prijatelja: Dolaze ljeta! Sunce sija! Što je odlično. No, kako ultraljubičasto (UV) zračenje postaje sve intenzivnije, ljudi poput mene dobivaju pjege, male smeđe otočiće koji plivaju u moru crvene, opečene od sunca, kože koja svrbi. Biti u mogućnosti imati informacije u stvarnom vremenu