树莓派机器人的计算机视觉与编程实践
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. 机器人类型与工具选择
机器人技术涵盖了很多不同的领域,其定义取决于个人的兴趣。例如,有人对小型桌面机器人感兴趣,有人则对大型机器人、飞行机器人、水下机器人、自动驾驶汽车或家庭自动化和物联网等领域感兴趣。
不同的机器人领域需要不同的工具。在软件方面,有很多选择,如 PyCharm、Spyder 和 Microsoft 的 Visual Studio 等。PyCharm 的社区版提供了几乎所有项目所需的功能,适用于 Windows 和 Linux 系统;Spyder 包含在 Anaconda 发行版中,为科学和学术社区提供了许多工具;Visual Studio 的社区版也越来越易于使用,可满足大多数开发需求。
graph LR
A[开始] --> B[安装摄像头]
B --> C[安装OpenCV]
C --> D[简单斑点检测]
D --> E[斑点跟踪]
E --> F[追球机器人编程]
F --> G[选择合适工具]
G --> H[结束]
通过以上内容,我们可以看到如何利用树莓派和 OpenCV 实现计算机视觉相关的功能,从简单的斑点检测到复杂的追球机器人编程,同时了解了不同机器人领域所需的工具选择。
树莓派机器人的计算机视觉与编程实践
7. 机器人领域的多样性及工具考量
机器人技术的范畴极为广泛,不同人对其的关注点差异很大。比如,有些人热衷于小型桌面机器人的开发,这类机器人通常结构相对简单,设计时甚至可以凭借临时构思或纸笔记录来完成。而对于大型机器人、飞行机器人或是水下机器人等更为复杂的项目,可能就需要借助 CAD 软件进行精确设计。
在工具使用上,可分为硬件和软件两类。这里着重探讨软件工具,因为它在机器人技术的各个领域都有广泛应用,并且选择工具主要取决于个人的习惯和实际需求。
| 软件工具 | 特点 | 适用场景 |
|---|---|---|
| PyCharm | 专业级 Python 集成开发环境,社区版功能丰富,支持 Windows 和 Linux 系统 | 大多数 Python 项目开发 |
| Spyder | 包含在 Anaconda 发行版中,为科学和学术社区提供众多工具 | 科研和学术相关的 Python 编程 |
| Visual Studio | 功能强大,社区版对初学者和爱好者越来越友好,可满足多种开发需求 | 多领域的综合开发 |
8. 不同 IDE 的优势与选择
在选择集成开发环境(IDE)时,有多种选择可供考虑。
- PyCharm :这是一款专业的 Python 开发工具,其社区版具备了几乎所有项目所需的功能。对于经常在 Windows 和 Linux 系统间切换工作的开发者来说非常方便,在 Windows 机器上完成代码编写和调试后,可无缝转移到树莓派上继续使用相同的工具进行代码更新。
- Spyder :它是 Anaconda 发行版中的一部分,安装较为便捷。该 IDE 提供了许多针对科学和学术领域的工具,深受数据科学家的喜爱。如果你从事科研或学术相关的 Python 编程工作,Spyder 会是一个不错的选择。
- Visual Studio :曾经主要面向专业开发者,如今其社区版对初学者和爱好者也越来越友好。它功能强大,可以满足大多数开发需求,无论是 Python 编程还是其他类型的项目开发都能胜任。
graph LR
A[选择 IDE] --> B{需求和习惯}
B --> |专业 Python 开发| C[PyCharm]
B --> |科研学术编程| D[Spyder]
B --> |综合开发需求| E[Visual Studio]
9. 总结与展望
通过一系列的实践,我们充分利用了树莓派的强大功能,借助计算机视觉技术实现了比单纯使用微控制器更为复杂的任务。从安装摄像头到安装 OpenCV,再到进行简单斑点检测、斑点跟踪以及编程实现追球机器人,每一个步骤都让我们对机器人的视觉处理和控制有了更深入的理解。
在这个过程中,我们不仅学习了 OpenCV 的基本操作和应用,还结合了 PID 控制器等算法,将理论知识应用到实际项目中。同时,我们也了解到机器人技术领域的多样性,以及不同领域所需的工具选择。
未来,随着技术的不断发展,树莓派和 OpenCV 在机器人领域的应用将会更加广泛。我们可以进一步探索更多的计算机视觉算法,优化机器人的视觉处理能力;也可以结合其他传感器,如激光雷达、超声波传感器等,让机器人具备更强大的感知能力。此外,在软件工具方面,我们也可以不断尝试新的 IDE 和开发框架,提高开发效率和代码质量。
总之,机器人技术充满了无限的可能性,通过不断学习和实践,我们可以创造出更多有趣和实用的机器人项目。
超级会员免费看

1136

被折叠的 条评论
为什么被折叠?



