材料:
ESP32-CAM模块
两个N20减速电机
MPU6050陀螺仪和加速度计模块
电池供电模块或锂电池
电机驱动模块(例如L298N驱动模块)
连接线和焊接工具
用于图像传输的WebSocket库(例如ArduinoWebsockets)
代码: 全选
#include <WiFi.h>
#include <WebServer.h>
#include <WebSocketServer.h>
#include <MPU6050.h>
#include <esp_camera.h>
// Wi-Fi网络设置
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
// WebSocket服务器设置
WebSocketServer webSocketServer;
// 电机引脚设置
const int motorPin1 = 2; // 第一个电机引脚
const int motorPin2 = 3; // 第二个电机引脚
// MPU6050对象和角度变量
MPU6050 mpu;
float angle;
float angleOffset = 0;
WebServer server(80);
void handleRoot() {
// 返回Web页面,包括实时图像和控制按钮
server.send(200, "text/html", "<html><body><h1>ESP32-CAM Control</h1>< img src=\"/stream\" width=\"640\" height=\"480\"><br><button onclick=\"sendCommand('forward')\">Forward</button><button onclick=\"sendCommand('backward')\">Backward</button><button onclick=\"sendCommand('left')\">Left</button><button onclick=\"sendCommand('right')\">Right</button><script>var socket=new WebSocket('ws://'+location.hostname+':81/');socket.onmessage=function(event){console.log(event.data);};function sendCommand(command){socket.send(command);}</script></body></html>");
}
void handleNotFound() {
// 处理404错误页面
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
// 处理WebSocket事件
switch (type) {
case WStype_TEXT:
String command = String((char*)payload);
if (command == "forward") {
// 前进指令
// 控制电机前进的代码
} else if (command == "backward") {
// 后退指令
// 控制电机后退的代码
} else if (command == "left") {
// 左移指令
// 控制电机左移的代码
} else if (command == "right") {
// 右移指令
// 控制电机右移的代码
}
break;
}
}
void setup() {
Serial.begin(115200);
// 初始化摄像头
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = 5;
config.pin_d1 = 18;
config.pin_d2 = 19;
config.pin_d3 = 21;
config.pin_d4 = 36;
config.pin_d5 = 39;
config.pin_d6 = 34;
config.pin_d7 = 35;
config.pin_xclk = 0;
config.pin_pclk = 22;
config.pin_vsync = 25;
config.pin_href = 23;
config.pin_sscb_sda = 26;
config.pin_sscb_scl = 27;
config.pin_pwdn = 32;
config.pin_reset = -1;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if (psramFound()) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// 启动摄像头
esp_err_t cam_err = esp_camera_init(&config);
if (cam_err != ESP_OK) {
Serial.printf("Camera initialization failed with error 0x%x", cam_err);
return;
}
// 连接Wi-Fi网络
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// 初始化WebSocket服务器
webSocketServer.begin();
webSocketServer.onEvent(webSocketEvent);
// 设置Web服务器处理函数
server.on("/", handleRoot);
server.onNotFound(handleNotFound);
// 启动Web服务器
server.begin();
Serial.println("Web server started");
// 初始化MPU6050模块
mpu.initialize();
// 计算初始倾斜角度的偏移量
for (int i = 0; i < 200; i++) {
angleOffset += getAngle();
delay(5);
}
angleOffset /= 200.0;
// 设置电机引脚
pinMode(motorPin1, OUTPUT);
pinMode(motorPin2, OUTPUT);
}
void loop() {
// 处理WebSocket客户端连接
webSocketServer.loop();
// 处理Web服务器请求
server.handleClient();
// 获取倾斜角度
angle = getAngle() - angleOffset;
// 根据倾斜角度控制电机运动
// 根据angle的值来调整电机运动的代码
}
float getAngle() {
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
mpu.getMotion6(&AcX, &AcY, &AcZ, &GyX, &GyY, &GyZ);
float accelAngleX = atan(-1.0 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180.0 / PI;
float gyroAngleX = GyX / 131.0; // 陀螺仪的刻度因数为131
float angleX = 0.98 * (gyroAngleX + 0.01 * accelAngleX) + 0.02 * accelAngleX;
return angleX;
}