ESP32-AI摄像头物体检测+边框追踪
本帖最后由 楼主 于 2025-5-16 21:32 编辑本次测试的内容为ESP32 AI camera实现物体检测和物体追踪 五一前收到了心心念念的新一代ESP32 camera,听说从硬件上对产品做了很大的提升。不过最近工作任务太多,测试早早做完了,但是一直没有发帖子分享,这周终于挤出了时间码一篇心得,话不多说看看实测效果。一、实验目的掌握ESP32 AI Camera的硬件配置与开发环境搭建方法。设计算法实现动态物体追踪,结合摄像头锁定目标。二、实验器材与软件环境硬件1.ESP32 AI Camera开发板(带OV2640摄像头模块)2.伺服舵机(用于物理追踪)3.USB数据线、杜邦线4.PC(Windows/Linux)
软件1.Arduino IDE2.OpenCV库(模型训练与数据预处理)三、实验原理1.物体检测使用封装好的openCVLibrary348库的模型进行实时图像推理。通过摄像头采集图像,模型输出目标类别与边界框坐标。2.物体追踪中心点追踪:计算检测框中心与画面中心的偏移量,控制云台转动。手动追踪:通过手动锁定物体框进行追踪,控制云台转动。四、实验步骤1.环境搭建安装Arduino ESP32开发板支持包导入openCV库及摄像头驱动连接摄像头模块至开发板2.代码测试#include <WebSocketsServer.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include "camera_wrap.h"
// #define DEBUG
// #define SAVE_IMG
enum TRACK{
TRACK_NONE = 0,
TRACK_FW,
TRACK_LEFT,
TRACK_RIGHT,
TRACK_STOP
};
const char* ssid = "PS4"; // <<< change this as yours
const char* password = "22222222"; // <<< change this as yours
//holds the current upload
int cameraInitState = -1;
uint8_t* jpgBuff = new uint8_t;
size_t jpgLength = 0;
uint8_t camNo=0;
bool clientConnected = false;
//Creating UDP Listener Object.
WiFiUDP UDPServer;
IPAddress addrRemote;
unsigned int portRemote;
unsigned int UDPPort = 6868;
const int RECVLENGTH = 16;
byte packetBuffer;
WebSocketsServer webSocket = WebSocketsServer(86);
String html_home;
const int LED_BUILT_IN = 4;
const uint8_t TRACK_DUTY = 100;
const int PIN_SERVO_PITCH = 12;
// const int PIN_SERVO_YAW = 2;
const int PINDC_LEFT_BACK = 13;
const int PINDC_LEFT_FORWARD= 15;
const int PINDC_RIGHT_BACK = 14;
const int PINDC_RIGHT_FORWARD = 2;
const int LEFT_CHANNEL = 2;
const int RIGHT_CHANNEL = 3;
const int SERVO_PITCH_CHANNEL = 4;
const int SERVO_YAW_CHANNEL = 5;
const int SERVO_RESOLUTION = 16;
unsigned long previousMillisServo = 0;
const unsigned long intervalServo = 10;
bool servoUp = false;
bool servoDown = false;
bool servoRotateLeft = false;
bool servoRotateRight = false;
int posServo = 75;
int PWMTrackHIGH = 138;
int PWMTrackLOW = 138;
void servoWrite(uint8_t channel, uint8_t angle) {
// regarding the datasheet of sg90 servo, pwm period is 20 ms and duty is 1->2ms
uint32_t maxDuty = (pow(2,SERVO_RESOLUTION)-1)/10;
uint32_t minDuty = (pow(2,SERVO_RESOLUTION)-1)/20;
uint32_t duty = (maxDuty-minDuty)*angle/180 + minDuty;
ledcWrite(channel, duty);
}
void controlServo(){
if(servoUp){
if(posServo>2){
posServo -= 2;
}
}
if(servoDown){
if(posServo<180){
posServo += 2;
}
}
servoWrite(SERVO_PITCH_CHANNEL,posServo);
}
void controlDC(int left0, int left1, int right0, int right1){
digitalWrite(PINDC_LEFT_BACK, left0);
if(left1 == HIGH){
ledcWrite(LEFT_CHANNEL, 255);
}else{
ledcWrite(LEFT_CHANNEL, 0);
}
digitalWrite(PINDC_RIGHT_BACK, right0);
if(right1 == HIGH){
ledcWrite(RIGHT_CHANNEL, 255);
}else{
ledcWrite(RIGHT_CHANNEL, 0);
}
}
void controlDCTrack(int left, int right){
digitalWrite(PINDC_LEFT_BACK, 0);
ledcWrite(LEFT_CHANNEL, left);
digitalWrite(PINDC_RIGHT_BACK, 0);
ledcWrite(RIGHT_CHANNEL, right);
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", num);
camNo = num;
clientConnected = false;
break;
case WStype_CONNECTED:
Serial.printf("[%u] Connected!\n", num);
clientConnected = true;
break;
case WStype_TEXT:
case WStype_BIN:
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
Serial.println(type);
break;
}
}
std::vector<String> splitString(String data, String delimiter){
std::vector<String> ret;
// initialize first part (string, delimiter)
char* ptr = strtok((char*)data.c_str(), delimiter.c_str());
while(ptr != NULL) {
ret.push_back(String(ptr));
// create next part
ptr = strtok(NULL, delimiter.c_str());
}
return ret;
}
void processUDPData(){
int cb = UDPServer.parsePacket();
if (cb) {
UDPServer.read(packetBuffer, RECVLENGTH);
addrRemote = UDPServer.remoteIP();
portRemote = UDPServer.remotePort();
String strPackage = String((const char*)packetBuffer);
#ifdef DEBUG
Serial.print("receive: ");
// for (int y = 0; y < RECVLENGTH; y++){
// Serial.print(packetBuffer);
// Serial.print("\n");
// }
Serial.print(strPackage);
Serial.print(" from: ");
Serial.print(addrRemote);
Serial.print(":");
Serial.println(portRemote);
#endif
if(strPackage.equals("whoami")){
UDPServer.beginPacket(addrRemote, portRemote-1);
String res = "ESP32-CAM";
UDPServer.write((const uint8_t*)res.c_str(),res.length());
UDPServer.endPacket();
Serial.println("response");
}else if(strPackage.equals("forward")){
controlDC(LOW,HIGH,LOW,HIGH);
}else if(strPackage.equals("backward")){
controlDC(HIGH,LOW,HIGH,LOW);
}else if(strPackage.equals("left")){
controlDC(LOW,LOW,LOW,HIGH);
}else if(strPackage.equals("right")){
controlDC(LOW,HIGH,LOW,LOW);
}else if(strPackage.equals("stop")){
controlDC(LOW,LOW,LOW,LOW);
}else if(strPackage.equals("camup")){
servoUp = true;
}else if(strPackage.equals("camdown")){
servoDown = true;
}else if(strPackage.equals("camstill")){
servoUp = false;
servoDown = false;
}else if(strPackage.equals("ledon")){
digitalWrite(LED_BUILT_IN, HIGH);
}else if(strPackage.equals("ledoff")){
digitalWrite(LED_BUILT_IN, LOW);
}else if(strPackage.equals("lefttrack")){
controlDCTrack(0, PWMTrackHIGH);
}else if(strPackage.equals("righttrack")){
controlDCTrack(PWMTrackHIGH, 0);
}else if(strPackage.equals("fwtrack")){
controlDCTrack(PWMTrackLOW, PWMTrackLOW);
}
memset(packetBuffer, 0, RECVLENGTH);
}
}
void setup(void) {
Serial.begin(115200);
Serial.print("\n");
#ifdef DEBUG
Serial.setDebugOutput(true);
#endif
pinMode(LED_BUILT_IN, OUTPUT);
digitalWrite(LED_BUILT_IN, LOW);
pinMode(PINDC_LEFT_BACK, OUTPUT);
ledcSetup(LEFT_CHANNEL, 100, 8);//channel, freq, resolution
ledcAttachPin(PINDC_LEFT_FORWARD, LEFT_CHANNEL);
pinMode(PINDC_RIGHT_BACK, OUTPUT);
ledcSetup(RIGHT_CHANNEL, 100, 8);//channel, freq, resolution
ledcAttachPin(PINDC_RIGHT_FORWARD, RIGHT_CHANNEL);
controlDC(LOW,LOW,LOW,LOW);
// 1. 50hz ==> period = 20ms (sg90 servo require 20ms pulse, duty cycle is 1->2ms: -90=>90degree)
// 2. resolution = 16, maximum value is 2^16-1=65535
// From 1 and 2 => -90=>90 degree or 0=>180degree ~ 3276=>6553
ledcSetup(SERVO_PITCH_CHANNEL, 50, 16);//channel, freq, resolution
ledcAttachPin(PIN_SERVO_PITCH, SERVO_PITCH_CHANNEL);// pin, channel
servoWrite(SERVO_PITCH_CHANNEL, posServo);
// ledcSetup(SERVO_YAW_CHANNEL, 50, 16);//channel, freq, resolution
// ledcAttachPin(PIN_SERVO_YAW, SERVO_YAW_CHANNEL);// pin, channel
// servoWrite(SERVO_YAW_CHANNEL, posServo);
cameraInitState = initCamera();
Serial.printf("camera init state %d\n", cameraInitState);
if(cameraInitState != 0){
return;
}
//WIFI INIT
Serial.printf("Connecting to %s\n", ssid);
if (String(WiFi.SSID()) != String(ssid)) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
}
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected! IP address: ");
String ipAddress = WiFi.localIP().toString();;
Serial.println(ipAddress);
webSocket.begin();
webSocket.onEvent(webSocketEvent);
UDPServer.begin(UDPPort);
}
void loop(void) {
webSocket.loop();
if(clientConnected == true){
grabImage(jpgLength, jpgBuff);
webSocket.sendBIN(camNo, jpgBuff, jpgLength);
// Serial.print("send img: ");
// Serial.println(jpgLength);
}
unsigned long currentMillis = millis();
if (currentMillis - previousMillisServo >= intervalServo) {
previousMillisServo = currentMillis;
processUDPData();
controlServo();
}
#ifdef DEBUG
if (Serial.available()) {
String data = Serial.readString();
Serial.println(data);
std::vector<String> vposVals = splitString(data, ",");
if(vposVals.size() != 4){
return;
}
int left0 = vposVals.toInt();
int left1 = vposVals.toInt();
int left2 = vposVals.toInt();
int left3 = vposVals.toInt();
controlDC(left0, left1, left2, left3);
}
#endif
}
五、实验结果
六、实验总结本次实验成功在ESP32上实现了实时物体检测与基础追踪功能,验证了边缘计算设备的视觉处理能力。尽管存在处理速度与精度的平衡挑战,同时APP软件我只能使用其他国外创客者的开发产品(需要恶补一下app开发的知识,然后将这个软件再做进一步优化).
页:
[1]