Съдържание:

Автономна кола за поддържане на лента, използваща Raspberry Pi и OpenCV: 7 стъпки (със снимки)
Автономна кола за поддържане на лента, използваща Raspberry Pi и OpenCV: 7 стъпки (със снимки)

Видео: Автономна кола за поддържане на лента, използваща Raspberry Pi и OpenCV: 7 стъпки (със снимки)

Видео: Автономна кола за поддържане на лента, използваща Raspberry Pi и OpenCV: 7 стъпки (със снимки)
Видео: [투싼/올뉴투싼 옵션과 솔직리뷰]올뉴투싼 옵션과 옵션활용법,신형투싼,The all new Tucson 2024, Юли
Anonim
Автономна кола за поддържане на лентата, използваща Raspberry Pi и OpenCV
Автономна кола за поддържане на лентата, използваща Raspberry Pi и OpenCV

В тези инструкции ще бъде внедрен автономен робот за поддържане на лентата и ще премине през следните стъпки:

  • Събиране на части
  • Инсталиране на софтуерни предпоставки
  • Хардуерен монтаж
  • Първи тест
  • Откриване на линии на лентата и показване на водещата линия с помощта на openCV
  • Внедряване на PD контролер
  • Резултати

Стъпка 1: Събиране на компоненти

Събиране на компоненти
Събиране на компоненти
Събиране на компоненти
Събиране на компоненти
Събиране на компоненти
Събиране на компоненти
Събиране на компоненти
Събиране на компоненти

Изображенията по -горе показват всички компоненти, използвани в този проект:

  • RC кола: Моята я взех от местен магазин в моята страна. Оборудван е с 3 двигателя (2 за дроселиране и 1 за управление). Основният недостатък на тази кола е, че кормилното управление е ограничено между „без управление“и „пълно управление“. С други думи, той не може да се управлява под определен ъгъл, за разлика от RC автомобилите със серво управление. Можете да намерите подобен комплект за кола, създаден специално за малиново пи от тук.
  • Raspberry pi 3 модел b+: това е мозъкът на колата, който ще се справи с много етапи на обработка. Базиран е на четириядрен 64-битов процесор с тактова честота 1,4 GHz. Аз си взех моята от тук.
  • Модул за камера Raspberry pi 5 mp: Поддържа 1080p @ 30 fps, 720p @ 60 fps и 640x480p 60/90 запис. Той също така поддържа сериен интерфейс, който може да бъде включен директно в малиновото пи. Това не е най -добрият вариант за приложения за обработка на изображения, но е достатъчен за този проект, както и че е много евтин. Аз си взех моята от тук.
  • Шофьор на мотор: Използва се за контрол на посоките и скоростите на DC двигателите. Той поддържа управлението на 2 dc мотора в 1 платка и може да издържи 1,5 A.
  • Power Bank (по избор): Използвах power bank (номинална стойност 5V, 3A), за да захранвам малиновото пи отделно. Трябва да се използва понижаващ преобразувател (доларов преобразувател: 3A изходен ток), за да се захранва малиновото пи от 1 източник.
  • 3s (12 V) LiPo батерия: Литиево -полимерните батерии са известни с отличните си характеристики в областта на роботиката. Използва се за захранване на драйвера на двигателя. Моята си я купих от тук.
  • Мъжки към мъжки и женски към женски джъмперни проводници.
  • Двустранна лента: Използва се за монтиране на компонентите на RC автомобила.
  • Синя лента: Това е много важен компонент на този проект, той се използва за направата на двете линии на платното, между които колата ще се движи. Можете да изберете всеки цвят, който искате, но аз препоръчвам да изберете цветове, различни от тези в заобикалящата ви среда.
  • Ципове и дървени решетки.
  • Отвертка.

Стъпка 2: Инсталиране на OpenCV на Raspberry Pi и настройване на отдалечен дисплей

Инсталиране на OpenCV на Raspberry Pi и настройване на отдалечен дисплей
Инсталиране на OpenCV на Raspberry Pi и настройване на отдалечен дисплей

Тази стъпка е малко досадна и ще отнеме известно време.

OpenCV (Open Source Computer Vision) е компютърна библиотека с отворен код и софтуер за машинно обучение. Библиотеката разполага с над 2500 оптимизирани алгоритми. Следвайте това много просто ръководство, за да инсталирате openCV на вашето малиново пи, както и да инсталирате малиновата пи ОС (ако все още не сте го направили). Моля, обърнете внимание, че процесът на изграждане на openCV може да отнеме около 1,5 часа в добре охладена стая (тъй като температурата на процесора ще стане много висока!), Така че изпийте чай и изчакайте търпеливо: D.

За дистанционното показване също следвайте това ръководство, за да настроите отдалечен достъп до вашето малиново пи от вашето Windows/Mac устройство.

Стъпка 3: Свързване на части заедно

Свързване на части заедно
Свързване на части заедно
Свързване на части заедно
Свързване на части заедно
Свързване на части заедно
Свързване на части заедно

Изображенията по -горе показват връзките между raspberry pi, модула на камерата и драйвера на двигателя. Моля, обърнете внимание, че използваните от мен двигатели поглъщат 0,35 А при 9 V всеки, което го прави безопасно за водача на мотора да работи с 3 двигателя едновременно. И тъй като искам да контролирам скоростта на 2 -те дроселиращи двигателя (1 отзад и 1 отпред) точно по същия начин, аз ги свързах към същия порт. Монтирах драйвера на двигателя от дясната страна на колата, използвайки двойна лента. Що се отнася до модула на камерата, аз вмъкнах цип между отворите за винтове, както показва горното изображение. След това монтирам камерата на дървена пръчка, за да мога да коригирам позицията на камерата, както искам. Опитайте се да инсталирате камерата в средата на колата колкото е възможно повече. Препоръчвам да поставите камерата най -малко на 20 см над земята, така че зрителното поле пред колата да се подобри. Схемата на Fritzing е приложена по -долу.

Стъпка 4: Първи тест

Първи тест
Първи тест
Първи тест
Първи тест

Тестване на камерата:

След като камерата е инсталирана и библиотеката на openCV е създадена, е време да тестваме първото ни изображение! Ще направим снимка от pi cam и ще я запазим като „original.jpg“. Това може да стане по 2 начина:

1. Използване на терминални команди:

Отворете нов прозорец на терминала и въведете следната команда:

raspistill -o оригинал.jpg

Това ще вземе неподвижно изображение и ще го запише в директорията "/pi/original.jpg".

2. Използване на всеки python IDE (използвам IDLE):

Отворете нова скица и напишете следния код:

импортиране на cv2

video = cv2. VideoCapture (0) while 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 () ще прочете всеки кадър, идващ от камерата, и ще го запише в променлива, наречена "frame". Функцията flip () ще обърне изображението по оста y (вертикално), тъй като монтирам камерата си обратно. imshow () ще покаже нашите рамки начело с думата "оригинал" и imwrite () ще запише снимката ни като original.jpg. waitKey (1) ще изчака 1 ms за натискане на който и да е бутон на клавиатурата и връща своя ASCII код. ако е натиснат бутон escape (esc), се връща десетична стойност 27 и съответно ще прекъсне цикъла. video.release () ще спре записа и унищожи AllWindows () ще затвори всяко изображение, отворено от функцията imshow ().

Препоръчвам да тествате снимката си с втория метод, за да се запознаете с функциите на openCV. Изображението се записва в директорията "/pi/original.jpg". Оригиналната снимка, направена от моя фотоапарат, е показана по -горе.

Тестващи двигатели:

Тази стъпка е от съществено значение за определяне на посоката на въртене на всеки двигател. Първо, нека да направим кратко въведение за принципа на работа на шофьор на мотор. Изображението по-горе показва pin-out на драйвера на двигателя. Активиране А, вход 1 и вход 2 са свързани с управлението на мотор А. Активиране B, вход 3 и вход 4 са свързани с управлението на мотор B. Управлението на посоката се установява от частта "Input", а контролът на скоростта се установява от частта "Enable". За да контролирате посоката на мотор А например, задайте вход 1 на ВИСОК (3,3 V в този случай, тъй като използваме малинов пи) и настройте вход 2 на НИСКО, моторът ще се върти в определена посока и като зададе противоположните стойности към вход 1 и вход 2, двигателят ще се върти в обратна посока. Ако вход 1 = вход 2 = (висок или нисък), двигателят няма да се завърти. Активиращите щифтове вземат входен сигнал за Pulme Width Modulation (PWM) от малина (0 до 3.3 V) и пускат двигателите съответно. Например, 100% PWM сигнал означава, че работим върху максималната скорост, а 0% PWM сигнал означава, че двигателят не се върти. Следният код се използва за определяне на посоките на двигателите и тестване на техните скорости.

време за импортиране

импортиране на RPi. GPIO като GPIO GPIO.setwarnings (False) # Щифтове на кормилния двигател brake_enable = 22 # Физически ПИН 15 в1 = 17 # Физически ПИН 11 в2 = 27 # Физически ПИН 13 # Двигатели на дроселните клапани throttle_enable = 25 # Физически ПИН 22 в3 = 23 # Физически ПИН 16 in4 = 24 # Физически ПИН 18 GPIO.setmode (GPIO. BCM) # Използвайте GPIO номериране вместо физическо номериране GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO. настройка (in3, GPIO.out) GPIO.setup (in4, GPIO.out) GPIO.setup (throttle_enable, GPIO.out) GPIO.setup (upravljanje_enable, GPIO.out) # Управление на кормилния двигател GPIO.output (in1, GPIO. ВИСОКИ) GPIO.изход (in2, GPIO. LOW) кормилно управление = GPIO. PWM (управляващ_ активиран, 1000) # задайте честотата на превключване на 1000 Hz управление..output (in4, GPIO. LOW) throttle = GPIO. PWM (throttle_enable, 1000) # задайте честотата на превключване на 1000 Hz throttle.stop () time.sleep (1) throttle.start (25) # стартира двигателя при 25 % ШИМ сигнал-> (0.25 * напрежение на батерията) - на водача загуба на кормилно управление.старт (100) # стартира двигателя при 100% ШИМ сигнал-> (1 * Напрежение на батерията) - време на загуба на водача. сън (3) дроселова клапа

Този код ще задейства дроселиращите двигатели и кормилния двигател за 3 секунди и след това ще ги спре. (Загубата на водача) може да се определи с помощта на волтметър. Например, ние знаем, че 100% PWM сигнал трябва да даде пълното напрежение на батерията на клемата на двигателя. Но, като зададох PWM на 100%, установих, че драйверът причинява спад на 3 V и моторът получава 9 V вместо 12 V (точно това, от което се нуждая!). Загубата не е линейна, т.е. загубата при 100% е много различна от загубата при 25%. След стартирането на горния код резултатите ми бяха следните:

Резултати от дроселиране: ако in3 = ВИСОКО и in4 = НИСКО, дроселиращите двигатели ще имат въртене по часовник (CW), т.е.колата ще се движи напред. В противен случай колата ще се движи назад.

Резултати от кормилното управление: ако in1 = ВИСОКО и in2 = НИСКО, кормилният двигател ще се завърти максимално наляво, т.е. колата ще завие наляво. В противен случай колата ще завие надясно. След някои експерименти установих, че кормилният двигател няма да се завърти, ако ШИМ сигналът не е 100% (т.е. двигателят ще се насочи или напълно надясно, или напълно наляво).

Стъпка 5: Откриване на линии на платното и изчисляване на линия на посока

Откриване на линии на платното и изчисляване на посоката
Откриване на линии на платното и изчисляване на посоката
Откриване на линии на платното и изчисляване на посоката
Откриване на линии на платното и изчисляване на посоката
Откриване на линии на платното и изчисляване на посоката
Откриване на линии на платното и изчисляване на посоката

В тази стъпка ще бъде обяснен алгоритъмът, който ще контролира движението на автомобила. Първото изображение показва целия процес. Входът на системата е изображения, изходът е тета (ъгъл на управление в градуси). Имайте предвид, че обработката се извършва на 1 изображение и ще се повтори на всички кадри.

Камера:

Камерата ще започне да записва видео с резолюция (320 x 240). Препоръчвам да намалите разделителната способност, за да получите по -добра скорост на кадрите (fps), тъй като падането на fps ще настъпи след прилагане на техники за обработка към всеки кадър. Кодът по -долу ще бъде основният цикъл на програмата и ще добавя всяка стъпка над този код.

импортиране на cv2

import numpy като np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # задайте ширината на 320 p video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) # задайте височината на 240 p # Цикълът докато Вярно: 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 edge можете да намерите тук. Едно правило е да изберете параметрите на функцията Canny () в съотношение 1: 2 или 1: 3.

def detect_edges (кадър):

lower_blue = np.array ([90, 120, 0], dtype = "uint8") # долна граница на син цвят upper_blue = np.array ([150, 255, 255], dtype = "uint8") # горна граница на маска със син цвят = cv2.inRange (hsv, lower_blue, upper_blue) # тази маска ще филтрира всичко освен синьото # открива ръбовете ръбове = cv2. Canny (маска, 50, 100) cv2.imshow ("ръбове", ръбове) връща ръбове

Тази функция също ще бъде извикана от главния цикъл, който приема като параметър рамката на цветовото пространство на HSV и връща рамката с ръбове. Кадровата рамка, която получих, се намира по -горе.

Изберете регион на интерес (ROI):

Изборът на област на интерес е от решаващо значение, за да се съсредоточите само върху 1 област от рамката. В този случай не искам колата да вижда много предмети в околната среда. Просто искам колата да се съсредоточи върху линиите на лентата и да игнорира всичко друго. P. S: координатната система (оси x и y) започва от горния ляв ъгъл. С други думи, точката (0, 0) започва от горния ляв ъгъл. оста y е височината, а оста x е ширината. Кодът по -долу избира област на интерес, която да се фокусира само върху долната половина на кадъра.

def region_of_interest (ръбове):

височина, ширина = ръбове. форма # извличане на височината и ширината на рамката маска на рамката = np.zeros_like (ръбове) # направете празна матрица със същите размери на рамката на ръбовете # фокусирайте само долната половина на екрана # посочете координатите на 4 точки (долу вляво, горе вляво, горе вдясно, долу вдясно) многоъгълник = np. Масив (

Тази функция ще вземе остриената рамка като параметър и нарисува многоъгълник с 4 предварително зададени точки. Той ще се фокусира само върху това, което е вътре в полигона, и ще игнорира всичко извън него. Моята рамка на интереси е показана по -горе.

Откриване на сегменти на линия:

Hough трансформацията се използва за откриване на сегменти на линия от рамка с ръб. Hough преобразуването е техника за откриване на всяка форма в математическа форма. Той може да открие почти всеки обект, дори ако е изкривен според определен брой гласове. тук е показана страхотна справка за трансформацията на Hough. За това приложение функцията cv2. HoughLinesP () се използва за откриване на линии във всеки кадър. Важните параметри, които тази функция приема, са:

cv2. HoughLinesP (frame, rho, theta, min_threshold, minLineLength, maxLineGap)

  • Рамка: е рамката, в която искаме да открием линии.
  • rho: Това е точността на разстоянието в пиксели (обикновено е = 1)
  • тета: ъглова точност в радиани (винаги = np.pi/180 ~ 1 градус)
  • min_threshold: минималният глас, който трябва да получи, за да се счита за ред
  • minLineLength: минимална дължина на реда в пиксели. Всеки ред, по -къс от този номер, не се счита за ред.
  • maxLineGap: максимална празнина в пиксели между 2 реда, която да се третира като 1 ред. (В моя случай не се използва, тъй като линиите на лентата, които използвам, нямат празнина).

Тази функция връща крайните точки на ред. Следващата функция се извиква от моя основен цикъл за откриване на линии, използвайки преобразуване на Hough:

def detect_line_segments (cropped_edges):

rho = 1 theta = np.pi / 180 min_threshold = 10 line_segments = cv2. HoughLinesP (изрязани ръбове, rho, theta, min_threshold, np.array (), minLineLength = 5, maxLineGap = 0) връщане_сегменти на линия

Среден наклон и прихващане (m, b):

припомняме, че уравнението на права е дадено от y = mx + b. Където m е наклонът на линията и b е y-прихващането. В тази част ще бъде изчислена средната стойност на наклоните и прихващанията на сегменти от линии, открити с помощта на трансформация на Hough. Преди да направим това, нека да разгледаме оригиналната снимка на рамката, показана по -горе. Изглежда, че лявата лента върви нагоре, така че има отрицателен наклон (помните началната точка на координатната система?). С други думи, лявата лента има x1 <x2 и y2 x1 и y2> y1, което ще даде положителен наклон. Така че всички линии с положителен наклон се считат за точки от дясната лента. В случай на вертикални линии (x1 = x2), наклонът ще бъде безкраен. В този случай ще пропуснем всички вертикални линии, за да предотвратим получаването на грешка. За да се добави по -голяма точност на това откриване, всеки кадър е разделен на две области (дясна и лява) през 2 гранични линии. Всички точки на ширина (точки на оста x), по-големи от дясната гранична линия, са свързани с изчисляването на дясната лента. И ако всички точки на ширина са по -малки от лявата гранична линия, те се свързват с изчисление на лявата лента. Следващата функция отнема кадъра в процес на обработка и сегменти на лентата, открити с помощта на трансформация на Hough и връща средния наклон и прихващане на две линии на лентата.

def average_slope_intercept (frame, line_segments):

lane_lines = ако line_segments е None: print ("не е открит сегмент на линия") връщане lane_lines височина, ширина, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - border) right_region_boundary = ширина * граница за линия_сегмент в линии_сегменти: за x1, y1, x2, y2 в сегмент_на линия: ако x1 == x2: печат ("прескачане на вертикални линии (наклон = безкрайност)") продължете да прилягате = np.polyfit ((x1, x2), (y1, y2), 1) наклон = (y2 - y1) / (x2 - x1) прихващане = y1 - (наклон * x1), ако наклон <0: ако x1 <ляв_регион_граница и x2 десен_регион_граница и x2> десен_регион_граница: десен_подходящ. append ((склон, прихващане))) ако len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # lane_lines е двуизмерен масив, състоящ се от координатите на дясната и лявата лента # например: lan e_lines =

make_points () е помощна функция за функцията average_slope_intercept (), която ще върне ограничените координати на линиите на лентата (отдолу до средата на рамката).

def make_points (рамка, линия):

височина, ширина, _ = рамка. наклон на формата, прихващане = линия y1 = височина # дъното на рамката y2 = int (y1 / 2) # направете точки от средата на рамката надолу, ако наклон == 0: наклон = 0,1 x1 = int ((y1 - прихващане) / наклон) x2 = int ((y2 - прихващане) / наклон) връщане

За да се предотврати разделянето на 0, се представя условие. Ако наклон = 0, което означава y1 = y2 (хоризонтална линия), дайте стойност на наклона близо до 0. Това няма да повлияе на работата на алгоритъма, както и ще предотврати невъзможния случай (разделяне на 0).

За да се покажат линиите на лентата на кадрите, се използва следната функция:

def display_lines (frame, lines, line_color = (0, 255, 0), line_width = 6): # цвят на линията (B, G, R)

line_image = np.zeros_like (frame), ако линиите не са None: за ред в редове: за 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 (изображение1, алфа, изображение2, бета, гама)

И изчислява изходното изображение, като използва следното уравнение:

изход = алфа * изображение1 + бета * изображение2 + гама

Повече информация за функцията cv2.addWeighted () са получени тук.

Изчисляване и показване на заглавната линия:

Това е последната стъпка, преди да приложим скорост към нашите двигатели. Линията на посоката е отговорна да даде на кормилния двигател посоката, в която трябва да се върти, и да даде на дроселните двигатели скоростта, с която те ще работят. Изчисляването на заглавната линия е чиста тригонометрия, използват се тригонометрични функции tan и atan (tan^-1). Някои екстремни случаи са, когато камерата открие само една лента или когато не открие никаква линия. Всички тези случаи са показани в следната функция:

def get_steering_angle (frame, lane_lines):

височина, ширина, _ = frame.shape, ако len (lane_lines) == 2: # ако са открити две линии ленти _, _, left_x2, _ = lane_lines [0] [0] # извлечете ляво x2 от масива lane_lines _, _, right_x2, _ = lane_lines [1] [0] # извлечете дясно x2 от масив lane_lines mid = int (width / 2) x_offset = (left_x2 + right_x2) / 2 - mid y_offset = int (height / 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 (height / 2) angle_to_mid_radian = math.atan (x_offset / y_offset) angle_to_mid_deg = int (angle_to_mid_radian * 180.0 / math.pi)

x_offset в първия случай е доколко средната стойност ((вдясно x2 + ляво x2) / 2) се различава от средата на екрана. y_offset винаги се приема за височина / 2. Последното изображение по -горе показва пример за заглавна линия. angle_to_mid_radians е същото като "тета", показано на последното изображение по -горе. Ако ъгъл на управление = 90, това означава, че колата има линия на посока, перпендикулярна на линията "височина / 2" и колата ще се движи напред без кормилно управление. Ако кормил_ъгъл> 90, колата трябва да се насочи надясно, в противен случай трябва да завие наляво. За показване на заглавната линия се използва следната функция:

def display_heading_line (рамка, кормилен_ъгъл, line_color = (0, 0, 255), line_width = 5)

heading_image = np.zeros_like (рамка) височина, ширина, _ = frame.shape ръководен_ъгъл_радиан = кормилен_ъгъл / 180.0 * math.pi x1 = int (ширина / 2) y1 = височина x2 = int (x1 - височина / 2 / math.tan (управляващ ъгъл_радиан)) y2 = int (височина / 2) cv2.line (заглавие_изображение, (x1, y1), (x2, y2), линия_цветно, ширина_на линия) heading_image = cv2.add връщане 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 (frame) ръбове = detect_edges (hsv) roi = region_of_interest (ръбове) line_segments = detect_line_segments (roi) lane_lines = average_slope_intercept (frame, line_segments) lane_lines_image = display_line_ frame) = get_steering_angle (frame, lane_lines) heading_image = display_heading_line (lane_lines_image, кіруващ ъгъл) ключ = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Стъпка 6: Прилагане на PD контрол

Прилагане на PD контрол
Прилагане на PD контрол

Сега имаме ъгъла на управление, готов за подаване към двигателите. Както бе споменато по -рано, ако ъгълът на завиване е по -голям от 90, колата трябва да завие надясно, в противен случай трябва да завие наляво. Приложих прост код, който завърта кормилния двигател надясно, ако ъгълът е над 90, и го завива наляво, ако ъгълът на кормилното управление е по -малък от 90 при постоянна скорост на регулиране (10% PWM), но получих много грешки. Основната грешка, която получих е, когато колата наближи всеки завой, кормилният двигател действа директно, но дроселиращите двигатели се задръстват. Опитах се да увелича скоростта на дроселиране до (20% PWM) при завои, но завърших с излизането на робота от лентите. Имах нужда от нещо, което значително увеличава скоростта на дроселиране, ако ъгълът на кормилното управление е много голям и увеличава скоростта малко, ако ъгълът на кормилното управление не е толкова голям, след това намалява скоростта до първоначална стойност, когато колата се приближава до 90 градуса (движи се право). Решението беше да се използва PD контролер.

PID контролерът означава Пропорционален, интегрален и производен контролер. Този тип линейни контролери се използва широко в приложенията за роботика. Изображението по -горе показва типичния контур за управление на обратната връзка с PID. Целта на този контролер е да достигне "зададената стойност" по най -ефективния начин за разлика от контролерите "включване -изключване", които включват или изключват инсталацията при определени условия. Трябва да са известни някои ключови думи:

  • Зададена стойност: е желаната стойност, която искате вашата система да достигне.
  • Действителна стойност: е действителната стойност, засечена от сензора.
  • Грешка: е разликата между зададената и действителната стойност (грешка = зададена точка - действителна стойност).
  • Контролирана променлива: от нейното име, променливата, която искате да контролирате.
  • Kp: Пропорционална константа.
  • Ki: Интегрална константа.
  • Kd: Производна константа.

Накратко, цикълът на системата за PID контрол работи, както следва:

  • Потребителят определя зададената точка, необходима за достигане на системата.
  • Грешката се изчислява (грешка = зададена стойност - действителна).
  • P контролерът генерира действие, пропорционално на стойността на грешката. (грешката се увеличава, действието 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 Докато е вярно: now = time.time () # текуща променлива на времето dt = now - отклонение на последното време = ръководствен ъгъл - 90 # еквивалент към angle_to_mid_deg променлива грешка = abs (отклонение), ако отклонение -5: # не се управлява, ако има отклонение на обхвата на грешката 10 градуса = 0 грешка = 0 GPIO.изход (in1, GPIO. LOW) GPIO.output (in2, GPIO. LOW) отклонение на кормилното управление.stop () elif> 5: # направлявайте надясно, ако отклонението е положително GPIO.изход (in1, GPIO. LOW) GPIO.изход (in2, GPIO. HIGH) управление.старт (100) elif отклонение < -5: # управление наляво, ако отклонението е отрицателно GPIO.изход (in1, GPIO. HIGH) GPIO.output (in2, GPIO. LOW) ръководство.старт (100) производно = kd * (грешка - lastError) / dt пропорционално = kp * грешка PD = int (скорост + производна + пропорционална) spd = abs (PD), ако spd> 25: spd = 25 дросела.старт (spd) lastError = грешка lastTime = time.time ()

Ако грешката е много голяма (отклонението от средата е голямо), пропорционалните и производни действия са големи, което води до висока скорост на дроселиране. Когато грешката се доближи до 0 (отклонението от средата е ниско), деривативното действие действа обратно (наклонът е отрицателен) и скоростта на дроселиране намалява, за да се поддържа стабилността на системата. Пълният код е приложен по -долу.

Стъпка 7: Резултати

Горните видеоклипове показват резултатите, които получих. Има нужда от повече настройка и допълнителни настройки. Свързвах малиновото пи към моя LCD дисплей, тъй като видео стриймингът през мрежата ми имаше висока латентност и беше много разочароващ за работа, затова във видеото има проводници, свързани към малиново пи. Използвах дъски от пяна, за да нарисувам пистата.

Очаквам да чуя вашите препоръки за подобряване на този проект! Тъй като се надявам, че тези инструкции бяха достатъчно добри, за да ви дадат нова информация.

Препоръчано: