Мазмуну:

Raspberry Pi жана OpenCVди колдонгон автономдуу тилке: 7 кадам (сүрөттөр менен)
Raspberry Pi жана OpenCVди колдонгон автономдуу тилке: 7 кадам (сүрөттөр менен)

Video: Raspberry Pi жана OpenCVди колдонгон автономдуу тилке: 7 кадам (сүрөттөр менен)

Video: Raspberry Pi жана OpenCVди колдонгон автономдуу тилке: 7 кадам (сүрөттөр менен)
Video: Холодные руки и ноги - стоит ли беспокоиться? 2024, Ноябрь
Anonim
Raspberry Pi жана OpenCVди колдонгон автономдуу тилке
Raspberry Pi жана OpenCVди колдонгон автономдуу тилке

Бул көрсөтмөлөрдө автономдуу тилкени сактоочу робот ишке ашырылат жана төмөнкү баскычтардан өтөт:

  • Бөлүктөрдү чогултуу
  • Программалык камсыздоону орнотуу
  • Аппараттык монтаж
  • Биринчи тест
  • OpenCV аркылуу тилке сызыктарын аныктоо жана жетектөөчү линияны көрсөтүү
  • PD контроллерин ишке ашыруу
  • Жыйынтыктар

1 -кадам: Компоненттерди чогултуу

Компоненттерди чогултуу
Компоненттерди чогултуу
Компоненттерди чогултуу
Компоненттерди чогултуу
Компоненттерди чогултуу
Компоненттерди чогултуу
Компоненттерди чогултуу
Компоненттерди чогултуу

Жогорудагы сүрөттөр бул долбоордо колдонулган бардык компоненттерди көрсөтөт:

  • RC унаа: Меники өлкөмдөгү жергиликтүү дүкөндөн алынды. Бул 3 мотор менен жабдылган (2 дроссель үчүн, 1 руль үчүн). Бул унаанын негизги кемчилиги - рулду "рулсуз" менен "толук рулду" ортосунда чектөө. Башкача айтканда, ал RC унааларынан айырмаланып, белгилүү бир бурчта башкара албайт. Бул жерден атайын малина пи үчүн иштелип чыккан окшош унаа топтомун таба аласыз.
  • Raspberry pi 3 model b+: бул машинанын мээси, ал көптөгөн иштетүү этаптарын башкарат. Бул 1,4 ГГц жыштыктагы төрт ядролук 64 биттик процессорго негизделген. Мен бул жерден өзүмдүкүн алдым.
  • Raspberry pi 5 MP камера модулу: 1080p @ 30 fps, 720p @ 60 fps жана 640x480p 60/90 жазууну колдойт. Ал ошондой эле түздөн -түз малина пи -ге туташтырыла турган сериялык интерфейсти колдойт. Бул сүрөт иштетүүчү тиркемелер үчүн эң жакшы вариант эмес, бирок бул долбоор үчүн жетиштүү, ошондой эле абдан арзан. Мен бул жерден өзүмдүкүн алдым.
  • Мотор айдоочу: DC кыймылдаткычтарынын багыттарын жана ылдамдыгын көзөмөлдөө үчүн колдонулат. Бул 1 тактадагы 2 DC кыймылдаткычын башкарууну колдойт жана 1,5 Ага туруштук бере алат.
  • Power Bank (Милдеттүү эмес): Мен малина пиин өзүнчө иштетүү үчүн кубат банкын (5V, 3A деп бааланган) колдондум. 1 -булактан малина пи күйгүзүү үчүн бир кадам ылдый которгучту (бак конвертери: 3А чыгаруу агымы) колдонуу керек.
  • 3s (12 V) LiPo батарейкасы: Литий -полимердик батареялар робототехника тармагында эң сонун көрсөткүчтөрү менен белгилүү. Бул мотор айдоочусун иштетүү үчүн колдонулат. Мен бул жерден өзүмдү сатып алдым.
  • Эркектен эркекке жана ургаачыдан секирүүчү зымдар.
  • Эки тараптуу лента: компоненттерди RC машинасына орнотуу үчүн колдонулат.
  • Көк тасма: Бул бул долбоордун абдан маанилүү компоненти, ал машина ортосунда айдай турган эки тилкени жасоо үчүн колдонулат. Сиз каалаган түстү тандай аласыз, бирок мен айланадагы чөйрөдөн башка түстөрдү тандап алууну сунуштайм.
  • Зип галстуктар жана жыгач куймалары.
  • Бурама айдоочу.

2 -кадам: Raspberry Piде OpenCV орнотуу жана Алыскы дисплейди орнотуу

Raspberry Piде OpenCV орнотуу жана Алыскы дисплейди орнотуу
Raspberry Piде OpenCV орнотуу жана Алыскы дисплейди орнотуу

Бул кадам бир аз тажатма жана бир аз убакытты талап кылат.

OpenCV (Open Source Computer Vision) - бул компьютердин ачык программалык камсыздоо китепканасы. Китепканада 2500дөн ашык оптималдаштырылган алгоритмдер бар. OpenCVди малина пиңизге орнотуу үчүн, ошондой эле малина пи ОСти орнотуу үчүн БУ абдан жөнөкөй көрсөтмөнү ээрчиңиз (эгер дагы эле жок болсо). OpenCV түзүү процесси жакшы муздаган бөлмөдө болжол менен 1,5 саатка созулушу мүмкүн экенин эске алыңыз (процессордун температурасы өтө жогору болот!) Андыктан чай ичип, чыдамдуулук менен күтүңүз: D.

Алыстан көрсөтүү үчүн, Windows/Mac түзмөгүңүздөн малина пиңизге алыстан кирүүнү орнотуу үчүн БУЛ көрсөтмөнү аткарыңыз.

3 -кадам: Бөлүктөрдү бирге туташтыруу

Бөлүктөрдү бириктирүү
Бөлүктөрдү бириктирүү
Бөлүктөрдү бириктирүү
Бөлүктөрдү бириктирүү
Бөлүктөрдү бириктирүү
Бөлүктөрдү бириктирүү

Жогорудагы сүрөттөр малина пи, камера модулу жана мотор драйверинин ортосундагы байланышты көрсөтөт. Көңүл буруңуз, мен колдонгон моторлор 0,35 А ар биринде 9 В сиңирет, бул мотор айдоочусунун бир эле учурда 3 моторду иштетүүсүн коопсуз кылат. Мен 2 дроссель кыймылдаткычынын ылдамдыгын (1 арткы жана 1 алдыңкы) так ошондой башкаргым келгендиктен, мен аларды ошол портко туташтырдым. Мен мотордун айдоочусун машинанын оң жагына кош лента менен орноттум. Камера модулуна келсек, жогорудагы сүрөттө көрүнүп тургандай, бурама тешиктердин ортосуна сыдырма галстук кыстардым. Андан кийин камераны жыгач тилкеге орнотуп алам, ошондо камеранын ордун каалагандай тууралай алам. Камераны мүмкүн болушунча машинанын ортосуна орнотууга аракет кылыңыз. Мен камераны жерден кеминде 20 см бийиктикте коюуну сунуштайм, андыктан машинанын алдындагы көрүү талаасы жакшырат. Fritzing схемасы төмөндө тиркелет.

4 -кадам: Биринчи тест

Биринчи тест
Биринчи тест
Биринчи тест
Биринчи тест

Камераны тестирлөө:

Камера орнотулуп, openCV китепканасы курулгандан кийин, биздин биринчи сүрөтүбүздү сынап көрүүгө убакыт келди! Биз pi camдан сүрөт тартып, аны "original.jpg" катары сактайбыз. Ал 2 жол менен жасалышы мүмкүн:

1. Терминалдын буйруктарын колдонуу:

Жаңы терминал терезесин ачып, төмөнкү буйрукту териңиз:

raspistill -o original.jpg

Бул кыймылсыз сүрөттү алып, аны "/pi/original.jpg" каталогуна сактайт.

2. Ар кандай python IDEди колдонуу (мен IDLE колдоном):

Жаңы эскиз ачып, төмөнкү кодду жазыңыз:

cv2 импорттоо

video = cv2. VideoCapture (0) True, ал эми: ret, frame = video.read () frame = cv2.flip (frame, -1) # сүрөттү вертикалдуу которуу үчүн колдонулат cv2.imshow ('оригиналдуу', кадр) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: video.release () cv2.destroyAllWindows ()

Бул коддо эмне болгонун карап көрөлү. Биринчи сап, анын бардык функцияларын колдонуу үчүн openCV китепканабызды импорттоп жатат. VideoCapture (0) функциясы бул функция тарабынан аныкталган булактан жандуу видеону тарта баштайт, бул учурда 0 - бул raspi камераны билдирет. Эгерде сизде бир нече камера болсо, анда башка номерлерди коюу керек. video.read () ар бир кадр камерадан келгенин окуп, аны "кадр" деп аталган өзгөрмөгө сактайт. flip () функциясы сүрөттү y огуна (вертикалдуу) бурат, анткени мен камерамды тескери орнотуп жатам. imshow () "түпнуска" сөзү башталган кадрларыбызды көрсөтөт жана imwrite () биздин сүрөттү оригинал-j.webp

Мен OpenCV функциялары менен таанышуу үчүн сүрөтүңүздү экинчи ыкма менен сынап көрүүнү сунуштайм. Сүрөт "/pi/original.jpg" каталогунда сакталат. Менин камерам тарткан оригиналдуу сүрөт жогоруда көрсөтүлгөн.

Сыноочу моторлор:

Бул кадам ар бир мотордун айлануу багытын аныктоо үчүн абдан маанилүү. Биринчиден, мотор айдоочунун иштөө принциби жөнүндө кыскача маалымат берели. Жогорудагы сүрөттө мотор айдоочусунун pin-out көрсөтүлгөн. А иштетүү, Киргизүү 1 жана Киргизүү 2 мотор А башкаруусу менен байланышкан. Иштетүү В, Киргизүү 3 жана Киргизүү 4 мотор В көзөмөлү менен байланышкан. Багытты башкаруу "Киргизүү" бөлүгү менен, ылдамдыкты башкаруу "Иштетүү" бөлүгү менен белгиленет. А моторунун багытын көзөмөлдөө үчүн, мисалы, Киргизүүнү 1ди ЖОКко коюңуз (бул учурда биз малина пи колдонуп жаткандыктан 3.3 В) жана Киргизүүнү 2 ТӨМӨН кылып коюңуз, мотор белгилүү бир багытта жана карама -каршы маанилерди коюу менен айланат Input 1 жана Input 2ге мотор карама -каршы багытта айланат. Эгерде Input 1 = Input 2 = (HIGH же LOW) болсо, мотор бурулбайт. Ишке киргизиңиз, малинадан импульстун туурасы модуляциясын (PWM) киргизүү сигналын алыңыз (0дөн 3.3 В чейин) жана ошого жараша моторлорду иштетиңиз. Мисалы, 100% PWM сигналы биз максималдуу ылдамдыкта иштеп жатканыбызды билдирет жана 0% PWM сигналы мотор айланбай турганын билдирет. Төмөнкү код моторлордун багыттарын аныктоо жана алардын ылдамдыгын текшерүү үчүн колдонулат.

импорттоо убактысы

импорттоо RPi. GPIO катары GPIO GPIO.setwarnings (Жалган) # Рулдук мотор казыктары steering_enable = 22 # Физикалык Pin 15 in1 = 17 # Physical Pin 11 in2 = 27 # Physical Pin 13 # Throttle Motors Pins throttle_enable = 25 # Physical Pin 22 in3 = 23 # Физикалык пин 16 in4 = 24 # Физикалык пин 18 GPIO.setmode (GPIO. BCM) # GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO ордуна GPIO номерин колдонуңуз. жөндөө (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (steering_enable, GPIO.out) # Рулду башкаруу Control GPIO.output (in1, GPIO. ЖОГОРУ) GPIO.output (in2, GPIO. LOW) руль = GPIO. PWM (steering_enable, 1000) # которуу жыштыгын 1000 Гц рульго коюу.стоп () # Throttle Motors Control GPIO.output (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) дроссель = GPIO. PWM (throttle_enable, 1000) # которуу жыштыгын 1000 Гц дроссельге коюу % PWM сигналы-> (0,25 * батареянын чыңалуусу) - айдоочунун рулду жоготуу.старт (100) # моторду 100% PWM сигналында баштайт -> (1 * Батарея чыңалуусу) - айдоочунун жоготуу убактысы.уктоо (3) дроссель.

Бул код дроссель моторлорун жана рулду башкаруучу моторду 3 секунд иштетет, анан аларды токтотот. (Айдоочунун жоготуусу) вольтметрдин жардамы менен аныкталышы мүмкүн. Мисалы, биз 100% PWM сигналы мотордун терминалында батарейканын толук чыңалуусун бериши керек экенин билебиз. Бирок, PWMди 100%га коюу менен, мен айдоочу 3 В төмөндөшүн жана мотор 12 В ордуна 9 В аларын аныктадым (так мага керек!). Жоготуу сызыктуу эмес, башкача айтканда 100% жоготуу 25% жоготуудан абдан айырмаланат. Жогорудагы кодду иштеткенден кийин менин жыйынтыктарым төмөнкүдөй болду:

Ыкчамдатуу жыйынтыктары: эгер in3 = HIGH жана in4 = LOW болсо, дроссель кыймылдаткычтары Clock-Wise (CW) айлануусуна ээ болот, б.а. машина алдыга жылат. Болбосо, машина артка жылат.

Рулду башкаруунун жыйынтыктары: эгер in1 = HIGH жана in2 = LOW болсо, рулдун мотору максималдуу солго бурулат, башкача айтканда машина солго бурулат. Болбосо, машина туура рулду башкарат. Кээ бир эксперименттерден кийин, мен PWM сигналы 100% болбогондо, рулду башкаруучу мотор бурулбай турганын байкадым (б.а. мотор толугу менен оңго же толугу менен солго башкарат).

5 -кадам: Линия сызыктарын аныктоо жана башкы линияны эсептөө

Линия сызыктарын аныктоо жана башкы линияны эсептөө
Линия сызыктарын аныктоо жана башкы линияны эсептөө
Линия сызыктарын аныктоо жана башкы линияны эсептөө
Линия сызыктарын аныктоо жана башкы линияны эсептөө
Линия сызыктарын аныктоо жана башкы линияны эсептөө
Линия сызыктарын аныктоо жана башкы линияны эсептөө

Бул кадамда машинанын кыймылын башкара турган алгоритм түшүндүрүлөт. Биринчи сүрөт бүт процессти көрсөтөт. Системанын кириши сүрөттөр, чыгаруу тета (руль бурчу градус). Белгилей кетсек, иштетүү 1 сүрөттө жүргүзүлөт жана бардык кадрларда кайталанат.

Камера:

Камера (320 x 240) чечилишинде видео жаза баштайт. Мен токтомду төмөндөтүүнү сунуштайм, ошондо кадр ылдамдыгын жакшырта аласыз (кадр ылдамдыгы), анткени ар бир кадрга иштетүү ыкмаларын колдонгондон кийин кадр ылдамдыгы төмөндөйт. Төмөндөгү код программанын негизги цикли болот жана бул коддун ар бир кадамын кошот.

cv2 импорттоо

numpy'ди np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) катары тууралаңыз Ырас: 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 ()

Бул жердеги код 4 -кадамда алынган баштапкы сүрөттү көрсөтөт жана жогорудагы сүрөттөрдө көрсөтүлгөн.

HSV түс мейкиндигине айландыруу:

Эми видео жазууну камерадан кадр катары алгандан кийин, кийинки кадам - ар бир кадрды Hue, Saturation жана Value (HSV) түс мейкиндигине айландыруу. Мунун негизги артыкчылыгы - түстөрдү жаркыроо деңгээли боюнча айырмалай билүү. Жана бул жерде HSV түс мейкиндигинин жакшы түшүндүрмөсү. HSVге айландыруу төмөнкү функция аркылуу ишке ашырылат:

def convert_to_HSV (кадр):

hsv = cv2.cvtColor (кадр, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) hsv кайтаруу

Бул функция негизги циклден чакырылат жана кадрды HSV түс мейкиндигине кайтарат. HSV түс мейкиндигинде мен алган кадр жогоруда көрсөтүлгөн.

Көк түсүн жана кырларын аныктоо:

Сүрөттү HSV түс мейкиндигине айландыргандан кийин, биз кызыккан түстү гана аныктоо убактысы келди (б.а. көк түс, анткени бул тилке сызыктарынын түсү). HSV алкагынан көк түстү алуу үчүн, түс, каныктыруу жана маанинин диапазону көрсөтүлүшү керек. HSV баалуулуктары жөнүндө жакшыраак түшүнүк алуу үчүн бул жерге кайрылыңыз. Кээ бир эксперименттерден кийин, көк түстүн жогорку жана төмөнкү чектери төмөнкү коддо көрсөтүлгөн. Жана ар бир кадрдагы жалпы бурмалоону азайтуу үчүн, четтери канней детекторунун жардамы менен гана аныкталат. Кенни кыры жөнүндө көбүрөөк бул жерден табууга болот. Эреже катары, Canny () функциясынын параметрлерин 1: 2 же 1: 3 катышы менен тандоо керек.

def detect_edges (кадр):

low_blue = np.array ([90, 120, 0], dtype = "uint8") # көк түстүн төмөнкү чеги жогорку_көк = np.array ([150, 255, 255], dtype = "uint8") # жогорку чеги көк түс маскасы = cv2.inRange (hsv, төмөнкү_көк, жогорку_көк) # бул маска бардыгын чыпкалайт, бирок көк # четтерин аныктайт = cv2. Канни (маска, 50, 100) cv2.imshow ("четтери", четтери)

Бул функция ошондой эле HSV түс мейкиндигинин параметрин алган жана кырдуу кадрды кайтаруучу негизги циклден чакырылат. Мен алган кырдуу рамка жогоруда табылган.

Кызыккан аймакты (ROI) тандаңыз:

Кызыккан аймакты тандоо кадрдын 1 регионуна гана басым жасоо үчүн өтө маанилүү. Бул учурда, мен машинанын чөйрөдө көп нерселерди көрүшүн каалабайм. Мен жөн гана машинанын тилкеге көңүл буруп, башка нерсеге көңүл бурбоосун каалайм. P. S: координаттар системасы (x жана y огу) жогорку сол бурчтан башталат. Башкача айтканда, чекит (0, 0) жогорку сол бурчтан башталат. у огу-бийиктик, ал эми х огу-туурасы. Төмөндөгү код кадрдын төмөнкү жарымына гана басым жасоо үчүн кызыккан аймакты тандайт.

def region_of_interest (четтери):

бийиктик, туурасы = жээктер.шапка # четтердин бийиктигин жана туурасын чыгарыңыз кадр маскасы = np.zeros_like (четтер) # четтердин алкагынын бирдей өлчөмдөрү менен бош матрица жасаңыз # экрандын ылдыйкы жарымына гана көңүл буруңуз # координаттарын көрсөтүңүз 4 упай (төмөнкү сол, жогорку сол, жогорку оң, төмөнкү оң) полигон = np.array (

Бул функция параметр катары кырдуу алкакты алат жана 4 алдын ала коюлган чекит менен көп бурчтук тартат. Бул көп бурчтуктун ичиндеги нерселерге гана көңүл бурат жана анын сыртындагы нерселерге көңүл бурбайт. Менин кызыгуу алкагым жогоруда көрсөтүлгөн.

Сызык сегменттерин аныктоо:

Hough трансформациясы кырдуу алкактан сызык сегменттерин аныктоо үчүн колдонулат. Hough трансформациясы - бул кандайдыр бир форманы математикалык түрдө аныктоо ыкмасы. Ал добуштардын санына жараша бурмаланган болсо дагы, дээрлик бардык объектилерди аныктай алат. бул жерде Хоу трансформациясы үчүн чоң шилтеме көрсөтүлгөн. Бул колдонмо үчүн cv2. HoughLinesP () функциясы ар бир кадрдагы сызыктарды аныктоо үчүн колдонулат. Бул функциянын маанилүү параметрлери:

cv2. HoughLinesP (кадр, rho, theta, min_threshold, minLineLength, maxLineGap)

  • Frame: биз сызыктарды аныктоону каалаган кадр.
  • rho: Бул аралыктын тактыгы пиксель (көбүнчө = 1)
  • тета: радиандык бурчтук тактык (дайыма = np.pi/180 ~ 1 градус)
  • min_threshold: минималдуу добуш, аны сап катары кароо үчүн алышы керек
  • minLineLength: пикселдик саптын минималдуу узундугу. Бул сандан кыска болгон сызык сызык катары эсептелбейт.
  • maxLineGap: 2 сызыктын ортосундагы пикселдеги максималдуу боштук 1 сап катары каралат. (Бул менин учурда колдонулбайт, анткени мен колдонгон тилкелерде боштук жок).

Бул функция саптын акыркы чекиттерин кайтарат. Төмөнкү функция менин негизги циклымдан Hough трансформациясын колдонуп линияларды аныктоо үчүн чакырылган:

def detect_line_segments (кесилген_чеги):

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

Орточо эңкейиш жана кесилиш (м, б):

сызыктын теңдемеси y = mx + b менен берилгенин эстеңиз. Бул жерде m-сызыктын эңкейиши, b-y-кесилиш. Бул бөлүктө Hough трансформациясы аркылуу аныкталган линия сегменттеринин эңкейиштеринин жана кесилиштеринин орточо эсеби эсептелет. Муну жасоодон мурун, жогоруда көрсөтүлгөн баштапкы кадр фотосун карап көрөлү. Сол тилке өйдө бараткандай сезилет, андыктан анын терс жанташы бар (координаттар системасынын башталыш чекитин эстейсизби?). Башка сөз менен айтканда, сол тилке сызыгы x1 <x2 жана y2 x1 жана y2> y1ге ээ, бул оң жантайышты берет. Ошентип, позитивдүү эңкейишке ээ болгон бардык сызыктар оң тилкенин чекиттери болуп эсептелет. Тик сызыктар болгон учурда (x1 = x2), жантаюу чексиз болот. Бул учурда, биз ката кетирбөө үчүн бардык вертикалдуу сызыктарды өткөрүп жиберебиз. Бул аныктоого көбүрөөк тактык кошуу үчүн, ар бир кадр 2 чек ара сызыгы аркылуу эки регионго (оң жана сол) бөлүнөт. Оң чек ара сызыгынан чоңураак болгон бардык тууралык чекиттер (х огунун чекиттери) оң тилкени эсептөө менен байланышкан. Ал эми бардык тууралык чекиттер сол чек ара сызыгынан аз болсо, алар сол тилкени эсептөө менен байланышкан. Төмөнкү функция кадрды кайра иштетүүдө жана Hough трансформациясы аркылуу аныкталган тилкенин сегменттерин алат жана эки тилкенин орточо жантаюусун жана кесилишин кайтарат.

def average_slope_intercept (кадр, сызык_сегменттери):

lane_lines = эгерде line_segments None болсо: басып чыгаруу ("эч кандай сегмент сегмент аныкталган жок") кайтуу lane_lines бийиктиги, туурасы, _ = frame.shape left_fit = right_fit = чек = left_region_boundary = туурасы * (1 - чек) right_region_boundary = туурасы * чек_чегиндеги линия_сегментинин чеги: x1, y1, x2, y2 үчүн line_se segment: эгер x1 == x2: басып чыгаруу ("вертикалдуу сызыктарды аттап өтүү (эңкейиш = чексиздик")) fit = np.polyfit ((x1, x2), (y1, y2), 1) жантайыш = (y2 - y1) / (x2 - x1) кесилиш = y1 - (жантайыш * x1) эгерде жантайың <0: эгерде x1 <left_region_boundary жана x2 right_region_boundary жана x2> right_region_boundary: right_fit. кошуу ((эңиш, кесүү)) left_fit_average = np.average (left_fit, огу = 0) if len (left_fit)> 0: lane_lines.append (make_points (frame, left_fit_average)) right_fit_average = np. орточо (оңго_фит, огу = 0)) эгер len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines-бул оң жана сол тилке линияларынын координаттарынан турган 2-D массиви # мисалы: lan e_lines =

make_points () - бул орточо_слоп_интерсепт () функциясы үчүн жардамчы функция, ал тилкелердин чектелген координаттарын кайтарат (кадрдын ылдыйынан ортосуна чейин).

def make_points (кадр, сызык):

бийиктик, туурасы, _ = кадр.шакалдын жантайышы, кесилиш = сызык y1 = бийиктик # алкактын түбү y2 = int (y1 / 2) # эгер жантайыңкы болсо, рамканын ортосунан ылдый чекит коюңуз == 0: жантайыңкы = 0.1 x1 = int ((y1 - кесилиш) / эңиш) x2 = int ((y2 - кесилиш) / эңиш) кайтуу

0го бөлүнбөө үчүн шарт берилет. Эгерде y1 = y2 (горизонталдык сызык) дегенди билдирген жантайыңкы 0 болсо, жантайыңкыга 0го жакын маанини бериңиз. Бул алгоритмдин иштөөсүнө таасир этпейт, ошондой эле мүмкүн болбогон иштин алдын алат (0гө бөлүү).

Жээкчелердеги тилкелерди көрсөтүү үчүн төмөнкү функция колдонулат:

def display_lines (кадр, сызыктар, line_color = (0, 255, 0), line_width = 6): # сап түсү (B, G, R)

line_image = np.zeros_like (кадр) эгер саптар Эч болбосо: саптардагы саптар үчүн: x1, y1, x2, y2 үчүн: cv2.line (line_image, (x1, y1), (x2, y2), line_color, line_width) line_image = cv2.addWeighted (frame, 0.8, line_image, 1, 1) return line_image

cv2.addWeighted () функциясы төмөнкү параметрлерди алат жана ал эки сүрөттү бириктирүү үчүн колдонулат, бирок ар бирине салмагын берүү менен.

cv2.addWeighted (image1, альфа, image2, бета, гамма)

Жана төмөнкү теңдемени колдонуу менен чыгуучу сүрөттү эсептейт:

чыгаруу = alpha * image1 + beta * image2 + гамма

Cv2.addWeighted () функциясы жөнүндө көбүрөөк маалымат бул жерден алынган.

Баш линиясын эсептөө жана көрсөтүү:

Бул моторлорго ылдамдыкты колдонуунун алдындагы акыркы кадам. Багыттоочу линия рулду башкаруучу моторго анын айлануу багытын берүүгө жана дроссель кыймылдаткычтарына иштөө ылдамдыгын берүүгө милдеттүү. Баштоо линиясы таза тригонометрия, тан жана атан (tan^-1) тригонометриялык функциялар колдонулат. Кээ бир экстремалдык учурлар камера бир гана тилкени аныктаганда же эч кандай сызыкты аныктабаганда болот. Бул учурлардын баары төмөнкү функцияда көрсөтүлгөн:

def get_steering_angle (фрейм, тилкенин_саптары):

бийиктик, туурасы, _ = frame.shape if len (lane_lines) == 2: # эгер эки тилке сызыгы аныкталса _, _, left_x2, _ = lane_lines [0] [0] # x2 lane_lines массивинен калтырылган _, _, right_x2, _ = lane_lines [1] [0] # lane_lines массивинен x2 чыгаруу оң x2 mid = int (туурасы / 2) x_offset = (left_x2 + right_x2) / 2 - орто y_offset = int (бийиктик / 2) elif len (lane_lines)) == 1: # эгерде бир гана сызык аныкталса x1, _, x2, _ = lane_lines [0] [0] x_offset = x2 - x1 y_offset = int (бийиктик / 2) elif len (lane_lines) == 0: # эгер эч кандай сызык аныкталбаса x_offset = 0 y_offset = int (бийиктик / 2) angle_to_mid_radian = math.atan (x_offset / y_offset) angle_to_mid_deg = int (angle_to_mid_radian * 180.0 / math.pi) steering_angle = angle_to_mid_deg + 90 return steering_angle

Биринчи учурда x_offset - экрандын ортосунан орточо ((оң x2 + сол x2) / 2) канчалык айырмаланат. y_offset дайыма бийиктик катары кабыл алынат / 2. Жогорудагы акыркы сүрөттө аталыш линиясынын мисалы көрсөтүлгөн. angle_to_mid_radians жогорудагы акыркы сүрөттө көрсөтүлгөн "тета" менен бирдей. Эгерде steering_angle = 90 болсо, анда бул машинанын "бийиктиги / 2" сызыгына перпендикулярдуу багыт сызыгы бар экенин билдирет жана унаа рулду башкарбай алдыга жылат. Эгерде steering_angle> 90 болсо, унаа оңго бурулушу керек, болбосо солго бурулушу керек. Башкы сапты көрсөтүү үчүн төмөнкү функция колдонулат:

def display_heading_line (фрейм, руль_бурчагы, сызык_сүсү = (0, 0, 255), сызыктын туурасы = 5)

heading_image = np.zeros_like (кадр) бийиктиги, туурасы, _ = frame.shape steering_angle_radian = руль_бурчу / 180.0 * math.pi x1 = int (туурасы / 2) y1 = бийиктик x2 = int (x1 - бийиктик / 2 / math.tan (steering_angle_radian)) y2 = int (бийиктик / 2) cv2.line (heading_image, (x1, y1), (x2, y2), line_color, line_width) heading_image = cv2.addWeighted (кадр, 0.8, heading_image, 1, 1) heading_image кайтаруу

Жогорудагы функция кирүү катары башкы сызык тартыла турган рулду жана рулду бурууну алат. Бул баш сызыктын сүрөтүн кайтарат. Менин учурда алынган баш линия алкагы жогорудагы сүрөттө көрсөтүлгөн.

Бардык коддорду бириктирүү:

Код азыр чогултууга даяр. Төмөнкү код ар бир функцияны чакырган программанын негизги циклин көрсөтөт:

cv2 импорттоо

numpy'ди np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) катары video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) катары импорттоо True: ret, frame = video.read () frame = cv2.flip (кадр, -1) #Функцияларды чакыруу hsv = convert_to_HSV (кадр) четтери = detect_edges (hsv) roi = region_of_interest (кырлар) line_segments = detect_line_segments (roi) lane_lines = орточо_киришүү (frame, line_segments) lane_lines_image = display_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 ()

6 -кадам: PD Control колдонуу

PD Control колдонулууда
PD Control колдонулууда

Эми биз рулду башкара турган бурчту моторлорго берүүгө даярбыз. Жогоруда айтылгандай, руль бурчу 90дон жогору болсо, унаа оңго бурулушу керек, болбосо солго бурулушу керек. Мен бурчу 90дон жогору болсо, рулду кыймылдаткычты оңго бурган жөнөкөй кодду колдондум, ал эми руль бурулушу 90% дан аз болсо (10% PWM), бирок мен көп ката кетирдим. Мен алган негизги ката - унаа каалаган бурулушка жакындаганда, рулду түз эле иштетет, бирок дроссель моторлору тыгылып калат. Мен дүүлүктүрүү ылдамдыгын (20% PWM) көбөйтүүгө аракет кылдым, бирок роботтун тилкеден чыгып кетиши менен аяктады. Мага рулду буруу абдан чоң болсо, дроссель ылдамдыгын бир топ жогорулатуучу бир нерсе керек болчу, эгер руль бурчу анча чоң болбосо, ылдамдыкты бир аз жогорулатат, анда машина 90 градуска жакындаганда ылдамдыкты баштапкы мааниге чейин төмөндөтөт (түз жылып). Чечим PD контроллерин колдонуу болчу.

PID контроллери пропорционалдуу, интегралдык жана туунду контроллерди билдирет. Сызыктуу контроллерлердин бул түрү робот техникасында кеңири колдонулат. Жогорудагы сүрөт типтүү PID кайтарым байланыш контролун көрсөтөт. Бул контроллердин максаты - заводду кээ бир шарттарга ылайык күйгүзүүчү же өчүрүүчү "күйгүзүү" контроллерлеринен айырмаланып эң эффективдүү жол менен "белгиленген чекке" жетүү. Кээ бир ачкыч сөздөр белгилүү болушу керек:

  • Орнотуу: бул тутумуңузга жетүүнү каалаган каалаган мааниси.
  • Чыныгы маани: бул сенсор тарабынан сезилген чыныгы баалуулук.
  • Ката: белгиленген чек менен чыныгы баалуулуктун ортосундагы айырма (ката = Орнотуу чындыгы - Чыныгы баалуулук).
  • Башкарылуучу өзгөрмө: анын аталышынан, сиз башкаргыңыз келген өзгөрмө.
  • Kp: Пропорционалдык туруктуу.
  • Ки: Интегралдык туруктуу.
  • Kd: туунду туруктуу.

Кыскача айтканда, PID башкаруу тутуму төмөнкүдөй иштейт:

  • Колдонуучу системага жетүү үчүн керектүү чекитти аныктайт.
  • Ката эсептелет (ката = белгиленген чек - чыныгы).
  • P контролери катанын маанисине пропорционалдуу аракетти жаратат. (ката көбөйөт, Р аракети да жогорулайт)
  • Мен контролер убакыттын өтүшү менен катаны бириктирип, системанын туруктуу абалындагы катаны жок кылат, бирок анын чегин жогорулатат.
  • D контролери жөн гана ката үчүн убакыт туундусу. Башкача айтканда, бул катанын эңкейиши. Бул катанын туундусуна пропорционалдуу аракет кылат. Бул контролер системанын туруктуулугун жогорулатат.
  • Контроллердин өндүрүшү үч контролердун суммасы болот. Эгерде ката 0 болуп калса, контроллердин өндүрүшү 0 болуп калат.

PID контроллеринин чоң түшүндүрмөсүн бул жерден тапса болот.

Машинага кайтып келе жатып, менин башкарылуучу өзгөргүчүм ылдамдыкты кыскартты (анткени рулду башкаруунун оң же сол гана эки абалы бар). ПД контроллери ушул максатта колдонулат, анткени D аракети дроссель ылдамдыгын бир топ жогорулатат, эгер ката өтө чоң болсо (б.а. чоң четтөө) жана бул ката 0гө жакындаса машинаны жайлатат. Мен PDны ишке ашыруу үчүн төмөнкү кадамдарды жасадым. контролер:

  • Орнотуу чекитин 90 градуска коюңуз (мен ар дайым машинанын түз жылышын каалайм)
  • Ортодон четтөө бурчу эсептелди
  • Четтөө эки маалыматты берет: Канчалык чоң ката (четтөө чоңдугу) жана рулду кайсы багытка бурушу керек (четтөө белгиси). Эгерде четтөө оң болсо, унаа оңго бурулушу керек, болбосо солго бурулушу керек.
  • Четтөө терс же оң болгондуктан, "ката" өзгөрмөсү аныкталат жана ар дайым четтөөнүн абсолюттук маанисине барабар.
  • Ката туруктуу Kp менен көбөйтүлөт.
  • Ката убакыт дифференциациясынан өтөт жана туруктуу Kd менен көбөйтүлөт.
  • Моторлордун ылдамдыгы жаңыртылып, цикл кайра башталат.

Тыгыздоочу моторлордун ылдамдыгын көзөмөлдөө үчүн негизги циклде төмөнкү код колдонулат:

ылдамдык = 10 # иштөө ылдамдыгы % PWMде

# Өзгөрмөлөр ар бир циклде жаңыртылышы керек lastTime = 0 lastError = 0 # PD константалары Kp = 0.4 Kd = Kp * 0.65 Чынында: азыр = time.time () # учурдагы убакыт өзгөрмөсү dt = азыр - lastTime четтөөсү = steering_angle - 90 # эквиваленти to angle_to_mid_deg өзгөрмө катасы = abs (четтөө) эгерде четтөө -5: # 10 -даражадагы ката диапазону бар болсо, # башкарбаңыз = 0 ката = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO). LOW) руль.стоп () elif четтөөсү> 5: эгер четтөө оң болсо GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) руль.старт (100) elif четтөөсү < -5: # четтөө четтөө терс GPIO.output (in1, GPIO. HIGH) GPIO.output (in2, GPIO. LOW) steering.start (100) туунду = kd * (ката - lastError) / дт пропорционалдуу = кп * ката PD = int (ылдамдык + туунду + пропорционалдуу) spd = abs (PD) эгерде spd> 25: spd = 25 throttle.start (spd) lastError = ката lastTime = time.time ()

Эгерде ката өтө чоң болсо (ортодон четтөө жогору), пропорционалдуу жана туунду аракеттер жогорку ылдамдыкка алып келет. Ката 0гө жакындаганда (ортодон четтөө аз), туунду аракет тескери иштейт (эңиш терс) жана системанын туруктуулугун сактоо үчүн дроссель ылдамдыгы төмөндөйт. Толук код төмөндө тиркелет.

7 -кадам: Жыйынтыктар

Жогорудагы видеолор мен алган жыйынтыктарды көрсөтөт. Бул көбүрөөк жөндөөнү жана андан ары тууралоону талап кылат. Мен малина пиин LCD дисплей экранына туташтырып жаттым, анткени менин тармагымдагы видео агымы өтө кечигип калды жана иштөө мени абдан капалантты, ошол себептен видеодо малина пи менен байланышкан зымдар бар. Мен тректи тартуу үчүн көбүк тактайларды колдондум.

Мен бул долбоорду жакшыртуу үчүн сиздин сунуштарыңызды угууну күтөм! Мен бул көрсөтмөлөр сизге жаңы маалыматтарды берүү үчүн жакшы болду деп үмүттөнөм.

Сунушталууда: