本帖最后由 kylinpoet 于 2025-4-11 16:21 编辑
一、【项目背景】
DF的ESP32-S3 AI CAM产品 (产品链接) ,板载200 W像 素 160 °广角红外夜视摄像头, MEMS麦克风和功放喇叭,以及基于ESP32的SOC芯片,妥妥的人工智能多模态交互利器。本项目预计通过网络大语言模型实现包括但不限于:语音识别、实时拍照、AI理解生成、语音合成等功能,做一个自主可控的多模态交互机器人 。可用于实际生活的学习交流、视障人士环境理解等。
二、【功能实现】
1. 代码简析:
ESP32-S3 AI CAM的文档提供了一个 ( OpenAI图像问答)点击访问 ,为节约成功,我们可以直接套用。但因为原作者的Github发布时间为2年前,有些代码需要更新修改,再加上网络原因,我们需要对其进行一些改造。
1)参数自定义
因为源码是使用 openai 的接口,这里可以根据实际情况进行参数自选。我修改了里面的 system 提示词,和 Temperature 让它的输出更可控点。
system提示词:An Artificial Intelligence Big Language Model for Everyday Communication,always response in chinese,Your answer should focus on the key and be concise and comprehensive. 复制代码
文件名称:imageAnswering.ino
2)OpenAI.h 头文件的修改
原作者在代码里硬编码了openai的官网地址,但因为众所周知的原因,我们难以访问。因此我在OpenAI类的构造函数里添加了 openai_base_url,作为基址参数。这样可以自定义 openai 通用接口进行访问。
然后修改 OpenAI.CPP 主函数部分。注意的是每个函数都要修改。(因为原作者每个函数都硬编码了一遍 ),你可以搜索替换。
3)还有个需要修改的地方是 OpenAI::audio_post 函数。我们看到代码里作者也是硬编码了 wav 格式的播放。因为我的自定义接口不是以流的方式返回,并且返回的只有mp3格式,所以,这里就不处理 流 的问题,而是等它全部返回后直接处理。(这也是,我待会的演示视频里语音播放会慢的原因)。 和小智对话机器人相比,我没有使用 websocket进行语音流接收,因此不是实时对话。(当然这只是演示实现,具体功能可后期再完善)
esp32的 I2S 包含库里有现成了 I2S::playMP3 和 I2S::playWAV 包装函数可以直接使用。因此,我们播放代码就大大简化了。
2. 完整代码:
完整代码,涉及 4 个文件,其中 camera.h 不做修改,其它文件请各位自行下载修改。这里只给出 OpenAI::audio_post 语音合成的代码作为实例:
int OpenAI::audio_post(const String& endpoint, const String& jsonBody) {
log_d(""%s": %s", endpoint.c_str(), jsonBody.c_str());
// 初始化I2S
I2SClass i2s;
i2s.setPins(45, 46, 42);
// pinMode(41, OUTPUT);
if (!i2s.begin(I2S_MODE_STD, 24000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
log_e("MAX98357 initialization failed!");
return -1;
}
// WiFiClientSecure client;
HTTPClient http;
http.setTimeout(60000);
http.useHTTP10(true);
http.begin(base_url + endpoint);
//http.begin("" + endpoint);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + api_key);
int httpCode = http.POST(jsonBody);
int totalBytesRead = 0; // 记录总共读取的字节数
if (httpCode != HTTP_CODE_OK) {
log_e("HTTP_ERROR: %d", httpCode);
http.end();
return -1;
}
// 获取响应内容
int contentLength = http.getSize();
uint8_t *audioData = (uint8_t *)malloc(contentLength); // 动态分配内存用于存储音频数据
if (audioData == nullptr) {
Serial.println("Failed to allocate memory");
return -1;
}
WiFiClient* stream = http.getStreamPtr();
// 读取完整的响应内容
int bytesRead = stream->readBytes(audioData, contentLength);
if (bytesRead == contentLength) {
// 调用您的播放函数
if (!i2s.playMP3(audioData, bytesRead)) {
log_e("Failed to play MP3");
free(stream);
return -1;
}
} else {
Serial.printf("Failed to read complete audio data, read only %d bytes\n", bytesRead);
}
http.end();
free(audioData); // 释放分配的内存
return bytesRead; // 返回实际解码数据大小
} 复制代码
三、【功能演示】
本项目的基本操作是这样的。reset按键——>自动连接网络——>指示灯亮——>按 boot 键输入语音(5S)——>语音转文字——>拍照截图——>提交大语言模型识别——>返回生成内容——>文本转语音——>功放播放