全方位跟踪友好机器人
组件和用品
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 3 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 10 | ||||
| × | 1 | ||||
| × | 1 |
必要的工具和机器
| ||||
|
应用和在线服务
|
关于这个项目
初次提交
我最初提交给“Hackster Terminate the Competition”比赛的目的是创造一个寻找人类的机器人,但与终结者宇宙中的机器人不同,它不会四处杀人,而是永远使用它的力量。
这个全方位的机器人会找到你、检测你、关注你并赞美你!
我对自己指定的一个要求是,我将确保无需自己构建任何电子设备即可制造。该机器人内部的每个部件都可以从 eBay 购买,使用免费提供的教程将它们连接在一起,它们应该可以工作。然后,外壳将可以 3D 打印,因此您可以自己打印或在 3D Hub 上制作。我认为这个项目符合这个目标,现在我将指导您完成构建自己的Hunter Flatterer Robot 的步骤
一切如何运作!
因此,让我们从一个系统图开始,该图显示了机器人的所有部件以及它们如何连接在一起。我们将在整个构建过程中引用它,并在进行过程中对其进行检查。
如果您以前从未使用过电机,请不要被这张图吓到。基本上可以归结为四个主要部分:
- 左侧负责取出电池并确保系统电压正确。
- 聪明的一点是在 ci20 上完成的
- Arduino 用于告诉电机该做什么
接下来的四个部分将反映此列表并指导您设置每个部分。
处理电力
所以你有一块电池,接下来怎么办?
上图显示了如何连接所有电源组件,如果您按照原理图中的接线进行操作,这就是现实生活中的表现。
将 Arduino 连接到 L298N
我可以解释这一点,但最好按照我所做的教程进行操作:http://www.instructables.com/id/Arduino-Modules-L298N-Dual-H-Bridge-Motor-Controll。
所以现在你已经把所有的东西都连接起来了,到处都是蓝灯闪烁,没有蓝烟,所以你很高兴。让我们继续让这个坏男孩工作。
3D 打印自己的箱子
当我设计这个时,我决定我想要两件事情发生,它看起来很酷并且尽可能地展示 Ci20。我的意思是这就是比赛的重点吧?
我将这个机器人设计为完全可 3D 打印,设计如下。基本上在这一点上去打印自己的顶部,底部和轮子。如果您无法使用 3D 打印机,您可以访问 www.3dhubs.com 找人为您打印!你可以看看下面的设计
这是我对设计所做的漂亮渲染,以确保 ci20 适合并处于领先地位
全部打印完成后,您可以按照上述方法将所有部件粘在一起(如果您觉得两个都不自信,则可以使用遮蔽胶带。您将需要一个 M4 螺栓来固定电机。
让 CI20 变魔术
CI20 是老大,操作的大脑。没有它,机器人将陷入困境。那么它有什么作用?
那么 ci20 将使用 OpenCV 来检测您的脸,然后通过串行向 Arduino 发送适当的命令,以使电机朝着正确的方向运行。
第一步:安装 OpenCV
就像我在本指南中所说的那样,我并不是要重新发明苹果车。我正在尝试构建一些您可以在家中完成并相对轻松地扩展的东西。因此,要安装 OpenCV,请按照本教程进行操作:
它会逐步指导您启动和运行 OpenCV。
第 2 步:运行代码
Face Tracking 代码需要编译运行,在 Ci20 上的文件上运行这个命令。
g++ -I/usr/local/include/opencv -I/usr/local/include/opencv2 -L/usr/local/lib/ -g -o binary main.cpp -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml -lopencv_video -lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_contrib -lopencv_legacy -lopencv_stitching
要创建的二进制文件:FaceTracking:main.cpp> 源文件:FaceTracking.cpp
简单吧?
Ci20 和 Arduino 魔术
使用下面附件中的代码刷新 Arduino 后。将 Arduino 插入 CI20 上的 USB 端口并运行您最近创建的 FaceTracker。你会看到一个摄像头弹出(哦,是的,插入网络摄像头),如果你把脸贴在中间,机器人上的轮子应该开始让它向前行驶!
工作完成了吗?
现在由于时间限制,我并没有完全让它做任何其他事情,所以对于已经走到这一步的人来说,这是一个挑战。你可以添加两件事来让这个机器人超级棒:
添加一组扬声器和一些声音文件,以便当屏幕上的人脸足够大时向人播放声音文件
- 如果机器人在 1-2 分钟内没有检测到人脸,则让它原地旋转。您可以通过向 Arduino 发送命令“R XX”来完成此操作。将 XX 替换为您想要转身的时间。
代码
- Arduino Sketch - 主要
- Arduino Sketch - 电机控制
- Ci20 的面部跟踪 C++
Arduino Sketch - MainArduino
此 Arduino 代码通过串行方式接收来自 Ci20 的驱动命令,并确保以正确的方式驱动电机以朝着正确的方向运行。我最初是为 Raspberry Pi 编写的,但它也适用于 Ci20
// Motor 1int dir1PinA =3;int dir2PinA =2;int speedPinA =9; //需要是PWM引脚才能控制电机速度//电机2int dir1PinB =4;int dir2PinB =5;int speedPinB =8; //需要是PWM引脚才能控制电机速度//电机3int dir1PinC =6;int dir2PinC =7;int speedPinC =10; // 需要是 PWM 引脚才能控制电机速度int x =0;int y =0;int distinctUltrasonic =0;bool moveMotor =false;int startTime =0;void setup() { Serial.begin(9600); pinMode(dir1PinA,OUTPUT); pinMode(dir2PinA,OUTPUT); pinMode(speedPinA,OUTPUT); pinMode(dir1PinB,OUTPUT); pinMode(dir2PinB,OUTPUT); pinMode(speedPinB,OUTPUT); pinMode(dir1PinC,OUTPUT); pinMode(dir2PinC,OUTPUT); pinMode(speedPinC,OUTPUT); startTime =millis();}String rpiString;void loop() { readDataFromRPi(); // 这将测试我们是否在最后一秒收到了来自 RPi 的值。 // 它有效地充当缓冲区,因此它不会继续停止和启动,因为移动命令不是连续的 // 如果没有来自 RPi 的输入,它也允许停止 // int elapsedTime =millis() - startTime; // if (elapsedTime> 1000)// {// x =0;// y =0;//dominantUltrasonic =0;// moveMotor =false;// startTime =millis();// // } //将 X &Y 发送到电机 //if(moveMotor ==true &&(x !=0 &&y !=0)) //{ //Serial.println("MovingMotor"); // driveInDirection(x,y); //} //if(x ==0 &&y ==0) //{ //Serial.println("ZeroMMotor"); // driveInDirection(x,y); //} // 这可能只是超声波遗留下来的 - 犹豫要删除 delay(30);}void readDataFromRPi(){ // 从 Rpi 读取 while (Serial.available()) { delay(3); } //延迟以允许缓冲区填充 if (Serial.available()>0) { char c =Serial.read(); //从串行缓冲区中获取一个字节 rpiString +=c; //使字符串为readString if(c =='n') { break; } } } // ENDWHILE // 如果从 RPi 中读取了某些内容,则将其放入 x,y &domniantUltrasonic if (rpiString.length()>0) { Serial.println(rpiString); //查看接收到的内容 String isRotate =getValue(rpiString, ' ',0); String xval =getValue(rpiString, ' ', 1); String yval =getValue(rpiString, ' ', 2); x =xval.toInt(); y =yval.toInt();开始时间 =毫秒();如果(isRotate ==“r”){旋转(x); } else { driveInDirection(x,y); } rpiString=""; } //ENDIF} String getValue(String data, char separator, int index){ int found =0; int strIndex[] ={0, -1 }; int maxIndex =data.length()-1; for(int i=0; i<=maxIndex &&found<=index; i++){ if(data.charAt(i)==separator || i==maxIndex){ found++; strIndex[0] =strIndex[1]+1; strIndex[1] =(i ==maxIndex) ?我+1:我;返回找到>索引? data.substring(strIndex[0], strIndex[1]) :"";}
Arduino Sketch - MotorControlArduino
这与主 ino 文件一致,需要包含在同一个项目文件中/* * 电机控制代码 * * 该类将包含使机器人向任何方向移动 * 并围绕中心点旋转的代码。 */void driveInDirection(float newX, float newY){ delay(20);浮动 x =newX;浮动 y =newY;浮动 theta =atan2(y,x);浮动 mag =sqrt((x*x) + (y*y));浮动 vx =mag * cos(theta);浮动 vy =mag * sin(theta);浮动 w1 =-vx;浮动 w2 =0.5 * vx - sqrt(3)/2 * vy;浮动 w3 =0.5 * vx + sqrt(3)/2 * vy; // 获取最大的 w 值 float wSet[] ={w1, w2, w3};浮动最大值 =0.0; for (int i =0; i <3; i++) { if(abs(wSet[i])> maximumValue) { maximumValue =abs(wSet[i]); } } float speedCoef =(float)147.0 / maximumValue; w1 =w1 * speedCoef; w2 =w2 * speedCoef; w3 =w3 * speedCoef; if (x ==0 &&y ==0) { w1 =0; w2 =0; w3 =0; Serial.println(w1); Serial.println(w2); Serial.println(w3); w1 =约束(w1, -150, 150); w2 =约束(w2, -150, 150); w3 =约束(w3, -150, 150);布尔值 w1_ccw =w1 <0 ?真假;布尔 w2_ccw =w2 <0 ?真假;布尔 w3_ccw =w3 <0 ?真假;字节 w1_speed =(byte) map(abs(w1), 0, 150, 0, 255);字节 w2_speed =(byte) map(abs(w2), 0, 150, 0, 255);字节 w3_speed =(byte) map(abs(w3), 0, 150, 0, 255);打印电机速度(w1_speed,1);打印电机速度(w2_speed,2);打印电机速度(w3_speed,3); analogWrite(speedPinA, w1_speed);//通过 PWM 设置速度变量analogWrite(speedPinB, w2_speed); AnalogWrite(speedPinC, w3_speed);//通过PWM设置速度变量 digitalWrite(dir1PinA, !w1_ccw);数字写入(dir2PinA,w1_ccw);数字写入(dir1PinB,!w2_ccw);数字写入(dir2PinB,w2_ccw);数字写入(dir1PinC,w3_ccw);数字写入(dir2PinC,!w3_ccw);}无效旋转(浮动毫秒){浮动w1 =255;浮动 w2 =255;浮动 w3 =255;布尔值 w1_ccw =w1 <0 ?真假;布尔 w2_ccw =w2 <0 ?真假;布尔值 w3_ccw =w3 <0 ?真假;字节 w1_speed =(byte) map(abs(w1), 0, 150, 0, 255);字节 w2_speed =(byte) map(abs(w2), 0, 150, 0, 255);字节 w3_speed =(byte) map(abs(w3), 0, 150, 0, 255);打印电机速度(w1_speed,1);打印电机速度(w2_speed,2);打印电机速度(w3_speed,3); analogWrite(speedPinA, w1_speed);//通过 PWM 设置速度变量analogWrite(speedPinB, w2_speed); AnalogWrite(speedPinC, w3_speed);//通过PWM设置速度变量 digitalWrite(dir1PinA, !w1_ccw);数字写入(dir2PinA,w1_ccw);数字写入(dir1PinB,!w2_ccw);数字写入(dir2PinB,w2_ccw);数字写入(dir1PinC,w3_ccw);数字写入(dir2PinC,!w3_ccw);延迟(毫秒); analogWrite(speedPinA, 0);//通过PWM设置速度变量analogWrite(speedPinB, 0); AnalogWrite(speedPinC, 0);//通过PWM设置速度变量 }void printMotorSpeed(byte motorSpeed, int motor){ Serial.print("Motor"); Serial.print(电机); Serial.print(":"); Serial.println(motorSpeed); }
Ci20 的人脸追踪 C++C/C++
遵循 Story#include "opencv2/objdetect/objdetect.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include#include using namespace std;using namespace cv;CascadeClassifier face_cascade, eyes_cascade;String window_name ="Face Detection";#include #include #include #include #include #include #include #include int sendSerial(char* message){int fd =open("/ dev/ttyUSB0", O_RDWR);if (fd ==-1){ perror("dev/ttyUSB0"); return 1;}struct termios tios;tcgetattr(fd, &tios);tios.c_iflag =IGNBRK | IGNPAR;tios.c_oflag =0;tios.c_lflag =0;cfsetspeed(&tios, B9600);tcsetattr(fd, TCSAFLUSH,&tios);usleep(1000);//char msg[] ="50 50";write(fd) , message, strlen(message));return 0;}/** * 检测人脸并在它们周围画一个椭圆 */void detectFaces(Mat frame) { std::vector faces;垫 frame_gray; // 转换为灰度 cvtColor(frame, frame_gray, COLOR_BGR2GRAY); // 均衡直方图 equalizeHist(frame_gray, frame_gray); // 检测人脸 face_cascade.detectMultiScale(frame_gray, faces, 1.1, 3, 0|CASCADE_SCALE_IMAGE, Size(30,30)); // 迭代所有的面 for(size_t i =0; i 眼睛; // 尝试检测每张脸内的眼睛 // eye_cascade.detectMultiScale(face, eyes, 1.1, 2, // 0 |CASCADE_SCALE_IMAGE, Size(50, 50) ); // if(eyes.size()> 0) // 在人脸椭圆周围画椭圆 椭圆(frame, center, Size(faces[i].width/2, faces[i].height/2), 0, 0, 360 , 标量 ( 255, 0, 255 ), 4, 8, 0 ); if(center.x> frame.cols/3 &¢er.x =0) // pause break; } return 0;}
定制零件和外壳
这是全向机器人的底座,由我设计,用于安装所有部件并附上盖子这是展示 Ci20 的所有荣耀的盖子。全向轮
这是全向轮的原始设计。我将其中的一半与轮辋一起使用,然后对其进行编辑以包含 thingiverse.com 上的电机轴CAD文件 来自 thingiverse 链接的混音轮示意图
在故事部分看到更好的描述制造工艺