ESP32-S3 AI 智能摄像头模块作为机器人的大脑1
本帖最后由 Anders项勇 于 2025-5-17 22:21 编辑【项目背景】
这次幸运抽取到试用ESP32-S3 AI板子,构思了一个机器人项目。随着特斯拉擎天柱机器人在工业领域的带动,现在国内外人形机器人行业蓬勃发展,大有一举替代人的简单劳动的趋势,国内也有许多优秀的机器人公司,如宇树、智元等等,人形机器人必将给社会和生活。为什么人形机器人行业在以前没有发展起来呢?是被机器人的大脑技术制约了,身体再灵活也只是小脑,离取代人相差甚远,随着人工智能的发展,大家看到了实现大脑功能的前景,大量资本涌入,行业蓬勃发展,在工业、家用领域都有了许多积极的场景尝试。前几天我也参加了在上海举办的2025中国人形机器人生态大会,看到了机器人整个行业的产业链发展。
【项目设计】
由于我有一个红外遥控的机器人,但是没有智能功能,想利用ESP32-S3 AI集成摄像头和强大的ai功能来开发机器人的大脑。思路是第一步要破解机器人的红外遥控控制、第二步是用ESP32-S3 AI训练识别一个球、第三步是让机器人根据识别球的坐标调用机器人控制走到合适的位置去执行踢球的动作。
1.拆解机器人:
先把机器人拆开看看,这个机器人是日本2007年的产品i-sobot,那时候能做到这么小巧确实不容易。舵机很小,齿轮是金属的,有17个,但不是串口舵机,但是线路隐藏多大很好,外观几乎看不见舵机线。拆开一看有点鼓包电池有点老化,和df的电池一比,几乎一样大小,改造了下塞进去,里面定制的板子很小,设计很紧凑。
2.破解机器人红外遥控控制:
用红外接收读不到特定的信息,确定不是使用的标准协议,是自定义的通讯协议。需要使用示波器来了解发射信号的特征,由于没有示波器,先去网上找找有没有相关的资料,这个机器人比较老,国内基本上很少资料,在国外网站找到一些资料参考尝试来破解红外控制。红外的知识参考:https://wiki.dfrobot.com.cn/_SKU_DFR0094_IR_Receiver_Module%E7%BA%A2%E5%A4%96%E6%8E%A5%E6%94%B6%E6%A8%A1%E5%9D%97
先使用Sparrow 微控制器来控制(https://wiki.dfrobot.com.cn/_SKU_DFR0589_Sparrow_%E5%BE%AE%E6%8E%A7%E5%88%B6%E5%99%A8)Sparrow 微控制器是Arduino Leonardo的高度集成版本。在arduino中使用如下代码控制机器人成功,基本原理是使用板子本身的时钟频率作为时钟参考生成红外遥控信号,由键盘发指令控制机器人。用Sparrow 接一个红外发射管来发射调制的红外信号控制机器人。
代码如下:
//-------------------动作编码-------------------------
#define forward 898819
#define backward 964611
#define fleft 1030403
#define fright 571907
#define forwardkickr 664832
#define k1 991744
#define g1 602624
#define zero 1034752
#define hp 775424
#define totallength 22
#define channelstart 0
#define commandstart 4
#define channellength4
#define commandlength18
#define headerlower 2300
#define headernom 2550
#define headerupper 2800
#define zerolower 300
#define zeronom 380
#define zeroupper 650
#define onelower 800
#define onenom 850
#define oneupper 1100
#define highnom 630
#define TXpin A1
#define RXpin 2
boolean bit2;
unsigned long buttonnum;
unsigned long power2(int power){
unsigned long integer=1;
for (int i=0; i<power; i++){
integer*=2;
}
return integer;
}
void ItoB(unsigned long integer, int length){
for (int i=0; i<length; i++){
if ((integer / power2(length-1-i))==1){
integer-=power2(length-1-i);
bit2=1;
}
else bit2=0;
}
}
unsigned long BtoI(int start, int numofbits, boolean bit[]){
unsigned long integer=0;
int i=start;
int n=0;
while(n<numofbits){
integer+=bit*power2((numofbits-n-1));
i++;
n++;
}
Serial.println();
return integer;
}
void oscWrite(int pin, int time) { //频率38khz
for(int i = 0; i < (time / 52) - 1; i++){ //预分频器 26 - 16mhz, 52 - 8mhz
digitalWrite(pin, HIGH);
delayMicroseconds(13);
digitalWrite(pin, LOW);
delayMicroseconds(13);
}
}
void buttonwrite(int txpin, unsigned long integer){
ItoB(integer, 22);
oscWrite(txpin, headernom);
for(int i=0;i<totallength;i++){
if (bit2==0)delayMicroseconds(zeronom);
else delayMicroseconds(onenom);
oscWrite(txpin, highnom);
}
delay(205);
}
//-----------------------program-----------------------
void setup() {
Serial.begin(38400);//接收键盘串口信号来控制机器人
Serial.println("Analyze Isobot IR Remote");
pinMode(RXpin, INPUT);
pinMode(TXpin, OUTPUT);
}
void loop(){
if(Serial.available()>0){
char switcher= Serial1.read();
switch (switcher){
case 'w':
buttonwrite(TXpin, forward);
break;
case 's':
buttonwrite(TXpin, backward);
break;
case 'a':
buttonwrite(TXpin, fleft);
break;
case 'd':
buttonwrite(TXpin, fright);
break;
case 'p':
buttonwrite(TXpin, forwardkickr);
break;
case 'k':
buttonwrite(TXpin, k1);
break;
case 'g':
buttonwrite(TXpin, g1);
break;
case 'h':
buttonwrite(TXpin, hp);
break;
case '0':
buttonwrite(TXpin, zero);
break;
}
}
}
3.代码Sparrow从移植到ESP32-S3 AI:
由于ESP32-S3 AI的频率是变动的,无法很好作为时钟参考,误差太大,但可以使用LEDC控制模块(特指其PWM脉冲宽度调制)作为时钟参考,但实际用下面代码执行机器人没有反应,不知道哪里有问题。
代码如下:
#include <driver/ledc.h>
//-------------------动作编码-------------------------
#define forward 898819
#define backward 964611
#define fleft 1030403
#define fright 571907
#define forwardkickr 664832
#define k1 991744
#define g1 602624
#define zero 1034752
#define hp 775424
#define totallength 22
#define channelstart 0
#define commandstart 4
#define channellength4
#define commandlength18
#define headerlower 2300
#define headernom 2550
#define headerupper 2800
#define zerolower 300
#define zeronom 380
#define zeroupper 650
#define onelower 800
#define onenom 850
#define oneupper 1100
#define highnom 630
#define TXpin 43
#define RXpin 2
boolean bit2;
unsigned long buttonnum;
unsigned long power2(int power){
unsigned long integer=1;
for (int i=0; i<power; i++){
integer*=2;
}
return integer;
}
void ItoB(unsigned long integer, int length){
for (int i=0; i<length; i++){
if ((integer / power2(length-1-i))==1){
integer-=power2(length-1-i);
bit2=1;
}
else bit2=0;
}
}
unsigned long BtoI(int start, int numofbits, boolean bit[]){
unsigned long integer=0;
int i=start;
int n=0;
while(n<numofbits){
integer+=bit*power2((numofbits-n-1));
i++;
n++;
}
Serial.println();
return integer;
}
void oscWrite(int time) {
// 使用LEDC输出指定时间的38kHz信号
int duty = (time * (1 << 13)) / 1000000;// 根据时间计算占空比(13位分辨率)
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
delayMicroseconds(time / 2);// 38kHz信号周期为1/38000秒,因此这里延迟一半周期
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0);// 关闭信号
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
}
void buttonwrite(unsigned long integer) {
// 将整数转换为二进制位数组
boolean bit2;
ItoB(integer, 22);
// 输出头部脉冲
oscWrite(headernom);
// 输出数据位和分隔高电平
for (int i = 0; i < 22; i++) {
if (bit2 == 0) {
oscWrite(zeronom);
} else {
oscWrite(onenom);
}
oscWrite(highnom);// 分隔高电平
}
// 输出结束低电平
delay(205);
}
//-----------------------program-----------------------
void setup() {
Serial.begin(38400);
Serial.println("Analyze Isobot IR Remote");
pinMode(RXpin, INPUT);
pinMode(TXpin, OUTPUT);
// 配置LEDC定时器
ledc_timer_config_t timer_conf = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = LEDC_TIMER_13_BIT,
.timer_num = LEDC_TIMER_0,
.freq_hz = 38000,// 设置为38kHz
.clk_cfg = LEDC_AUTO_CLK
};
ledc_timer_config(&timer_conf);
// 配置LEDC通道,绑定GPIO和定时器
ledc_channel_config_t channel_conf = {
.gpio_num = TXpin,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.timer_sel = LEDC_TIMER_0,
.duty = 0,
.hpoint = 0
};
ledc_channel_config(&channel_conf);
}
void loop() {
if (Serial.available() > 0) {
char switcher = Serial.read();
switch (switcher) {
case 'w':
buttonwrite(forward);
break;
case 's':
buttonwrite(backward);
break;
case 'a':
buttonwrite(fleft);
break;
case 'd':
buttonwrite(fright);
break;
case 'p':
buttonwrite(forwardkickr);
break;
case 'k':
buttonwrite(k1);
break;
case 'g':
buttonwrite(g1);
break;
case 'h':
buttonwrite(hp);
break;
case '0':
buttonwrite(zero);
break;
}
}
}
3.ESP32-S3 AI与Sparrow串口通讯:
然后考虑把ESP32-S3 AI和Sparrow通过串口对接,ESP32-S3 AI按下boot键发送命令串口传给Sparrow,Sparrow收到命令再给机器人发出红外动作执行命令,测试成功。视频:
https://www.bilibili.com/video/BV1RtGzzSE2k/?share_source=copy_web&vd_source=834e5e68930e699f2fd8d958479ba240
ESP32-S3 AI代码如下:
int led = 3;
void setup() {
Serial1.begin(115200);//物理串口
pinMode(led,OUTPUT);
}
void loop() {
digitalWrite(led,HIGH);
if(!digitalRead(0)){
Serial1.print("p");
delay(1000);
digitalWrite(led,LOW);
delay(1000);
}
}
Sparrow代码如下(主要把虚拟串口Serial改为物理串口Serial1):
//-------------------动作编码-------------------------
#define forward 898819
#define backward 964611
#define fleft 1030403
#define fright 571907
#define forwardkickr 664832
#define k1 991744
#define g1 602624
#define zero 1034752
#define hp 775424
#define totallength 22
#define channelstart 0
#define commandstart 4
#define channellength4
#define commandlength18
#define headerlower 2300
#define headernom 2550
#define headerupper 2800
#define zerolower 300
#define zeronom 380
#define zeroupper 650
#define onelower 800
#define onenom 850
#define oneupper 1100
#define highnom 630
#define TXpin A1
#define RXpin 2
boolean bit2;
unsigned long buttonnum;
unsigned long power2(int power){
unsigned long integer=1;
for (int i=0; i<power; i++){
integer*=2;
}
return integer;
}
void ItoB(unsigned long integer, int length){
for (int i=0; i<length; i++){
if ((integer / power2(length-1-i))==1){
integer-=power2(length-1-i);
bit2=1;
}
else bit2=0;
}
}
unsigned long BtoI(int start, int numofbits, boolean bit[]){
int i=start;
int n=0;
while(n<numofbits){
integer+=bit*power2((numofbits-n-1));
i++;
n++;
}
Serial.println();
return integer;
}
void oscWrite(int pin, int time) { //频率38khz
for(int i = 0; i < (time / 52) - 1; i++){ //预分频器 26 - 16mhz, 52 - 8mhz
digitalWrite(pin, HIGH);
delayMicroseconds(13);
digitalWrite(pin, LOW);
delayMicroseconds(13);
}
}
void buttonwrite(int txpin, unsigned long integer){
ItoB(integer, 22);
oscWrite(txpin, headernom);
for(int i=0;i<totallength;i++){
if (bit2==0)delayMicroseconds(zeronom);
else delayMicroseconds(onenom);
oscWrite(txpin, highnom);
}
delay(205);
}
//-----------------------program-----------------------
void setup() {
Serial1.begin(115200);//接收其他控制板发出的串口信号来控制机器人
Serial1.println("Analyze Isobot IR Remote");
pinMode(RXpin, INPUT);
pinMode(TXpin, OUTPUT);
}
void loop(){
if(Serial1.available()>0){
char switcher= Serial1.read();
switch (switcher){
case 'w':
buttonwrite(TXpin, forward);
break;
case 's':
buttonwrite(TXpin, backward);
break;
case 'a':
buttonwrite(TXpin, fleft);
break;
case 'd':
buttonwrite(TXpin, fright);
break;
case 'p':
buttonwrite(TXpin, forwardkickr);
break;
case 'k':
buttonwrite(TXpin, k1);
break;
case 'g':
buttonwrite(TXpin, g1);
break;
case 'h':
buttonwrite(TXpin, hp);
break;
case '0':
buttonwrite(TXpin, zero);
break;
}
}
}
页:
[1]