题目要求和机械设计参考本人博客 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