机器人、人工智能和机器学习领域正在迅速发展,它肯定会在不久的将来改变人类的生活方式。机器人被认为通过传感器和机器学习处理来理解现实世界并与之交互。图像识别是一种流行的方式,机器人被认为是通过相机观察现实世界来理解物体的一种方式,就像我们一样。在这个项目中,让我们利用树莓派的力量来构建一个机器人,它可以跟踪球并跟随它,就像踢足球的机器人一样。
OpenCV是一个非常著名的开源工具,用于图像处理,但在本教程中,为了简单起见,我们使用处理IDE。由于ARM的处理也发布了用于处理的GPIO库,因此我们不必再在python和处理之间切换即可使用Raspberry Pi。听起来很酷吧?因此,让我们开始吧。
所需硬件:
树莓派
带带状电缆的摄像头模块
机器人底盘
带轮齿轮的减速电机
L293D 电机驱动器
移动电源或任何其他便携式电源
编程要求:
树莓派的监视器或其他显示器
Pi 的键盘或鼠标
处理 ARM 软件
注意:在编程过程中,必须通过电线将显示器连接到Pi,因为只有这样才能查看相机的视频
在树莓派上设置处理:
如前所述,我们将使用处理环境来编程我们的树莓派,而不是使用 python 的默认方式。因此,请按照以下步骤操作:
第 1 步:-将树莓派连接到显示器、键盘和鼠标,然后将其打开。
第 2 步:-确保您的Pi已连接到有效的互联网连接,因为我们即将下载一些内容。
第 3 步:-单击“处理 ARM”,下载 Raspberry Pi 的处理 IDE。下载将以 ZIP 文件的形式进行。
第 4 步:-下载后,将ZIP文件夹中的文件解压缩到首选目录中。我刚刚在桌面上提取了它。
第 5 步:-现在,打开提取的文件夹,然后单击名为“处理”的文件。它应该打开一个窗口,如下所示。
第 6 步:-这是我们将输入代码的环境。对于熟悉Arduino的人来说,不要感到震惊,是的,IDE看起来确实与Arduino相似,程序也是如此。
第 7 步:-我们需要两个库才能让我们的球跟随程序工作,安装然后只需单击 Sketch -> 导入库 -> 添加库。将打开以下对话框。
第 8 步:-使用左上角的文本框搜索树莓派并按回车键,您的搜索结果应如下所示。
第 9 步:-搜索名为“GL 视频”和“硬件 I/O”的库,然后单击安装以安装它们。确保安装这两个库。
步骤10:-根据您的互联网,安装将需要几分钟。完成后,我们就可以使用处理软件了。
电路图:
这个树莓派球跟踪项目的电路图如下所示。
如您所见,该电路涉及PI相机,电机驱动器模块和一对连接到Raspberry Pi的电机。整个电路由移动电源供电(在上述电路中由AAA电池表示)。
由于树莓派上没有提到引脚细节,我们需要使用下图验证引脚
为了驱动电机,我们需要四个引脚(A,B,A,B)。这四个引脚分别从 GPIO14、4、17 和 18 连接。橙色和白色的电线共同构成了一个电机的连接。所以我们有两个这样的对用于两个电机。
如图所示,电机连接到L293D电机驱动器模块,驱动器模块由移动电源供电。确保移动电源的接地连接到树莓派的接地,只有这样您的连接才能正常工作。
这就是我们完成硬件连接的事情,让我们回到我们的处理环境并开始编程以教我们的机器人如何跟踪球。
树莓派球跟踪程序:
本页末尾给出了该项目的完整处理程序,您可以直接使用。下面,我解释了代码的工作原理,以便您可以将其用于其他类似的项目。
程序概念非常简单。虽然这个项目的目的是跟踪一个球,但我们实际上不会这样做。我们只是使用球的颜色来识别球。众所周知,视频只不过是连续的图片帧。所以我们把每张照片分成像素。然后我们将每个像素颜色与球的颜色进行比较;如果找到了一场比赛,那么我们可以说我们已经找到了球。有了这些信息,我们还可以识别球在屏幕上的位置(像素颜色)。如果位置在最左边,我们将机器人向右移动,如果位置最右,我们将机器人向左移动,以便像素位置始终保持在屏幕的中心。您可以观看Daniel shiffman的计算机视觉视频以获得清晰的图片。
与往常一样,我们首先导入下载的两个库。这可以通过以下两行完成。硬件 I/O 库用于直接从处理环境访问 PI 的 GPIO 引脚,glvideo 库用于访问树莓派相机模块。
import processing.io.*;
import gohai.glvideo.*;
在设置功能中,我们初始化输出引脚以控制电机,并从pi相机获取视频并将其调整为尺寸为320 * 240的窗口。
void setup() {
size(320, 240, P2D);
video = new GLCapture(this);
video.start();
trackColor = color(255, 0, 0);
GPIO.pinMode(4, GPIO.OUTPUT);
GPIO.pinMode(14, GPIO.OUTPUT);
GPIO.pinMode(17, GPIO.OUTPUT);
GPIO.pinMode(18, GPIO.OUTPUT);
}
void draw就像无限循环,只要程序终止,这个循环中的代码就会被执行。如果有可用的摄像机源,我们会读取从中发出的视频
void draw() {
background(0);
if (video.available()) {
video.read();
}}
然后我们开始将视频帧拆分为像素。每个像素都有一个红色、绿色和蓝色的值。这些值存储在变量 r1、g1 和 b1 中
for (int x = 0; x < video.width; x ++ ) {
for (int y = 0; y < video.height; y ++ ) {
int loc = x + y*video.width;
// What is current color
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
要最初检测球的颜色,我们必须单击颜色。点击后,球的颜色将存储在名为trackColor的变量中。
void mousePressed() {
// Save color where the mouse is clicked in trackColor variable
int loc = mouseX + mouseY*video.width;
trackColor = video.pixels[loc];
}
一旦我们有了轨道颜色和当前颜色,我们必须比较它们。此比较使用的是 dist 函数。它检查当前颜色与轨道颜色的接近程度。
float d = dist(r1, g1, b1, r2, g2, b2);
对于完全匹配,dist 的值将为零。因此,如果 dist 的值小于指定值(世界纪录),那么我们假设我们已经找到了轨道颜色。然后我们获取该像素的位置并将其存储在最近的 X 和最近的 Y 变量中,以找到球的位置
if (d < worldRecord) {
worldRecord = d;
closestX = x;
closestY = y;
}
我们还在找到的颜色周围画一个椭圆,以指示已找到该颜色。位置的值也打印在控制台上,这在调试时会有很大帮助。
if (worldRecord < 10) {
// Draw a circle at the tracked pixel
fill(trackColor);
strokeWeight(4.0);
stroke(0);
ellipse(closestX, closestY, 16, 16);
println(closestX,closestY);
最后,我们可以比较最近的X和最近的Y的位置,并以颜色到达屏幕中心的方式调整电机。下面的代码用于向右转动机器人,因为发现颜色的X位置在屏幕的左侧(<140)
if (closestX<140)
{
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.LOW);
delay(10);
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
println("Turn Right");
}
同样,我们可以检查 X 和 Y 的位置,以控制电机在所需方向上。
树莓派球跟踪机器人的工作:
一旦你准备好了硬件和程序,就该找点乐子了。在地面上测试我们的机器人之前,我们应该确保一切正常。连接您的 Pi 以监控和启动处理代码。您应该会在一个小窗口中看到视频源。现在,将球带入框架并单击球以指示机器人它应该跟踪这种特定颜色。现在在屏幕上移动球,您应该注意到轮子在旋转。
如果一切按预期工作,请将机器人释放到地面并开始使用它。确保房间均匀照明以获得最佳效果。
/*
Processing Raspberry Pi program for Ball following Robot
Project by: B.Aswnith Raj
Dated on: 18-11-2017
#This project would not have been possible without the help of Daniel Shiffman and Gottfried Haider
*/
import processing.io.*;
import gohai.glvideo.*;
GLCapture video;
color trackColor;
void setup() {
size(320, 240, P2D);
video = new GLCapture(this);
video.start();
trackColor = color(255, 0, 0);
GPIO.pinMode(4, GPIO.OUTPUT);
GPIO.pinMode(14, GPIO.OUTPUT);
GPIO.pinMode(17, GPIO.OUTPUT);
GPIO.pinMode(18, GPIO.OUTPUT);
}
void draw() {
background(0);
if (video.available()) {
video.read();
}
video.loadPixels();
image(video, 0, 0);
float worldRecord = 500;
int closestX = 0;
int closestY = 0;
// Begin loop to walk through every pixel
for (int x = 0; x < video.width; x ++ ) {
for (int y = 0; y < video.height; y ++ ) {
int loc = x + y*video.width;
// What is current color
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColor);
float g2 = green(trackColor);
float b2 = blue(trackColor);
// Using euclidean distance to compare colors
float d = dist(r1, g1, b1, r2, g2, b2); // We are using the dist( ) function to compare the current color with the color we are tracking.
// If current color is more similar to tracked color than
// closest color, save current location and current difference
if (d < worldRecord) {
worldRecord = d;
closestX = x;
closestY = y;
}
}
}
if (worldRecord < 10) {
// Draw a circle at the tracked pixel
fill(trackColor);
strokeWeight(4.0);
stroke(0);
ellipse(closestX, closestY, 16, 16);
println(closestX,closestY);
if (closestX<140)
{
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.LOW);
delay(10);
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
println("Turn Right");
}
else if (closestX>200)
{
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.LOW);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
delay(10);
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
println("Turn Left");
}
else if (closestY<170)
{
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.LOW);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.LOW);
delay(10);
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
println("Go Frwd");
}
else
{
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
}
}
else
{
GPIO.digitalWrite(4, GPIO.HIGH);
GPIO.digitalWrite(14, GPIO.HIGH);
GPIO.digitalWrite(17, GPIO.HIGH);
GPIO.digitalWrite(18, GPIO.HIGH);
}
}
void mousePressed() {
// Save color where the mouse is clicked in trackColor variable
int loc = mouseX + mouseY*video.width;
trackColor = video.pixels[loc];
}
评论
查看更多