树莓派机器人的计算机视觉与编程实践
1. OpenCV 简单斑点检测
在使用 OpenCV 进行简单斑点检测时,我们可以通过以下步骤操作:
1. 打印关键点的坐标和大小到终端,代码如下:
# print the coordinates and size of keypoints to terminal
for k in keypnts:
print k.pt[0]
print k.pt[1]
print k.size
cv2.waitKey(0)
- 保存文件。
- 打开终端窗口。
- 导航到保存文件的文件夹。
- 输入
python simple_blob_detect.py
并按回车键。
执行上述步骤后,会打开图像的三个版本,原始图像中蓝色球周围会绘制一个红色圆圈,同时在终端窗口中会打印出球的中心坐标和大小。
2. SimpleBlobDetector 参数
SimpleBlobDetector 类需要一些参数才能正常工作,强烈建议通过将相应参数设置为 true
或 false
来明确启用或禁用所有过滤选项。如果启用了某个过滤器,则还需要为其设置参数。默认参数配置为提取深色圆形斑点。
图像首先通过应用阈值转换为多个二值图像, minThreshold
和 maxThreshold
确定整体范围, thresholdStep
确定阈值之间的距离。然后使用 findContours()
处理每个二值图像的轮廓,计算每个斑点的中心,再使用 minDistanceBetweenBlobs
参数将多个斑点组合成一个组,最后返回组的中心作为关键点以及组的整体直径,并计算每个过滤器的参数并应用过滤器。
3. 过滤器及其参数
过滤器 | 参数 | 说明 |
---|---|---|
filterByColor | blobColor | 过滤每个二值图像的相对强度,测量斑点中心的强度值并与 blobColor 参数进行比较,强度范围为 0 - 255,0 为暗,255 为亮。 |
filterByArea | minArea, maxArea | 当单个斑点分组时,计算其总面积,该过滤器查找面积在 minArea 和 maxArea 之间的斑点。 |
filterByCircularity | minCircularity, maxCircularity | 圆度通过公式 4 * Area / (perimeter * perimeter) 计算,返回 0 到 1 之间的比率,与 minCircularity 和 maxCircularity 比较,若值在参数之间,则该斑点包含在结果中。 |
filterByInertia | minInertiaRatio, maxInertiaRatio | 惯性是对斑点伸长程度的估计,是 0 到 1 之间的比率,若值在 minInertiaRatio 和 maxInertiaRatio 之间,则该斑点包含在关键点结果中。 |
filterByConvexity | minConvexity, maxConvexity | 凸度是 0 到 1 之间的比率,测量斑点中凸曲线和凹曲线的比率,参数为 minConvexity 和 maxConvexity 。 |
4. 斑点跟踪
要跟踪斑点,需要使用机器人相机的实时视频流,并定义跟踪的含义。最简单的跟踪形式是让生成的圆圈随斑点移动。操作步骤如下:
1. 打开 Thonny IDE 并创建一个新文件。
2. 将文件保存为 blob_tracker.py
。
3. 输入以下代码:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
# setup detector and parameters
params = cv2.SimpleBlobDetector_Params()
params.filterByColor = False
params.filterByArea = True
params.minArea = 20000
params.maxArea = 30000
params.filterByInertia = False
params.filterByConvexity = False
params.filterByCircularity = True
params.minCircularity = 0.5
params.maxCircularity = 1
det = cv2.SimpleBlobDetector_create(params)
# define blue
lower_blue = np.array([80,60,20])
upper_blue = np.array([130,255,255])
while True:
ret, frame = cap.read()
imgHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
blueMask = cv2.inRange(imgHSV,lower_blue, upper_blue)
blur= cv2.blur(blueMask, (10,10))
res = cv2.bitwise_and(frame, frame, mask=blueMask)
# get and draw keypoint
keypnts = det.detect(blur)
cv2.drawKeypoints(frame, keypnts, frame, (0,0,255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow('frame', frame)
cv2.imshow('mask', blur)
for k in keypnts:
print k.size
if cv2.waitKey(1) & 0xff == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- 保存文件。
- 打开终端窗口。
- 导航到保存文件的文件夹。
- 输入
sudo python blob_tracker.py
并按回车键。
执行后会打开两个窗口,一个显示用于过滤颜色的掩码,另一个显示视频流,斑点周围会绘制一个圆圈。
5. 追球机器人
结合 PID 控制器和斑点跟踪程序,可以让机器人追逐蓝色球。具体步骤如下:
1. 打开 Thonny IDE 并创建一个新文件。
2. 将文件保存为 ball_chaser.py
。
3. 输入以下代码:
import cv2
import numpy as np
import time
from adafruit_motorkit import MotorKit
# create motor objects
kit = MotorKit()
# create motor list
motors = [kit.motor1, kit.motor2, kit.motor3, kit.motor4]
# motor multipliers
motorMultiplier = [1.0, 1.0, 1.0, 1.0, 1.0]
# motor speeds
motorSpeed = [0,0,0,0]
# speeds
speedDef = 1.0
leftSpeed = speedDef
rightSpeed = speedDef
diff= 0
maxDiff = 0.5
turnTime = 0.5
# create camera object
cap = cv2.VideoCapture(0)
time.sleep(1)
# PID
kp = 1.0
ki = 1.0
kd = 1.0
ballX = 0.0
ballY = 0.0
x = {'axis':'X',
'lastTime':int(round(time.time()*1000)),
'lastError':0.0,
'error':0.0,
'duration':0.0,
'sumError':0.0,
'dError':0.0,
'PID':0.0}
y = {'axis':'Y',
'lastTime':int(round(time.time()*1000)),
'lastError':0.0,
'error':0.0,
'duration':0.0,
'sumError':0.0,
'dError':0.0,
'PID':0.0}
# setup detector
params = cv2.SimpleBlobDetector_Params()
# define detector parameters
params.filterByColor = False
params.filterByArea = True
params.minArea = 15000
params.maxArea = 40000
params.filterByInertia = False
params.filterByConvexity = False
params.filterByCircularity = True
params.minCircularity = 0.5
params.maxCircularity = 1
# create blob detector object
det = cv2.SimpleBlobDetector_create(params)
# define blue
lower_blue = np.array([80,60,20])
upper_blue = np.array([130,255,255])
def driveMotors(leftChnl = speedDef, rightChnl = speedDef,
duration = defTime):
# determine the speed of each motor by multiplying
# the channel by the motors multiplier
motorSpeed[0] = leftChnl * motorMultiplier[0]
motorSpeed[1] = leftChnl * motorMultiplier[1]
motorSpeed[2] = rightChnl * motorMultiplier[2]
motorSpeed[3] = rightChnl * motorMultiplier[3]
# run the motors. if the channel is negative, run
# reverse. else run forward
if(leftChnl < 0):
motors[0].throttle(-motorSpeed[0])
motors[1].throttle(-motorSpeed[1])
else:
motors[0].throttle(motorSpeed[0])
motors[1].throttle(motorSpeed[1])
if (rightChnl < 0):
motors[2].throttle(motorSpeed[2])
motors[3].throttle(motorSpeed[3])
else:
motors[2].throttle(-motorSpeed[2])
motors[3].throttle(-motorSpeed[3])
def PID(axis):
lastTime = axis['lastTime']
lastError = axis['lastError']
# get the current time
now = int(round(time.time()*1000))
duration = now-lastTime
# calculate the error
axis['sumError'] += axis['error'] * duration
axis['dError'] = (axis['error'] - lastError)/duration
# prevent runaway values
if axis['sumError'] > 1:axis['sumError'] = 1
if axis['sumError'] < -1: axis['sumError'] = -1
# calculate PID
axis['PID'] = kp * axis['error'] + ki * axis['sumError'] + kd * axis['dError']
# update variables
axis['lastError'] = axis['error']
axis['lastTime'] = now
# return the output value
return axis
def killMotors():
motors[0].throttle(0)
motors[1].throttle(0)
motors[2].throttle(0)
motors[3].throttle(0)
# main program
try:
while True:
# capture video frame
ret, frame = cap.read()
# calculate center of frame
height, width, chan = np.shape(frame)
xMid = width/2 * 1.0
yMid = height/2 * 1.0
# filter image for blue ball
imgHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
blueMask = cv2.inRange(imgHSV, lower_blue, upper_blue)
blur = cv2.blur(blueMask, (10,10))
res = cv2.bitwise_and(frame,frame, mask=blur)
# get keypoints
keypoints = det.detect(blur)
try:
ballX = int(keypoints[0].pt[0])
ballY = int(keypoints[0].pt[1])
except:
pass
# draw keypoints
cv2.drawKeypoints(frame, keypoints, frame, (0,0,255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# calculate error and get PID ratio
xVariance = (ballX - xMid) / xMid
yVariance = (yMid - ballY) / yMid
x['error'] = xVariance/xMid
y['error'] = yVariance/yMid
x = PID(x)
y = PID(y)
# calculate left and right speeds
leftSpeed = (speedDef * y['PID']) + (maxDiff * x['PID'])
rightSpeed = (speedDef * y['PID']) - (maxDiff * x['PID'])
# another safety check for runaway values
if leftSpeed > (speedDef + maxDiff): leftSpeed = (speedDef + maxDiff)
if leftSpeed < -(speedDef + maxDiff): leftSpeed = -(speedDef + maxDiff)
if rightSpeed > (speedDef + maxDiff): rightSpeed = (speedDef + maxDiff)
if rightSpeed < -(speedDef + maxDiff): rightSpeed = -(speedDef + maxDiff)
# drive motors
driveMotors(leftSpeed, rightSpeed, driveTime)
# show frame
## cv2.imshow('frame', frame)
## cv2.waitKey(1)
except KeyboardInterrupt:
killMotors()
cap.release()
cv2.destroyAllWindows()
- 保存文件。
- 打开终端窗口。
- 导航到保存文件的文件夹。
- 输入
sudo python ball_chaser.py
并按回车键。
执行后,几秒钟后机器人应该开始向前移动,如果画面中有蓝色球,它会转向球,机器人会尝试将球保持在画面中心。
6. 机器人类型与工具选择
机器人技术涵盖的范围很广,不同的兴趣方向决定了使用的工具。例如,对小型桌面机器人感兴趣可能不需要电焊机,而构建更复杂的机器人(如四足机器人)可能需要 CAD 软件。
在软件方面,有很多工具可供选择,如 PyCharm、Spyder 等。PyCharm 的社区版提供了几乎所有项目所需的功能,适用于 Windows 和 Linux 系统;Spyder 包含在 Anaconda 发行版的 Python 中,为科学和学术社区提供了许多工具。
graph TD;
A[开始] --> B[安装摄像头];
B --> C[安装OpenCV];
C --> D[简单斑点检测];
D --> E[斑点跟踪];
E --> F[追球机器人];
F --> G[选择合适工具];
G --> H[结束];
树莓派机器人的计算机视觉与编程实践
7. 软件工具的选择与特点
在机器人编程中,软件工具的选择至关重要,不同的 IDE 具有不同的特点和适用场景。以下是几种常见的 Python 编程 IDE 的介绍:
| IDE 名称 | 特点 | 适用场景 | 下载地址 |
| ---- | ---- | ---- | ---- |
| PyCharm | 专业级 IDE,社区版提供了几乎所有项目所需的功能,适用于 Windows 和 Linux 系统,使 Python 编程更轻松 | 大多数 Python 项目 | www.jetbrains.com/pycharm |
| Spyder | 包含在 Anaconda 发行版的 Python 中,为科学和学术社区提供了许多工具,安装相对容易 | 科学和学术领域的 Python 项目 | www.anaconda.com |
| Visual Studio | 功能强大且越来越易于使用,社区版可从官网下载,可用于大多数开发需求 | 多种编程语言的开发项目 | www.visualstudio.com |
8. 计算机视觉与机器人编程的优势与挑战
计算机视觉为树莓派机器人带来了更强大的功能,使其能够执行比单纯使用微控制器更复杂的任务。然而,在实践过程中也面临一些挑战。
- 优势 :
- 复杂任务处理 :计算机视觉允许机器人进行更复杂的任务,如识别和跟踪物体,这是微控制器单独无法完成的。
- 开源平台 :OpenCV 是一个开源的计算机视觉平台,使许多视觉功能变得简单,降低了开发成本。
- 挑战 :
- 安装时间长 :在树莓派上安装 OpenCV 需要从源代码编译,由于树莓派处理能力有限,编译过程耗时较长。
- 参数调整 :在使用 SimpleBlobDetector 时,需要调整多个参数以获得理想的结果,这需要一定的经验和技巧。
9. 编程实践的总结与建议
通过上述的编程实践,我们可以总结出以下几点经验和建议:
- 基础学习 :在开始使用计算机视觉和机器人编程之前,建议先学习一些基础知识,如 Python 编程、Linux 系统操作等。
- 实践操作 :通过实际操作来加深对知识的理解和掌握,从简单的示例开始,逐步增加难度。
- 参数调整 :在使用 OpenCV 进行物体检测和跟踪时,需要根据实际情况调整参数,以获得最佳效果。
- 工具选择 :根据自己的需求和习惯选择合适的 IDE 和工具,提高编程效率。
10. 未来展望
随着技术的不断发展,树莓派机器人的应用前景将更加广阔。未来,我们可以期待以下方面的发展:
- 更强大的处理能力 :随着硬件技术的进步,树莓派的处理能力将不断提高,能够处理更复杂的计算机视觉任务。
- 更多的应用场景 :机器人将在更多领域得到应用,如智能家居、工业自动化、医疗保健等。
- 人工智能的融合 :将人工智能技术与计算机视觉相结合,使机器人具有更强的自主决策能力和学习能力。
graph TD;
A[选择软件工具] --> B[学习基础知识];
B --> C[进行编程实践];
C --> D[调整参数优化];
D --> E[应用于实际场景];
E --> F[融合人工智能];
F --> G[拓展应用领域];
总之,树莓派机器人的计算机视觉与编程实践为我们打开了一扇通往智能机器人世界的大门。通过不断学习和实践,我们可以充分发挥树莓派的潜力,创造出更加智能、实用的机器人。