使用 ESP32-CAM 构建 Wi-Fi 遥控汽车机器人。您将能够使用Web服务器控制机器人,该服务器显示机器人"看到"的视频流。您可以远程控制您的机器人,即使它不在你的视线范围内。ESP32-CAM 将使用 Arduino IDE 进行编程。
电路板兼容性:此项目需要 4 个 GPIO 来控制直流电机。因此,您可以使用任何带有 4 个可用 GPIO 的 ESP32 相机板,如 ESP32-CAM Ai-Thinker
项目概况
在开始项目之前,我们将重点介绍用于构建机器人的最重要功能和组件。
无线网络
机器人将使用您的 ESP32-CAM 通过 Wi-Fi 进行控制。我们将创建一个基于Web的界面来控制机器人,可以在本地网络内的任何设备中访问。
该网页还显示了机器人"看到"的视频流。为了获得良好的视频流效果,我们建议使用带外部天线。
机器人控制
Web 服务器有 5 个控件:"前进"、"向后"、"向左"、"向右"和"停止"。
只要您按下按钮,机器人就会移动。当您松开任何按钮时,机器人就会停止。但是,我们包含了"停止"按钮,如果您释放按钮时 ESP32 未收到停止命令,该按钮非常有用。
智能机器人底盘套件
我们将使用智能机器人底盘套件。您可以在大多数在线商店中找到它。该套件的价格约为10美元,并且易于组装 - 观看此视频,了解如何组装机器人底盘套件。
您可以使用任何其他机箱套件,只要它带有两个直流电机即可。
L298N 电机驱动器
控制直流电机的方法有很多种。我们将使用L298N电机驱动器,它提供了一种简单的方法来控制2个直流电机的速度和方向。
为了保持电路简单,我们将使用相同的电源为机器人(电机)和 ESP32 供电。我们使用了移动电源/便携式充电器(就像用于为智能手机充电的充电器一样),效果很好。
注意:电机消耗大量电流,因此,如果您觉得机器人无法正常移动,则可能需要为电机使用外部电源。这意味着您需要两种不同的电源。一个用于为直流电机供电,另一个用于为 ESP32 供电。
部件
对于此项目,我们将使用以下部分:
ESP32-CAM AI-Thinker(安信可原装性能有保障),价格较贵,不带外置天线
ESP32-CAM模块带外置天线,第三方,较实惠
L298N 电机驱动器
机器人汽车底盘套件
移动电源或其他5V电源
代码下载:https://gitee.com/jun-tian/esp32-cam-wi-fi-car
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // disable brownout problems
#include "soc/rtc_cntl_reg.h" // disable brownout problems
#include "esp_http_server.h"
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";//修改为你的wifi名称
const char* password = "REPLACE_WITH_YOUR_PASSWORD";//修改为你的WiFi密码
#define PART_BOUNDARY "123456789000000000000987654321"
#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM_B
//#define CAMERA_MODEL_WROVER_KIT
#if defined(CAMERA_MODEL_WROVER_KIT)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 21
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 19
#define Y4_GPIO_NUM 18
#define Y3_GPIO_NUM 5
#define Y2_GPIO_NUM 4
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 25
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 17
#define VSYNC_GPIO_NUM 22
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#elif defined(CAMERA_MODEL_M5STACK_PSRAM_B)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 15
#define XCLK_GPIO_NUM 27
#define SIOD_GPIO_NUM 22
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 19
#define Y8_GPIO_NUM 36
#define Y7_GPIO_NUM 18
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 5
#define Y4_GPIO_NUM 34
#define Y3_GPIO_NUM 35
#define Y2_GPIO_NUM 32
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 26
#define PCLK_GPIO_NUM 21
#else
#error "Camera model not selected"
#endif
#define MOTOR_1_PIN_1 14
#define MOTOR_1_PIN_2 15
#define MOTOR_2_PIN_1 13
#define MOTOR_2_PIN_2 12
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;
插入您的网络凭据,代码应该立即工作。
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
代码的工作原理
让我们来看看控制机器人的相关部件。定义将控制电机的 GPIO。每个电机由两个引脚控制。
#define MOTOR_1_PIN_1 14
#define MOTOR_1_PIN_2 15
#define MOTOR_2_PIN_1 13
#define MOTOR_2_PIN_2 12
单击这些按钮时,将在其他 URL 上发出请求。
<table>
<tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Forward</button></td></tr>
<tr><td align="center"><button class="button" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Left</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('stop');" ontouchstart="toggleCheckbox('stop');">Stop</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button></td></tr>
<tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Backward</button></td></tr>
</table>
<script>
function toggleCheckbox(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/action?go=" + x, true);
xhr.send();
}
window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
</script>
以下是根据所按下的按钮发出的请求:
向前:
<ESP_IP_ADDRESS>/action?go=forward
向后:
/action?go=backward
左:
/action?go=left
右:
/action?go=right
停:
/action?go=stop
当您松开按钮时,将在/action?go=stop网址。机器人只有在您按下按钮时才会移动。
处理请求
为了处理当我们收到对这些URL的请求时发生的情况,我们使用这些如果...其他语句:
<span style="background-color:#f5f2f0"><span style="color:#000000"><code class="language-c"><span style="color:#0077aa">if</span><span style="color:#999999">(</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">!</span></span><span style="color:#dd4a68">strcmp</span><span style="color:#999999">(</span>variable<span style="color:#999999">,</span> <span style="color:#669900">"forward"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
Serial<span style="color:#999999">.</span><span style="color:#dd4a68">println</span><span style="color:#999999">(</span><span style="color:#669900">"Forward"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
<span style="color:#0077aa">else</span> <span style="color:#0077aa">if</span><span style="color:#999999">(</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">!</span></span><span style="color:#dd4a68">strcmp</span><span style="color:#999999">(</span>variable<span style="color:#999999">,</span> <span style="color:#669900">"left"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
Serial<span style="color:#999999">.</span><span style="color:#dd4a68">println</span><span style="color:#999999">(</span><span style="color:#669900">"Left"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
<span style="color:#0077aa">else</span> <span style="color:#0077aa">if</span><span style="color:#999999">(</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">!</span></span><span style="color:#dd4a68">strcmp</span><span style="color:#999999">(</span>variable<span style="color:#999999">,</span> <span style="color:#669900">"right"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
Serial<span style="color:#999999">.</span><span style="color:#dd4a68">println</span><span style="color:#999999">(</span><span style="color:#669900">"Right"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
<span style="color:#0077aa">else</span> <span style="color:#0077aa">if</span><span style="color:#999999">(</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">!</span></span><span style="color:#dd4a68">strcmp</span><span style="color:#999999">(</span>variable<span style="color:#999999">,</span> <span style="color:#669900">"backward"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
Serial<span style="color:#999999">.</span><span style="color:#dd4a68">println</span><span style="color:#999999">(</span><span style="color:#669900">"Backward"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">1</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span>
<span style="color:#0077aa">else</span> <span style="color:#0077aa">if</span><span style="color:#999999">(</span><span style="background-color:rgba(255, 255, 255, 0.5)"><span style="color:#9a6e3a">!</span></span><span style="color:#dd4a68">strcmp</span><span style="color:#999999">(</span>variable<span style="color:#999999">,</span> <span style="color:#669900">"stop"</span><span style="color:#999999">)</span><span style="color:#999999">)</span> <span style="color:#999999">{</span>
Serial<span style="color:#999999">.</span><span style="color:#dd4a68">println</span><span style="color:#999999">(</span><span style="color:#669900">"Stop"</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_1_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_1<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#dd4a68">digitalWrite</span><span style="color:#999999">(</span>MOTOR_2_PIN_2<span style="color:#999999">,</span> <span style="color:#990055">0</span><span style="color:#999999">)</span><span style="color:#999999">;</span>
<span style="color:#999999">}</span></code></span></span>
测试代码
插入网络凭据后,您可以将代码上传到 ESP32-CAM 开发板。
上传后,打开串行监视器以获取其IP地址。
.
打开浏览器并键入 ESP IP 地址。类似的网页应加载:
按下按钮并查看串行监视器,以查看它是否正在流式传输而没有延迟,以及它是否正在接收命令而不会崩溃。
如果一切正常,是时候组装电路了。
电路
组装机器人底盘后,您可以按照下一个原理图连接电路。.
首先将 ESP32-CAM 连接到电机驱动器,如原理图所示。您可以使用迷你试验板或脱衣板来放置 ESP32-CAM 并构建电路。
下表显示了 ESP32-CAM 和 L298N 电机驱动器之间的连接。
L298N 电机驱动器 | ESP32-凸轮 |
国家警察 | GPIO 14 |
IN2 | GPIO 15 |
高级护士 | GPIO 13 |
IN4 | GPIO 12 |
我们将所有连接组装在一个迷你条板上,如下所示。
之后,将每个电机连接到其接线端子。
注意:我们建议将0.1 uF陶瓷电容器焊接到每个电机的正极和负极,如图所示,以帮助平滑任何电压尖峰。此外,您可以将滑块开关焊接到来自移动电源的红线上。这样,您可以打开和关闭电源。
最后,使用移动电源供电,如图示意图所示。您需要剥离USB电缆。在这个例子中,ESP32-CAM和电机使用相同的电源供电,并且运行良好。
注意:电机消耗大量电流,因此,如果您觉得机器人的移动速度不够快,则可能需要为电机使用外部电源。这意味着您需要两种不同的电源。一个用于为直流电机供电,另一个用于为 ESP32 供电。您可以使用 4 AA 电池组为电机供电。当您获得机器人底盘套件时,您通常会获得一个用于4节AA电池的电池座。
您的机器人应类似于下图:
不要忘记,您应该将外部天线与 ESP32-CAM 配合使用,否则 Web 服务器可能会非常慢。
示范
在 ESP32-CAM IP 地址上打开浏览器,您应该能够控制您的机器人。Web服务器在笔记本电脑或智能手机上运行良好。
您一次只能在一个设备/选项卡中打开Web服务器。
结束语
在本教程中,您学习了如何使用 ESP32-CAM 构建遥控机器人,以及如何使用 Web 服务器对其进行控制。
使用 ESP32-CAM 控制直流电机与使用"常规"ESP32 控制直流电机相同。
如果要在本地网络范围之外控制机器人,可以考虑将 ESP32-CAM 设置为接入点。这样,ESP32-CAM 就不需要连接到您的路由器,它会创建自己的 Wi-Fi 网络,附近的 Wi-Fi 设备(如智能手机)可以连接到它。