Агуулгын хүснэгт:

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 нь жолоодлогын хувьд). Энэхүү машины гол сул тал нь жолоодлого нь "жолоодлого байхгүй" ба "бүрэн жолоодлогын" хооронд хязгаарлагддаг явдал юм. Өөрөөр хэлбэл, servo-ruling RC машинуудаас ялгаатай нь тодорхой өнцгөөр жолоодож чадахгүй. Та бөөрөлзгөнө пи -д зориулан бүтээсэн ижил төстэй машины иж бүрдлийг эндээс олж болно.
  • Raspberry pi 3 загвар b+: энэ бол маш олон боловсруулалтын үе шатыг гүйцэтгэх машины тархи юм. Энэ нь 1.4 GHz давтамжтай дөрвөн цөмт 64 битийн процессор дээр суурилсан болно. Би эндээс өөрийнхийг авсан.
  • Raspberry pi 5 MP камерын модуль: 1080p @ 30 fps, 720p @ 60 fps, 640x480p 60/90 бичлэгийг дэмждэг. Энэ нь бөөрөлзгөнө pi руу шууд залгах боломжтой цуваа интерфейсийг дэмждэг. Энэ нь зураг боловсруулах програмуудын хувьд хамгийн сайн сонголт биш боловч энэ төсөлд хангалттай бөгөөд маш хямд юм. Би эндээс өөрийнхийг авсан.
  • Мотор драйвер: DC хөдөлгүүрийн чиглэл, хурдыг хянахад ашигладаг. Энэ нь 1 самбар дээрх 2 тогтмол гүйдлийн хөдөлгүүрийн хяналтыг дэмждэг бөгөөд 1.5 А тэсвэрлэх чадвартай.
  • Power Bank (Нэмэлт): Би бөөрөлзгөнө pi -ийг тусад нь асаахын тулд цахилгаан банк (5V, 3А гэсэн үнэлгээтэй) ашигласан. Бөөрөлзгөнө пи -ийг 1 эх үүсвэрээс асаахын тулд доош чиглэсэн хөрвүүлэгч (Бак хөрвүүлэгч: 3А гаралтын гүйдэл) ашиглах ёстой.
  • 3s (12 V) LiPo зай: Лити полимер батерей нь робот техникийн салбарт маш сайн гүйцэтгэлтэй гэдгээрээ алдартай. Энэ нь мотор драйверыг тэжээхэд ашиглагддаг. Би эндээс худалдаж авсан.
  • Эрэгтэй, эрэгтэй, эмэгтэй холбогч утас.
  • Хоёр талт соронзон хальс: RC машин дээр эд ангиудыг холбоход ашигладаг.
  • Цэнхэр соронзон хальс: Энэ бол төслийн маш чухал бүрэлдэхүүн хэсэг бөгөөд энэ нь машин хооронд явах хоёр эгнээтэй шугамыг хийхэд хэрэглэгддэг. Та хүссэн өнгөө сонгож болно, гэхдээ би хүрээлэн буй орчноос өөр өнгө сонгохыг зөвлөж байна.
  • Зип зангиа, модон баар.
  • Шургуулагч.

Алхам 2: Raspberry Pi дээр OpenCV суулгаж, алсын дэлгэцийг тохируулах

Raspberry Pi дээр OpenCV суулгаж, алсын дэлгэцийг тохируулах
Raspberry Pi дээр OpenCV суулгаж, алсын дэлгэцийг тохируулах

Энэ алхам нь жаахан ядаргаатай бөгөөд хэсэг хугацаа шаардагдана.

OpenCV (Open source Computer Vision) нь нээлттэй эхийн компьютерийн хараа, машин сургалтын програм хангамжийн номын сан юм. Номын сан нь 2500 гаруй оновчтой алгоритмтай. Бөөрөлзгөнө pi дээрээ openCV суулгах, бөөрөлзгөнө pi үйлдлийн системийг суулгахын тулд ЭНЭ маш энгийн гарын авлагыг дагана уу (хэрэв та одоо болтол хийгээгүй бол). OpenCV-ийг бүтээх үйл явц нь сайн хөргөлттэй өрөөнд 1.5 цаг орчим үргэлжилж болохыг анхаарна уу (процессорын температур маш өндөр болно!) Тиймээс цай уугаад тэвчээртэй хүлээгээрэй: D.

Алсын дэлгэцийн хувьд Windows/Mac төхөөрөмжөөс бөөрөлзгөнө pi руу алсын зайнаас нэвтрэх тохиргоог хийхийн тулд ЭНЭ гарын авлагыг дагана уу.

Алхам 3: эд ангиудыг хооронд нь холбох

Эд ангиудыг хооронд нь холбох
Эд ангиудыг хооронд нь холбох
Эд ангиудыг хооронд нь холбох
Эд ангиудыг хооронд нь холбох
Эд ангиудыг хооронд нь холбох
Эд ангиудыг хооронд нь холбох

Дээрх зургууд нь бөөрөлзгөнө pi, камерын модуль, мотор драйвер хоёрын холболтыг харуулж байна. Миний ашигласан мотор тус бүр 9 В хүчдэлтэй 0.35 А шингээдэг бөгөөд энэ нь мотор жолооч 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) байхад 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-j.webp

OpenCV функцтэй танилцахын тулд би зургаа хоёрдахь аргаар туршиж үзэхийг зөвлөж байна. Зургийг "/pi/original.jpg" санд хадгална. Миний камер авсан анхны зургийг дээр харуулав.

Туршилтын мотор:

Энэ алхам нь мотор бүрийн эргэх чиглэлийг тодорхойлоход зайлшгүй шаардлагатай. Эхлээд мотор жолоочийн ажиллах зарчмын талаар товч танилцуулъя. Дээрх зурагт мотор драйверийг хадаж байгааг харуулж байна. А -г идэвхжүүлснээр оролт 1 ба оролт 2 нь А хөдөлгүүрийн удирдлагатай холбоотой. В -ийг идэвхжүүл, оролт 3 ба оролт 4 нь моторын удирдлагатай холбоотой. Чиглэлийн хяналтыг "Оролт" хэсгээр, хурдны хяналтыг "Идэвхжүүлэх" хэсгээр тогтооно. Жишээлбэл, А моторын чиглэлийг хянахын тулд 1 -р оролтыг Өндөр болгож (бөөрөлзгөнө pi ашиглаж байгаа тул энэ тохиолдолд 3.3 В), 2 -р оролтыг LOW болгож тохируулахын тулд хөдөлгүүр тодорхой чиглэлд эргэж, эсрэг утгуудыг тохируулна. оролт 1 ба оролт 2 руу мотор эсрэг чиглэлд эргэх болно. Хэрэв оролт 1 = оролт 2 = (өндөр эсвэл бага) байвал хөдөлгүүр эргэхгүй. Зүүг идэвхжүүлэхийн тулд бөөрөлзгөнөөс (0 -ээс 3.3 В хүртэл) импульсийн өргөний модуляци (PWM) оролтын дохиог авч, моторыг зохих ёсоор ажиллуулна уу. Жишээлбэл, ХОУХ -ны 100% дохио нь бид хамгийн дээд хурд дээр ажиллаж байгаа гэсэн үг бөгөөд 0% ХОУХ -ны дохио нь мотор эргэхгүй байна гэсэн үг юм. Дараах кодыг ашиглан хөдөлгүүрийн чиглэлийг тодорхойлж, хурдыг нь шалгадаг.

импортлох хугацаа

RPi. GPIO -ийг GPIO GPIO болгон импортлох 23 # Physical Pin 16 in4 = 24 # Physical Pin 18 GPIO.setmode (GPIO. BCM) # GPIO.setup (in1, GPIO.out) GPIO.setup (in2, GPIO.out) GPIO -ийн оронд GPIO дугаарлалт ашиглана уу. тохиргоо (in3, GPIO.out) GPIO. тохиргоо (in4, GPIO.out) GPIO. тохиргоо (тохируулагчийг идэвхжүүлэх, GPIO.out) GPIO. тохиргоо (жолоодлогыг идэвхжүүлэх, GPIO.out) # Жолооны моторын хяналт GPIO. гаралт (in1, GPIO. HIGH) GPIO.output (in2, GPIO. LOW) steering = GPIO. PWM (steering_enable, 1000) # сэлгэн залгах давтамжийг 1000 Гц болгон тохируулна жолоодлого.stop () # Throttle Motors Control GPIO.output (in3, GPIO. HIGH) GPIO.output (in4, GPIO. LOW) throttle = GPIO. PWM (throttle_enable, 1000) # сэлгэн залгах давтамжийг 1000 Гц -т тохируулна.тоглох () цаг. % PWM дохио -> (0.25 * батерейны хүчдэл) - жолоочийн жолоодлогын алдагдал. эхлүүлэх (100) # моторыг 100% ХОУХ -ны дохиогоор асаадаг>> (1 * Батерейны хүчдэл) - жолоочийн алдагдсан хугацаа. унтах (3) тохируулагч. зогсоох () жолоодлого.

Энэ код нь тохируулагч болон жолооны моторыг 3 секундын турш ажиллуулж, дараа нь зогсоох болно. Жолоочийн алдагдлыг вольтметр ашиглан тодорхойлж болно. Жишээлбэл, 100% PWM дохио нь хөдөлгүүрийн терминал дахь батерейны бүрэн хүчдэлийг өгөх ёстой гэдгийг бид мэднэ. Гэхдээ ХОУХШ -ийг 100%болгосноор жолооч 3 В -ийн уналтыг үүсгэж, мотор нь 12 В -ийн оронд 9 В -ыг авдаг болохыг олж мэдсэн (яг надад хэрэгтэй зүйл!). Алдагдал нь шугаман бус, өөрөөр хэлбэл 100% -ийн алдагдал нь 25% -ийн алдагдлаас эрс ялгаатай юм. Дээрх кодыг ажиллуулсны дараа миний үр дүн дараах байдалтай байв.

Хяналтын үр дүн: хэрэв in3 = HIGH, in4 = LOW байвал тохируулагч моторууд нь Clock-Wise (CW) эргэдэг, өөрөөр хэлбэл машин урагшлах болно. Үгүй бол машин ухрах болно.

Жолоодлогын үр дүн: хэрэв in1 = HIGH, in2 = LOW бол жолооны мотор зүүн тийш эргэх болно, өөрөөр хэлбэл машин зүүн тийш жолоодох болно. Үгүй бол машин зөв жолоодох болно. Зарим туршилт хийсний дараа ХОУХ -ны дохио 100% биш байвал жолооны мотор эргэхгүй болохыг олж мэдэв (өөрөөр хэлбэл мотор бүрэн баруун тийш эсвэл зүүн тийш чиглүүлэх болно).

Алхам 5: Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох

Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох
Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох
Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох
Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох
Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох
Эгнээний шугамыг илрүүлж, гарчгийн шугамыг тооцоолох

Энэ алхамд машины хөдөлгөөнийг хянах алгоритмыг тайлбарлах болно. Эхний зураг нь бүх үйл явцыг харуулж байна. Системийн оролт нь зураг, гаралт нь тета (градусын жолооны өнцөг) юм. Боловсруулалт 1 зураг дээр хийгддэг бөгөөд бүх фрэйм дээр давтагдах болно гэдгийг анхаарна уу.

Камер:

Камер нь (320 x 240) нарийвчлалтай видео бичиж эхлэх болно. Би нарийвчлалыг бууруулахыг зөвлөж байна, ингэснээр хүрээ тус бүрт боловсруулалтын техникийг хэрэглэсний дараа кадрын хурд буурах болно. Доорх код нь програмын гол давталт байх бөгөөд энэ кодын алхам бүрийг нэмж оруулах болно.

cv2 импортлох

numpy -ийг np video = cv2 болгон импортлох. VideoCapture (0) video.set (cv2. CAP_PROP_FRAME_WIDTH, 320) # өргөнийг 320 p болгон тохируулах Үнэн: ret, frame = video.read () frame = cv2.flip (frame, -1) cv2.imshow ("original", frame) key = cv2.waitKey (1) 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 -ийн үнэ цэнийн талаар илүү сайн ойлголттой болохын тулд эндээс үзнэ үү. Зарим туршилтын дараа цэнхэр өнгөний дээд ба доод хязгаарыг доорх код дээр харуулав. Хүрээ тус бүрийн ерөнхий гажуудлыг багасгахын тулд ирмэгийг зөвхөн ирмэгийн детектор ашиглан илрүүлдэг. Канни ирмэгийн талаар илүү ихийг эндээс олж болно. Дүрэм бол 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)

Энэ функцийг мөн HSV өнгөний орон зайг параметр болгон авч, ирмэгийн хүрээг буцаах үндсэн давталтаас дуудах болно. Миний олж авсан ирмэгийн хүрээг дээрээс олж болно.

Сонирхлын бүсийг (ROI) сонгоно уу:

Хүрээний зөвхөн 1 бүсэд анхаарлаа төвлөрүүлэхийн тулд сонирхож буй бүсээ сонгох нь маш чухал юм. Энэ тохиолдолд би машиныг хүрээлэн буй орчинд олон зүйлийг харахыг хүсэхгүй байна. Би машиныг эгнээ шугам дээр анхаарлаа төвлөрүүлж, бусад зүйлийг үл тоомсорлохыг хүсч байна. P. S: координатын систем (x ба y тэнхлэгүүд) зүүн дээд булангаас эхэлдэг. Өөрөөр хэлбэл (0, 0) цэг нь зүүн дээд булангаас эхэлнэ. y тэнхлэг нь өндөр, x тэнхлэг нь өргөн юм. Доорх код нь хүрээний доод хагаст анхаарлаа төвлөрүүлэх сонирхолтой бүсийг сонгоно.

def_of_interest (ирмэгүүд):

Өндөр, өргөн = ирмэг.хэлбэр # ирмэгийн өндөр ба өргөнийг гаргаж авах хүрээ маск = np.zeros_like (ирмэг) # ирмэгийн хүрээтэй ижил хэмжээтэй хоосон матриц хийх # зөвхөн дэлгэцийн доод талыг анхаарч үзээрэй 4 оноо (зүүн доод, зүүн дээд, баруун дээд, баруун доод) олон өнцөгт = np. Массив (

Энэ функц нь ирмэгийн хүрээг параметр болгон авч, урьдчилан тогтоосон 4 цэг бүхий олон өнцөгт зурна. Энэ нь зөвхөн полигон дотор байгаа зүйлд анхаарлаа төвлөрүүлж, гаднах бүх зүйлийг үл тоомсорлох болно. Миний сонирхлын хүрээний бүсийг дээр харуулав.

Шугамын сегментүүдийг илрүүлэх:

Hough transform нь ирмэгийн хүрээнээс шугамын сегментүүдийг илрүүлэхэд ашиглагддаг. Hough transform бол аливаа хэлбэрийг математик хэлбэрээр илрүүлэх арга юм. Энэ нь хэд хэдэн саналын дагуу гажуудуулсан байсан ч бараг бүх объектыг илрүүлж чаддаг. 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 (cropped_edges, rho, theta, min_threshold, np.array (), minLineLength = 5, maxLineGap = 0) line_segments

Дундаж налуу ба хөндлөн огтлол (м, б):

шугамын тэгшитгэлийг y = mx + b -ээр өгснийг санаарай. Энд m нь шугамын налуу, b нь y таслал юм. Энэ хэсэгт Hough хувиргалтыг ашиглан илрүүлсэн шугамын сегментүүдийн налуу ба хөндлөн огтлолын дундажийг тооцоолно. Үүнийг хийхээсээ өмнө дээр үзүүлсэн фрэймийн анхны зургийг үзье. Зүүн эгнээ дээшээ явж байгаа бололтой, энэ нь сөрөг налуутай байна (координатын системийн эхлэх цэгийг санаж байна уу?). Өөрөөр хэлбэл, зүүн эгнээний шугам нь x1 <x2 ба y2 x1 ба y2> y1 -тэй бөгөөд энэ нь эерэг налууг өгөх болно. Тиймээс эерэг налуутай бүх шугамыг баруун эгнээний цэг гэж үзнэ. Босоо шугамын хувьд (x1 = x2) налуу нь хязгааргүй байх болно. Энэ тохиолдолд алдаа гаргахгүйн тулд бид бүх босоо шугамыг алгасах болно. Илрүүлэх нарийвчлалыг нэмэгдүүлэхийн тулд хүрээ бүрийг 2 хилийн шугамаар хоёр бүсэд (баруун ба зүүн) хуваадаг. Баруун хилийн шугамаас том бүх өргөн цэгүүд (x тэнхлэгийн цэгүүд) нь баруун эгнээний тооцоотой холбоотой байдаг. Хэрэв бүх өргөн цэгүүд зүүн хилийн шугамаас бага байвал тэдгээр нь зүүн эгнээний тооцоотой холбоотой байдаг. Дараахь функц нь Hough хувиргалтыг ашиглан илрүүлсэн эгнээний сегментүүдийг боловсруулж, хоёр эгнээний шугамын дундаж налуу ба огтлолцлыг буцаана.

def дундаж_салуу_нүгэл (хүрээ, шугамын_сегментүүд):

lane_lines = хэрэв шугамын_ сегментүүд байхгүй бол: хэвлэх ("мөрийн сегмент илрээгүй") буцах эгнээний шугамын өндөр, өргөн, _ = frame.shape left_fit = right_fit = border = left_region_boundary = width * (1 - хил) right_region_boundary = line * сегмент дэх шугамын_сегментийн өргөн * хил: x1, y1, x2, y2 -ийн мөрөнд: хэрвээ 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: эгнээ_ мөрүүд 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), шугамын өргөн = 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 (зураг1, альфа, зураг2, бета, гамма)

Дараах тэгшитгэлийг ашиглан гаралтын зургийг тооцоолно.

гаралт = альфа * дүрс1 + бета * дүрс2 + гамма

Cv2.addWeighted () функцын талаарх дэлгэрэнгүй мэдээллийг эндээс авсан болно.

Толгойн мөрийг тооцоолох, харуулах:

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

def get_steering_angle (хүрээ, эгнээний шугам):

өндөр, өргөн, _ = фрэйм, right_x2, _ = lane_lines [1] [0] # x2 -ийг эгнээ мөрийн массиваас гаргаж авах mid = int (width / 2) x_offset = (left_x2 + right_x2) / 2 - mid y_offset = int (height / 2) elif len (lane_lines)) == 1: # хэрэв зөвхөн нэг мөр илэрсэн бол x1, _, x2, _ = эгнээ_мөр [0] [0] x_offset = x2 - x1 y_offset = int (height / 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) 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 (frame, steering_angle, line_color = (0, 0, 255), line_width = 5)

heading_image = np.zeros_like (frame) өндөр, өргөн, _ = frame.shape steering_angle_radian = steering_angle / 180.0 * math.pi x1 = int (width / 2) y1 = height x2 = int (x1 - height / 2 / math.tan) (steering_angle_radian)) y2 = int (height / 2) cv2.line (heading_image, (x1, y1), (x2, y2), line_color, line_width) heading_image = cv2.addWeighted (frame, 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 (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 -ээс бага байвал (10% PWM) тогтмол эргүүлэх энгийн код ашигласан боловч маш их алдаа гарсан. Миний олж авсан гол алдаа бол машин ямар ч эргэлтэнд ойртох үед жолооны мотор шууд ажилладаг боловч тохируулагч мотор гацдаг. Би эргэлтийн хурдыг (PWM 20%) нэмэгдүүлэхийг хичээсэн боловч робот эгнээнээс бууснаар дууссан. Жолооны өнцөг нь маш том байвал тохируулагчийн хурдыг ихэсгэдэг, хэрвээ жолооны өнцөг тийм ч том биш байвал хурдыг бага зэрэг нэмэгдүүлдэг зүйл хэрэгтэй байсан бөгөөд дараа нь машин 90 градус ойртох тусам хурдыг анхны утга болгон бууруулдаг. Үүний шийдэл бол PD хянагч ашиглах явдал байв.

PID хянагч нь пропорциональ, интеграл ба дериватив хянагч гэсэн утгатай. Энэ төрлийн шугаман хянагчийг робот техникийн хэрэглээнд өргөн ашигладаг. Дээрх зураг нь ердийн PID санал хүсэлтийг хянах давталтыг харуулж байна. Энэхүү хянагчийн зорилго бол зарим нөхцлийн дагуу үйлдвэрийг асаах, унтраах "асаах" хянагчтай харьцуулахад хамгийн үр дүнтэй аргаар "тогтоосон цэг" -д хүрэх явдал юм. Зарим түлхүүр үгсийг мэддэг байх ёстой:

  • Тодорхой цэг: Энэ нь таны системд хүрэхийг хүсч буй үнэ цэнэ юм.
  • Бодит утга: Энэ нь мэдрэгчийн мэдэрч буй бодит утга юм.
  • Алдаа: энэ нь тогтоосон цэг ба бодит утгын зөрүү юм (алдаа = Тодорхой утга - Бодит утга).
  • Хяналттай хувьсагч: нэрнээс нь хянахыг хүсч буй хувьсагч.
  • Kp: Пропорциональ тогтмол.
  • Ки: Интеграл тогтмол.
  • Кд: Дериватив тогтмол.

Товчоор хэлбэл, PID хяналтын системийн давталт дараах байдлаар ажилладаг.

  • Хэрэглэгч системд хүрэхэд шаардлагатай тогтоосон цэгийг тодорхойлдог.
  • Алдааг тооцоолсон (алдаа = тогтоосон цэг - бодит).
  • P хянагч нь алдааны утгатай пропорциональ үйлдэл үүсгэдэг. (алдаа нэмэгдэж, P үйлдэл бас нэмэгддэг)
  • I хянагч нь алдааг цаг хугацааны явцад нэгтгэх бөгөөд энэ нь системийн тогтвортой байдлын алдааг арилгах боловч хэт ачааллыг нэмэгдүүлдэг.
  • D хянагч нь алдааны цаг хугацааны дериватив юм. Өөрөөр хэлбэл энэ нь алдааны налуу юм. Энэ нь алдааны деривативтай пропорциональ үйлдэл хийдэг. Энэхүү хянагч нь системийн тогтвортой байдлыг нэмэгдүүлдэг.
  • Хянагчийн гаралт нь гурван хянагчийн нийлбэр болно. Алдаа 0 болвол хянагчийн гаралт 0 болно.

PID хянагчийн гайхалтай тайлбарыг эндээс олж болно.

Эгнээ байрлуулах машин руу буцаж очиход миний хяналттай хувьсагч нь хурдыг бууруулж байв (жолоодлого нь баруун эсвэл зүүн гэсэн хоёрхон төлөвтэй байдаг). Алдааны өөрчлөлт нь маш том (өөрөөр хэлбэл их хазайлт) байвал D үйлдэл нь тохируулагчийн хурдыг их хэмжээгээр нэмэгдүүлж, хэрэв энэ алдаа 0 -д ойртвол машиныг удаашруулдаг тул PD хянагчийг энэ зорилгоор ашигладаг. Би PD -ийг хэрэгжүүлэхийн тулд дараах алхмуудыг хийсэн. хянагч:

  • Тогтоосон цэгийг 90 градусаар тохируулна уу (машин үргэлж шулуун явахыг би үргэлж хүсдэг)
  • Дундаас хазайлтын өнцгийг тооцоолсон
  • Хазайлт нь хоёр мэдээлэл өгдөг: Алдаа хэр том вэ (хазайлтын хэмжээ), жолооны мотор ямар чиглэлд явах ёстой вэ (хазайлтын шинж тэмдэг). Хэрэв хазайлт эерэг байвал машин баруун тийш, зүүн тийш эргэх ёстой.
  • Хазайлт нь сөрөг эсвэл эерэг байдаг тул "алдаа" хувьсагчийг тодорхойлж, хазайлтын үнэмлэхүй утгатай үргэлж тэнцүү байдаг.
  • Алдаа нь тогтмол Kp -ээр үржигддэг.
  • Алдаа нь цаг хугацааны ялгааг олж, тогтмол Kd -ээр үржигддэг.
  • Моторын хурд шинэчлэгдэж, давталт дахин эхэлнэ.

Тохируулагч хөдөлгүүрийн хурдыг хянахын тулд үндсэн гогцоонд дараах кодыг ашиглана.

хурд = 10 # үйлдлийн хурд % PWM

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

Хэрэв алдаа маш том байвал (дундаас хазайх нь өндөр) пропорциональ ба дериватив үйлдлүүд өндөр байдаг тул тохируулагч хурд өндөр болдог. Алдаа 0 -д ойртох үед (дундаас хазайх нь бага), үүсмэл үйлдэл нь эсрэгээрээ (налуу нь сөрөг), системийн тогтвортой байдлыг хангахын тулд тохируулагч хурд бага болдог. Бүрэн кодыг доор хавсаргасан болно.

Алхам 7: Үр дүн

Дээрх видеонууд миний олж авсан үр дүнг харуулж байна. Энэ нь илүү тааруулах, нэмэлт тохируулга хийх шаардлагатай байна. Би бөөрөлзгөнө pi -г LCD дэлгэцтэйгээ холбож байсан, учир нь миний сүлжээгээр дамжуулж буй видео бичлэг нь маш их хоцорч, ажиллахад маш их урам хугардаг байсан тул видеон дээр бөөрөлзгөнө pi -тэй холбогдсон утаснууд байдаг. Би хөөс хавтанг ашиглан замыг зурсан.

Энэ төслийг илүү сайн болгохын тулд таны зөвлөмжийг сонсохыг би хүлээж байна! Энэхүү зааварчилгаа нь танд шинэ мэдээлэл өгөхөд хангалттай сайн байсан гэж найдаж байна.

Зөвлөмж болгож буй: