Mundarija:

Raspberry Pi va OpenCV-dan foydalanadigan avtonom chiziqli avtomobil: 7 qadam (rasmlar bilan)
Raspberry Pi va OpenCV-dan foydalanadigan avtonom chiziqli avtomobil: 7 qadam (rasmlar bilan)

Video: Raspberry Pi va OpenCV-dan foydalanadigan avtonom chiziqli avtomobil: 7 qadam (rasmlar bilan)

Video: Raspberry Pi va OpenCV-dan foydalanadigan avtonom chiziqli avtomobil: 7 qadam (rasmlar bilan)
Video: Object Identification & Animal Recognition With Raspberry Pi + OpenCV + Python 2024, Noyabr
Anonim
Raspberry Pi va OpenCV-dan foydalangan holda avtonom chiziqli avtomobil
Raspberry Pi va OpenCV-dan foydalangan holda avtonom chiziqli avtomobil

Ushbu yo'riqnomada avtonom yo'lni ushlab turuvchi robot amalga oshiriladi va u quyidagi bosqichlardan o'tadi:

  • Qismlarni yig'ish
  • Dasturiy ta'minot old shartlarini o'rnatish
  • Uskuna yig'ish
  • Birinchi test
  • OpenCV yordamida chiziqli chiziqlarni aniqlash va ko'rsatuvchi chiziqni ko'rsatish
  • PD boshqaruvchisini ishga tushirish
  • Natijalar

1 -qadam: Komponentlarni yig'ish

Komponentlarni yig'ish
Komponentlarni yig'ish
Komponentlarni yig'ish
Komponentlarni yig'ish
Komponentlarni yig'ish
Komponentlarni yig'ish
Komponentlarni yig'ish
Komponentlarni yig'ish

Yuqoridagi rasmlarda ushbu loyihada ishlatiladigan barcha komponentlar ko'rsatilgan:

  • RC avtomashinasi: meniki o'z mamlakatimdagi mahalliy do'kondan. U 3 ta dvigatel bilan jihozlangan (2 tasi siqish uchun, 1 tasi rulda). Bu mashinaning asosiy kamchiligi shundaki, Rulda boshqaruv "cheklanmagan" va "to'liq boshqariladigan" o'rtasida cheklangan. Boshqacha qilib aytganda, u RC avtomashinalaridan farqli o'laroq, ma'lum burchak ostida boshqara olmaydi. Malinali pi uchun maxsus ishlab chiqarilgan shunga o'xshash avtomobil to'plamini bu erdan topishingiz mumkin.
  • Raspberry pi 3 model b+: bu mashinaning miyasi, u ko'p ishlov berish bosqichlarini boshqaradi. U 1,4 gigagertsli to'rt yadroli 64 bitli protsessorga asoslangan. Men bu erdan o'zimnikini oldim.
  • Raspberry pi 5 MP kamera moduli: 1080p @ 30 kadr, 720p @ 60 kadr va 640x480p 60/90 yozishni qo'llab -quvvatlaydi. U shuningdek, malina pi ga to'g'ridan -to'g'ri ulanadigan ketma -ket interfeysni qo'llab -quvvatlaydi. Bu tasvirni qayta ishlash dasturlari uchun eng yaxshi variant emas, lekin bu loyiha uchun etarli, shuningdek juda arzon. Men bu erdan o'zimnikini oldim.
  • Dvigatel haydovchisi: shahar motorlarining yo'nalishlarini va tezligini nazorat qilish uchun ishlatiladi. U 1 ta taxtada 2 ta doimiy shahar motorini boshqarishni qo'llab -quvvatlaydi va 1,5 A ga bardosh beradi.
  • Quvvat banki (ixtiyoriy): Men malina pi -ni alohida yoqish uchun quvvat bankidan (5V, 3A darajali) foydalandim. Malinali pi ni 1 manbadan quvvatlantirish uchun pastga tushirish konvertori (buk konvertori: 3A chiqish oqimi) ishlatilishi kerak.
  • 3s (12 V) LiPo batareyasi: Lityum polimer batareyalar robototexnika sohasida mukammal ishlashi bilan mashhur. Bu dvigatel haydovchisini quvvatlantirish uchun ishlatiladi. Men bu erdan o'zimni sotib oldim.
  • Erkak -erkak va ayol -jumper simlari.
  • Ikki tomonlama lenta: komponentlarni RC avtomobiliga o'rnatish uchun ishlatiladi.
  • Moviy lenta: Bu loyihaning juda muhim tarkibiy qismi bo'lib, u mashina ketadigan ikkita chiziqli chiziqni yasash uchun ishlatiladi. Siz xohlagan rangni tanlashingiz mumkin, lekin men atrofdagi ranglardan boshqacha ranglarni tanlashni tavsiya qilaman.
  • Fermuar bog'ichlari va yog'och panjaralar.
  • Tornavida.

2 -qadam: Raspberry Pi -ga OpenCV -ni o'rnatish va masofaviy displeyni sozlash

Raspberry Pi -ga OpenCV -ni o'rnatish va masofaviy displeyni sozlash
Raspberry Pi -ga OpenCV -ni o'rnatish va masofaviy displeyni sozlash

Bu qadam biroz zerikarli va biroz vaqt talab etiladi.

OpenCV (Open Source Computer Vision) - bu kompyuterni ko'rish va mashinani o'rganish uchun ochiq manbali dasturlar kutubxonasi. Kutubxonada 2500 dan ortiq optimallashtirilgan algoritmlar mavjud. OpenCV -ni malina pi -ga o'rnatish, shuningdek, malina pi -ni o'rnatish uchun BU juda oddiy ko'rsatmaga amal qiling (agar siz hali o'rnatmagan bo'lsangiz). E'tibor bering, ochiq CVni yaratish jarayoni yaxshi sovutilgan xonada taxminan 1,5 soat davom etishi mumkin (chunki protsessor harorati juda yuqori bo'ladi!), Choy iching va sabr bilan kuting: D.

Masofaviy displey uchun Windows/Mac qurilmangizdan malina pi ga masofadan kirishni sozlash uchun BU ko'rsatmaga amal qiling.

3 -qadam: qismlarni bir -biriga ulash

Qismlarni bir -biriga ulash
Qismlarni bir -biriga ulash
Qismlarni bir -biriga ulash
Qismlarni bir -biriga ulash
Qismlarni bir -biriga ulash
Qismlarni bir -biriga ulash

Yuqoridagi rasmlarda malina pi, kamera moduli va dvigatel drayveri o'rtasidagi aloqalar ko'rsatilgan. E'tibor bering, men ishlatgan dvigatellar har biri 9 V bo'lgan 0,35 A ni yutadi, bu esa dvigatel haydovchisining bir vaqtning o'zida 3 ta dvigatelni ishlashini xavfsiz qiladi. Va men 2 drenaj dvigatelining tezligini (1 orqa va 1 old) xuddi shunday boshqarishni xohlaganim uchun, ularni bir portga uladim. Men dvigatel drayverini ikkita lenta yordamida mashinaning o'ng tomoniga o'rnatdim. Kamera moduliga kelsak, yuqoridagi rasmda ko'rsatilgandek, men vintlardek teshiklari orasiga fermuar bog'ladim. Keyin men kamerani yog'och panjaraga joylashtirdim, shunda kameraning o'rnini xohlaganimcha sozlashim mumkin. Kamerani iloji boricha mashinaning o'rtasiga o'rnatishga harakat qiling. Men kamerani erdan kamida 20 sm balandlikda joylashtirishni maslahat beraman, shunda mashina oldidagi ko'rish maydoni yaxshilanadi. Fritzing sxemasi quyida biriktirilgan.

4 -qadam: Birinchi sinov

Birinchi test
Birinchi test
Birinchi test
Birinchi test

Kamera sinovlari:

Kamera o'rnatilgandan va OpenCV kutubxonasi qurilgandan so'ng, bizning birinchi rasmimizni sinab ko'rish vaqti keldi! Biz pi camdan rasm olamiz va uni "original.jpg" sifatida saqlaymiz. Buni 2 usulda amalga oshirish mumkin:

1. Terminal buyruqlaridan foydalanish:

Yangi terminal oynasini oching va quyidagi buyruqni kiriting:

raspistill -o original.jpg

Bu harakatsiz tasvirni oladi va uni "/pi/original.jpg" katalogiga saqlaydi.

2. Har qanday python IDE yordamida (men IDLE dan foydalanaman):

Yangi eskizni oching va quyidagi kodni yozing:

cv2 import qilish

video = cv2. VideoCapture (0) esa True: ret, frame = video.read () frame = cv2.flip (frame, -1) # tasvirni vertikal aylantirish uchun ishlatiladi cv2.imshow ('original', frame) cv2. imwrite ('original.jpg', frame) key = cv2.waitKey (1) if key == 27: break video.release () cv2.destroyAllWindows ()

Keling, ushbu kodda nima bo'lganini ko'rib chiqaylik. Birinchi qator, OpenCV kutubxonasini barcha funktsiyalarni ishlatish uchun import qiladi. VideoCapture (0) funktsiyasi ushbu funksiya tomonidan aniqlangan manbadan jonli videoni uzatishni boshlaydi, bu holda u 0 ga teng, bu raspi kamera degan ma'noni anglatadi. agar sizda bir nechta kameralar bo'lsa, turli raqamlarni qo'yish kerak. video.read () har bir kadr kameradan kelganini o'qiydi va uni "freym" deb nomlangan o'zgaruvchiga saqlaydi. flip () funktsiyasi tasvirni y o'qiga (vertikal) aylantiradi, chunki men kamerani teskari o'rnataman. imshow () "original" so'zi bilan boshlangan ramkalarimizni ko'rsatadi va imwrite () rasmimizni original-j.webp

OpenCV funktsiyalari bilan tanishish uchun rasmingizni ikkinchi usul bilan sinab ko'rishni tavsiya qilaman. Rasm "/pi/original.jpg" katalogida saqlanadi. Mening kameram olgan asl rasm yuqorida ko'rsatilgan.

Sinov motorlari:

Bu qadam har bir dvigatelning aylanish yo'nalishini aniqlash uchun zarurdir. Birinchidan, keling, avtoulov haydovchisining ishlash printsipi haqida qisqacha ma'lumot beraylik. Yuqoridagi rasmda dvigatel haydovchisining chiqib ketishi ko'rsatilgan. A ni yoqing, kirish 1 va kirish 2 vosita A boshqaruvi bilan bog'liq. B ni yoqing, Kirish 3 va Kirish 4 dvigatel B boshqaruvi bilan bog'liq. Yo'nalishni boshqarish "Kirish" qismi, tezlikni boshqarish "Enable" qismi bilan o'rnatiladi. Masalan, A dvigatelining yo'nalishini nazorat qilish uchun 1 -kirishni YUQORI (bu holatda 3,3 V), chunki biz malina pi ishlatamiz va 2 -kirishni LOW holatiga qo'yamiz, vosita ma'lum yo'nalishda aylanadi va teskari qiymatlarni o'rnatadi. Kirish 1 va Kirish 2 ga, vosita teskari yo'nalishda aylanadi. Agar kirish 1 = kirish 2 = (yuqori yoki past) bo'lsa, vosita qaytmaydi. Pimlarni yoqish malinadan (0 dan 3,3 V gacha) puls kengligi modulyatsiyasi (PWM) kirish signalini oladi va mos ravishda dvigatellarni ishga tushiradi. Masalan, 100% PWM signali biz maksimal tezlikda ishlayotganimizni va 0% PWM signali vosita aylanmaganligini bildiradi. Quyidagi kod dvigatellarning yo'nalishini aniqlash va ularning tezligini tekshirish uchun ishlatiladi.

import vaqti

RPi. GPIO -ni GPIO GPIO.setwarnings sifatida ogohlantiring (noto'g'ri) # Rulda -osiyolik pinlarni boshqarishga ruxsat beriladi = 22 # Jismoniy pin 15 in1 = 17 # Jismoniy pin 11 in2 = 27 # Jinsiy pin 13 # Gaz dvigatellari pinlari gaz kelebeği_enable = 25 # Jismoniy pin 22 in3 = 23 # Jismoniy pin 16 in4 = 24 # Jismoniy PIN 18 GPIO.setmode (GPIO. BCM) # GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO jismoniy raqamlash o'rniga GPIO raqamlashdan foydalaning. sozlash (in3, GPIO.out) GPIO. sozlash (in4, GPIO.out) GPIO. sozlash (gazni yoqish, GPIO.out) GPIO. sozlash (Rulda_enable, GPIO.out) # Rulda dvigatelini boshqarish GPIO. chiqish (in1, GPIO. YUQORI) GPIO.output (in2, GPIO. LOW) Rulda = GPIO. PWM (Rulda_enable, 1000) # kommutatsiya chastotasini 1000 Gts rulda sozlash.output (in4, GPIO. LOW) gaz kelebeği = GPIO. PWM (throttle_enable, 1000) # kommutatsiya chastotasini 1000 Hz ga sozlang. to'xtatish () time.sleep (1) throttle.start (25) # motorni 25 da ishga tushiradi % PWM signali -> (0,25 * batareya zo'riqishida) - haydovchi uchun boshqaruvni yo'qotish.start (100) # motorni 100% PWM signalida ishga tushiradi -> (1 * Batareya zo'riqishi) - haydovchining yo'qolish vaqti. uyqu (3) gaz kelebeği.

Bu kod gazni boshqaruvchi va boshqaruvchi motorni 3 soniya davomida ishga tushiradi va keyin ularni to'xtatadi. (Haydovchining yo'qolishi) voltmetr yordamida aniqlanishi mumkin. Masalan, biz bilamizki, 100% PWM signali dvigatel terminalida batareyaning to'liq kuchlanishini berishi kerak. Ammo, PWMni 100%ga o'rnatib, men haydovchi 3 V pasayishiga olib kelganini va dvigatel 12 V o'rniga 9 V ga ega ekanligini aniqladim (aynan menga kerak!). Yo'qotish chiziqli emas, ya'ni 100% yo'qotish 25% yo'qotishdan juda farq qiladi. Yuqoridagi kodni ishga tushirgandan so'ng, mening natijalarim quyidagicha edi:

Qisqartirish natijalari: agar in3 = YUQOR va in4 = LOW bo'lsa, qisish dvigatellari Clock-Wise (CW) aylanishiga ega bo'ladi, ya'ni mashina oldinga siljiydi. Aks holda, mashina orqaga qaytadi.

Rulda natijalari: agar in1 = YUQOR va in2 = LOW bo'lsa, rul mexanizmi maksimal chapga buriladi, ya'ni mashina chapga buriladi. Aks holda, mashina to'g'ri boshqariladi. Ba'zi tajribalardan so'ng, agar PWM signali 100% bo'lmasa, rul mexanizmi aylanmasligini aniqladim (ya'ni dvigatel to'liq o'ngga yoki to'liq chapga).

5 -qadam: chiziq chizig'ini aniqlash va sarlavha chizig'ini hisoblash

Chiziq chiziqlarini aniqlash va sarlavha chizig'ini hisoblash
Chiziq chiziqlarini aniqlash va sarlavha chizig'ini hisoblash
Chiziq chiziqlarini aniqlash va sarlavha chizig'ini hisoblash
Chiziq chiziqlarini aniqlash va sarlavha chizig'ini hisoblash
Chiziq chiziqlarini aniqlash va sarlavha chizig'ini hisoblash
Chiziq chiziqlarini aniqlash va sarlavha chizig'ini hisoblash

Bu bosqichda mashinaning harakatini boshqaradigan algoritm tushuntiriladi. Birinchi rasm butun jarayonni ko'rsatadi. Tizimning kirishi - tasvirlar, chiqish - teta (boshqaruv burchagi daraja). E'tibor bering, ishlov berish 1 ta rasmda amalga oshiriladi va barcha kadrlarda takrorlanadi.

Kamera:

Kamera (320 x 240) aniqlikdagi videoni yozishni boshlaydi. Men piksellar sonini pasaytirishni maslahat beraman, shuning uchun siz kadr tezligini (fps) yaxshiroq olishingiz mumkin, chunki har bir kadrga ishlov berish texnikasi qo'llanilgandan keyin kadr tezligi pasayadi. Quyidagi kod dasturning asosiy tsikli bo'ladi va har bir qadamni ushbu kodga qo'shib qo'yadi.

cv2 import qilish

numpy -ni np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) sifatida kengaytirish To'g'ri: 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 ()

Bu erda kod 4 -qadamda olingan asl tasvirni ko'rsatadi va yuqoridagi rasmlarda ko'rsatilgan.

HSV rang maydoniga aylantirish:

Kameradan kadr sifatida video yozib olgandan so'ng, keyingi qadam - har bir kadrni Hue, Saturation va Value (HSV) rang maydoniga aylantirish. Buning asosiy afzalligi - ranglarni yorqinlik darajasiga ko'ra farqlash. Va bu erda HSV rang maydonining yaxshi izohi. HSV -ga o'tish quyidagi funktsiya yordamida amalga oshiriladi:

def convert_to_HSV (ramka):

hsv = cv2.cvtColor (ramka, cv2. COLOR_BGR2HSV) cv2.imshow ("HSV", hsv) hsvga qaytish

Bu funksiya asosiy tsikldan chaqiriladi va ramkani HSV rang oralig'iga qaytaradi. HSV rang oralig'ida men olgan ramka yuqorida ko'rsatilgan.

Moviy rang va qirralarni aniqlash:

Rasmni HSV rang maydoniga aylantirgandan so'ng, bizni qiziqtirgan rangni aniqlash vaqti keldi (ya'ni ko'k rang, chunki bu chiziq chizig'ining rangi). HSV ramkasidan ko'k rangni olish uchun rang, to'yinganlik va qiymat oralig'ini ko'rsatish kerak. HSV qiymatlari haqida yaxshiroq tasavvurga ega bo'lish uchun bu erga qarang. Ba'zi tajribalardan so'ng, ko'k rangning yuqori va pastki chegaralari quyidagi kodda ko'rsatilgan. Va har bir freymdagi umumiy buzilishlarni kamaytirish uchun qirralar faqat qirrali detektor yordamida aniqlanadi. Canny edge haqida ko'proq ma'lumotni bu erda topishingiz mumkin. Qoida tariqasida, Canny () funktsiyasining parametrlarini 1: 2 yoki 1: 3 nisbatda tanlash kerak.

def detect_edges (ramka):

pastki_ko'k = np.array ([90, 120, 0], dtype = "uint8") # ko'k rangning pastki chegarasi yuqori_ko'k = np.array ([150, 255, 255], dtype = "uint8") # yuqori chegarasi ko'k rangli niqob = cv2.inRange (hsv, pastki_ko'k, yuqori_ko'k) # bu niqob hamma narsani filtrlaydi, lekin ko'k # qirralarini aniqlaydi = cv2. Canny (niqob, 50, 100) cv2.imshow ("qirralar", qirralar) qaytish

Bu funktsiya, shuningdek, HSV rangli bo'shliq ramkasini parametr sifatida qabul qiladigan va qirrali ramkani qaytaradigan asosiy pastadirdan ham chaqiriladi. Men olgan qirrali ramka yuqorida topilgan.

Qiziqish mintaqasini (ROI) tanlang:

Favqulodda hududni tanlashda faqat bitta freymga e'tibor qaratish kerak. Bunday holda, men mashinaning atrofdagi ko'p narsalarni ko'rishini xohlamayman. Men faqat mashina yo'l chizig'iga e'tibor qaratishini va boshqa narsalarga e'tibor bermasligini xohlayman. P. S: koordinata tizimi (x va y o'qlari) yuqori chap burchakdan boshlanadi. Boshqacha aytganda, (0, 0) nuqta yuqori chap burchakdan boshlanadi. y o'qi balandligi va x o'qi kengligi. Quyidagi kod faqat ramkaning pastki yarmiga e'tibor qaratadigan qiziqish hududini tanlaydi.

def region_of_interest (qirralar):

balandlik, kenglik = qirralar.shapa # qirralarning balandligi va kengligini chiqarib oling niqob n = np.zeros_like (qirralarning) # qirralarning bir xil o'lchamlari bilan bo'sh matritsani yasang # faqat ekranning pastki yarmiga e'tibor bering # koordinatalarini ko'rsating 4 nuqta (pastki chap, yuqori chap, yuqori o'ng, pastki o'ng) ko'pburchak = np.array (

Bu funktsiya qirrali ramkani parametr sifatida qabul qiladi va 4 ta oldindan belgilangan nuqtali ko'pburchakni chizadi. U faqat ko'pburchak ichidagi narsalarga e'tibor qaratadi va uning tashqarisidagi hamma narsaga e'tibor bermaydi. Mening qiziqish doiram yuqorida ko'rsatilgan.

Chiziq segmentlarini aniqlash:

Qirg'ichli ramkadan chiziq segmentlarini aniqlash uchun konvertatsiya ishlatiladi. Hough transform - bu har qanday shaklni matematik shaklda aniqlash usuli. U deyarli har qanday ob'ektni ovozlar soniga ko'ra buzilgan bo'lsa ham aniqlay oladi. bu erda Hough konvertatsiyasi haqida ajoyib ma'lumot berilgan. Ushbu dastur uchun har bir freymdagi chiziqlarni aniqlash uchun cv2. HoughLinesP () funktsiyasi ishlatiladi. Bu funktsiyaning muhim parametrlari:

cv2. HoughLinesP (ramka, rho, teta, min_tosh chegarasi, minLineLength, maxLineGap)

  • Frame: biz chiziqlarni aniqlamoqchi bo'lgan ramka.
  • rho: bu piksellar orasidagi masofaning aniqligi (odatda u = 1)
  • teta: radianlarda burchak aniqligi (har doim = np.pi/180 ~ 1 daraja)
  • min_threshold: chiziq sifatida ko'rib chiqilishi kerak bo'lgan minimal ovoz
  • minLineLength: chiziqdagi minimal uzunlik. Bu raqamdan qisqa bo'lgan har qanday chiziq chiziq deb hisoblanmaydi.
  • maxLineGap: 2 satr orasidagi piksellar orasidagi maksimal bo'shliq 1 qator sifatida ko'rib chiqiladi. (Bu mening holatimda ishlatilmaydi, chunki men foydalanadigan bo'lak chiziqlarida bo'sh joy yo'q).

Bu funksiya chiziqning oxirgi nuqtalarini qaytaradi. Hough transform yordamida chiziqlarni aniqlash uchun quyidagi asosiy funktsiyadan chaqiriladi:

def detect_line_segments (kesilgan_ qirralar):

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

O'rtacha qiyalik va kesishish (m, b):

Eslatib o'tamiz, chiziq tenglamasi y = mx + b bilan berilgan. Bu erda m-chiziqning qiyaligi va b-y kesishish. Bu qismda Hough konvertatsiyasi yordamida aniqlangan chiziq segmentlarining qiyaliklari va kesishmalarining o'rtacha qiymati hisoblab chiqiladi. Buni amalga oshirishdan oldin, yuqorida ko'rsatilgan asl ramka rasmini ko'rib chiqaylik. Chap chiziq yuqoriga ko'tarilgandek ko'rinadi, shuning uchun u salbiy qiyalikka ega (koordinata tizimining boshlanish nuqtasini eslaysizmi?). Boshqacha aytganda, chap chiziq chizig'i x1 <x2 va y2 x1 va y2> y1 ga ega, bu esa ijobiy qiyalik beradi. Shunday qilib, ijobiy qiyalikka ega bo'lgan barcha chiziqlar to'g'ri chiziqli nuqtalar hisoblanadi. Vertikal chiziqlar (x1 = x2) bo'lsa, qiyalik cheksiz bo'ladi. Bunday holda, biz xatoga yo'l qo'ymaslik uchun barcha vertikal chiziqlarni o'tkazib yuboramiz. Ushbu aniqlikka aniqlik kiritish uchun har bir ramka ikkita chegara chizig'i orqali ikkita mintaqaga (o'ng va chap) bo'linadi. O'ng chegara chizig'idan kattaroq barcha kenglik nuqtalari (x o'qi nuqtalari) o'ng chiziqni hisoblash bilan bog'liq. Va agar barcha kenglik nuqtalari chap chegara chizig'idan kichik bo'lsa, ular chap chiziqli hisoblash bilan bog'liq. Quyidagi funksiya Hough transformatsiyasi yordamida aniqlangan chiziqlar va ishlov beriladigan qismlarni oladi va o'rtacha qiyalik va ikkita chiziqli chiziqni qaytaradi.

def o'rtacha_sifiq_intercept (ramka, chiziq_segmentlari):

lane_lines = line_segments None bo'lsa: chop etish ("chiziq segmenti aniqlanmagan") qaytish lane_lines balandligi, kengligi, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - border) right_region_boundary = line * segmentlarida line_segment uchun kenglik * chegarasi: x1, y1, x2, y2 uchun line_segmentda: agar x1 == x2: chop etish ("vertikal chiziqlarni o'tkazib yuborish (qiyalik = cheksizlik)") davom ettirish fit = np.polyfit ((x1, x2), (y1, y2), 1) qiyalik = (y2 - y1) / (x2 - x1) kesishish = y1 - (qiyalik * x1) agar qiyalik <0: agar x1 <chap_region_shakarasi va x2 o'ng_region_ chegarasi va x2> o'ng_region_ chegarasi: o'ng_fit. qo'shish ((qiyalik, kesish)) left_fit_average = np.o'rtacha (chap_fit, o'q = 0) agar len (chap_fit)> 0: chiziqli chiziqlar) if len (right_fit)> 0: lane_lines.append (make_points (frame, right_fit_average)) # chiziqli chiziqlar-bu o'ng va chap chiziq chiziqlari koordinatalarini o'z ichiga olgan 2 o'lchamli qator # masalan: lan e_lines =

make_points () - o'rtacha_slope_intercept () funktsiyasi uchun yordamchi funktsiya bo'lib, u chiziqli chiziqlarning chegaralangan koordinatalarini qaytaradi (ramkaning pastidan o'rtasiga).

def make_points (ramka, chiziq):

balandlik, kenglik, _ = ramka. qiyalik qiyalik, kesish = chiziq y1 = balandlik # ramkaning pastki qismi y2 = int (y1 / 2) # agar qiyalik == 0 bo'lsa, ramkaning o'rtasidan pastga nuqta qo'ying: qiyalik = 0,1 x1 = int ((y1 - kesish) / qiyalik) x2 = int ((y2 - kesish) / qiyalik) qaytish

0 ga bo'linishni oldini olish uchun shart berilgan. Agar qiyalik = 0, bu y1 = y2 (gorizontal chiziq) ma'nosini bildirsa, qiyalikni 0 ga yaqin qo'ying. Bu algoritmning ishlashiga ta'sir qilmaydi, shuningdek, imkonsiz holatni (0 ga bo'lish) oldini oladi.

Kadrlarda chiziqli chiziqlarni ko'rsatish uchun quyidagi funktsiyadan foydalaniladi:

def display_lines (ramka, chiziqlar, line_color = (0, 255, 0), chiziq_ kengligi = 6): # chiziq rangi (B, G, R)

line_image = np.zeros_like (ramka), agar chiziqlar None bo'lmasa: satr ichidagi satr uchun: x1, y1, x2, y2 satrda: 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 () funktsiyasi quyidagi parametrlarni oladi va u ikkita tasvirni birlashtirish uchun ishlatiladi, lekin har biriga og'irlik beradi.

cv2.addWeighted (image1, alfa, image2, beta, gamma)

Quyidagi tenglama yordamida chiqish tasvirini hisoblab chiqadi:

chiqish = alfa * tasvir1 + beta * tasvir2 + gamma

Cv2.addWeighted () funktsiyasi haqida ko'proq ma'lumot bu erda.

Sarlavha chizig'ini hisoblash va ko'rsatish:

Bu bizning motorimizga tezlikni qo'llashdan oldingi oxirgi qadam. Sarlavha chizig'i, rul dvigateliga aylanishi kerak bo'lgan yo'nalishni berishi va gaz dvigatellariga ishlash tezligini berishi kerak. Sarlavha chizig'ini hisoblash-aniq trigonometriya, tan va atan (tan^-1) trigonometrik funktsiyalari ishlatiladi. Ba'zi o'ta og'ir holatlarda, kamera faqat bitta chiziqli chiziqni aniqlasa yoki hech qanday chiziq aniqlanmasa. Bu holatlarning barchasi quyidagi funksiyada ko'rsatilgan:

def get_steering_angle (ramka, chiziqli chiziqlar):

balandlik, kenglik, _ = ramka.shakl agar chiziq (chiziqli chiziqlar) == 2: # agar ikkita chiziqli chiziq aniqlansa _, _, chap_x2, _ = chiziqli chiziqlar [0] [0] # x2 chiziqli qatorlar qatoridan x2 qoldirilgan., o'ng_x2, _ = chiziqli chiziqlar [1] [0] # chiziqli chiziqlar qatoridan o'ng x2 ajratish mid = int (kenglik / 2) x_offset = (chap_x2 + o'ng_x2) / 2 - o'rtadagi y_offset = int (balandlik / 2) elif len (chiziqli chiziqlar)) == 1: # agar bitta chiziq aniqlansa x1, _, x2, _ = chiziqli chiziqlar [0] [0] x_offset = x2 - x1 y_offset = int (balandlik / 2) elif len (chiziqli chiziqlar) == 0: # agar hech qanday chiziq aniqlanmasa x_offset = 0 y_offset = int (balandlik / 2) burchak_to_mid_radian = matematik

Birinchi holda x_offset - bu o'rtacha ((o'ng x2 + chap x2) / 2) ekranning o'rtasidan qanchalik farq qiladi. y_offset har doim balandlik sifatida qabul qilinadi / 2. Yuqoridagi oxirgi rasmda sarlavha chizig'ining namunasi ko'rsatilgan. angle_to_mid_radians yuqoridagi oxirgi rasmda ko'rsatilgan "teta" bilan bir xil. Agar steering_angle = 90 bo'lsa, demak, mashinada "balandlik / 2" chizig'iga perpendikulyar yo'nalish chizig'i bor va mashina rulsiz oldinga siljiydi. Rulda burilish burchagi> 90 bo'lsa, mashina o'ngga, aks holda chapga burilishi kerak. Sarlavha chizig'ini ko'rsatish uchun quyidagi funksiya ishlatiladi:

def display_heading_line (ramka, rul_burchagi, line_color = (0, 0, 255), chiziq_ kengligi = 5)

heading_image = np.zeros_like (ramka) balandlik, kenglik, _ = frame.shakl steering_angle_radian = rul_angle / 180.0 * math.pi x1 = int (kenglik / 2) y1 = balandlik x2 = int (x1 - balandlik / 2 / matematik.tan) (steering_angle_radian)) y2 = int (balandlik / 2) cv2.line (sarlavha_ tasviri, (x1, y1), (x2, y2), chiziq_rang, chiziq_ kengligi) sarlavha_ tasviri = cv2.addWeighted (ramka, 0,8, sarlavha_ tasviri, 1, 1) heading_image -ga qaytish

Yuqoridagi funktsiya sarlavha chizig'i chizilgan ramka va boshqaruv burchagini kiritadi. Sarlavha chizig'ining tasvirini qaytaradi. Mening holatimda olingan sarlavha chizig'i yuqoridagi rasmda ko'rsatilgan.

Barcha kodlarni birlashtirish:

Endi kod yig'ishga tayyor. Quyidagi kod har bir funktsiyani chaqiradigan dasturning asosiy tsiklini ko'rsatadi:

cv2 import qilish

np video = cv2. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) video.set (cv2. CAP_PROP_FRAME_HEIGHT, 240) sifatida numpy -ni import qilish True: ret, frame = video.read () frame = cv2.flip (ramka, -1) #funktsiyalarni chaqirish hsv = convert_to_HSV (ramka) qirralari = detect_edges (hsv) roi = qiziqish_foydasi (qirralar) line_segments = aniqlash_line_segmentlari (roi) chiziqli chiziqlar = o'rtacha_samlik_qabul qilish (ramka, chiziq_segmentlari) chiziqli chiziqlar_shakli_foydalanishi = 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 -qadam: PD boshqaruvini qo'llash

PD nazoratini qo'llash
PD nazoratini qo'llash

Endi biz boshqaruv burchagini dvigatellarga berishga tayyormiz. Yuqorida aytib o'tilganidek, agar rul burchagi 90 dan katta bo'lsa, mashina o'ngga buriladi, aks holda u chapga buriladi. Men burchakni 90 dan yuqori bo'lsa, rulni o'ng tomonga buradigan oddiy burchak kodini qo'lladim, agar burilish tezligi (10% PWM) da burchak burchagi 90 dan past bo'lsa, chapga buriladi, lekin menda juda ko'p xatolar bor edi. Men olgan asosiy xato shundaki, mashina har qanday burilishga yaqinlashganda, rulda rotor to'g'ridan -to'g'ri harakat qiladi, lekin qisish dvigatellari tiqilib qoladi. Men burilish tezligini (20% PWM) oshirishga harakat qildim, lekin robot yo'llardan chiqib ketishi bilan tugadi. Menga rulda burchak juda katta bo'lsa, tezlikni biroz oshiradigan va rulda burchagi unchalik katta bo'lmasa tezlikni biroz oshiradigan narsa kerak edi, keyin mashina 90 gradusga yaqinlashganda tezlikni boshlang'ich qiymatiga tushiradi. Yechim PD tekshirgichidan foydalanish edi.

PID tekshiruvi mutanosib, integral va lotin nazoratchi degan ma'noni anglatadi. Ushbu turdagi chiziqli boshqaruvchilar robototexnika dasturlarida keng qo'llaniladi. Yuqoridagi rasmda odatda PID qayta aloqa nazorat qilish davri ko'rsatilgan. Bu nazoratchining maqsadi - zavodni ba'zi shartlarga ko'ra yoqadigan yoki o'chiradigan "yoqish" kontrollerlaridan farqli o'laroq, "belgilangan nuqtaga" erishish. Ba'zi kalit so'zlar ma'lum bo'lishi kerak:

  • O'rnatish nuqtasi: bu sizning tizimingizga erishmoqchi bo'lgan kerakli qiymat.
  • Haqiqiy qiymat - bu sensor tomonidan seziladigan haqiqiy qiymat.
  • Xato: bu belgilangan qiymat va haqiqiy qiymat o'rtasidagi farq (xato = O'rnatish nuqtasi - Haqiqiy qiymat).
  • Boshqariladigan o'zgaruvchi: uning nomidan, siz boshqarmoqchi bo'lgan o'zgaruvchi.
  • Kp: mutanosib doimiy.
  • Ki: integral doimiy.
  • Kd: lotin doimiysi.

Qisqacha aytganda, PID boshqaruv tizimining aylanishi quyidagicha ishlaydi:

  • Foydalanuvchi tizimga erishish uchun kerakli nuqtani belgilaydi.
  • Xato hisoblab chiqilgan (xato = belgilangan nuqta - haqiqiy).
  • P tekshiruvi xato qiymatiga mutanosib harakat yaratadi. (xato kuchayadi, P harakati ham oshadi)
  • Men nazoratchi vaqt o'tishi bilan tizimning barqaror holatidagi xatoni yo'q qiladigan xatoni birlashtiradi, lekin uning haddan tashqari ko'payishini oshiradi.
  • D tekshiruvi - bu xato uchun vaqt hosilasi. Boshqacha qilib aytganda, bu xatoning qiyaligi. U xato hosilasiga mutanosib harakat qiladi. Bu boshqaruvchi tizimning barqarorligini oshiradi.
  • Nazoratchining chiqishi uchta nazoratchining yig'indisi bo'ladi. Agar xato 0 bo'lsa, tekshirgichning chiqishi 0 bo'ladi.

PID tekshirgichining ajoyib izohini bu erda topishingiz mumkin.

Yo'lni ushlab turuvchi mashinaga qaytib, men boshqaradigan o'zgaruvchi tezlikni pasaytirdi (chunki rulda faqat o'ng yoki chap ikkita holat bor). PD nazorat qilish moslamasi shu maqsadda ishlatiladi, chunki agar xato o'zgarishi juda katta bo'lsa (ya'ni katta burilish) D harakati tezlikni tezligini oshiradi va agar bu xato 0 ga yaqinlashsa mashinani sekinlashtiradi. Men PDni amalga oshirish uchun quyidagi amallarni qildim. nazoratchi:

  • Belgilangan nuqtani 90 darajaga qo'ying (men har doim mashinaning to'g'ri harakatlanishini xohlayman)
  • O'rtadan burilish burchagi hisoblangan
  • Burilish ikkita ma'lumotni beradi: xato qanchalik katta (burilish kattaligi) va rulda dvigatel qaysi tomonga burilishi kerak (og'ish belgisi). Agar burilish ijobiy bo'lsa, mashina o'ngga, aks holda chapga burilishi kerak.
  • Burilish salbiy yoki ijobiy bo'lgani uchun "xato" o'zgaruvchisi aniqlanadi va har doim burilishning mutlaq qiymatiga teng bo'ladi.
  • Xato doimiy Kp ga ko'paytiriladi.
  • Xato vaqt farqiga uchraydi va doimiy Kd ga ko'paytiriladi.
  • Dvigatellarning tezligi yangilanadi va tsikl yana boshlanadi.

Qisqartuvchi dvigatellarning tezligini boshqarish uchun asosiy tsiklda quyidagi kod ishlatiladi:

tezlik = 10 # ish tezligi % PWMda

# O'zgaruvchilar har bir tsiklni yangilash kerak lastTime = 0 lastError = 0 # PD konstantalari Kp = 0.4 Kd = Kp * 0.65 To'g'ri bo'lsa: hozir = time.time () # joriy vaqt o'zgaruvchisi dt = hozir - lastTime og'ish = Rulda_burchak - 90 # ekvivalent to ang_to_mid_deg o'zgarmaydigan xato = abs (og'ish), agar burilish -5: # 10 graduslik xato diapazoni mavjud bo'lsa, # boshqarmang, 0 xato = 0 GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO). LOW) Rulda to'xtash () elif burilish> 5: agar burilish ijobiy bo'lsa GPIO.output (in1, GPIO. LOW) GPIO.output (in2, GPIO. HIGH) Rulda.start (100) elif burilish < -5: burilish manfiy bo'lsa GPIO.output (in1, GPIO. HIGH) GPIO.output (in2, GPIO. LOW) steering.start (100) lotin = kd * (xato - lastError) / dt proportsional = kp * xato PD = int (tezlik + hosilasi + mutanosib) spd = abs (PD), agar spd> 25: spd = 25 throttle.start (spd) lastError = xato lastTime = time.time ()

Agar xato juda katta bo'lsa (o'rtadan chetga chiqish yuqori bo'lsa), proportsional va lotin harakatlar yuqori bo'ladi, buning natijasida yuqori tezlik pasayadi. Xato 0 ga yaqinlashganda (o'rtadan chetga chiqish past), lotin harakati teskari harakat qiladi (qiyalik manfiy) va tizimning barqarorligini ta'minlash uchun tezlikni pasaytirish tezligi pasayadi. To'liq kod quyida biriktirilgan.

7 -qadam: Natijalar

Yuqoridagi videolar men olgan natijalarni ko'rsatadi. Bu ko'proq sozlash va qo'shimcha sozlashlarni talab qiladi. Men malina pi -ni LCD displey ekraniga uladim, chunki mening tarmog'imdagi video oqimi kechikkan edi va u bilan ishlash juda asabiylashdi, shuning uchun videoda malina pi ga ulangan simlar bor. Yo'lni chizish uchun ko'pikli taxtalardan foydalandim.

Men ushbu loyihani yaxshiroq qilish uchun sizning tavsiyalaringizni eshitishni kutaman! Umid qilamanki, bu ko'rsatma sizga yangi ma'lumot berish uchun etarlicha yaxshi bo'ldi.

Tavsiya: