Tartalomjegyzék:
- 1. lépés: Összetevők összegyűjtése
- 2. lépés: Az OpenCV telepítése a Raspberry Pi -re és a távoli kijelző beállítása
- 3. lépés: Az alkatrészek összekapcsolása
- 4. lépés: Első teszt
- 5. lépés: Sávvonalak észlelése és az irányvonal kiszámítása
- 6. lépés: A PD vezérlés alkalmazása
- 7. lépés: Eredmények
Videó: Autonóm sávtartó autó a Raspberry Pi és az OpenCV használatával: 7 lépés (képekkel)
2024 Szerző: John Day | [email protected]. Utoljára módosítva: 2024-01-30 09:40
Ebben az utasításban egy autonóm sávtartó robot kerül bevezetésre, amely a következő lépéseken megy keresztül:
- Alkatrészek összegyűjtése
- A szoftver telepítésének előfeltételei
- Hardver összeszerelés
- Első teszt
- Sávvonalak észlelése és a vezetővonal megjelenítése az openCV használatával
- PD vezérlő megvalósítása
- Eredmények
1. lépés: Összetevők összegyűjtése
A fenti képek a projektben használt összes összetevőt mutatják:
- RC autó: Az enyémet hazám helyi boltjából szereztem be. 3 motorral van felszerelve (2 a fojtószelephez és 1 a kormányzáshoz). Ennek az autónak a fő hátránya, hogy a kormányzás korlátozott a "nincs kormányzás" és a "teljes kormányzás" között. Más szavakkal, nem tud meghatározott szögben kormányozni, ellentétben a szervokormányos RC autóval. Itt találhat hasonló autós készletet, amelyet kifejezetten a málna pi -hez terveztek.
- Raspberry pi 3 b+modell: ez az autó agya, amely sok feldolgozási lépést kezel. Ez egy négymagos 64 bites processzoron alapul, 1,4 GHz-en. Innen kaptam az enyémet.
- Raspberry pi 5 mp kamera modul: Támogatja az 1080p @ 30 fps, 720p @ 60 fps és 640x480p 60/90 felvételt. Támogatja a soros interfészt is, amely közvetlenül a málna pi -hez csatlakoztatható. Ez nem a legjobb megoldás képfeldolgozó alkalmazásokhoz, de elegendő ehhez a projekthez, valamint nagyon olcsó. Innen kaptam az enyémet.
- Motorhajtó: Az egyenáramú motorok irányának és sebességének szabályozására szolgál. Támogatja a 2 egyenáramú motor vezérlését 1 kártyán, és ellenáll 1,5 A -nak.
- Tápegység (opcionális): A málna pi külön tápellátásához tápegységet használtam (5V, 3A). A málna pi 1 forrásból történő áramellátásához lépcsőzetes átalakítót (bak konverter: 3A kimeneti áram) kell használni.
- 3s (12 V) LiPo akkumulátor: A lítium -polimer akkumulátorok kiváló teljesítményükről ismertek a robotika területén. A motor meghajtására szolgál. Az enyémet innen vettem.
- Férfi -férfi és női -női jumper vezetékek.
- Kétoldalas szalag: az alkatrészek RC autóra szerelésére szolgál.
- Kék szalag: Ez egy nagyon fontos eleme ennek a projektnek, abból a két sávból készülnek, amelyek között az autó haladni fog. Bármilyen színt választhat, de azt javaslom, hogy válasszon más színeket, mint a környezetben.
- Cipzárak és fa rudak.
- Csavarhúzó.
2. lépés: Az OpenCV telepítése a Raspberry Pi -re és a távoli kijelző beállítása
Ez a lépés kissé idegesítő, és eltart egy ideig.
Az OpenCV (Open source Computer Vision) egy nyílt forráskódú számítógépes látás- és gépi tanulási szoftverkönyvtár. A könyvtár több mint 2500 optimalizált algoritmust tartalmaz. Kövesse EZT a nagyon egyszerű útmutatót az openCV telepítéséhez a málna pi -re, valamint a raspberry pi operációs rendszer telepítéséhez (ha még nem tette meg). Kérjük, vegye figyelembe, hogy az openCV felépítése körülbelül 1,5 órát vehet igénybe egy jól hűtött helyiségben (mivel a processzor hőmérséklete nagyon magas lesz!), Ezért igyon egy teát és várjon türelmesen: D.
A távoli megjelenítéshez kövesse ezt az útmutatót is, hogy beállítsa a Windows/Mac -eszközéről a raspberry pi távoli elérését.
3. lépés: Az alkatrészek összekapcsolása
A fenti képek a Raspberry pi, a kameramodul és a motorvezérlő közötti kapcsolatokat mutatják be. Kérjük, vegye figyelembe, hogy az általam használt motorok mindegyike 0,35 A -t vesz fel 9 V -on, ami biztonságossá teszi a motorvezető számára, hogy egyszerre 3 motort futtasson. És mivel a két fojtómotor sebességét (1 hátul és 1 elöl) pontosan ugyanúgy akarom szabályozni, ezért ugyanahhoz a porthoz kötöttem őket. A motor meghajtót az autó jobb oldalára szereltem dupla szalaggal. Ami a kamera modult illeti, behelyeztem egy cipzárat a csavarlyukak közé, ahogy a fenti kép mutatja. Ezután illesszem a fényképezőgépet egy fatörzsre, hogy tetszés szerint beállíthassam a kamera helyzetét. Próbálja a kamerát az autó közepére telepíteni, amennyire csak lehetséges. Javaslom, hogy a fényképezőgépet legalább 20 cm -rel helyezze el a talaj felett, hogy az autó előtti látómező jobb legyen. A Fritzing vázlata az alábbiakban található.
4. lépés: Első teszt
Kamera tesztelés:
Miután telepítette a kamerát és felépítette az openCV könyvtárat, itt az ideje, hogy teszteljük első képünket! Fényképet készítünk a pi cam -ról, és elmentjük "original.jpg" néven. Ezt kétféleképpen lehet megtenni:
1. A terminálparancsok használata:
Nyisson meg egy új terminál ablakot, és írja be a következő parancsot:
raspistill -o original.jpg
Ez állóképet készít, és a "/pi/original.jpg" könyvtárba menti.
2. Bármilyen python IDE használatával (IDLE -t használok):
Nyisson meg egy új vázlatot, és írja be a következő kódot:
import cv2
video = cv2. VideoCapture (0), míg True: ret, frame = video.read () frame = cv2.flip (frame, -1) # a kép függőleges elforgatására szolgál cv2.imshow ('eredeti', keret) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()
Lássuk, mi történt ebben a kódban. Az első sor az openCV könyvtár importálása, hogy minden funkcióját használhassa. a VideoCapture (0) függvény élő videó közvetítését kezdi el a funkció által meghatározott forrásból, ebben az esetben 0, ami raspi kamerát jelent. ha több kamerája van, akkor különböző számokat kell elhelyezni. A video.read () olvassa, hogy minden képkocka a kamerából származik, és elmenti a "frame" nevű változóba. A flip () függvény elfordítja a képet az y tengelyhez képest (függőlegesen), mivel fordítva szerelem fel a kamerát. Az imshow () megjeleníti a kereteinket az "eredeti" szóval, az imwrite () pedig a fényképünket eredeti-j.webp
Javaslom, hogy tesztelje fotóját a második módszerrel, hogy megismerkedjen az openCV funkcióival. A kép az "/pi/original.jpg" könyvtárba kerül mentésre. A fényképezőgépem által készített eredeti fénykép fent látható.
Motorok tesztelése:
Ez a lépés elengedhetetlen az egyes motorok forgásirányának meghatározásához. Először nézzük meg röviden a motorvezető működési elvét. A fenti képen látható a motor meghajtójának kivezetése. Az A engedélyezése, az 1. és a 2. bemenet az A motor vezérléséhez kapcsolódik. A B engedélyezése, a 3. és 4. bemenet a B motor vezérléséhez kapcsolódik. Az irányítást az "Input" rész, a sebességszabályozást az "Enable" rész határozza meg. Például az A motor irányításának szabályozásához állítsa az 1. bemenetet HIGH (ebben az esetben 3,3 V -ra, mivel málna pi -t használunk), és állítsa a 2. bemenetet LOW -ra, a motor meghatározott irányba forog, és az ellenkező értékeket állítja be az 1. és 2. bemenetre a motor az ellenkező irányba forog. Ha 1. bemenet = 2. bemenet = (HIGH vagy LOW), a motor nem forog. Az engedélyező csapok impulzusszélesség -modulációs (PWM) bemeneti jelet vesznek a málnából (0–3,3 V), és ennek megfelelően működtetik a motorokat. Például a 100% PWM jel azt jelenti, hogy a maximális fordulatszámon dolgozunk, a 0% PWM jel pedig azt, hogy a motor nem forog. A következő kódot használjuk a motorok irányának meghatározására és sebességük tesztelésére.
importálási idő
importálja az RPi. GPIO fájlt GPIO GPIO.setwarnings (hamis) 23 # Fizikai pin 16 in4 = 24 # Physical Pin 18 GPIO.setmode (GPIO. BCM) # GPIO számozás használata fizikai számozás helyett GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. beállítás (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (kormány_enable, GPIO.out) # Steering Motor Control GPIO.output (in1, GPIO). HIGH) GPIO.output (in2, GPIO. LOW) kormányzás = GPIO. PWM (kormányzás engedélyezhető, 1000) # állítsa be a kapcsolási frekvenciát 1000 Hz -es kormányzásra. Stop () # Throttle Motors Control GPIO.output (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) fojtószelep = GPIO. PWM (throttle_enable, 1000) # állítsa a kapcsolási frekvenciát 1000 Hz -es fojtószelepre. stop () time.sleep (1) fojtószelep. start (25) # 25 -nél indítja el a motort % PWM jel-> (0,25 * akkumulátor feszültség) - vezetői veszteségkormányzás.indítás (100) # elindítja a motort 100% -os PWM jelnél-> (1 * akkumulátorfeszültség) - a vezető veszteségi ideje. alvó (3) fojtószelep.leáll () kormányzás
Ez a kód 3 másodpercig működteti a fojtó- és kormánymotorokat, majd leállítja őket. A (vezető vesztesége) voltmérővel határozható meg. Például tudjuk, hogy a 100% -os PWM jelnek meg kell adnia a teljes akkumulátor feszültségét a motor terminálján. De a PWM 100%-ra állításával azt tapasztaltam, hogy a meghajtó 3 V -os csökkenést okoz, és a motor 12 V helyett 9 V -ot kap (pontosan erre van szükségem!). A veszteség nem lineáris, azaz a 100% -os veszteség nagyon különbözik a 25% -os veszteségtől. A fenti kód futtatása után az eredményeim a következők voltak:
Fojtási eredmények: ha in3 = HIGH és in4 = LOW, akkor a fojtószelep-motorok óramutató (CW) forgatással rendelkeznek, azaz az autó előre fog haladni. Ellenkező esetben az autó hátrafelé mozog.
Kormányzási eredmények: ha in1 = HIGH és in2 = LOW, a kormánymotor a maximális bal oldalán forog, azaz az autó balra fog kormányozni. Ellenkező esetben az autó jobbra fog kormányozni. Néhány kísérlet után megállapítottam, hogy a kormánymotor nem forog, ha a PWM jel nem 100% (azaz a motor vagy teljesen jobbra, vagy teljesen balra fog kormányozni).
5. lépés: Sávvonalak észlelése és az irányvonal kiszámítása
Ebben a lépésben ismertetjük az autó mozgását vezérlő algoritmust. Az első kép az egész folyamatot mutatja. A rendszer bemenete képek, a kimenet théta (kormányzási szög fokban). Ne feledje, hogy a feldolgozás 1 képen történik, és minden képkockán megismétlődik.
Kamera:
A fényképezőgép megkezdi a (320 x 240) felbontású videó rögzítését. Azt javaslom, hogy csökkentse a felbontást, hogy jobb képkockasebességet (fps) kapjon, mivel az képkocka / másodperc csökkenés az egyes képkockákra vonatkozó feldolgozási technikák alkalmazása után következik be. Az alábbi kód lesz a program fő ciklusa, és minden lépéshez hozzáadja ezt a kódot.
import cv2
importálja a számjegyeket np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # állítsa be a szélességet 320 p videó.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # állítsa be a magasságot 240 p # A ciklus közben Igaz: 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 ()
Az itt található kód a 4. lépésben kapott eredeti képet mutatja, és a fenti képeken látható.
Konvertálás HSV színtérré:
Most, miután a videofelvételt képkockának vette a fényképezőgépről, a következő lépés az egyes képkockák színárnyalat, telítettség és érték (HSV) színtérré alakítása. Ennek fő előnye, hogy meg tudja különböztetni a színeket fényerősségük alapján. És itt van egy jó magyarázat a HSV színtérre. A HSV -re történő konvertálás a következő funkción keresztül történik:
def convert_to_HSV (keret):
hsv = cv2.cvtColor (keret, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) return hsv
Ezt a funkciót a fő hurokból hívják meg, és visszaadja a keretet a HSV színtérben. Az általam HSV színtérben kapott keret a fent látható.
Kék szín és élek észlelése:
Miután a képet HSV színtérré alakította, itt az ideje, hogy csak az általunk érdekelt színt észlelje (azaz a kék színt, mivel ez a sávvonalak színe). A HSV keretből a kék szín kinyeréséhez meg kell adni egy színárnyalatot, telítettséget és értéket. ide kattintva jobb képet kaphat a HSV -értékekről. Néhány kísérlet után a kék szín felső és alsó határa látható az alábbi kódban. És az egyes képkockák általános torzításának csökkentése érdekében az éleket csak az éles érzékelővel érzékeli. További információ a canny edge -ről itt található. Alapszabály, hogy a Canny () függvény paramétereit 1: 2 vagy 1: 3 arányban kell kiválasztani.
def detect_edges (keret):
alsó_kék = np.tömb ([90, 120, 0], dtype = "uint8") # kék szín alsó határa felső_kék = np.tömb ([150, 255, 255], dtype = "uint8") # felső határa kék színű maszk = cv2.inRange (hsv, alsó_kék, felső_kék) # ez a maszk kiszűr mindent, kivéve a kék # észleli az éleket élek = cv2. Canny (maszk, 50, 100) cv2.imshow ("élek", élek) visszatérő élek
Ezt a funkciót a fő hurokból is meghívják, amely paraméterként a HSV színtér keretet veszi fel, és visszaadja a szélezett keretet. A szélezett keret, amit kaptam, fent található.
Érdekes régió (ROI) kiválasztása:
Az érdeklődési körzet kiválasztása kulcsfontosságú ahhoz, hogy csak a keret 1 régiójára összpontosítson. Ebben az esetben nem szeretném, ha az autó sok elemet látna a környezetben. Csak azt akarom, hogy az autó a sávvonalakra összpontosítson, és figyelmen kívül hagyjon bármi mást. P. S.: a koordinátarendszer (x és y tengely) a bal felső sarokból indul. Más szóval, a pont (0, 0) a bal felső sarokból indul. y tengely magassága és x tengely szélessége. Az alábbi kód az érdeklődésre számot tartó régiót választja ki, hogy csak a keret alsó felére fókuszáljon.
def region_of_interest (élek):
magasság, szélesség = élek.shape # kivonja az élek magasságát és szélességét keretmaszk = np.zeros_like (élek) # készítsen üres mátrixot az élek keretének azonos méreteivel # csak a képernyő alsó felére fókuszáljon # adja meg a koordinátákat 4 pont (bal alsó, bal felső, jobb felső, jobb alsó) sokszög = np. Tömb (
Ez a funkció az élű keretet veszi paraméterként, és rajzol egy sokszöget 4 előre beállított ponttal. Csak arra fog összpontosítani, ami a sokszög belsejében van, és figyelmen kívül hagy mindent, ami rajta kívül van. Az érdeklődési régióm kerete fent látható.
Vonalszegmensek észlelése:
A Hough transzformációt a vonalszakaszok észlelésére használják egy élű keretből. A Hough transzformáció olyan technika, amely bármilyen alak matematikai formában történő kimutatására szolgál. Szinte minden objektumot képes észlelni, még akkor is, ha bizonyos számú szavazat szerint eltorzult. A Hough -transzformációra vonatkozó nagy hivatkozás itt látható. Ehhez az alkalmazáshoz a cv2. HoughLinesP () függvény a vonalak észlelésére szolgál minden keretben. A funkció fontos paraméterei a következők:
cv2. HoughLinesP (frame, rho, théta, min_threshold, minLineLength, maxLineGap)
- Keret: az a keret, amelyben vonalakat szeretnénk észlelni.
- rho: Ez a távolság pontossága pixelben (általában = 1)
- théta: szögpontosság radiánban (mindig = np.pi/180 ~ 1 fok)
- min_threshold: minimális szavazat, amelyet meg kell kapnia ahhoz, hogy vonalnak tekintse
- minLineLength: a sor minimális hossza pixelben. Az ennél rövidebb sor nem tekinthető vonalnak.
- maxLineGap: 2 sor közötti maximális pixelköz, amelyet 1 sorként kell kezelni. (Az én esetemben nem használják, mivel az általam használt sávvonalakon nincs rés).
Ez a függvény egy sor végpontjait adja vissza. A következő függvényt hívom meg a fő hurokból a vonalak észleléséhez a Hough transzformáció használatával:
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
Átlagos meredekség és metszés (m, b):
emlékezzen arra, hogy az egyenlet egyenletét y = mx + b adja. Ahol m az egyenes meredeksége és b az y-metszéspont. Ebben a részben kiszámítják a Hough transzformációval észlelt vonalszakaszok lejtéseinek és metszéseinek átlagát. Mielőtt ezt megtennénk, nézzük meg a fent látható eredeti keretfotót. Úgy tűnik, hogy a bal oldali sáv felfelé halad, így negatív lejtése van (emlékszik a koordináta -rendszer kezdőpontjára?). Más szóval, a bal oldali sáv x1 <x2 és y2 x1 és y2> y1 pozitív lejtést ad. Tehát minden pozitív lejtésű vonal jobb oldali sávpontnak minősül. Függőleges vonalak (x1 = x2) esetén a meredekség végtelen. Ebben az esetben kihagyjuk az összes függőleges vonalat, hogy elkerüljük a hibát. Annak érdekében, hogy nagyobb pontosságot nyújtson az észleléshez, minden keret két határvonalon keresztül két régióra (jobbra és balra) van felosztva. Minden szélességi pont (x tengely pont) nagyobb, mint a jobb oldali határvonal, a jobb oldali sáv kiszámításához kapcsolódik. És ha minden szélességi pont kisebb, mint a bal oldali határvonal, akkor a bal sáv kiszámításához kapcsolódnak. A következő függvény feldolgozza a keretet és a Hough transzformációval észlelt sávszegmenseket, és visszaadja két sávvonal átlagos meredekségét és metszését.
def átlagos_slope_intercept (frame, line_segments):
lane_lines = ha a line_segments értéke nincs: print ("nincs vonalszakasz észlelve") return lane_lines magasság, szélesség, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - border) right_region_boundary = szélesség * határvonal a sor_szegmenséhez a vonalszegmensekben: x1, y1, x2, y2 esetén a vonalszakaszban: ha x1 == x2: nyomtatás ("függőleges vonalak kihagyása (lejtés = végtelen)") folytatás illeszkedés = np.polyfit ((x1, x2), (y1, y2), 1) lejtés = (y2 - y1) / (x2 - x1) metszés = y1 - (lejtő * x1) ha lejtés <0: ha x1 <bal_régió_határ és x2 jobb_régióhatár és x2> jobb_régióhatár: jobb_illesztés. hozzáfűzni ((lejtés, elfogás)) 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)) e_vonalak =
A make_points () az átlagos_slope_intercept () függvény segítő függvénye, amely visszaadja a sávvonalak korlátos koordinátáit (alulról a keret közepéig).
def make_points (keret, vonal):
magasság, szélesség, _ = keret. alakú lejtés, metszés = y1 vonal = magasság # a keret alja int ((y1 - elfog) / lejtő) x2 = int ((y2 - elfog) / lejtő) return
A 0 -val való osztás megakadályozása érdekében feltétel kerül bemutatásra. Ha a meredekség = 0, ami azt jelenti, hogy y1 = y2 (vízszintes vonal), adjon a meredekséghez 0 közeli értéket. Ez nem befolyásolja az algoritmus teljesítményét, és megakadályozza a lehetetlen eseteket (osztás 0 -val).
A sávvonalak megjelenítéséhez a kereteken a következő funkciót kell használni:
def display_lines (frame, lines, line_color = (0, 255, 0), line_width = 6): # vonalszín (B, G, R)
line_image = np.zeros_like (frame), ha a vonalak nincsenek line_width) line_image = cv2.addWeighted (frame, 0,8, line_image, 1, 1) return line_image
A cv2.addWeighted () függvény a következő paramétereket veszi fel, és két kép kombinálására szolgál, de mindegyiknek súlyt ad.
cv2.add Súlyos (kép1, alfa, kép2, béta, gamma)
És kiszámítja a kimeneti képet a következő egyenlet segítségével:
output = alfa * kép1 + béta * kép2 + gamma
További információ a cv2.addWeighted () függvényről itt található.
A címsor kiszámítása és megjelenítése:
Ez az utolsó lépés, mielőtt motorjainkra fordulatszámot alkalmazunk. Az irányvonal felelős a kormánymotor forgásirányának megadásáért és a fojtószelep -motorok működési sebességéért. Az irányvonal kiszámítása tiszta trigonometria, tan és atan (tan^-1) trigonometriai függvényeket használnak. Néhány szélsőséges eset az, amikor a kamera csak egy sávos vonalat érzékel, vagy amikor egyetlen vonalat sem. Mindezek az esetek a következő függvényben láthatók:
def get_steering_angle (keret, sáv_vonalak):
magasság, szélesség, _ = frame.shape if len (lane_lines) == 2: # ha két sávvonalat észlel _, _, left_x2, _ = lane_lines [0] [0] # kivonat bal x2 a lane_lines tömbből _, _, right_x2, _ = lane_lines [1] [0] # jobb oldali x2 kivonat a lane_lines tömbből mid = int (width / 2) x_offset = (left_x2 + right_x2) / 2 - y_offset = int (height / 2) elif len (lane_lines) == 1: # ha csak egy sort észlel x1, _, x2, _ = sáv_vonalak [0] [0] x_offset = x2 - x1 y_offset = int (magasság / 2) elif len (lane_lines) == 0: # ha vonal nem észlelhető
Az x_eltolás az első esetben az, hogy az átlag ((jobb x2 + bal x2) / 2) mennyiben különbözik a képernyő közepétől. y_eltolás mindig magasság / 2. A fenti utolsó képen egy példa látható a címsorra. A szög_to_mid_radiánok értéke megegyezik a fenti utolsó képen látható "thétával". Ha a kormányszög = 90, az azt jelenti, hogy az autónak van egy irányvonala, amely merőleges a "magasság / 2" vonalra, és az autó kormányzás nélkül halad előre. Ha a kormányszög> 90, az autónak jobbra kell kormányoznia, különben balra. A címsor megjelenítéséhez a következő funkciót kell használni:
def display_heading_line (keret, kormányszög, line_color = (0, 0, 255), line_width = 5)
head_image = np.zeros_like (frame) magasság, szélesség, _ = frame.shape kormányzási_szög_radian = kormányzási szög / 180,0 * math.pi x1 = int (szélesség / 2) y1 = magasság x2 = int (x1 - magasság / 2 / math.tan [kormányzási_szög_radián]) y2 = int (magasság / 2) cv2.line (head_image, (x1, y1), (x2, y2), line_color, line_width) head_image = cv2.addWeighted (frame, 0.8, head_image, 1, 1) return head_image
A fenti funkció a keretet, amelybe az irányvonalat rajzolja, és a kormányzási szöget veszi be bemenetként. Visszaadja a címsor képét. Az én esetemben vett címsor keret a fenti képen látható.
Az összes kód kombinálása:
A kód készen áll az összeszerelésre. A következő kód az egyes funkciókat meghívó program fő ciklusát mutatja:
import cv2
importálja a számokat np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240), míg True: ret, frame = video.read () frame = cv2.flip (frame, -1) #A függvények meghívása = get_steering_angle (frame, lane_lines) head_image = display_heading_line (lane_lines_image, kormány_szög) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()
6. lépés: A PD vezérlés alkalmazása
Most már készen állunk a kormányzási szögünkre, hogy betáplálhassuk a motorokhoz. Mint korábban említettük, ha a kormányzási szög nagyobb, mint 90, az autónak jobbra kell fordulnia, különben balra. Alkalmaztam egy egyszerű kódot, amely jobbra fordítja a kormánymotort, ha a szög 90 felett van, és balra fordul, ha a kormányzási szög 90 -nél kisebb, állandó (10% PWM) fojtószelep mellett, de sok hibát kaptam. A fő hiba az, amikor az autó bármelyik kanyarhoz közeledik, a kormánymotor közvetlenül működik, de a fojtószelep motorok elakadnak. Megpróbáltam növelni a fojtószelep sebességét (20% PWM) a kanyarokban, de azzal értem véget, hogy a robot kiszállt a sávokból. Szükségem volt valamire, ami nagyon megnöveli a gázpedált, ha a kormányzási szög nagyon nagy, és egy kicsit növeli a sebességet, ha a kormányzási szög nem olyan nagy, majd csökkenti a sebességet a kezdeti értékre, amikor az autó 90 fokra közeledik (egyenesen halad). A megoldás egy PD vezérlő használata volt.
A PID szabályozó az arányos, az integrált és a származtatott vezérlőket jelenti. Az ilyen típusú lineáris vezérlőket széles körben használják a robotikai alkalmazásokban. A fenti kép a tipikus PID visszacsatolási szabályozó hurkot mutatja. Ennek a vezérlőnek az a célja, hogy a "leghatékonyabb módon" elérje az "alapjelet", ellentétben az "on -off" vezérlőkkel, amelyek bizonyos feltételeknek megfelelően be- vagy kikapcsolják a berendezést. Néhány kulcsszót ismerni kell:
- Alapjel: az a kívánt érték, amelyet el szeretne érni a rendszeren.
- Tényleges érték: az érzékelő által érzékelt tényleges érték.
- Hiba: a különbség az alapjel és a tényleges érték között (hiba = alapjel - tényleges érték).
- Vezérelt változó: a neve alapján az a változó, amelyet szabályozni szeretne.
- Kp: Arányos állandó.
- Ki: integrálállandó.
- Kd: Származékos állandó.
Röviden, a PID vezérlőrendszer hurka a következőképpen működik:
- A felhasználó határozza meg a rendszer eléréséhez szükséges alapértéket.
- A hiba kiszámításra kerül (hiba = alapjel - tényleges).
- A P vezérlő a hiba értékével arányos műveletet generál. (a hiba nő, a P művelet is nő)
- Az I vezérlő idővel integrálja a hibát, ami kiküszöböli a rendszer egyensúlyi állapotának hibáját, de növeli a túllépést.
- A D vezérlő egyszerűen a hiba időderiváltja. Más szóval, ez a hiba lejtése. A hiba deriváltjával arányos műveletet hajt végre. Ez a vezérlő növeli a rendszer stabilitását.
- A vezérlő kimenete a három vezérlő összege lesz. A vezérlő kimenete 0 lesz, ha a hiba 0 lesz.
A PID -szabályozó nagyszerű magyarázata itt található.
Visszatérve a sávtartó autóhoz, a szabályozott változóm a gázpedál volt (mivel a kormányzásnak csak két állapota van, jobbra vagy balra). PD vezérlőt használnak erre a célra, mivel a D művelet nagyon megnöveli a fojtószelep sebességét, ha a hiba változása nagyon nagy (azaz nagy eltérés), és lelassítja az autót, ha ez a hibaváltás megközelíti a 0 értéket. A PD végrehajtásához a következő lépéseket tettem vezérlő:
- Állítsa az alapértéket 90 fokra (mindig azt akarom, hogy az autó egyenesen mozogjon)
- Kiszámította az eltérési szöget a közepétől
- Az eltérés két információt ad: Mekkora a hiba (az eltérés nagysága), és milyen irányba kell haladnia a kormánymotornak (az eltérés jele). Ha az eltérés pozitív, az autónak jobbra kell kormányoznia, különben balra.
- Mivel az eltérés negatív vagy pozitív, "hiba" változó van definiálva, és mindig egyenlő az eltérés abszolút értékével.
- A hibát megszorozzuk egy állandó Kp -val.
- A hiba idődifferenciálódik, és megszorozzuk egy állandó Kd -vel.
- A motorok fordulatszáma frissül, és a ciklus újra elindul.
A fő kódban a következő kódot használják a fojtómotorok fordulatszámának szabályozására:
sebesség = 10 # működési sebesség % PWM -ben
#Változók, amelyeket minden ciklusban frissíteni kell szögig_milliméterig változó hiba = absz (eltérés), ha az eltérés -5: # ne kormányozzon, ha 10 fokos hiba tartománybeli eltérés van = 0 hiba = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. LOW) kormányzás.megállás () elif eltérés> 5: # jobbra kormányozni, ha az eltérés pozitív -5: # balra kanyarodik, ha az eltérés negatív * hiba PD = int (sebesség + derivált + arányos) spd = abs (PD), ha spd> 25: spd = 25 fojtószelep.indítás (spd) lastError = hiba lastTime = time.time ()
Ha a hiba nagyon nagy (a középsőtől való eltérés nagy), akkor az arányos és a derivált műveletek magasak, ami nagy fojtási sebességet eredményez. Amikor a hiba megközelíti a 0 -t (a közepétől való eltérés alacsony), a derivált művelet fordítva működik (a meredekség negatív), és a fojtószelep sebessége csökken a rendszer stabilitásának fenntartása érdekében. A teljes kód az alábbiakban található.
7. lépés: Eredmények
A fenti videók az általam elért eredményeket mutatják. Ez további hangolást és további beállításokat igényel. Azért csatlakoztattam a málna pi -t az LCD kijelzőmhöz, mert a hálózatomon keresztül továbbított videó nagy késleltetésű volt, és nagyon frusztráló volt vele dolgozni, ezért vannak vezetékek a málna pi -hez csatlakoztatva a videóban. A pálya rajzolásához hablapokat használtam.
Várom javaslatait a projekt jobbá tétele érdekében! Remélem, hogy ez az oktatóanyag elég jó volt ahhoz, hogy új információkat adjon nektek.
Ajánlott:
Miniatürizáló Arduino autonóm robot (Land Rover / autó) 1. szakasz: 3: 6 lépés
Miniatürizáló Arduino autonóm robot (Land Rover / autó) 1. szakasz
Raspberry Pi - Autonóm Mars Rover OpenCV objektumkövetéssel: 7 lépés (képekkel)
Raspberry Pi - Autonóm Mars Rover OpenCV objektumkövetéssel: Raspberry Pi 3, Open CV objektumfelismerés, ultrahangos érzékelők és hajtóműves egyenáramú motorok. Ez a rover képes követni minden olyan tárgyat, amelyre kiképezték, és bármilyen terepen mozoghat
Autonóm párhuzamos parkoló autó készítése az Arduino használatával: 10 lépés (képekkel)
Autonóm párhuzamos parkoló autók készítése Arduino használatával: Az autonóm parkolás során algoritmusokat és helyzetérzékelőket kell létrehoznunk bizonyos feltételezések szerint. Feltételezéseink a következők lesznek ebben a projektben. A forgatókönyv szerint az út bal oldala falakból és parkterületekből áll. Mint te
Vezeték nélküli távirányító 2,4 GHz -es NRF24L01 modul használatával Arduino - Nrf24l01 4 csatorna / 6 csatornás adó vevő négykópás - Rc Helikopter - Rc sík az Arduino használatával: 5 lépés (képekkel)
Vezeték nélküli távirányító 2,4 GHz -es NRF24L01 modul használatával Arduino | Nrf24l01 4 csatorna / 6 csatornás adó vevő négykópás | Rc Helikopter | Rc sík Arduino használatával: Rc autó működtetése | Quadcopter | Drone | RC sík | RC csónak, mindig szükségünk van vevőre és adóra, tegyük fel, hogy az RC QUADCOPTER esetében szükségünk van egy 6 csatornás adóra és vevőre, és az ilyen típusú TX és RX túl költséges, ezért készítünk egyet
Az autonóm távvezérelt autó: 6 lépés
Az autonóm távvezérelt autó: Ez az oktatható a Dél-Floridai Egyetem (www.makecourse.com) Makecourse projekt követelményeinek teljesítésével jött létre. Ez a projekt bemutatja, hogyan képes az Arduino és a kettős H-híd motorhajtás négyet irányítani