title: Python-opencv-視覺巡線
tags:
- python
- python opencv應(yīng)用
categories: python
date: 2019-08-26
thumbnail: https://upload-images.jianshu.io/upload_images/16271012-d7e69820d7306670.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240
一、概述
通過攝像頭采集圖像,
將圖像灰度化、二值化、膨脹、腐蝕操作后,
提取第400行像素值v,接近于圖像底線位置,
提取中間值(這里為白色)的數(shù)量和位置,
根據(jù)數(shù)量和位置,利用簡(jiǎn)單的數(shù)學(xué)公式,(首項(xiàng)+尾項(xiàng))/2,計(jì)算出白色的中間位置,
然后對(duì)比實(shí)際的中間位置320(不需要改),計(jì)算出偏移量,
最后根據(jù)偏移量計(jì)算出電機(jī)應(yīng)有的轉(zhuǎn)角。
邊緣檢測(cè)實(shí)驗(yàn)
#!/usr/bin/env python3
# 識(shí)別的是中線為白色
import cv2
import numpy as np
# center定義
center = 320
# 打開攝像頭,圖像尺寸640*480(長(zhǎng)*高),opencv存儲(chǔ)值為480*640(行*列)
cap = cv2.VideoCapture(0)
while (1):
ret, frame = cap.read()
cv2.imshow("recognize_face", frame)
# 轉(zhuǎn)化為灰度圖
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray", gray)
# 大津法二值化
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
cv2.imshow("dst", dst)
# 膨脹,白區(qū)域變大
dst = cv2.dilate(dst, None, iterations=2)
cv2.imshow("dst2", dst)
# # 腐蝕,白區(qū)域變小 #
dst = cv2.erode(dst, None, iterations=6)
cv2.imshow("dst3", dst)
# 單看第400行的像素值v
color = dst[400]
try:
# 找到白色的像素點(diǎn)個(gè)數(shù),如尋黑色,則改為0
white_count = np.sum(color == 255)
# 找到白色的像素點(diǎn)索引,如尋黑色,則改為0
white_index = np.where(color == 255)
# 防止white_count=0的報(bào)錯(cuò)
if white_count == 0:
white_count = 1
# 找到黑色像素的中心點(diǎn)位置
# 計(jì)算方法應(yīng)該是邊緣檢測(cè),計(jì)算白色邊緣的位置和/2,即是白色的中央位置。
center = (white_index[0][white_count - 1] + white_index[0][0]) / 2
# 計(jì)算出center與標(biāo)準(zhǔn)中心點(diǎn)的偏移量,因?yàn)閳D像大小是640,因此標(biāo)準(zhǔn)中心是320,因此320不能改。
direction = center - 320
print(direction)
except:
continue
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release() #釋放cap
cv2.destroyAllWindows()#銷毀所有窗口
樹莓派GPIO應(yīng)用
# coding:utf-8
# 實(shí)現(xiàn)樹莓派小車的變速控制
import RPi.GPIO as gpio
# 定義引腳
in1 = 12
in2 = 16
in3 = 18
in4 = 22
# 設(shè)置GPIO口為BOARD編號(hào)規(guī)范,從左到右,從上到下。
gpio.setmode(gpio.BOARD)
# 設(shè)置GPIO口為輸出
gpio.setup(in1, gpio.OUT)
gpio.setup(in2, gpio.OUT)
gpio.setup(in3, gpio.OUT)
gpio.setup(in4, gpio.OUT)
# 設(shè)置PWM波,頻率為500Hz
pwm1 = gpio.PWM(in1, 500)
pwm2 = gpio.PWM(in2, 500)
pwm3 = gpio.PWM(in3, 500)
pwm4 = gpio.PWM(in4, 500)
# 初始化
pwm1.start(0)
pwm2.start(0)
pwm3.start(0)
pwm4.start(0)
# 定義向前
def go():
pwm1.ChangeDutyCycle(50)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(50)
pwm4.ChangeDutyCycle(0)
# 定義向右
def right():
pwm1.ChangeDutyCycle(50)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(30)
pwm4.ChangeDutyCycle(0)
# 定義向左
def left():
pwm1.ChangeDutyCycle(30)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(50)
pwm4.ChangeDutyCycle(0)
# 定義向后
def back():
pwm1.ChangeDutyCycle(0)
pwm2.ChangeDutyCycle(50)
pwm3.ChangeDutyCycle(0)
pwm4.ChangeDutyCycle(50)
# 定義停止
def stop():
pwm1.ChangeDutyCycle(0)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(0)
pwm4.ChangeDutyCycle(0)
pwm1.stop()
pwm2.stop()
pwm3.stop()
pwm4.stop()
gpio.cleanup()
視覺巡線
# coding:utf-8
# 加入攝像頭模塊,讓小車實(shí)現(xiàn)自動(dòng)循跡行駛
# 思路為:攝像頭讀取圖像,進(jìn)行二值化,將白色的賽道凸顯出來
# 選擇下方的一行像素,黑色為0,白色為255 # 找到白色值的中點(diǎn)
# 目標(biāo)中點(diǎn)與標(biāo)準(zhǔn)中點(diǎn)(320)進(jìn)行比較得出偏移量
# 根據(jù)偏移量來控制小車左右輪的轉(zhuǎn)速
# 考慮了偏移過多失控->停止;偏移量在一定范圍內(nèi)->高速直行(這樣會(huì)速度不穩(wěn)定,已刪)
import RPi.GPIO as gpio
import time
import cv2
import numpy as np
import serial
ser=serial.Serial('/dev/ttyAMA0',115200,timeout=1)
# 定義引腳
pin1 = 12
pin2 = 16
pin3 = 18
pin4 = 22
# 設(shè)置GPIO口為BOARD編號(hào)規(guī)范
gpio.setmode(gpio.BOARD)
# 設(shè)置GPIO口為輸出
gpio.setup(pin1, gpio.OUT)
gpio.setup(pin2, gpio.OUT)
gpio.setup(pin3, gpio.OUT)
gpio.setup(pin4, gpio.OUT)
# 設(shè)置PWM波,頻率為500Hz
pwm1 = gpio.PWM(pin1, 500)
pwm2 = gpio.PWM(pin2, 500)
pwm3 = gpio.PWM(pin3, 500)
pwm4 = gpio.PWM(pin4, 500)
# pwm波控制初始化
pwm1.start(0)
pwm2.start(0)
pwm3.start(0)
pwm4.start(0)
# center定義
center = 320
# 打開攝像頭,圖像尺寸640*480(長(zhǎng)*高),opencv存儲(chǔ)值為480*640(行*列)
cap = cv2.VideoCapture(0)
while (1):
ret, frame = cap.read()
cv2.imshow("recognize_face", frame)
# 轉(zhuǎn)化為灰度圖
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 大津法二值化
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
# 膨脹,白區(qū)域變大
dst = cv2.dilate(dst, None, iterations=2)
cv2.imshow("dst", dst)
# # 腐蝕,白區(qū)域變小 #
#dst = cv2.erode(dst, None, iterations=6)
# 單看第400行的像素值s
color = dst[400]
try:
# 找到白色的像素點(diǎn)個(gè)數(shù),如尋黑色,則改為0
white_count = np.sum(color == 255)
# 找到白色的像素點(diǎn)索引,如尋黑色,則改為0
white_index = np.where(color == 255)
# 防止white_count=0的報(bào)錯(cuò)
if white_count == 0:
white_count = 1
# 找到黑色像素的中心點(diǎn)位置
# 計(jì)算方法應(yīng)該是邊緣檢測(cè),計(jì)算白色邊緣的位置和/2,即是白色的中央位置。
center = (white_index[0][white_count - 1] + white_index[0][0]) / 2
# 計(jì)算出center與標(biāo)準(zhǔn)中心點(diǎn)的偏移量,因?yàn)閳D像大小是640,因此標(biāo)準(zhǔn)中心是320,因此320不能改。
direction = center - 320
print(direction)
ser.write(direction)
except:
continue
# 停止
if abs(direction) > 250:
pwm1.ChangeDutyCycle(0)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(0)
pwm4.ChangeDutyCycle(0)
# 右轉(zhuǎn)
elif direction >= 0:
# 限制在70以內(nèi)
if direction > 70:
direction = 70
pwm1.ChangeDutyCycle(30 + direction)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(30)
pwm4.ChangeDutyCycle(0)
# 左轉(zhuǎn)
elif direction < -0:
if direction < -70:
direction = -70
pwm1.ChangeDutyCycle(30)
pwm2.ChangeDutyCycle(0)
pwm3.ChangeDutyCycle(30 - direction)
pwm4.ChangeDutyCycle(0)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 釋放清理
cap.release()
cv2.destroyAllWindows()
pwm1.stop()
pwm2.stop()
pwm3.stop()
pwm4.stop()
gpio.cleanup()
文章參考德強(qiáng)師兄博客https://purethought.cn/