| 本帖最后由 楼主 于 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)软件
 2.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[68123];
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[RECVLENGTH];
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[y]);
      //   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[0].toInt();
    int left1 = vposVals[1].toInt();
    int left2 = vposVals[2].toInt();
    int left3 = vposVals[3].toInt();
    controlDC(left0, left1, left2, left3);
  }
  #endif
}
 五、实验结果 
 
 六、实验总结 本次实验成功在ESP32上实现了实时物体检测与基础追踪功能,验证了边缘计算设备的视觉处理能力。尽管存在处理速度与精度的平衡挑战,同时APP软件我只能使用其他国外创客者的开发产品(需要恶补一下app开发的知识,然后将这个软件再做进一步优化). 
 |