全向轮跟随机器人:OpenCV 人脸检测与安全陪伴
零部件与物料
| 零部件 | 数量 | 备注 |
|---|---|---|
| L298N 双电机驱动板 | 2 | 驱动 3 个麦克纳姆轮电机 |
| LM2596 降压模块 | 1 | 12V→5V 供电 Ci20 与 Arduino |
| 锂电池低压报警器 | 1 | 防止过放保护电池 |
| 24RPM 12V 减速电机 (4mm 轴) | 3 | 三全向轮驱动 |
| Arduino Nano R3 | 1 | 电机底层控制 |
| Microsoft LifeCam HD-3000 摄像头 | 1 | USB 接入 Ci20,人脸检测 |
| Creator Ci20 开发板 | 1 | 双核 MIPS,运行 OpenCV |
| 11.1V 3S 锂聚合物电池 | 1 | 主电源 |
| 跳线若干 | 1 套 | 电源与信号连接 |
所需工具
- 3D 打印机(打印底盘、轮子、盖板)
- 电烙铁
软件与在线服务
- OpenCV (Ci20 上编译运行)
项目简介
这是一个「反向终结者」机器人:不伤害人类,而是主动寻找人脸、靠近并赞美你!项目名为 Hunter Flatterer Robot,采用三轮全向驱动(麦克纳姆轮),可平移、旋转、斜向移动,配合 Ci20 开发板运行 OpenCV 人脸检测,通过串口指挥 Arduino Nano 控制三电机矢量合成,实现平滑跟随。
四大子系统
- 电源管理:11.1V 3S LiPo → LM2596 降压 5V 供 Ci20、Arduino、逻辑电路;电机直供 12V;低压报警器监控电池
- Ci20 视觉大脑:运行 Linux,OpenCV 加载 Haar 级联分类器 (
haarcascade_frontalface_default.xml) 实时检测人脸,计算人脸中心偏移量,发送X Y或旋转指令R ms给 Arduino - Arduino 运动控制:接收串口指令,将 (X,Y) 映射为三电机速度矢量 (w1,w2,w3),通过 L298N PWM 驱动
- 3D 打印机械结构:底盘容纳电池、Ci20、Arduino;盖板展示 Ci20;三麦克纳姆轮直接连接电机轴
Ci20 OpenCV 部署
- 安装 OpenCV:参考官方教程编译安装
opencv4至/usr/local - 编译人脸跟踪程序:
g++ -I/usr/local/include/opencv -I/usr/local/include/opencv2 \ -L/usr/local/lib/ -g -o FaceTracker main.cpp \ -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \ -lopencv_video -lopencv_features2d -lopencv_calib3d \ -lopencv_objdetect -lopencv_contrib -lopencv_legacy -lopencv_stitching - 运行
./FaceTracker,弹出摄像头窗口,人脸居中时发送F 50 50前进,偏左/偏右发送相应矢量,无人脸发送0 0停止
Arduino 运动学解算
三轮全向模型(120° 间隔):
// 目标速度矢量 (vx, vy)
float theta = atan2(y, x);
float mag = sqrt(x*x + y*y);
float vx = mag * cos(theta);
float vy = mag * sin(theta);
// 三电机速度分解
float w1 = -vx;
float w2 = 0.5*vx - sqrt(3)/2*vy;
float w3 = 0.5*vx + sqrt(3)/2*vy;
// 归一化到 PWM 0-255
float maxW = max(abs(w1), max(abs(w2), abs(w3)));
float scale = 147.0 / maxW; // 经验系数
w1 *= scale; w2 *= scale; w3 *= scale;
w1 = constrain(w1, -150, 150); // 同 w2, w3
// 方向位 + PWM 速度输出到 L298N
3D 打印件
- 底盘:容纳所有电子件,预留 Ci20 散热口
- 盖板:透明亚克力或镂空设计,展示 Ci20「大脑」
- 麦克纳姆轮:基于 Thingiverse 改进,半轮+轮辋一体化打印,适配 4mm 电机轴
未完成的挑战(留给读者)
- 加装扬声器 + 语音合成:人脸面积足大时播放赞美语音
- 失踪重搜:连续 1-2 分钟未检测到人脸,发送
R 2000原地旋转搜索 - 电池电量监测 + 自动回充桩
- ROS 移植:将 Ci20 替换为 Jetson Nano / Raspberry Pi 4 + ROS2
代码
Arduino 主程序 (接收串口指令、驱动电机)
// Motor 1
int dir1PinA = 3, dir2PinA = 2, speedPinA = 9;
// Motor 2
int dir1PinB = 4, dir2PinB = 5, speedPinB = 8;
// Motor 3
int dir1PinC = 6, dir2PinC = 7, speedPinC = 10;
String rpiString;
int x = 0, y = 0;
unsigned long lastCmd = 0;
void setup() {
Serial.begin(9600);
// 所有引脚设为 OUTPUT
for (int p : {dir1PinA,dir2PinA,speedPinA,dir1PinB,dir2PinB,speedPinB,dir1PinC,dir2PinC,speedPinC})
pinMode(p, OUTPUT);
lastCmd = millis();
}
void loop() {
readSerial();
// 超时保护:1 秒无指令自动停车
if (millis() - lastCmd > 1000) { x = 0; y = 0; }
if (x != 0 || y != 0) driveInDirection(x, y);
else stopAll();
delay(30);
}
void readSerial() {
while (Serial.available()) {
char c = Serial.read();
if (c == 'n') break; // 指令以 'n' 结尾
rpiString += c;
}
if (rpiString.length() > 0) {
String cmd = getValue(rpiString, ' ', 0);
x = getValue(rpiString, ' ', 1).toInt();
y = getValue(rpiString, ' ', 2).toInt();
lastCmd = millis();
if (cmd == "r") rotate(x); else driveInDirection(x, y);
rpiString = "";
}
}
String getValue(String data, char sep, int index) {
int found = 0, strIndex[] = {0, -1};
for (int i = 0; i <= data.length()-1 && found <= index; i++) {
if (data.charAt(i) == sep || i == data.length()-1) {
found++;
strIndex[0] = strIndex[1] + 1;
strIndex[1] = (i == data.length()-1) ? i+1 : i;
}
}
return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}
Arduino 运动控制库 (需放同一文件夹)
void driveInDirection(float x, float y) {
delay(20);
float theta = atan2(y, x);
float mag = sqrt(x*x + y*y);
float vx = mag * cos(theta), vy = mag * sin(theta);
float w1 = -vx;
float w2 = 0.5*vx - sqrt(3)/2*vy;
float w3 = 0.5*vx + sqrt(3)/2*vy;
float wSet[3] = {w1, w2, w3};
float maxW = 0;
for (int i=0;i<3;i++) if (abs(wSet[i]) > maxW) maxW = abs(wSet[i]);
float scale = 147.0 / maxW;
for (int i=0;i<3;i++) wSet[i] *= scale;
if (x==0 && y==0) { w1=w2=w3=0; }
// 限幅、映射 PWM、设置方向位、analogWrite...
// 详见完整代码附件
}
void rotate(float ms) {
// 三轮同向全速旋转 ms 毫秒
// 详见完整代码附件
}
Ci20 OpenCV 人脸跟踪 (C++)
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include
#include
#include
#include
using namespace std; using namespace cv;
CascadeClassifier face_cascade;
string win_name = "Face Detection";
int sendSerial(const char* msg) {
int fd = open("/dev/ttyUSB0", O_RDWR);
if (fd == -1) return 1;
struct termios t; tcgetattr(fd, &t);
t.c_iflag = IGNBRK | IGNPAR; t.c_oflag = 0; t.c_lflag = 0;
cfsetspeed(&t, B9600); tcsetattr(fd, TCSAFLUSH, &t);
usleep(1000);
write(fd, msg, strlen(msg));
close(fd); return 0;
}
void detectFaces(Mat frame) {
vector faces; Mat gray;
cvtColor(frame, gray, COLOR_BGR2GRAY);
equalizeHist(gray, gray);
face_cascade.detectMultiScale(gray, faces, 1.1, 3, 0|CASCADE_SCALE_IMAGE, Size(30,30));
for (size_t i=0; i frame.cols/3 && center.x < frame.cols*2/3)
sendSerial("F 50 50"); // 前进
else
sendSerial("0 0"); // 停止/微调
}
imshow(win_name, frame);
}
int main() {
sendSerial("0 0");
VideoCapture cap(0);
cap.set(CAP_PROP_FRAME_WIDTH, 480);
cap.set(CAP_PROP_FRAME_HEIGHT, 240);
if (!face_cascade.load("haarcascade_frontalface_default.xml")) return -1;
Mat frame;
while (cap.read(frame)) {
detectFaces(frame);
if (waitKey(30) >= 0) break;
}
return 0;
}
自定义零件与外壳
- 底盘 STL:容纳电池、Ci20、Arduino、电机驱动板
- 盖板 STL:展示 Ci20,预留摄像头窗口
- 麦克纳姆轮 STL:改进自 Thingiverse #XXXXX,半轮+轮辋一体化
原理图
制造工艺
- 工业机器人:设计逻辑、制造体系与智能化未来
- 树莓派 OpenCV 乒乓球追踪进阶教程
- Raspoulette:太阳能自主鸡舍门控制系统原型
- DIY仿Wall-E树莓派CD盒机器人
- 滚动闹钟机器人:树莓派+Google Calendar智能移动唤醒系统
- 在 Windows 10 IoT Core 上构建语音驱动的计算机视觉机器人
- ROS 机器人:树莓派终端控制导航
- Simple Pi Robot:用树莓派打造 2WD 自主巡航车
- 适用于 Arduino 项目的智能室内导航机器人套件
- 线性电机原理与演进:从电能到精密直线运动的技术演变
- 饮料机器人:自动化技术重塑饮品服务新体验
- 伺服电机深度解析:工作原理、分类与核心价值