Мазмұны:

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

Бейне: Автокөлік Raspberry Pi мен OpenCV көмегімен автокөлікпен жүреді: 7 қадам (суреттермен)

Бейне: Автокөлік Raspberry Pi мен OpenCV көмегімен автокөлікпен жүреді: 7 қадам (суреттермен)
Бейне: MJC Stream: Видишь енота? А он есть! Главное об ML и компьютерном зрении 2024, Қараша
Anonim
Raspberry Pi мен OpenCV көмегімен автокөлік жолын сақтайтын автономды автокөлік
Raspberry Pi мен OpenCV көмегімен автокөлік жолын сақтайтын автономды автокөлік

Бұл нұсқаулықта автономды жолақты сақтайтын робот іске қосылады және келесі кезеңдерден өтеді:

  • Бөлшектерді жинау
  • Бағдарламалық қамтамасыз етудің алғышарттарын орнату
  • Аппараттық қондырғы
  • Бірінші тест
  • OpenCV көмегімен жолақтарды анықтау және бағыттаушы сызықты көрсету
  • PD контроллерін енгізу
  • Нәтижелер

1 -қадам: компоненттерді жинау

Компоненттерді жинау
Компоненттерді жинау
Компоненттерді жинау
Компоненттерді жинау
Компоненттерді жинау
Компоненттерді жинау
Компоненттерді жинау
Компоненттерді жинау

Жоғарыдағы суреттер осы жобада қолданылатын барлық компоненттерді көрсетеді:

  • RC автокөлігі: Менікін өз елімдегі жергілікті дүкеннен алдым. Ол 3 қозғалтқышпен жабдықталған (2 дроссельге және 1 рульге арналған). Бұл көліктің басты кемшілігі - рульдік басқару «рульсіз» мен «толық рульдік» арасында шектелген. Басқаша айтқанда, ол RC автокөліктерінен айырмашылығы, белгілі бір бұрышта басқара алмайды. Сіз осы жерден таңқурай пи үшін арнайы жасалған ұқсас автокөлік жинағын таба аласыз.
  • Raspberry pi 3 моделі b+: бұл машинаның миы, ол көптеген өңдеу кезеңдерін басқарады. Ол 1,4 ГГц жиіліктегі төрт ядролы 64 биттік процессорға негізделген. Мен бұл жерден өзімді алдым.
  • Raspberry pi 5 мегапиксельді камера модулі: 1080p @ 30 fps, 720p @ 60 fps және 640x480p 60/90 жазуды қолдайды. Ол сондай -ақ тікелей таңқурайға қосылатын сериялық интерфейсті қолдайды. Бұл суретті өңдеуге арналған ең жақсы нұсқа емес, бірақ бұл жоба үшін жеткілікті, сонымен қатар өте арзан. Мен бұл жерден өзімді алдым.
  • Мотор драйвері: тұрақты ток қозғалтқыштарының бағыттары мен жылдамдықтарын бақылау үшін қолданылады. Ол 1 тақтадағы 2 тұрақты ток қозғалтқышын басқаруды қолдайды және 1,5 А -ға төтеп бере алады.
  • Қуат банкі (міндетті емес): Мен таңқурай пиін бөлек қуаттандыру үшін қуат банкін (5В, 3А бағаланған) қолдандым. Таңқурай пиін 1 көзден қосу үшін төмен түсетін түрлендіргішті (бак түрлендіргіші: 3А шығыс тогы) қолдану керек.
  • 3s (12 В) LiPo аккумуляторы: Литий полимерлі аккумуляторлар робототехника саласындағы тамаша көрсеткіштерімен танымал. Ол драйверді қуаттандыру үшін қолданылады. Мен өз жерімді осы жерден сатып алдым.
  • Еркектен еркекке және әйелден секіруге арналған сымдар.
  • Екі жақты таспа: компоненттерді RC автокөлігіне бекіту үшін қолданылады.
  • Көк таспа: Бұл бұл жобаның өте маңызды құрамдас бөлігі, ол автомобиль жүретін екі жолақты сызықты жасау үшін қолданылады. Сіз қалаған түсті таңдай аласыз, бірақ мен қоршаған ортаға қарағанда басқа түстерді таңдауға кеңес беремін.
  • Зығыр байланыстар мен ағаш штангалар.
  • Бұрауыш.

2 -қадам: Raspberry Pi -ге OpenCV орнату және қашықтан дисплейді орнату

Raspberry Pi -ге OpenCV орнату және қашықтан дисплейді орнату
Raspberry Pi -ге OpenCV орнату және қашықтан дисплейді орнату

Бұл қадам аздап тітіркендіреді және біраз уақытты алады.

OpenCV (Open Source Computer Vision) - бұл компьютерді көру мен машиналық оқыту бағдарламалық қамтамасыз етуінің ашық көзі. Кітапханада 2500 -ден астам оңтайландырылған алгоритмдер бар. OpenCV -ді таңқурай пи -ге орнату үшін, сондай -ақ таңқурай pi операциялық жүйесін орнату үшін мына қарапайым нұсқаулықты орындаңыз (егер сіз әлі орнатпаған болсаңыз). OpenCV құру процесі жақсы салқындатылған бөлмеде шамамен 1,5 сағатты алатынын ескеріңіз (процессордың температурасы өте жоғары болады!), Сондықтан шай ішіп, шыдамдылықпен күтіңіз: D.

Қашықтан дисплей үшін Windows/Mac құрылғысынан таңқурай пи -ге қашықтан кіруді орнату үшін осы нұсқаулықты орындаңыз.

3 -қадам: бөлшектерді бір -біріне қосу

Бөлшектерді біріктіру
Бөлшектерді біріктіру
Бөлшектерді біріктіру
Бөлшектерді біріктіру
Бөлшектерді біріктіру
Бөлшектерді біріктіру

Жоғарыдағы суреттер raspberry pi, камера модулі мен мотор драйверінің арасындағы байланысты көрсетеді. Назар аударыңыз, мен қолданған қозғалтқыштардың әрқайсысы 9 В 0,35 А сіңіреді, бұл мотор жүргізушісінің бір мезгілде 3 қозғалтқышты басқаруына қауіпсіз етеді. Мен екі дроссельді қозғалтқыштың жылдамдығын (1 артқы және 1 алдыңғы) дәл солай басқарғым келетіндіктен, мен оларды сол портқа жалғадым. Мен мотор драйверін қос таспамен көліктің оң жағына орнаттым. Камера модуліне келетін болсақ, жоғарыдағы суретте көрсетілгендей, бұрандалы саңылаулардың арасына ілмек салдым. Содан кейін мен камераны орманға орнатамын, осылайша камераның орнын қалағанымша реттей аламын. Камераны мүмкіндігінше көліктің ортасына орнатуға тырысыңыз. Мен камераны жерден 20 см биіктікте орналастыруды ұсынамын, осылайша көліктің алдындағы көру өрісі жақсарады. Фритзинг схемасы төменде берілген.

4 -қадам: Бірінші тест

Бірінші тест
Бірінші тест
Бірінші тест
Бірінші тест

Камералық тестілеу:

Камера орнатылып, OpenCV кітапханасы салынғаннан кейін, біздің алғашқы суретті тексеретін уақыт келді! Біз pi cam -дан суретке түсіріп, оны «original.jpg» ретінде сақтаймыз. Оны 2 тәсілмен жасауға болады:

1. Терминал пәрмендерін қолдану:

Жаңа терминал терезесін ашып, келесі пәрменді теріңіз:

raspistill -түпнұсқа.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: break video.release () cv2.destroyAllWindows ()

Бұл кодта не болғанын көрейік. Бірінші жол - бұл барлық функцияларды пайдалану үшін біздің OpenCV кітапханасын импорттау. VideoCapture (0) функциясы осы функциямен анықталатын көзден тікелей бейнені ағынмен бастайды, бұл жағдайда 0 - бұл raspi камераны білдіреді. егер сізде бірнеше камера болса, әр түрлі нөмірлерді қою керек. video.read () әрбір кадр камерадан келетінін оқиды және оны «кадр» деп аталатын айнымалыға сақтайды. flip () функциясы кескінді y осіне (вертикаль) айналдырады, себебі мен камерамды кері қарай орнатамын. imshow () «түпнұсқа» сөзімен басталатын кадрларымызды көрсетеді және imwrite () біздің фотоны original-j.webp

Мен OpenCV функциясымен танысу үшін фотосуретті екінші әдіспен тексеруді ұсынамын. Сурет «/pi/original.jpg» каталогында сақталады. Менің камерам түсірген түпнұсқа фотосурет жоғарыда көрсетілген.

Сынақ қозғалтқыштары:

Бұл қадам әр қозғалтқыштың айналу бағытын анықтау үшін қажет. Алдымен мотор жүргізушінің жұмыс принципі туралы қысқаша мәлімет берейік. Жоғарыдағы суретте мотор драйверінің бекітілгені көрсетілген. A қосу, кіріс 1 және кіріс 2 қозғалтқыш А басқаруымен байланысты. В қосу, кіріс 3 және кіріс 4 қозғалтқыш В басқаруымен байланысты. Бағытты басқару «Кіріс» бөлігімен, ал жылдамдықты басқару «Қосу» бөлігімен орнатылады. А қозғалтқышының бағытын басқару үшін, мысалы, 1 -ші кірісті ЖОҒАРЫ етіп орнатыңыз (бұл жағдайда таңқурай пи қолданатындықтан, бұл жағдайда 3,3 В) және 2 -ші кірісті LOW күйіне қойыңыз, қозғалтқыш белгілі бір бағытта және қарама -қарсы мәндерді орнату арқылы айналады. 1 -ші кіріс пен 2 -ші кіріске қозғалтқыш қарама -қарсы бағытта айналады. Егер кіріс 1 = кіріс 2 = (жоғары немесе төмен) болса, қозғалтқыш бұрылмайды. Іске қосу таңқурайдан импульстік ені модуляциясының кіріс сигналын қабылдайды (0 -ден 3.3 В дейін) және қозғалтқыштарды сәйкесінше іске қосады. Мысалы, 100% PWM сигналы біз максималды жылдамдықта жұмыс жасайтынымызды білдіреді, ал 0% PWM сигналы қозғалтқыштың айналмайтынын білдіреді. Келесі код қозғалтқыштардың бағыттарын анықтау және олардың жылдамдығын тексеру үшін қолданылады.

импорт уақыты

GPO GPIO.setwarnings ретінде жалған RPi. GPIO импорттау (жалған) # Рульдік қозғалтқыш түйреуіштері рульдік басқарылатын = 22 # Физикалық түйреуіш 15 in1 = 17 # Физикалық түйреуіш 11 in2 = 27 # Физикалық түйреуіш 13 # Дроссельді қозғалтқыштардың түйреуіштері дроссель_және = 25 # Физикалық түйреуіш 22 д3 = 23 # Физикалық түйреуіш 16 in4 = 24 # Физикалық PIN 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 (дроссельді -қосуға болады, GPIO.out) GPIO.setup (рульге қосуға болады, GPIO.out) # Рульдік қозғалтқышты басқару GPIO. шығысы (in1, GPIO. ЖОҒАРЫ) GPIO.output (in2, GPIO. LOW) руль = GPIO. PWM (рульге қосылу мүмкіндігі, 1000) # коммутация жиілігін 1000 Гц рульге қойыңыз. Тоқтату () # Дроссельді қозғалтқыштар GPIO. шығысы (in3, GPIO. HIGH) GPIO.шығу (in4, GPIO. LOW) дроссель = GPIO. PWM (дроссель_мүмкін, 1000) # коммутация жиілігін 1000 Гц дроссельге қойыңыз. тоқтау () уақыт.ұйықтау (1) дроссель.старт (25) # моторды 25 -те бастайды % PWM сигналы -> (0,25 * батарея кернеуі) - жүргізушіге арналған руль жоғалту

Бұл код дроссельді қозғалтқыштар мен рульдік қозғалтқышты 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 импорттау

np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) ретінде numpy импорттау Рас: 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 түс кеңістігіне түрлендіру:

Енді бейнежазбаны камерадан кадр ретінде алғаннан кейін, келесі қадам - әр кадрды реңк, қанықтылық және мән (HSV) түс кеңістігіне түрлендіру. Мұның басты артықшылығы - түстерді олардың жарықтық деңгейі бойынша ажырата білу. Міне, HSV түс кеңістігін жақсы түсіндіру. HSV түрлендіру келесі функция арқылы жүзеге асырылады:

def convert_to_HSV (кадр):

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

Бұл функция негізгі циклден шақырылады және кадрды HSV түс кеңістігінде қайтарады. Мен HSV түс кеңістігінде алған кадр жоғарыда көрсетілген.

Көк түс пен жиектерді анықтау:

Кескінді HSV түс кеңістігіне түрлендіргеннен кейін, бізді қызықтыратын түсті ғана анықтайтын уақыт келді (яғни көк түс - бұл жолақтардың түсі). HSV кадрынан көк түс алу үшін реңк, қанықтылық және мән диапазоны көрсетілуі керек. HSV мәндері туралы жақсы түсінік алу үшін осында қараңыз. Кейбір эксперименттерден кейін көк түстің жоғарғы және төменгі шекаралары төмендегі кодта көрсетілген. Әр жақтаудағы жалпы бұрмалануды азайту үшін жиектер тек қана шетінен детектордың көмегімен анықталады. Canny Edge туралы толығырақ мына жерден табуға болады. Негізгі ереже - Canny () функциясының параметрлерін 1: 2 немесе 1: 3 қатынасында таңдау.

def detect_edges (кадр):

төменгі_көк = np.array ([90, 120, 0], dtype = «uint8») # көк түстің төменгі шегі жоғарғы_көк = np.array ([150, 255, 255], dtype = «uint8») # жоғарғы шегі көк түсті маска = cv2.inRange (hsv, төменгі_көк, жоғарғы_көк) # бұл маска көк түстен басқа бәрін сүзгіден өткізеді # жиектер жиектерін анықтайды = cv2. Canny (маска, 50, 100)

Бұл функция HSV түс кеңістігінің параметрі ретінде қабылдайтын және жиектелген кадрды қайтаратын негізгі циклден де шақырылады. Мен алған жиекті жақтау жоғарыда орналасқан.

Қызығушылық аймағын (ROI) таңдаңыз:

Тек кадрдың 1 аймағына назар аудару үшін қызығушылық аймағын таңдау өте маңызды. Бұл жағдайда мен көліктің қоршаған ортадағы көптеген заттарды көруін қаламаймын. Мен көліктің жолақ сызығына назар аударып, басқа нәрсені елемегенін қалаймын. P. S: координаталар жүйесі (x және y осьтері) жоғарғы сол жақ бұрыштан басталады. Басқаша айтқанда, (0, 0) нүктесі жоғарғы сол жақ бұрыштан басталады. y осі-биіктік, ал x осі-ені. Төмендегі код кадрдың төменгі жартысына ғана назар аудару үшін қызығушылық аймағын таңдайды.

Def region_of_interest (жиектер):

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

Бұл функция жиекті жақтауды параметр ретінде қабылдайды және 4 алдын ала орнатылған нүктесі бар көпбұрыш салады. Ол көпбұрыштың ішіндегі нәрсеге ғана назар аударады және оның сыртындағы барлық нәрсені елемейді. Менің қызығушылық аймағы жоғарыда көрсетілген.

Сызық сегменттерін анықтау:

Қатты трансформация жиектелген кадрдан сызық сегменттерін анықтау үшін қолданылады. Hough трансформациясы - бұл кез келген пішінді математикалық түрде анықтау әдісі. Ол кез келген объектіні анықтай алады, тіпті егер ол дауыс саны бойынша бұрмаланса да. мұнда Hough түрлендіруге арналған тамаша сілтеме көрсетілген. Бұл қосымша үшін cv2. HoughLinesP () функциясы әр кадрдағы сызықтарды анықтау үшін қолданылады. Бұл функцияның маңызды параметрлері:

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

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

Бұл функция жолдың соңғы нүктелерін қайтарады. Hough түрлендіруінің көмегімен сызықтарды анықтау үшін менің негізгі циклымнан келесі функция шақырылады:

def detect_line_segments (қысқартылған_шеттер):

rho = 1 theta = np.pi / 180 min_threshold = 10 line_segments = cv2. HoughLinesP (қысқартылған_шеттер, rho, theta, min_threshold, np.array (), minLineLength = 5, maxLineGap = 0) қайтару сызығы_ сегменттері

Орташа еңіс пен қиылысу (м, б):

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

def орташа_бұрышты_арқау (кадр, сызық_ сегменттері):

lane_lines = егер line_segments None болса: басып шығару («жол сегменті анықталмады») қайтару lane_lines биіктігі, ені, _ = frame.shape left_fit = оңға_фит = шекара = left_region_boundary = ені * (1 - шекара) оң_ аймақ_шекарасы = ені * line_segments ішіндегі line_segment үшін шекара: x1, y1, x2, y2 for line_se segment үшін: егер x1 == x2: басып шығару («тік сызықтарды өткізіп жіберу (көлбеу = шексіздік)») жалғастыру fit = np.polyfit ((x1, x2), (y1, y2), 1) көлбеу = (y2 - y1) / (x2 - x1) қиылысу = y1 - (көлбеу * x1) егер көлбеу <0: егер x1 <солға_аймақ_шекарасы мен x2 оң_аймақ_шекарасы мен x2> оң_аймақ_шекарасы: оңға_фит. қосу ((көлбеу, ұстау)) 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.average (right_fit, ось = 0)) егер len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # жолақ сызықтары-бұл оң және сол жақ жолақтарының координаттарынан тұратын 2 өлшемді массив # мысалы: 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 (кадр, 0,8, сызық_суреті, 1, 1) қайтару жолы_суреті

cv2.addWeighted () функциясы келесі параметрлерді қабылдайды және ол екі суретті біріктіру үшін қолданылады, бірақ әрқайсысына салмақ береді.

cv2.addWeighted (сурет1, альфа, сурет2, бета, гамма)

Және шығыс кескінін келесі теңдеу арқылы есептейді:

шығару = альфа * сурет1 + бета * сурет2 + гамма

Cv2.addWeighted () функциясы туралы қосымша ақпарат осы жерден алынған.

Тақырып жолын есептеу және көрсету:

Бұл қозғалтқышқа жылдамдықты қолданар алдындағы соңғы қадам. Бағыт сызығы рульдік қозғалтқышқа айналу бағытын беруге және дроссель қозғалтқыштарына олардың жұмыс жылдамдығын беруге жауапты. Тақырып сызығы таза тригонометрия болып табылады, тан және атан (tan^-1) тригонометриялық функциялар қолданылады. Кейбір төтенше жағдайлар - бұл камера тек бір жолақты анықтаса немесе ол ешқандай сызықты анықтамаса. Барлық осы жағдайлар келесі функцияда көрсетілген:

def get_steering_angle (жақтау, жолақ сызықтары):

биіктігі, ені, _ = кадр. пішіні егер линия (жолақ_жолдары) == 2: # егер екі жолақты сызық анықталса _, _, left_x2, _ = lane_lines [0] [0] # x2 жолақ сызықтары массивінен x2 қалды, оң_х2, _ = жолдың сызықтары [1] [0] # x2 жолақты сызықтар массивінен шығару x = off (ені / 2) x_offset = (солға_х2 + оңға_х2) / 2 - ортаңғы_қосылу = int (биіктік / 2) элиф лин (жолдың сызықтары)) == 1: # егер бір ғана жол анықталса x1, _, x2, _ = жолдың сызықтары [0] [0] x_offset = x2 - x1 y_offset = int (биіктік / 2) элиф лин (жолдың сызықтары) == 0: # егер ешқандай сызық анықталмаса x_offset = 0 y_offset = int (биіктік / 2) burchak_to_mid_radian = math.atan (x_offset / y_offset) burchak_to_mid_deg = int (бұрыш_то_мид_радиандық * 180.0 / math.pi) steering_angle = angle_to_mid_deg + 90 қайтару рульдік басқару

x_offset бірінші жағдайда орташа ((оң x2 + сол x2) / 2) экранның ортасынан қаншалықты ерекшеленеді. y_offset әрқашан биіктік ретінде қабылданады / 2. Жоғарыдағы соңғы суретте тақырып жолының мысалы көрсетілген. angle_to_mid_radians - жоғарыдағы соңғы суретте көрсетілген «тета» сияқты. Егер steering_angle = 90 болса, бұл автомобильде «биіктік / 2» сызығына перпендикуляр бағыттау сызығы бар екенін білдіреді және машина рульсіз алға жылжиды. Егер рульдік бұрылыс> 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 оралу

Жоғарыдағы функция кіріс сызығы және руль бұрышы тартылатын жақтауды алады. Ол тақырып жолының кескінін қайтарады. Менің жағдайда алынған тақырыптық жолақ жақтауы жоғарыдағы суретте көрсетілген.

Барлық кодты біріктіру:

Енді код жинауға дайын. Келесі код әр функцияны шақыратын бағдарламаның негізгі циклін көрсетеді:

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 (frame, -1) #Функцияларды шақыру hsv = convert_to_HSV (кадр) жиектері = анықтау_шектері (hsv) roi = қызығушылық_ аймағы (жиектер) line_segments = detect_line_segments (roi) жолақ сызықтары = орташа_біліс_қабылдауы (кадр, сызық_сегменттері) = 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 бақылауын қолдану

PD бақылауын қолдану
PD бақылауын қолдану

Енді бізде руль бұрышы моторға берілуге дайын. Жоғарыда айтылғандай, егер руль бұрышы 90 -нан үлкен болса, машина оңға бұрылуы керек, әйтпесе солға бұрылуы керек. Мен бұрыш 90 -дан жоғары болса, рульдік қозғалтқышты оңға айналдыратын қарапайым кодты қолдандым, ал рульдің бұрылу жылдамдығы 90% -дан төмен болса, оны солға бұрады, бірақ менде көптеген қателіктер болды. Менің басты қателігім - көлік кез келген бұрылысқа жақындағанда, руль қозғалтқышы тікелей әрекет етеді, бірақ дроссель қозғалтқыштары істен шығады. Мен дроссель жылдамдығын бұрылыстарда (20% PWM) арттыруға тырыстым, бірақ робот жолақтардан түсумен аяқталды. Маған руль бұрышы өте үлкен болса, дроссель жылдамдығын едәуір арттыратын нәрсе қажет болды, ал егер руль бұрышы онша үлкен болмаса, жылдамдықты сәл арттырады, содан кейін машина 90 градусқа жақындағанда жылдамдықты бастапқы мәнге дейін төмендетеді. Шешім PD контроллерін қолдану болды.

PID контроллері пропорционалды, интегралды және туынды контроллерді білдіреді. Сызықтық реттегіштердің бұл түрі робототехникада кеңінен қолданылады. Жоғарыдағы суретте PID кері байланысының басқару циклі көрсетілген. Бұл контроллердің мақсаты - белгілі бір шарттарға сәйкес қондырғыны қосатын немесе өшіретін «қосу» контроллерлерінен айырмашылығы неғұрлым тиімді әдіспен «белгіленген нүктеге» жету. Кейбір кілт сөздер белгілі болуы керек:

  • Орнату нүктесі: бұл сіздің жүйеге қол жеткізгіңіз келетін қажетті мән.
  • Нақты мән - бұл сенсормен сезілетін нақты мән.
  • Қате - бұл белгіленген мән мен нақты мән арасындағы айырмашылық (қате = Орнату нүктесі - Нақты мән).
  • Басқарылатын айнымалы: оның атауынан, басқарғыңыз келетін айнымалы.
  • Kp: пропорционалды тұрақты.
  • Ки: интегралды тұрақты.
  • Kd: Туынды тұрақты.

Қысқаша айтқанда, PID басқару жүйесінің циклы келесідей жұмыс істейді:

  • Пайдаланушы жүйеге жету үшін қажетті нүктені анықтайды.
  • Қате есептеледі (қате = белгіленген нүкте - нақты).
  • P контроллері қатенің мәніне пропорционалды әрекетті жасайды. (қате жоғарылайды, Р әрекеті де артады)
  • I контроллері уақыт өте келе қатені біріктіреді, бұл жүйенің тұрақты күй қатесін жояды, бірақ оның асып кетуін арттырады.
  • D контроллері - бұл қате үшін уақыт туындысы. Басқаша айтқанда, бұл қатенің көлбеуі. Ол қатенің туындысына пропорционалды әрекет жасайды. Бұл контроллер жүйенің тұрақтылығын арттырады.
  • Контроллердің шығысы үш контроллердің қосындысы болады. Егер қате 0 болса, контроллердің шығысы 0 болады.

PID контроллерінің керемет түсіндірмесін мына жерден табуға болады.

Жолды сақтайтын көлікке қайтып оралғанда, менің басқарылатын айнымалы жылдамдығым болды (өйткені рульде оң немесе сол жақта тек екі күй бар). PD контроллері осы мақсатта пайдаланылады, себебі D әрекеті қателіктің өзгеруі өте үлкен болса (яғни үлкен ауытқу) дроссель жылдамдығын едәуір арттырады және егер бұл қате 0 -ге жақындаса, машинаны баяулатады. Мен PD енгізу үшін келесі әрекеттерді жасадым. контроллер:

  • Орнату нүктесін 90 градусқа қойыңыз (мен әрқашан көліктің түзу қозғалуын қалаймын)
  • Ортадан ауытқу бұрышын есептеді
  • Ауытқу екі ақпаратты береді: қате қаншалықты үлкен (ауытқу шамасы) және рульдік қозғалтқыш қандай бағытта жүруі керек (ауытқу белгісі). Егер ауытқу оң болса, көлік оңға бұрылуы керек, әйтпесе солға бұрылуы керек.
  • Девиация теріс немесе оң болғандықтан, «қате» айнымалысы анықталады және әрқашан ауытқудың абсолюттік мәніне тең болады.
  • Қате тұрақты Kp -ге көбейтіледі.
  • Қате уақыт дифференциациясынан өтеді және тұрақты Kd көбейтіледі.
  • Қозғалтқыштардың жылдамдығы жаңартылып, цикл қайтадан басталады.

Дроссельді қозғалтқыштардың жылдамдығын бақылау үшін негізгі циклде келесі код қолданылады:

жылдамдық = 10 # жұмыс жылдамдығы % PWM

# Айнымалылар әр циклды жаңарту керек lastTime = 0 lastError = 0 # PD тұрақтылары Kp = 0.4 Kd = Kp * 0.65 True кезінде: қазір = time.time () # ағымдағы уақыт айнымалысы dt = қазір - lastTime ауытқуы = рульдік бұрыш - 90 # эквивалент to angle_to_mid_deg айнымалы қатесі = abs (ауытқу) ауытқу -5: # қателік диапазонының 10 градус ауытқуы болса, # басқармаңыз = 0 қате = 0 GPIO. шығысы (in1, GPIO. LOW) GPIO. шығысы (in2, GPIO). LOW) басқаруды тоқтату () elif ауытқуы> 5: егер ауытқу оң болса GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) рульдік басқару. Бастау (100) elif ауытқуы < -5: ауытқу теріс болса, # руль солға қарай * қате PD = int (жылдамдық + туынды + пропорционалды) spd = abs (PD), егер spd> 25: spd = 25 throttle.start (spd) lastError = lastTime = time.time () қатесі

Егер қате өте үлкен болса (ортадан ауытқу жоғары), пропорционалды және туынды әрекеттер жоғары болады, нәтижесінде жоғары дроссель жылдамдығы пайда болады. Қате 0 -ге жақындағанда (ортадан ауытқу төмен), туынды әрекет кері әсер етеді (көлбеу теріс) және жүйенің тұрақтылығын сақтау үшін дроссель жылдамдығы төмендейді. Толық код төменде берілген.

7 -қадам: Нәтижелер

Жоғарыдағы бейнелер мен алған нәтижелерді көрсетеді. Ол қосымша баптауды және қосымша түзетулерді қажет етеді. Мен таңқурай пиін СК дисплей экранына қосатынмын, себебі менің желімдегі бейне ағыны өте кідірісті болды және онымен жұмыс істеу өте көңілсіз болды, сондықтан бейнеде таңқурайға қосылған сымдар бар. Жолды салу үшін мен көбік тақталарын қолдандым.

Мен бұл жобаны жақсарту үшін сіздің ұсыныстарыңызды күтемін! Бұл нұсқаулар сізге жаңа ақпарат беруге жеткілікті болды деп үміттенемін.

Ұсынылған: