-
題目要求和機械設計參考本人博客 https://blog.csdn.net/DerrickRose25/article/details/90181677
經過幾番測試之後我才用的硬件設計是 樹莓派 + 攝像頭 + STM32
軟件方面採用 OpenCV + Python + PID算法 -
攝像頭部分參考本人博客:https://blog.csdn.net/DerrickRose25/article/details/90733386
先通過如下代碼得到板和球在攝像頭中的位置:
圖像處理代碼
import cv2
import numpy as np
class ball():
def __init__(self):
self.point_flag = False
self.ban_flag = False
def detect(self, frame ,contours):
# 板的長度和麪積的最大值和最小值
BanarclengthMin = 1000
BanarclengthMax = 2000
BansquareMin = 60000
BansquareMax = 100000
# 球的長度和麪積的最大值和最小值
BallarclengthMin = 50
BallarclengthMax = 100
BallsquareMin = 350
BallsquareMax = 600
# print(len(contours))
for cnt in range(len(contours)):
# cv2.drawContours(frame, contours, cnt, (0, 255, 0), 2)
square = cv2.contourArea(contours[cnt]) # 面積
arcLength = cv2.arcLength(contours[cnt], True) # 長度
# print(square, arcLength)
# 板定位
if BanarclengthMin < arcLength < BanarclengthMax and BansquareMin < square < BansquareMax:
approx = cv2.approxPolyDP(contours[cnt], arcLength * 0.02, 1)
cv2.polylines(frame, [approx], True, (0, 0, 255), 2) # 擬合
mm = cv2.moments(approx) # 中心求解
cx = int(mm['m10'] / mm['m00'])
cy = int(mm['m01'] / mm['m00'])
cv2.circle(frame, (cx, cy), 3, (0, 0, 255), -1)
print("板的中心座標是 (%d,%d)" % (np.int(cx), np.int(cy)))
# cv2.drawContours(image, contours, cnt, (0, 255, 0), 2)
self.ban_flag = True
# 球定位
elif BallarclengthMin < arcLength < BallarclengthMax and BallsquareMin < square < BallsquareMax:
# cv2.drawContours(frame, contours, cnt, (0, 255, 0), 2)
epsilon = 0.01 * arcLength # 輪廓逼近,主要是對閉合輪廓進行檢測,即水管的漏油點
approx = cv2.approxPolyDP(contours[cnt], epsilon, True) # 進行圖像擬合
rrt = cv2.fitEllipse(contours[cnt]) # 橢圓擬合
cv2.ellipse(frame, rrt, (255, 0, 0), 2, cv2.LINE_AA) # 畫出擬合橢圓
x, y = rrt[0] # 求出橢圓中心
print("球的座標是 (%d,%d)" % (np.int(x), np.int(y)))
cv2.circle(frame, (np.int(x), np.int(y)), 4, (255, 0, 0), -1, 8, 0) # 畫出橢圓中心
self.point_flag = True
def cameraAnalysis(self, frame):
frame = cv2.resize(frame, (640, 360), interpolation = cv2.INTER_CUBIC) # 因爲攝像頭問題,對圖像進行了大小修改
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 轉爲灰度圖
# cv2.imshow("gray", gray)
ret, binary = cv2.threshold(gray, 140, 255, cv2.THRESH_BINARY) # 二值化
# cv2.imshow("binary", binary)
blurred = cv2.GaussianBlur(binary, (3, 3), 0) # 高斯濾波處理
cv2.imshow("blur", blurred) # 顯示濾波圖像
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 建立7 * 7的卷積核
closed = cv2.morphologyEx(blurred, cv2.MORPH_RECT, kernel) # 去除噪點
cv2.imshow("closed", closed)
ret, binary = cv2.threshold(closed, 30, 255, cv2.THRESH_BINARY) # 再次二值化
cv2.imshow("binary", binary)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # 尋找輪廓
self.detect(frame, contours)
cv2.imshow("frame", frame)
if gunqiu.point_flag == True and gunqiu.ban_flag == True :
print("------------------ Successful positioning! ------------------")
if __name__ == "__main__":
frame = cv2.imread("./image/qiu6.jpg")
gunqiu = ball()
gunqiu.cameraAnalysis(frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
之後進行線性擬合得到球在板上的真實座標,先把板球放在指定的9個位置並記錄在攝像頭中的位置,爲了提高精度來調節PID,我將原來代碼的單位從釐米調成了毫米
這裏擬合就不用筆算了,直接用matlab進行擬合
得出x方向方程爲y = -2.381 * x + 1013
同理y方向方程y = -2.15 * x + 744.2
代入原代碼中
3.PID的調節
PID stm32部分代碼上傳github上,因爲時間問題PID沒有調好,但是程序基本結構已經出來,只需要修改調試PID參數即可
https://github.com/DerrickRose25/Ball_rolling_control