Orodha ya maudhui:

Gari ya Kuweka Njia ya Kuendesha Inayotumia Raspberry Pi na OpenCV: Hatua 7 (na Picha)
Gari ya Kuweka Njia ya Kuendesha Inayotumia Raspberry Pi na OpenCV: Hatua 7 (na Picha)

Video: Gari ya Kuweka Njia ya Kuendesha Inayotumia Raspberry Pi na OpenCV: Hatua 7 (na Picha)

Video: Gari ya Kuweka Njia ya Kuendesha Inayotumia Raspberry Pi na OpenCV: Hatua 7 (na Picha)
Video: Распознавание номеров авто с YOLOv7 + OCR на Google Colab | Учебник ANPR/ALPR 2023 2024, Julai
Anonim
Gari ya Kuweka Njia ya Kujitegemea Kutumia Raspberry Pi na OpenCV
Gari ya Kuweka Njia ya Kujitegemea Kutumia Raspberry Pi na OpenCV

Katika mafunzo haya, roboti ya njia ya uhuru itatekelezwa na itapita hatua zifuatazo:

  • Sehemu za Kukusanya
  • Kuweka mahitaji ya programu
  • Mkutano wa vifaa
  • Mtihani wa Kwanza
  • Kugundua mistari ya njia na kuonyesha laini inayoongoza ukitumia openCV
  • Utekelezaji wa mtawala wa PD
  • Matokeo

Hatua ya 1: Kukusanya Vipengele

Vipengele vya Kukusanya
Vipengele vya Kukusanya
Vipengele vya Kukusanya
Vipengele vya Kukusanya
Vipengele vya Kukusanya
Vipengele vya Kukusanya
Vipengele vya Kukusanya
Vipengele vya Kukusanya

Picha hapo juu zinaonyesha vifaa vyote vilivyotumika katika mradi huu:

  • RC gari: Nilipata yangu kutoka duka la ndani nchini mwangu. Ina vifaa vya motors 3 (2 kwa kupiga na 1 kwa usukani). Ubaya kuu wa gari hili ni kwamba uendeshaji ni mdogo kati ya "hakuna uendeshaji" na "uendeshaji kamili". Kwa maneno mengine, haiwezi kuongoza kwa pembe maalum, tofauti na magari ya RC ya servo. Unaweza kupata kitanda sawa cha gari iliyoundwa mahsusi kwa pi ya raspberry kutoka hapa.
  • Raspberry pi 3 mfano b +: hii ni ubongo wa gari ambayo itashughulikia hatua nyingi za usindikaji. Inategemea processor ya msingi ya quad-64 iliyowekwa saa 1.4 GHz. Nilipata yangu kutoka hapa.
  • Moduli ya kamera ya Raspberry pi 5 mp: Inasaidia fps ya 1080p @ 30, 720p @ 60 fps, na 640x480p 60/90 kurekodi. Pia inasaidia interface ya serial ambayo inaweza kuziba moja kwa moja kwenye pi ya rasipberry. Sio chaguo bora kwa matumizi ya usindikaji wa picha lakini ni ya kutosha kwa mradi huu na vile vile ni rahisi sana. Nilipata yangu kutoka hapa.
  • Dereva wa Magari: Inatumika kudhibiti mwelekeo na kasi ya motors DC. Inasaidia udhibiti wa motors 2 dc katika bodi 1 na inaweza kuhimili 1.5 A.
  • Power Bank (Hiari): Nilitumia benki ya nguvu (iliyokadiriwa kwa 5V, 3A) kuwezesha pi ya rasipberry kando. Kigeuzi cha kushuka chini (kibadilishaji cha dume: 3A pato la sasa) inapaswa kutumiwa ili kuwezesha pi ya rasipberry kutoka chanzo 1.
  • 3s (12 V) LiPo betri: Lithium Polymer betri zinajulikana kwa utendaji wao mzuri katika uwanja wa roboti. Inatumika kumpa nguvu dereva wa gari. Nilinunua yangu kutoka hapa.
  • Mwanaume kwa mwanamume na mwanamke kwa waya za kuruka za kike.
  • Mkanda wa pande mbili: Inatumika kuweka vifaa kwenye gari la RC.
  • Mkanda wa samawati: Hii ni sehemu muhimu sana ya mradi huu, hutumiwa kutengeneza mistari miwili ya njia ambayo gari itaendesha kati. Unaweza kuchagua rangi yoyote unayotaka lakini ninapendekeza kuchagua rangi tofauti na zile zilizo kwenye mazingira ya karibu.
  • Vifungo vya Zip na baa za kuni.
  • Screw dereva.

Hatua ya 2: Kuweka OpenCV kwenye Raspberry Pi na Kuweka Upangiaji wa Kijijini

Kuweka OpenCV kwenye Raspberry Pi na Kuweka Upangiaji wa Kijijini
Kuweka OpenCV kwenye Raspberry Pi na Kuweka Upangiaji wa Kijijini

Hatua hii inakera kidogo na itachukua muda.

OpenCV (Open source Computer Vision) ni maono ya kompyuta ya chanzo wazi na maktaba ya programu ya kujifunza mashine. Maktaba ina zaidi ya algorithms 2500 zilizoboreshwa. Fuata mwongozo huu wa moja kwa moja kusanidi openCV kwenye pi yako ya rasipiberi na vile vile kusanikisha OS raspberry pi (ikiwa bado haukufanya). Tafadhali kumbuka kuwa mchakato wa kujenga openCV inaweza kuchukua karibu masaa 1.5 kwenye chumba kilichopozwa vizuri (kwani joto la processor litakuwa juu sana!) Kwa hivyo pata chai na subiri kwa subira: D.

Kwa onyesho la kijijini, fuata pia mwongozo HUU wa kusanikisha ufikiaji wa kijijini kwa rasipberry yako kutoka kwa kifaa chako cha Windows / Mac.

Hatua ya 3: Kuunganisha Sehemu Pamoja

Kuunganisha Sehemu Pamoja
Kuunganisha Sehemu Pamoja
Kuunganisha Sehemu Pamoja
Kuunganisha Sehemu Pamoja
Kuunganisha Sehemu Pamoja
Kuunganisha Sehemu Pamoja

Picha hapo juu zinaonyesha unganisho kati ya rasiberi pi, moduli ya kamera na dereva wa gari. Tafadhali kumbuka kuwa motors nilizotumia zinachukua 0.35 A kwa 9 V kila moja ambayo hufanya iwe salama kwa dereva wa gari kuendesha motors 3 kwa wakati mmoja. Na kwa kuwa ninataka kudhibiti kasi 2 za gari zinazopiga (1 nyuma na 1 mbele) kwa njia ile ile, niliwaunganisha kwenye bandari ile ile. Nilipandisha dereva wa gari upande wa kulia wa gari kwa kutumia mkanda mara mbili. Kwa moduli ya kamera, niliingiza tie ya zip kati ya mashimo ya screw kama picha hapo juu inavyoonyesha. Kisha, ninaweka kamera kwenye baa ya kuni ili niweze kurekebisha msimamo wa kamera kama vile nataka. Jaribu kusanikisha kamera katikati ya gari iwezekanavyo. Ninapendekeza kuweka kamera angalau 20 cm juu ya ardhi ili uwanja wa maoni mbele ya gari uwe bora. Skimu ya Fritzing imeambatanishwa hapa chini.

Hatua ya 4: Mtihani wa Kwanza

Mtihani wa Kwanza
Mtihani wa Kwanza
Mtihani wa Kwanza
Mtihani wa Kwanza

Upimaji wa Kamera:

Mara kamera ikiwa imewekwa, na maktaba ya openCV imejengwa, ni wakati wa kujaribu picha yetu ya kwanza! Tutachukua picha kutoka kwa pi cam na kuihifadhi kama "original.jpg". Inaweza kufanywa kwa njia 2:

1. Kutumia Amri za Kituo:

Fungua dirisha mpya la wastaafu na andika amri ifuatayo:

raspistill -o asili.jpg

Hii itachukua picha bado na kuihifadhi katika saraka ya "/pi/original.jpg".

Kutumia chatu yoyote IDE (ninatumia IDLE):

Fungua mchoro mpya na andika nambari ifuatayo:

kuagiza cv2

video = cv2. VideoCapture (0) wakati True: ret, frame = video.read () frame = cv2.flip (frame, -1) # used to flip the image wima cv2.imshow ('original', frame) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) ikiwa ufunguo == 27: vunja video. tafadhali () cv2.destroyAllWindows ()

Wacha tuone kilichotokea katika nambari hii. Mstari wa kwanza ni kuagiza maktaba yetu ya openCV kutumia kazi zake zote. kazi ya VideoCapture (0) huanza kutiririsha video ya moja kwa moja kutoka kwa chanzo kilichoamuliwa na kazi hii, katika kesi hii ni 0 ambayo inamaanisha kamera ya raspi. ikiwa una kamera nyingi, nambari tofauti zinapaswa kuwekwa. video.read () itasoma kila fremu inatoka kwa kamera na kuihifadhi katika anuwai inayoitwa "fremu". kazi ya flip () itabadilisha picha kwa heshima na mhimili wa y (wima) kwani ninaweka kamera yangu kinyume. imshow () itaonyesha muafaka wetu ulioongozwa na neno "asili" na imwrite () itaokoa picha yetu kama asili.jpg. waitKey (1) itasubiri 1 ms kwa kitufe chochote cha kibodi kushinikizwa na kurudisha nambari yake ya ASCII. ikiwa kitufe cha kutoroka (esc) kimeshinikizwa, thamani ya desimali ya 27 inarejeshwa na itavunja kitanzi ipasavyo. video.release () itaacha kurekodi na kuharibu Windows zote () zitafunga kila picha iliyofunguliwa na imshow () kazi.

Ninapendekeza kujaribu picha yako na njia ya pili ili ujue na kazi za openCV. Picha imehifadhiwa katika saraka ya "/pi/original.jpg". Picha ya asili ambayo kamera yangu ilipiga imeonyeshwa hapo juu.

Kujaribu Motors:

Hatua hii ni muhimu kuamua mwelekeo wa mzunguko wa kila motor. Kwanza, wacha tuwe na utangulizi mfupi juu ya kanuni ya kufanya kazi ya dereva wa gari. Picha hapo juu inaonyesha dereva wa gari-nje. Wezesha A, Ingizo 1 na Ingizo la 2 linahusishwa na udhibiti wa motor A. Wezesha B, Ingizo la 3 na Ingizo 4 zinahusishwa na udhibiti wa motor B. Udhibiti wa mwelekeo umeanzishwa na sehemu ya "Ingizo" na udhibiti wa kasi umewekwa na sehemu ya "Wezesha". Ili kudhibiti mwelekeo wa motor A kwa mfano, weka Ingizo 1 hadi JUU (3.3 V katika kesi hii kwa kuwa tunatumia pi rasiberi) na weka Ingizo la 2 hadi LOW, motor itazunguka kwa mwelekeo maalum na kwa kuweka maadili tofauti Kuingiza 1 na Pembejeo 2, motor itazunguka kwa mwelekeo tofauti. Ikiwa Ingizo 1 = Ingizo 2 = (JUU au LOW), motor haitageuka. Wezesha pini kuchukua ishara ya kuingiza Upana wa Pulse (PWM) kutoka kwa rasipberry (0 hadi 3.3 V) na uendeshe motors ipasavyo. Kwa mfano, ishara ya 100% ya PWM inamaanisha tunafanya kazi kwa kasi kubwa na ishara ya PWM ya 0% inamaanisha kuwa motor haizunguki. Nambari ifuatayo inatumiwa kuamua mwelekeo wa motors na kujaribu kasi zao.

muda wa kuagiza

kuagiza RPi. GPIO kama GPIO GPIO. maonyo (Uwongo) # Pini za Uendeshaji Kuendesha_kuwezeshwa = 22 # Pini ya Kimwili 15 in1 = 17 # Pini ya Kimwili 11 in2 = 27 # Pini ya Kimwili 13 # Vifungo vya Motors za Kichocheo vinaweza = 25 # Siri ya Kimwili 22 in3 = 23 # Pini ya Kimwili 16 in4 = 24 # Siri ya Kimwili 18 GPIO.setmode (GPIO. BCM) # Tumia nambari ya GPIO badala ya nambari ya mwili GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. kuanzisha (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (steering_enable, GPIO.out) # Udhibiti wa Magari ya Usimamizi GPIO.output (in1, GPIO. JUU) GPIO.output (in2, GPIO. LOW) uendeshaji = GPIO. PWM (uendeshaji_kuwezesha, 1000) # weka mzunguko wa kubadilisha kuwa usukani wa 1000 Hz. pato (in4, GPIO. LOW) throttle = GPIO. PWM (throttle_enable, 1000) # weka mzunguko wa kubadilisha kuwa 1000 Hz throttle.stop () time.sleep (1) kaba. % PWM ishara-> (0.25 * Voltage ya betri) - ya dereva kuanza uendeshaji (100) # huanza gari kwa ishara ya PWM 100-> (1 * Voltage ya Batri) - wakati wa upotezaji wa dereva. lala (3) kaba.

Nambari hii itaendesha gari zinazopiga na motor ya usukani kwa sekunde 3 na kisha itawazuia. (Upotezaji wa dereva) unaweza kuamua kutumia voltmeter. Kwa mfano, tunajua kuwa ishara ya PWM 100% inapaswa kutoa voltage kamili ya betri kwenye kituo cha gari. Lakini, kwa kuweka PWM kwa 100%, niligundua kuwa dereva anasababisha kushuka kwa 3 V na motor inapata 9 V badala ya 12 V (haswa kile ninahitaji!). Hasara sio laini, kwa hivyo hasara kwa 100% ni tofauti sana na hasara kwa 25%. Baada ya kutumia nambari hapo juu, matokeo yangu yalikuwa kama ifuatavyo:

Matokeo ya kusisimua: ikiwa in3 = JUU na in4 = LOW, motors za kukoroga zitakuwa na mzunguko wa Saa-Hekima (CW) yaani gari litasonga mbele. Vinginevyo, gari litasonga nyuma.

Matokeo ya Uendeshaji: ikiwa in1 = JUU na in2 = LOW, motor ya usukani itageuka kushoto kabisa yaani gari litaelekea kushoto. Vinginevyo, gari litaendesha kulia. Baada ya majaribio kadhaa, niligundua kuwa gari inayoendesha haitageuka ikiwa ishara ya PWM haikuwa 100% (i.e. motor itaelekeza kulia kabisa au kushoto kabisa).

Hatua ya 5: Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa

Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa
Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa
Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa
Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa
Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa
Kugundua Mistari ya Mistari na Kuhesabu Mstari wa Kichwa

Katika hatua hii, algorithm ambayo itadhibiti mwendo wa gari itaelezwa. Picha ya kwanza inaonyesha mchakato mzima. Uingizaji wa mfumo ni picha, pato ni theta (pembe ya uendeshaji kwa digrii). Kumbuka kuwa, usindikaji unafanywa kwenye picha 1 na utarudiwa kwenye fremu zote.

Kamera:

Kamera itaanza kurekodi video na azimio (320 x 240). Ninapendekeza kupunguza azimio ili uweze kupata kiwango bora cha fremu (fps) kwani kushuka kwa fps kutatokea baada ya kutumia mbinu za usindikaji kwa kila fremu. Nambari hapa chini itakuwa kitanzi kuu cha programu na itaongeza kila hatua juu ya nambari hii.

kuagiza cv2

kuagiza numpy kama np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # weka upana kuwa video ya 320 p (seti (cv2. CAP_PROP_FRAME_HEIGHT, 240) # weka urefu kuwa 240 p # Kitanzi wakati Kweli: ret, frame = video.read () frame = cv2.flip (frame, -1) cv2.imshow ("original", frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAll Windows ()

Nambari hapa itaonyesha picha ya asili iliyopatikana katika hatua ya 4 na imeonyeshwa kwenye picha hapo juu.

Badilisha kuwa Nafasi ya Rangi ya HSV:

Sasa baada ya kuchukua kurekodi video kama muafaka kutoka kwa kamera, hatua inayofuata ni kubadilisha kila fremu iwe nafasi ya rangi ya Hue, Kueneza, na Thamani (HSV). Faida kuu ya kufanya hivyo ni kuweza kutofautisha kati ya rangi na kiwango chao cha mwangaza. Na hapa kuna maelezo mazuri ya nafasi ya rangi ya HSV. Kubadilisha HSV hufanywa kupitia kazi ifuatayo:

def convert_to_HSV (fremu):

hsv = cv2.cvt Rangi (fremu, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) rudisha hsv

Kazi hii itaitwa kutoka kitanzi kuu na itarudisha sura katika nafasi ya rangi ya HSV. Sura niliyoipata katika nafasi ya rangi ya HSV imeonyeshwa hapo juu.

Gundua Rangi ya Bluu na Kando:

Baada ya kubadilisha picha kuwa nafasi ya rangi ya HSV, ni wakati wa kugundua tu rangi tunayovutiwa nayo (yaani rangi ya hudhurungi kwa kuwa ni rangi ya mistari ya njia). Ili kutoa rangi ya samawati kutoka kwa fremu ya HSV, anuwai, kueneza na thamani inapaswa kutajwa. rejea hapa kuwa na wazo bora juu ya maadili ya HSV. Baada ya majaribio kadhaa, mipaka ya juu na chini ya rangi ya hudhurungi imeonyeshwa kwenye nambari hapa chini. Na kupunguza upotovu wa jumla katika kila fremu, kingo hugunduliwa tu kwa kutumia kichunguzi cha makali. Zaidi juu ya makali ya canny inapatikana hapa. Sheria ya kidole gumba ni kuchagua vigezo vya kazi ya Canny () na uwiano wa 1: 2 au 1: 3.

def detect_edges (fremu):

chini_bluu = np.array ([90, 120, 0], dtype = "uint8") # kikomo cha chini cha rangi ya bluu juu_blue = np.array ([150, 255, 255], dtype = "uint8") # kikomo cha juu cha rangi ya samawati mask = cv2.in Upeo (hsv, chini_blue, juu_blue) # kinyago hiki kitachuja kila kitu lakini bluu # gundua kingo = cv2. Canny (mask, 50, 100) cv2.imshow ("kingo", kingo) kingo za kurudi

Kazi hii pia itaitwa kutoka kwa kitanzi kuu ambacho huchukua kama parameta fremu ya nafasi ya rangi ya HSV na kurudisha fremu yenye ukingo. Sura ya kuwili niliyopata inapatikana hapo juu.

Chagua Mkoa wa Riba (ROI):

Kuchagua eneo la kupendeza ni muhimu kuzingatia tu mkoa 1 wa fremu. Katika kesi hii, sitaki gari kuona vitu vingi kwenye mazingira. Nataka tu gari izingatie laini za njia na kupuuza kitu kingine chochote. P. S: mfumo wa uratibu (x na y axes) huanza kutoka kona ya juu kushoto. Kwa maneno mengine, uhakika (0, 0) huanza kutoka kona ya juu kushoto. mhimili kuwa urefu na x-mhimili kuwa upana. Nambari iliyo hapa chini huchagua eneo la kupendeza ili kuzingatia tu nusu ya chini ya fremu.

def region_of_interest (kingo):

urefu, upana = kingo. umbo # dondoa urefu na upana wa kingo za sura ya kando = np.zeros_like (kingo) # tengeneza matrix tupu na vipimo sawa vya fremu za kingo # zingatia nusu ya chini ya skrini # taja kuratibu za Pointi 4 (kushoto chini, kushoto juu, kulia juu, chini kulia) poligoni = np safu (

Kazi hii itachukua sura iliyokunjwa kama parameta na kuchora poligoni na alama 4 zilizowekwa tayari. Itazingatia tu yaliyo ndani ya poligoni na kupuuza kila kitu nje yake. Kanda yangu ya sura ya riba imeonyeshwa hapo juu.

Gundua Sehemu za Mstari:

Ubadilishaji wa Hough hutumiwa kugundua sehemu za laini kutoka kwa sura iliyo na ukingo. Kubadilisha Hough ni mbinu ya kugundua sura yoyote katika fomu ya hesabu. Inaweza kugundua karibu kitu chochote hata ikiwa imepotoshwa kulingana na idadi kadhaa ya kura. kumbukumbu nzuri ya mabadiliko ya Hough imeonyeshwa hapa. Kwa programu tumizi hii, kazi ya cv2. HoughLinesP () hutumiwa kugundua mistari katika kila fremu. Vigezo muhimu ambavyo kazi hii huchukua ni:

cv2. HoughLinesP (fremu, rho, theta, min_ kizingiti, minLineLength, maxLineGap)

  • Sura: ndio sura tunayotaka kugundua mistari ndani.
  • rho: Ni usahihi wa umbali katika saizi (kawaida ni = 1)
  • theta: usahihi wa angular katika radians (kila wakati = np.pi / 180 ~ 1 digrii)
  • min_threshold: kiwango cha chini cha kura inapaswa kupata ili ichukuliwe kama laini
  • urefuLineLineth: urefu wa chini wa mstari katika saizi. Mstari wowote mfupi kuliko nambari hii haizingatiwi kuwa laini.
  • maxLineGap: pengo kubwa katika saizi kati ya mistari 2 itibiwe kama laini 1. (Haitumiki katika kesi yangu kwani laini za njia ninazotumia hazina pengo lolote).

Kazi hii inarudi mwisho wa mstari. Kazi ifuatayo inaitwa kutoka kitanzi changu kuu ili kugundua mistari inayotumia mabadiliko ya Hough:

def detect_line_segments (vipande vilivyopunguzwa):

rho = 1 theta = np.pi / 180 min_ kizingiti = 10 line_segments = cv2.

Mteremko wa wastani na kukatiza (m, b):

kumbuka kuwa equation ya mstari imetolewa na y = mx + b. Ambapo m ni mteremko wa mstari na b ni y-kukatiza. Katika sehemu hii, wastani wa mteremko na vifungu vya sehemu za laini zilizogunduliwa kutumia Hough transform zitahesabiwa. Kabla ya kufanya hivyo, wacha tuangalie picha ya sura ya asili iliyoonyeshwa hapo juu. Njia ya kushoto inaonekana kwenda juu kwa hivyo ina mteremko hasi (kumbuka hatua ya kuanza ya uratibu wa mfumo?). Kwa maneno mengine, mstari wa mstari wa kushoto una x1 <x2 na y2 x1 na y2> y1 ambayo itatoa mteremko mzuri. Kwa hivyo, mistari yote iliyo na mteremko mzuri inachukuliwa kuwa sehemu za kulia. Katika hali ya mistari wima (x1 = x2), mteremko hautakuwa na mwisho. Katika kesi hii, tutaruka mistari yote wima ili kuzuia kupata hitilafu. Ili kuongeza usahihi zaidi kwa utambuzi huu, kila fremu imegawanywa katika mikoa miwili (kulia na kushoto) kupitia mistari 2 ya mpaka. Sehemu zote za upana (x-axis points) kubwa kuliko mstari wa mpaka wa kulia, zinahusishwa na hesabu ya njia ya kulia. Na ikiwa sehemu zote za upana ziko chini ya mstari wa mpaka wa kushoto, zinahusishwa na hesabu ya njia ya kushoto. Kazi ifuatayo inachukua sura chini ya usindikaji na sehemu za njia zilizogunduliwa kwa kutumia mabadiliko ya Hough na kurudisha mteremko wastani na kukatiza mistari miwili ya njia.

def average_slope_intercept (fremu, sehemu za laini):

lane_lines = ikiwa sehemu za mstari hazipo: chapa ("hakuna sehemu ya laini iliyogunduliwa") rudisha urefu wa urefu wa urefu, upana, _ = fremu. upana * mpaka wa sehemu_ya mstari katika sehemu za mstari: kwa x1, y1, x2, y2 katika sehemu_ya mstari: ikiwa x1 == x2: chapa ("kuruka mistari ya wima (mteremko = infinity)") endelea fit = np. polyfit ((x1, x2), (y1, y2), 1) mteremko = (y2 - y1) / (x2 - x1) kukatiza = y1 - (mteremko * x1) ikiwa mteremko append ((mteremko, kukatiza)) kushoto_fit_a wastani = np wastani (kushoto_fit, mhimili = 0) ikiwa len (kushoto_fit)> 0: lane_lines.append (make_points (fremu, left_fit_average)) right_fit_average = np wastani (kulia_fit, mhimili = 0 ikiwa len (kulia_fit)> 0: lane_lines.append (make_points (fremu, kulia_fit_a wastani)) # lane_line ni safu ya 2-D inayojumuisha uratibu wa mistari ya kulia na kushoto # kwa mfano: lan e_lines =

make_points () ni kazi ya msaidizi wa average_slope_intercept () ambayo itarudisha kuratibu zilizofungwa za laini za laini (kutoka chini hadi katikati ya fremu).

def make_points (fremu, laini):

Urefu, upana, int ((y1 - kukatiza) / mteremko) x2 = int ((y2 - kukatiza) / mteremko) kurudi

Ili kuzuia kugawanya na 0, hali imewasilishwa. Ikiwa mteremko = 0 ambayo inamaanisha y1 = y2 (mstari usawa), mpe mteremko thamani karibu na 0. Hii haitaathiri utendaji wa algorithm na vile vile itazuia kesi isiyowezekana (kugawanya na 0).

Ili kuonyesha mistari ya laini kwenye muafaka, kazi ifuatayo inatumiwa:

nambari za kuonyesha_za (fremu, mistari, laini_mbala = (0, 255, 0), upana_wmstari = 6): # rangi ya laini (B, G, R)

line_image = np.zeros_like (fremu) ikiwa mistari haipo: kwa laini katika mistari: kwa x1, y1, x2, y2 katika mstari: cv2.line (line_image, (x1, y1), (x2, y2), line_color, line_width) line_image = cv2.addWeighted (fremu, 0.8, line_image, 1, 1) kurudi line_image

kazi ya cv2.addWeighted () inachukua vigezo vifuatavyo na hutumiwa kuchanganya picha mbili lakini kwa kumpa kila mmoja uzito.

cv2.addWeighted (picha1, alpha, picha2, beta, gamma)

Na huhesabu picha ya pato kwa kutumia equation ifuatayo:

pato = alfa * picha1 + beta * picha2 + gamma

Habari zaidi juu ya kazi ya cv2.addWeighted () hutolewa hapa.

Hesabu na Onyesha Mstari wa Kichwa:

Hii ni hatua ya mwisho kabla ya kutumia kasi kwa motors zetu. Mstari wa kichwa unawajibika kutoa gari inayoongoza mwelekeo ambayo inapaswa kuzunguka na kuwapa motors za kupindua kasi ambayo watafanya kazi. Kuhesabu mstari wa kichwa ni trigonometry safi, tan na atan (tan ^ -1) kazi za trigonometri hutumiwa. Kesi zingine kali ni wakati kamera inagundua laini moja tu ya laini au wakati haigundua laini yoyote. Kesi hizi zote zinaonyeshwa katika kazi ifuatayo:

def kupata_steering_angle (fremu, laini za laini):

Urefu, upana,, kulia_x2, _ = lane_line [1] [0] # dondoo x2 kulia kutoka kwa lane_line safu katikati = int (upana / 2) x_offset = (kushoto_x2 + kulia_x2) / 2 - katikati y_offset = int (urefu / 2) elif len (lane_lines) = = 1: # ikiwa mstari mmoja tu umegunduliwa x1, _, x2, _ = lane_lines [0] [0] x_offset = x2 - x1 y_offset = int (urefu / 2) elif len (lane_lines) == 0: # ikiwa hakuna mstari hugunduliwa x_offset = 0 y_offset = int (urefu / 2) angle_to_mid_radian = math.atan (x_offset / y_offset) angle_to_mid_deg = int (angle_to_mid_radian * 180.0 / math.pi)_string_angle = angle_to_mid_deg + 90 kurudi steering_angle

x_offset katika kesi ya kwanza ni kiasi gani wastani ((kulia x2 + kushoto x2) / 2) hutofautiana kutoka katikati ya skrini. y_offset daima inachukuliwa kuwa urefu / 2. Picha ya mwisho hapo juu inaonyesha mfano wa mstari wa kichwa. angle_to_mid_radians ni sawa na "theta" iliyoonyeshwa kwenye picha ya mwisho hapo juu. Ikiwa steering_angle = 90, inamaanisha kuwa gari ina laini ya kichwa inayoendana sawa na "urefu / 2" na gari itasonga mbele bila usukani. Ikiwa usukani_mwendo> 90, gari inapaswa kuelekea kulia vinginevyo inapaswa kuelekeza kushoto. Ili kuonyesha mstari wa kichwa, kazi ifuatayo inatumiwa:

mstari wa kuonyesha_kichwa_ya kichwa (fremu, pembetatu ya usukani, line_color = (0, 0, 255), line_width = 5)

heading_image = np.zeros_like (fremu) urefu, upana, _ = fremu. (steering_angle_radian)) y2 = int (urefu / 2) cv2.line (heading_image, (x1, y1), (x2, y2), line_color, line_width) heading_image = cv2.addWeighted (fremu, 0.8, heading_image, 1, 1) kurudi heading_image

Kazi hapo juu inachukua fremu ambayo mstari wa kichwa utachorwa na pembe ya uendeshaji kama pembejeo. Inarudisha picha ya mstari wa kichwa. Sura ya mstari wa kichwa iliyochukuliwa katika kesi yangu imeonyeshwa kwenye picha hapo juu.

Kuchanganya Kanuni Zote Pamoja:

Nambari sasa iko tayari kukusanywa. Nambari ifuatayo inaonyesha kitanzi kuu cha programu inayoita kila kazi:

kuagiza cv2

kuagiza numpy kama np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) wakati ya Kweli: ret, frame = video.read () frame = cv2.flip (fremu, -1) #Kuita kazi hsv = kubadilisha_na_HSV (fremu) kingo = detect_edges (hsv) roi = mkoa_wa_kuvutia (kingo) line_segments = detect_line_segments (roi) lane_lines = average_slope_intercept (fremu, mstari_jambazi) lane_lines_image = mistari ya kuonyesha) = kupata_steering_angle (fremu, laini za mistari) heading_image = onyesha_line_lineline (lane_lines_image, steering_angle) ufunguo = cv2.waitKey (1) ikiwa ufunguo == 27: vunja video. tafadhali () cv2.destroyAllWindows ()

Hatua ya 6: Kutumia Udhibiti wa PD

Kutumia Udhibiti wa PD
Kutumia Udhibiti wa PD

Sasa tuna pembe yetu ya uendeshaji tayari kulishwa kwa motors. Kama ilivyoelezwa hapo awali, ikiwa pembe ya uendeshaji ni kubwa kuliko 90, gari inapaswa kugeukia kulia vinginevyo inapaswa kugeuka kushoto. Nilitumia nambari rahisi ambayo inageuza gari ya uendeshaji kulia ikiwa pembe iko juu ya 90 na kuigeuza kushoto ikiwa pembe ya uendeshaji iko chini ya 90 kwa kasi ya kugongana ya (10% PWM) lakini nilipata makosa mengi. Kosa kuu nililopata ni wakati gari inakaribia zamu yoyote, motor ya uendeshaji hufanya moja kwa moja lakini motors za kusonga hukwama. Nilijaribu kuongeza kasi ya kupinduka kuwa (20% PWM) kwa zamu lakini nikamalizia kwa roboti kutoka kwenye vichochoro. Nilihitaji kitu ambacho huongeza kasi ya kukoroma sana ikiwa pembe ya usukani ni kubwa sana na inaongeza mwendo kidogo ikiwa pembe ya uendeshaji sio kubwa basi inapunguza kasi kwa thamani ya kwanza wakati gari linakaribia digrii 90 (kusonga moja kwa moja). Suluhisho lilikuwa kutumia mtawala wa PD.

Mdhibiti wa PID anasimama kwa Mdhibiti sawia, Jumuishi na inayotokana. Aina hii ya vidhibiti vya mstari hutumiwa sana katika matumizi ya roboti. Picha hapo juu inaonyesha kitanzi cha kawaida cha kudhibiti maoni ya PID. Lengo la mtawala huyu ni kufikia "setpoint" na njia bora zaidi tofauti na vidhibiti vya "on-off" ambavyo huwasha au kuzima mmea kulingana na hali kadhaa. Maneno mengine muhimu yanapaswa kujulikana:

  • Seti: ni thamani unayotaka mfumo wako ufikie.
  • Thamani halisi: ni thamani halisi inayotambuliwa na sensa.
  • Kosa: ni tofauti kati ya setpoint na thamani halisi (error = Setpoint - Thamani halisi).
  • Tofauti inayodhibitiwa: kutoka kwa jina lake, ubadilishaji unaotaka kudhibiti.
  • Kp: Mara kwa mara sawia.
  • Ki: Jumuishi ya mara kwa mara.
  • Kd: Mara kwa mara inayotokana.

Kwa kifupi, kitanzi cha mfumo wa kudhibiti PID hufanya kazi kama ifuatavyo:

  • Mtumiaji anafafanua setpoint inayohitajika kwa mfumo kufikia.
  • Hitilafu imehesabiwa (makosa = setpoint - halisi).
  • Kidhibiti cha P hutengeneza hatua kulingana na thamani ya kosa. (kosa linaongezeka, hatua ya P pia huongezeka)
  • Mdhibiti nitaunganisha kosa kwa muda ambao huondoa makosa ya hali ya mfumo lakini huongeza kasi yake.
  • D mtawala tu ni derivative ya wakati wa kosa. Kwa maneno mengine, ni mteremko wa kosa. Inafanya hatua sawia na inayotokana na kosa. Mdhibiti huu huongeza utulivu wa mfumo.
  • Pato la mtawala litakuwa jumla ya watawala watatu. Pato la mtawala litakuwa 0 ikiwa kosa linakuwa 0.

Ufafanuzi mzuri wa mtawala wa PID unaweza kupatikana hapa.

Kurudi kwenye gari ya kuweka barabara, mabadiliko yangu yaliyodhibitiwa yalikuwa kasi ya kupiga kasi (kwani uendeshaji una majimbo mawili tu kulia au kushoto). Kidhibiti cha PD kinatumika kwa kusudi hili kwani kitendo cha D huongeza kasi ya kupindukia sana ikiwa mabadiliko ya kosa ni makubwa sana (yaani kupotoka kubwa) na hupunguza gari ikiwa mabadiliko haya yanakaribia 0. Nilifanya hatua zifuatazo kutekeleza PD mtawala:

  • Weka setpoint kwa digrii 90 (siku zote ninataka gari isonge moja kwa moja)
  • Imehesabu pembe ya kupotoka kutoka katikati
  • Kupotoka kunatoa habari mbili: Hitilafu ni kubwa kiasi gani (ukubwa wa kupotoka) na mwelekeo gani motor ya kuchukua inapaswa kuchukua (ishara ya kupotoka). Ikiwa kupotoka ni chanya, gari inapaswa kuelekeza kulia vinginevyo inapaswa kuelekeza kushoto.
  • Kwa kuwa kupotoka ni hasi au chanya, ubadilishaji wa "kosa" hufafanuliwa na kila wakati ni sawa na thamani kamili ya kupotoka.
  • Hitilafu huzidishwa na Kp ya mara kwa mara.
  • Hitilafu hupata utofautishaji wa wakati na huzidishwa na Kd mara kwa mara.
  • Kasi ya Motors inasasishwa na kitanzi huanza tena.

Nambari ifuatayo inatumiwa kwenye kitanzi kuu kudhibiti kasi ya gari zinazopinduka:

kasi = 10 # kasi ya kufanya kazi katika% PWM

#Vitu vinavyohitaji kusasishwa kila kitanzi lastTime = 0 lastError = 0 # PD constants Kp = 0.4 Kd = Kp * 0.65 Wakati Ni Kweli: sasa = time.time () # time variable variable dt = now - lastTime kupotoka = usukani_angle - 90 # sawa kwa angle_to_mid_deg variable error = abs (kupotoka) ikiwa kupotoka -5: # usielekeze ikiwa kuna kupotoka kwa kiwango cha digrii 10 = 0 kosa = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO Kuendesha chini.. simama () kupotoka kwa elif> 5: # steer kulia ikiwa kupotoka ni chanya GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) uendeshaji. Anza (100) kupotoka kwa < -5: # mwendo wa kushoto ikiwa kupotoka ni hasi GPIO. * kosa PD = int (kasi + derivative + sawia) spd = abs (PD) ikiwa spd> 25: spd = 25 throttle. kuanza (spd) mwishoError = kosa lastTime = time.time ()

Ikiwa kosa ni kubwa sana (kupotoka kutoka katikati ni juu), vitendo sawia na vinatokana ni juu na kusababisha kasi kubwa ya kupindana. Wakati hitilafu inakaribia 0 (kupotoka kutoka katikati ni chini), hatua inayotokana hufanya kinyume chake (mteremko ni hasi) na kasi ya kupindana hupungua ili kudumisha utulivu wa mfumo. Nambari kamili imeambatanishwa hapa chini.

Hatua ya 7: Matokeo

Video zilizo hapo juu zinaonyesha matokeo niliyoyapata. Inahitaji kutazama zaidi na marekebisho zaidi. Nilikuwa nikiunganisha pi ya raspberry kwenye skrini yangu ya kuonyesha LCD kwa sababu video iliyotiririka juu ya mtandao wangu ilikuwa na latency ya juu na ilikuwa ya kusumbua sana kufanya kazi nayo, ndiyo sababu kuna waya zilizounganishwa na pi ya raspberry kwenye video. Nilitumia bodi za povu kuteka wimbo.

Ninasubiri kusikia mapendekezo yako ili kufanikisha mradi huu! Kama ninavyotumaini kwamba mafundisho haya yalikuwa ya kutosha kukupa habari mpya.

Ilipendekeza: