INHOUDSOPGAWE:

Outonome baanhouer met behulp van Raspberry Pi en OpenCV: 7 stappe (met foto's)
Outonome baanhouer met behulp van Raspberry Pi en OpenCV: 7 stappe (met foto's)

Video: Outonome baanhouer met behulp van Raspberry Pi en OpenCV: 7 stappe (met foto's)

Video: Outonome baanhouer met behulp van Raspberry Pi en OpenCV: 7 stappe (met foto's)
Video: Webinar - MAX78000 Neural Network Accelerator 2024, November
Anonim
Outonome baanhouer met behulp van Raspberry Pi en OpenCV
Outonome baanhouer met behulp van Raspberry Pi en OpenCV

In hierdie instruksies sal 'n outonome baanhourobot geïmplementeer word en deur die volgende stappe gaan:

  • Versamel dele
  • Voorvereiste vir die installering van sagteware
  • Hardeware vergadering
  • Eerste toets
  • Spoorlynlyne op te spoor en die riglyn te vertoon met behulp van openCV
  • Implementering van 'n PD -beheerder
  • Resultate

Stap 1: Versamel komponente

Versamelkomponente
Versamelkomponente
Versamelkomponente
Versamelkomponente
Versamelkomponente
Versamelkomponente
Versamelkomponente
Versamelkomponente

Die prente hierbo toon al die komponente wat in hierdie projek gebruik word:

  • RC -motor: ek het myne by 'n plaaslike winkel in my land gekry. Dit is toegerus met 3 motors (2 vir versnelling en 1 vir stuur). Die grootste nadeel van hierdie motor is dat die stuur beperk is tussen "geen stuur" en "volle stuur". Met ander woorde, dit kan nie teen 'n spesifieke hoek stuur nie, anders as servostuurende RC-motors. U kan hier 'n soortgelyke motorstel vind wat spesiaal ontwerp is vir framboospi.
  • Framboos pi 3 model b+: dit is die brein van die motor wat baie verwerkingsfases kan hanteer. Dit is gebaseer op 'n vier-core 64-bis verwerker met 'n klok van 1,4 GHz. Ek het myne hiervandaan gekry.
  • Framboos pi 5 mp kamera module: Dit ondersteun 1080p @ 30 fps, 720p @ 60 fps en 640x480p 60/90 opname. Dit ondersteun ook 'n seriële koppelvlak wat direk in die framboos pi ingeprop kan word. Dit is nie die beste opsie vir toepassings vir beeldverwerking nie, maar dit is voldoende vir hierdie projek, en dit is ook baie goedkoop. Ek het myne hiervandaan gekry.
  • Motorbestuurder: word gebruik om die rigtings en snelhede van die GS -motors te beheer. Dit ondersteun die bestuur van 2 dc -motors op 1 bord en kan 1,5 A.
  • Power Bank (opsioneel): ek het 'n kragbank (met 'n waarde van 5V, 3A) gebruik om die framboospi afsonderlik aan te skakel. 'N Trap -omskakelaar (boksomskakelaar: 3A uitsetstroom) moet gebruik word om die framboos -pi van 1 bron af te skakel.
  • 3s (12 V) LiPo -battery: Litium -polimeerbatterye is bekend vir hul uitstekende prestasie op robotika gebied. Dit word gebruik om die motorbestuurder aan te dryf. Ek het myne hiervandaan gekoop.
  • Trui van man tot man en vrou tot vrou.
  • Dubbelzijdige band: word gebruik om die komponente op die RC -motor te monteer.
  • Blou band: dit is 'n baie belangrike komponent van hierdie projek; dit word gebruik om die twee baanlyne te maak waarin die motor sal ry. U kan enige kleur kies, maar ek raai u aan om ander kleure te kies as die in die omgewing.
  • Ritsbande en houtstawe.
  • Skroewedraaier.

Stap 2: Installeer OpenCV op Raspberry Pi en stel eksterne skerm op

Installeer OpenCV op Raspberry Pi en stel eksterne skerm op
Installeer OpenCV op Raspberry Pi en stel eksterne skerm op

Hierdie stap is 'n bietjie irriterend en sal tyd neem.

OpenCV (Open source Computer Vision) is 'n open source rekenaarvisie en masjienleer sagteware biblioteek. Die biblioteek het meer as 2500 geoptimaliseerde algoritmes. Volg hierdie baie eenvoudige gids om die openCV op u framboospi te installeer, sowel as om die framboospi -bedryfstelsel te installeer (as u dit nog nie gedoen het nie). Let daarop dat die proses om die openCV te bou ongeveer 1,5 uur in 'n goed afgekoelde kamer kan neem (aangesien die verwerker se temperatuur baie hoog sal word!), Drink 'n bietjie tee en wag geduldig: D.

Vir die afstandskerm, volg ook HIERDIE gids om afstandtoegang tot u framboospi vanaf u Windows/Mac -toestel op te stel.

Stap 3: Koppel dele aan mekaar

Koppel dele aan mekaar
Koppel dele aan mekaar
Koppel dele aan mekaar
Koppel dele aan mekaar
Koppel dele aan mekaar
Koppel dele aan mekaar

Die afbeeldings hierbo toon die verbindings tussen framboospi, kameramodule en motorbestuurder. Let daarop dat die motors wat ek gebruik het, 0,35 A by 9 V elk absorbeer, wat dit vir die motorbestuurder veilig maak om 3 motors gelyktydig te laat loop. En omdat ek die snelheid van die 2 versnellermotors (1 agter en 1 voor) presies op dieselfde manier wil beheer, het ek dit aan dieselfde poort gekoppel. Ek het die motorbestuurder aan die regterkant van die motor met dubbelband vasgemaak. Wat die kameramodule betref, het ek 'n rits tussen die skroefgate aangebring, soos op die foto hierbo getoon word. Dan pas ek die kamera op 'n houtstaaf sodat ek die posisie van die kamera kan aanpas soos ek wil. Probeer om die kamera soveel as moontlik in die middel van die motor te installeer. Ek beveel aan dat u die kamera minstens 20 cm bo die grond plaas sodat die gesigsveld voor die motor beter word. Die Fritzing -skema is hieronder aangeheg.

Stap 4: Eerste toets

Eerste toets
Eerste toets
Eerste toets
Eerste toets

Kamera toets:

Sodra die kamera geïnstalleer is en die openCV -biblioteek gebou is, is dit tyd om ons eerste prentjie te toets! Ons neem 'n foto van pi cam en stoor dit as 'original.jpg'. Dit kan op 2 maniere gedoen word:

1. Gebruik terminale opdragte:

Maak 'n nuwe terminale venster oop en tik die volgende opdrag:

raspistill -o oorspronklike.jpg

Dit neem 'n stilstaande beeld en stoor dit in die gids "/pi/original.jpg".

2. Gebruik enige luislang IDE (ek gebruik IDLE):

Maak 'n nuwe skets oop en skryf die volgende kode:

voer cv2 in

video = cv2. VideoCapture (0) terwyl True: ret, frame = video.read () frame = cv2.flip (raam, -1) # gebruik om die beeld vertikaal te draai cv2.imshow ('oorspronklik', raam) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Kom ons kyk wat in hierdie kode gebeur het. Die eerste reël is die invoer van ons openCV -biblioteek om al sy funksies te gebruik. die VideoCapture (0) -funksie begin 'n lewendige video stroom vanaf die bron wat deur hierdie funksie bepaal word, in hierdie geval is dit 0 wat raspi -kamera beteken. As u meer kameras het, moet verskillende nommers geplaas word. video.read () lees dat elke raam van die kamera af kom en dit stoor in 'n veranderlike genaamd "raam". flip () -funksie draai die beeld ten opsigte van die y-as (vertikaal) omdat ek my kamera omgekeerd monteer. imshow () sal ons rame met die woord 'oorspronklik' vertoon en imwrite () sal ons foto as original-j.webp

Ek beveel aan dat u u foto met die tweede metode toets om vertroud te raak met openCV -funksies. Die prent word in die "/pi/original.jpg" -gids gestoor. Die oorspronklike foto wat my kamera geneem het, word hierbo getoon.

Toetsmotors:

Hierdie stap is noodsaaklik om die draairigting van elke motor te bepaal. Laat ons eers 'n kort inleiding gee oor die werkbeginsel van 'n motorbestuurder. Die prent hierbo toon die motor se bestuurder se uitrywing. Aktiveer A, Input 1 en Input 2 word geassosieer met motor A -beheer. Aktiveer B, Input 3 en Input 4 word geassosieer met motor B -beheer. Rigtingbeheer word bepaal deur die "Invoer" -deel en die snelheidsbeheer word bepaal deur die gedeelte "Aktiveer". Om byvoorbeeld die rigting van motor A te beheer, stel Invoer 1 op HOOG (3.3 V in hierdie geval, aangesien ons 'n framboospi gebruik) en stel Invoer 2 op LAAG, draai die motor in 'n spesifieke rigting en stel die teenoorgestelde waardes in na Invoer 1 en Invoer 2, sal die motor in die teenoorgestelde rigting draai. As Invoer 1 = Invoer 2 = (HOOG of LAAG), sal die motor nie draai nie. Aktiveer penne neem 'n Pulse Width Modulation (PWM) insetsein van die framboos (0 tot 3.3 V) en voer die motors dienooreenkomstig aan. Byvoorbeeld, 'n 100% PWM -sein beteken dat ons aan die maksimum spoed werk en 0% PWM -sein beteken dat die motor nie draai nie. Die volgende kode word gebruik om motors se rigtings te bepaal en hul snelhede te toets.

invoer tyd

voer RPi. GPIO in as GPIO GPIO.setwaarskuwings (vals) # Stuurmotorpennetjies stuur_enable = 22 # Fisiese pen 15 in1 = 17 # Fisiese pen 11 in2 = 27 # Fisiese pen 13 # Gaspedaalmotors penne gashendel = 25 # Fisiese pen 22 in3 = 23 # Fisiese pen 16 in4 = 24 # Fisiese pen 18 GPIO.setmode (GPIO. BCM) # Gebruik GPIO -nommering in plaas van fisiese nommering 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 (steering_enable, GPIO.out) # Stuurmotorbeheer GPIO.output (in1, GPIO. HOOG) GPIO.output (in2, GPIO. LOW) stuur = GPIO. PWM (steering_enable, 1000) # stel die skakelfrekwensie in op 1000 Hz steering.stop () # Throttle Motors Control GPIO.output (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) throttle = GPIO. PWM (throttle_enable, 1000) # stel die skakelfrekwensie in op 1000 Hz throttle.stop () time.sleep (1) throttle.start (25) # begin die motor op 25 % PWM -sein-> (0,25 * batteryspanning) - bestuurders verliesstuur. begin (100) # begin die motor by 100% PWM -sein-> (1 * batteryspanning) - bestuurder se verlies tyd. slaap (3) smoor.stop () stuur.stop ()

Hierdie kode sal die smoormotors en stuurmotor vir 3 sekondes laat loop en dit dan stop. Die (bestuurder se verlies) kan bepaal word met behulp van 'n voltmeter. Ons weet byvoorbeeld dat 'n 100% PWM -sein die volle battery se spanning by die motor se aansluiting moet gee. Maar deur PWM op 100%te stel, het ek gevind dat die bestuurder 'n 3 V -daling veroorsaak en dat die motor 9 V in plaas van 12 V kry (presies wat ek nodig het!). Die verlies is nie lineêr nie, dit wil sê die verlies teen 100% verskil baie van die verlies van 25%. Nadat ek die kode hierbo uitgevoer het, was my resultate soos volg:

Grensresultate: as in3 = HOOG en in4 = LAAG, sal die smoormotors 'n klok-wyse (CW) -rotasie hê, dit wil sê die motor sal vorentoe beweeg. Andersins sal die motor agteruit beweeg.

Stuurresultate: as in1 = HOOG en in2 = LAAG, sal die stuurmotor maksimum links draai, dws die motor stuur na links. Anders stuur die motor regs. Na 'n paar eksperimente het ek agtergekom dat die stuurmotor nie sal draai as die PWM -sein nie 100% is nie (dit wil sê dat die motor regs of links na links stuur).

Stap 5: Spoorlyne opspoor en koerslyn bereken

Spoorlyne op te spoor en koerslyn te bereken
Spoorlyne op te spoor en koerslyn te bereken
Spoorlyne op te spoor en koerslyn te bereken
Spoorlyne op te spoor en koerslyn te bereken
Spoorlyne op te spoor en koerslyn te bereken
Spoorlyne op te spoor en koerslyn te bereken

In hierdie stap word die algoritme wat die motor se beweging beheer, verduidelik. Die eerste prentjie toon die hele proses. Die invoer van die stelsel is beelde, die uitset is theta (stuurhoek in grade). Let daarop dat die verwerking op 1 prent gedoen word en op alle rame herhaal word.

Kamera:

Die kamera begin 'n video met 'n resolusie van (320 x 240) opneem. Ek beveel aan dat u die resolusie verlaag, sodat u 'n beter raamkoers (fps) kan kry, aangesien fps -daling sal plaasvind nadat verwerkingstegnieke op elke raam toegepas is. Die onderstaande kode is die hooflus van die program en voeg elke stap by hierdie kode.

voer cv2 in

invoer numpy as np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # stel die breedte in op 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # stel die hoogte op 240 p # Die lus terwyl 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 ()

Die kode hier sal die oorspronklike beeld wat in stap 4 verkry is, vertoon en word in die bostaande beelde getoon.

Skakel oor na HSV -kleurruimte:

Nadat die video -opname as rame van die kamera geneem is, is die volgende stap om elke raam om te skakel in kleurruimte Hue, Saturation en Value (HSV). Die grootste voordeel hiervan is om te kan onderskei tussen kleure deur hul helderheid. En hier is 'n goeie verduideliking van HSV -kleurruimte. Omskakeling na HSV vind plaas met die volgende funksie:

def convert_to_HSV (raam):

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

Hierdie funksie word vanaf die hooflus genoem en sal die raam in die HSV -kleurruimte teruggee. Die raam wat ek in HSV -kleurruimte gekry het, word hierbo getoon.

Ontdek blou kleur en rande:

Na die omskakeling van die beeld in HSV -kleurruimte, is dit tyd om slegs die kleur waaroor ons belangstel op te spoor (dws blou kleur, aangesien dit die kleur van die baanlyne is). Om 'n blou kleur uit 'n HSV -raam te onttrek, moet 'n reeks kleure, versadiging en waarde gespesifiseer word. verwys hier om 'n beter idee te kry van HSV -waardes. Na 'n paar eksperimente word die boonste en onderste grense van blou kleur in die onderstaande kode getoon. En om die algehele vervorming in elke raam te verminder, word rande slegs opgespoor met behulp van 'n skelm randdetektor. Meer oor canny edge word hier gevind. 'N Duimreël is om die parameters van die Canny () -funksie te kies met 'n verhouding van 1: 2 of 1: 3.

def detect_edges (raam):

lower_blue = np.array ([90, 120, 0], dtype = "uint8") # onderste grens van blou kleur upper_blue = np.array ([150, 255, 255], dtype = "uint8") # boonste limiet van blou kleurmasker = cv2.inRange (hsv, lower_blue, upper_blue) # hierdie masker filtreer alles behalwe blou # kante kante opspoor = cv2. Canny (masker, 50, 100) cv2.imshow ("rande", rande) terugkante

Hierdie funksie sal ook vanaf die hooflus genoem word, wat as parameter die HSV -kleurruimte raam neem en die randraam teruggee. Die rande wat ek wel gekry het, word hierbo gevind.

Kies 'n gebied van belang (ROI):

Dit is van kardinale belang om 'n belangegebied te kies om slegs op 1 deel van die raam te fokus. In hierdie geval wil ek nie hê dat die motor baie items in die omgewing sien nie. Ek wil net hê dat die motor op die baanlyne moet fokus en enigiets anders ignoreer. PS: die koördinaatstelsel (x- en y -asse) begin vanaf die linker boonste hoek. Met ander woorde, die punt (0, 0) begin in die linker boonste hoek. y-as die hoogte en x-as die breedte. Die onderstaande kode kies 'n gebied van belang om slegs op die onderste helfte van die raam te fokus.

def gebied_ van_belang (rande):

hoogte, breedte = rande.vorm # onttrek die hoogte en breedte van die rande raammasker = np.zeros_like (rande) # maak 'n leë matriks met dieselfde afmetings van die rande raam # fokus slegs die onderste helfte van die skerm # spesifiseer die koördinate van 4 punte (links onder, links bo, regs bo, regs onder) veelhoek = np.array (

Hierdie funksie neem die randraam as parameter en teken 'n veelhoek met 4 vooraf ingestelde punte. Dit fokus slegs op wat binne die veelhoek is en ignoreer alles daarbuite. My raam van belangstelling word hierbo getoon.

Ontdek lynsegmente:

Hoë transformasie word gebruik om lynsegmente uit 'n randrand op te spoor. Hough transform is 'n tegniek om enige vorm in wiskundige vorm op te spoor. Dit kan byna enige voorwerp opspoor, selfs al word dit verdraai volgens 'n aantal stemme. 'n goeie verwysing vir Hough transform word hier getoon. Vir hierdie toepassing word die funksie cv2. HoughLinesP () gebruik om lyne in elke raam op te spoor. Die belangrike parameters wat hierdie funksie neem, is:

cv2. HoughLinesP (raam, rho, theta, min_drempel, minLineLengte, maxLineGap)

  • Raam: is die raam waarin ons lyne wil opspoor.
  • rho: Dit is die presisie van die afstand in pixels (gewoonlik = 1)
  • theta: hoekpresisie in radiale (altyd = np.pi/180 ~ 1 graad)
  • min_drempel: minimum stem wat dit moet behaal om as 'n reël beskou te word
  • minLineLength: minimum lengte van die lyn in pixels. Enige reël wat korter is as hierdie getal, word nie as 'n reël beskou nie.
  • maxLineGap: maksimum gaping in pixels tussen 2 reëls wat as 1 reël behandel moet word. (Dit word in my geval nie gebruik nie, aangesien die baanlyne wat ek gebruik, geen gaping het nie).

Hierdie funksie gee die eindpunte van 'n lyn terug. Die volgende funksie word vanaf my hooflus genoem om lyne op te spoor met behulp van Hough transform:

def detect_line_segments (gesnyde_rande):

rho = 1 theta = np.pi / 180 min_drempel = 10 reël_segmente = cv2. HoughLinesP (geknipte rande, rho, theta, min_drempel, np.array (), minLineLength = 5, maxLineGap = 0) retoer lyn_segmente

Gemiddelde helling en afsnypunt (m, b):

onthou dat die lynvergelyking deur y = mx + b gegee word. Waar m die helling van die lyn is en b die y-afsnit is. In hierdie deel word die gemiddelde hellings en afsnitte van lynsegmente wat met behulp van Hough -transformasie opgespoor word, bereken. Voordat ons dit doen, kyk ons na die oorspronklike raamfoto hierbo. Dit blyk dat die linkerbaan opwaarts gaan, sodat dit 'n negatiewe helling het (onthou die beginpunt van die koördinaatstelsel?). Met ander woorde, die linkerbaanlyn het x1 <x2 en y2 x1 en y2> y1 wat 'n positiewe helling sal gee. Alle lyne met 'n positiewe helling word dus as die regte baanpunte beskou. In die geval van vertikale lyne (x1 = x2), sal die helling oneindig wees. In hierdie geval slaan ons alle vertikale lyne oor om te voorkom dat u 'n fout kry. Om hierdie akkuraatheid meer akkuraat te maak, word elke raam deur twee grenslyne in twee streke (regs en links) verdeel. Alle breedtepunte (x-as-punte) groter as die regtergrenslyn word geassosieer met die regterbaanberekening. En as alle breedtepunte minder is as die linker grenslyn, word dit geassosieer met die berekening van die linkerbaan. Die volgende funksie neem die raam onder verwerking en laansegmente wat met behulp van Hough -transformasie opgespoor word, en gee die gemiddelde helling en afsnypunt van twee baanlyne terug.

def average_slope_intercept (raam, reëlsegmente):

lane_lines = as reëlsegmente Geen is: druk ("geen lynsegment bespeur nie") terugbaanbaanlyne hoogte, breedte, _ = raam.vorm links_fit = right_fit = boundary = left_region_boundary = breedte * (1 - grens) right_region_boundary = breedte * grens vir reëlsegment in reëlsegmente: vir x1, y1, x2, y2 in reëlsegment: as x1 == x2: druk ("vertikale lyne oorskakel (helling = oneindig)") pas by pas = np.polyfit ((x1, x2), (y1, y2), 1) helling = (y2 - y1) / (x2 - x1) afsnit = y1 - (helling * x1) as helling <0: as x1 <links_gebied_grens en x2 regter_gebied_grens en x2> regs_gebied_grens: regs_fit. voeg ((helling, onderskep)) 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) as len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines is 'n 2-D-skikking wat die koördinate van die regter- en linkerbaanlyne bevat # byvoorbeeld: lan e_lines =

make_points () is 'n helperfunksie vir die gemiddelde_slope_intercept () -funksie wat die begrensde koördinate van die baanlyne (van onder na die middel van die raam) sal teruggee.

def make_points (raam, reël):

hoogte, breedte, _ = raam.vormvormige helling, afsnit = lyn y1 = hoogte # onderkant van die raam y2 = int (y1 / 2) # maak punte vanaf die middel van die raam af as die helling == 0: helling = 0,1 x1 = int ((y1 - afsnit) / helling) x2 = int ((y2 - afsnit) / helling) terugkeer

Om te verhoed dat die deel met 0 deel, word 'n voorwaarde aangebied. As helling = 0 wat y1 = y2 (horisontale lyn) beteken, gee die helling 'n waarde naby 0. Dit sal nie die prestasie van die algoritme beïnvloed nie, maar dit sal ook onmoontlike gevalle voorkom (deel met 0).

Om die baanlyne op die rame te vertoon, word die volgende funksie gebruik:

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

line_image = np.zeros_like (raam) as reëls nie None is nie: vir reël in reëls: vir x1, y1, x2, y2 in reël: cv2.line (line_image, (x1, y1), (x2, y2), line_color, line_width) line_image = cv2.addWeighted (raam, 0,8, line_image, 1, 1) return line_image

cv2.addWeighted () funksie neem die volgende parameters en dit word gebruik om twee beelde te kombineer, maar met elkeen 'n gewig.

cv2.addWeighted (image1, alfa, image2, beta, gamma)

En bereken die uitsetbeeld met behulp van die volgende vergelyking:

uitset = alfa * beeld1 + beta * beeld2 + gamma

Meer inligting oor die funksie cv2.addWeighted () word hier verkry.

Bereken en wys opskriflyn:

Dit is die laaste stap voordat ons snelhede op ons motors toepas. Die koerslyn is verantwoordelik om die stuurmotor die rigting te gee waarin dit moet draai en die versnellermotors die snelheid te gee waarmee hulle sal werk. Die opskriflyn is suiwer trigonometrie, tan en atan (tan^-1) word trigonometriese funksies gebruik. Sommige uiterste gevalle is wanneer die kamera slegs een baanlyn opspoor of geen lyn opspoor nie. Al hierdie gevalle word in die volgende funksie getoon:

def get_steering_angle (raam, laanlyne):

hoogte, breedte, _ = raam.vorm as len (laanlyne) == 2: # as twee baanlyne bespeur word _, _, links_x2, _ = laan_lyne [0] [0] # uittreksel links x2 uit baanbaanlyne skikking _, _, right_x2, _ = lane_lines [1] [0] # onttrek regs x2 uit lane_lines array mid = int (breedte / 2) x_offset = (left_x2 + right_x2) / 2 - mid y_offset = int (hoogte / 2) elif len (lane_lines)) == 1: # as slegs een reël opgespoor word x1, _, x2, _ = laan_lyne [0] [0] x_offset = x2 - x1 y_offset = int (hoogte / 2) elif len (baanbane) == 0: # as geen lyn opgespoor word nie x_offset = 0 y_offset = int (hoogte / 2) hoek_to_mid_radiaan = math.atan (x_offset / y_offset) hoek_to_mid_deg = int (hoek_to_mid_radiaan * 180.0 / math.pi) stuurhoek = hoek_to_mid_deg + 90 terugstuurhoek

x_offset in die eerste geval is hoeveel die gemiddelde ((regs x2 + links x2) / 2) van die middel van die skerm verskil. y_offset word altyd as hoogte / 2. Die laaste prent hierbo toon 'n voorbeeld van opskriflyn. angle_to_mid_radians is dieselfde as "theta" wat in die laaste prent hierbo getoon word. As stuurhoek = 90 beteken dit dat die motor 'n koerslyn loodreg op die "hoogte / 2" -lyn het, en die motor sal vorentoe beweeg sonder om te stuur. As die stuurhoek meer as 90 is, moet die motor regs stuur, anders moet dit na links stuur. Om die opskrifreël te vertoon, word die volgende funksie gebruik:

def display_heading_line (frame, steering_angle, line_color = (0, 0, 255), line_width = 5)

heading_image = np.zeros_like (raam) hoogte, breedte, _ = frame.shape steering_angle_radian = stuurhoek # 180.0 * math.pi x1 = int (breedte / 2) y1 = hoogte x2 = int (x1 - hoogte / 2 / wiskunde.tan (steering_angle_radian)) y2 = int (hoogte / 2) cv2.line (heading_image, (x1, y1), (x2, y2), line_color, line_width) heading_image = cv2.addWeighted (raam, 0.8, heading_image, 1, 1) stuur opskrif_beeld terug

Die funksie hierbo neem die raam waarin die koerslyn getrek sal word en stuurhoek as invoer. Dit gee die beeld van die opskrifreël terug. Die raam van die opskriflyn wat in my geval geneem is, word in die prent hierbo getoon.

Die kombinasie van alle kode saam:

Die kode is nou gereed om saamgestel te word. Die volgende kode toon die hooflus van die program wat elke funksie noem:

voer cv2 in

invoer numpy as np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) terwyl True: ret, frame = video.read () frame = cv2.flip (raam, -1) #Roep die funksies op hsv = convert_to_HSV (frame) edge = detect_edges (hsv) roi = region_of_interest (edge) line_segments = detect_line_segments (roi) lane_lines = average_scope_intercept (frame, line_segments) lane_lines_image = display_lines (frame_lines) = get_steering_angle (frame, lane_lines) heading_image = display_heading_line (lane_lines_image, steering_angle) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Stap 6: Pas PD Control toe

Pas PD Control toe
Pas PD Control toe

Nou het ons ons stuurhoek gereed om aan die motors gevoer te word. Soos vroeër genoem, moet die motor regs draai as die stuurhoek groter as 90 is, anders draai dit links. Ek het 'n eenvoudige kode toegepas wat die stuurmotor regs draai as die hoek bo 90 is en dit links draai as die stuurhoek minder as 90 is met 'n konstante versnellingspoed van (10% PWM), maar ek het baie foute. Die hooffout wat ek gekry het, is dat die stuurmotor direk in werking tree, maar as die motor 'n draai nader, die stuurmotor belemmer. Ek het probeer om die versnellingspoed te verhoog tot (20% PWM) by draaie, maar eindig met die robot wat uit die bane kom. Ek het iets nodig gehad wat die versnellingspoed baie verhoog as die stuurhoek baie groot is en die spoed 'n bietjie verhoog as die stuurhoek nie so groot is nie, dan verminder die spoed tot 'n aanvanklike waarde namate die motor 90 grade nader (reguit beweeg). Die oplossing was om 'n PD -beheerder te gebruik.

PID -beheerder staan vir proporsionele, integrale en afgeleide beheerder. Hierdie tipe lineêre beheerders word wyd gebruik in robotika -toepassings. Die prent hierbo toon die tipiese PID -terugvoerbeheerlus. Die doel van hierdie beheerder is om die "setpoint" te bereik met die doeltreffendste manier, in teenstelling met "aan - af" beheerders wat die aanleg aan of uit skakel volgens 'n paar voorwaardes. Sommige sleutelwoorde moet bekend wees:

  • Setpoint: is die gewenste waarde wat u stelsel wil bereik.
  • Werklike waarde: is die werklike waarde wat deur die sensor waargeneem word.
  • Fout: is die verskil tussen setpoint en werklike waarde (error = Setpoint - Werklike waarde).
  • Beheerde veranderlike: van sy naam, die veranderlike wat u wil beheer.
  • Kp: proporsionele konstante.
  • Ki: Integrale konstante.
  • Kd: Afgeleide konstante.

Kortom, die PID -beheerstelsellus werk soos volg:

  • Die gebruiker definieer die setpoint wat nodig is om die stelsel te bereik.
  • Die fout word bereken (error = setpoint - actual).
  • P -kontroleerder genereer 'n aksie wat eweredig is aan die waarde van die fout. (fout neem toe, P -aksie neem ook toe)
  • I -kontroleerder sal die fout mettertyd integreer, wat die stelsel se steady state -fout uitskakel, maar die oorskryding verhoog.
  • D -beheerder is eenvoudig die afgeleide tyd vir die fout. Met ander woorde, dit is die helling van die fout. Dit doen 'n aksie wat eweredig is aan die afgeleide van die fout. Hierdie kontroleerder verhoog die stabiliteit van die stelsel.
  • Die uitset van die beheerder is die som van die drie beheerders. Die uitvoer van die beheerder sal 0 word as die fout 0 word.

U kan hier 'n uitstekende verduideliking van die PID -beheerder kry.

Om terug te keer na die baan om die motor te bestuur, was my beheerde veranderlike die spoed (aangesien die stuur slegs twee toestelle regs of links het). 'N PD -kontroleerder word hiervoor gebruik, aangesien D -aksie die versnellingsnelheid baie verhoog as die foutverandering baie groot is (dws groot afwyking) en die motor vertraag as hierdie foutverandering nader. 0. Ek het die volgende stappe gedoen om 'n PD te implementeer beheerder:

  • Stel die setpoint op 90 grade (ek wil altyd hê die motor moet reguit beweeg)
  • Bereken die afwykingshoek van die middel
  • Die afwyking gee twee inligting: Hoe groot die fout is (grootte van afwyking) en watter rigting die stuurmotor moet inslaan (teken van afwyking). As die afwyking positief is, moet die motor regs stuur, anders moet dit links stuur.
  • Aangesien afwyking negatief of positief is, word 'n "fout" veranderlike gedefinieer en altyd gelyk aan die absolute waarde van die afwyking.
  • Die fout word vermenigvuldig met 'n konstante Kp.
  • Die fout ondergaan tydsonderskeiding en word vermenigvuldig met 'n konstante Kd.
  • Motors se spoed word opgedateer en die lus begin weer.

Die volgende kode word in die hooflus gebruik om die snelheid van die smoormotors te beheer:

snelheid = 10 # werksnelheid in % PWM

#Variabels wat elke lus opgedateer word lastTime = 0 lastError = 0 # PD konstantes Kp = 0.4 Kd = Kp * 0.65 Terwyl dit waar is: nou = time.time () # huidige tyd veranderlike dt = nou - lastTime afwyking = stuur_angle - 90 # ekwivalent na angle_to_mid_deg veranderlike fout = abs (afwyking) as afwyking -5: # moenie stuur as daar 'n afwyking van 10 grade is nie = 0 fout = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. LOW) steering.stop () elif afwyking> 5: # stuur regs as die afwyking positief is GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) steering.start (100) elif afwyking < -5: # stuur links as afwyking negatief is GPIO.output (in1, GPIO. HIGH) GPIO.output (in2, GPIO. LOW) stuur.start (100) afgeleide = kd * (fout - lastError) / dt proporsioneel = kp * fout PD = int (spoed + afgeleide + proporsioneel) spd = abs (PD) as spd> 25: spd = 25 gashendel. begin (spd) lastError = fout lastTime = time.time ()

As die fout baie groot is (die afwyking van die middel is hoog), is die proporsionele en afgeleide aksies hoog, wat 'n hoë versnellingspoed tot gevolg het. As die fout 0 nader (afwyking van die middel is laag), werk die afgeleide aksie omgekeerd (helling is negatief) en word die versnellingsnelheid laag om die stabiliteit van die stelsel te handhaaf. Die volledige kode is hieronder aangeheg.

Stap 7: Resultate

Die video's hierbo toon die resultate wat ek behaal het. Dit het meer afstemming en verdere aanpassings nodig. Ek het die framboos -pi aan my LCD -skerm gekoppel, omdat die videostroom oor my netwerk 'n hoë vertraging gehad het en baie frustrerend was om mee te werk, daarom is daar drade aan die framboos -pi in die video. Ek het skuimplanke gebruik om die baan op te trek.

Ek wag om u aanbevelings te hoor om hierdie projek beter te maak! Soos ek hoop dat hierdie instruksies goed genoeg was om u nuwe inligting te gee.

Aanbeveel: