帮助盲人“看到”世界的 AI 硬件,能行吗?

444 阅读5分钟

我们打算做一款帮助盲人“看到”世界的 AI 硬件。

这个想法的起因是某天中午下班,笔者正在路上走着,忽听到后面传来很轻的歌声。回头看,一个戴着墨镜的男人拿着盲杖,正摸索着沿盲道前行。生活中很少遇到盲人,于是特意留意了下。他立得很直,嘴巴轻轻开合发出歌声,虽然走得慢,却每一步都很扎实。笔者和他一起走了一段,看到前方的盲道上停了一辆摩托车。走在我前面另一个女生尝试去搬走摩托车,见状我也快步上前搭了把手,一起移开了它。因为赶时间,移开车后我就走开了,歌声也越来越远。

后来我想,假如没有人把车移开,就算有盲杖探路,可能也会给这位朋友造成一些不大不小的困扰吧。有什么办法可以提前告知他前方的路况吗?巧的是,多模态大模型能通过自然语言对话、理解图像,理论上正好能充当这位朋友的眼睛。

于是有了这个产品的尝试。

按设想,用户交互的流程是佩戴硬件->摄像头拍摄物理世界发给大模型->大模型理解后以语音形式告知用户前方的路况信息。硬件不可少的模块包括:摄像头、麦克风,网络连接先用WIFI,另外可能需要蓝牙连接手机。

选择的开发板是 XIAO ESP32 S3,笔者是 ESP32 开发的纯新人,故而拿到硬件之后先测试各个模块的功能,同时学习开发过程。

开发板真容如下:

测试代码直接选择的官方示例,地址: https://wiki.seeedstudio.com/cn/xiao_esp32s3_camera_usage/

测试摄像头

下方仅摘抄主要流程代码并在其中加上注释,可视作伪代码理解流程。

void photo_save(const char * fileName) {
  // 调用摄像头拍摄一帧画面
  camera_fb_t *fb = esp_camera_fb_get();

  // 保存图像
  writeFile(SD, fileName, fb->buf, fb->len);

  // 释放图像 buffer
  esp_camera_fb_return(fb);
}

// 图像存储到 SD 卡
void writeFile(fs::FS &fs, const char * path, uint8_t * data, size_t len){

  File file = fs.open(path, FILE_WRITE);

  file.close();
}

// ESP32 的初始化代码
void setup() {
  // 初始化串口
  Serial.begin(115200);

  // 初始化相机
  esp_err_t err = esp_camera_init(&config);
}

// ESP32 的循环程序入口
void loop() {

  char filename[32];

  sprintf(filename, "/image1.jpg");

  photo_save(filename);

  Serial.printf("Saved picture: %s\r\n", filename);
}

上面的代码执行完毕会在 SD 卡中保存摄像头拍摄的画面,我们在其中找到下图,是笔者电脑屏幕正播放的内容,摄像头功能正常。

测试麦克风

void setup() {
  Serial.begin(115200);

  // 设置 I2S 接口使用的引脚,并初始化 I2S 接口
  I2S.setAllPins(-1, 42, 41, -1, -1);
  I2S.begin(PDM_MONO_MODE, SAMPLE_RATE, SAMPLE_BITS)

  // 麦克风开始录制音频
  record_wav();
}

void loop() {
  // 不执行复杂逻辑
}

void record_wav()
{
  // 打开要保存的音频文件
  File file = SD.open("/"WAV_FILE_NAME".wav", FILE_WRITE);

  // 生成 wav 格式文件的头信息
  generate_wav_header(wav_header, record_size, SAMPLE_RATE);

  // 往 wav 文件写头信息
  file.write(wav_header, WAV_HEADER_SIZE);

  // 开始记录音频
  esp_i2s::i2s_read(esp_i2s::I2S_NUM_0, rec_buffer, record_size, &sample_size, portMAX_DELAY);

  // 往 wav 文件保存音频
  file.write(rec_buffer, record_size)

  // 释放 buffer
  free(rec_buffer);
  file.close();

}

上面代码执行完毕后,会在 SD 卡中保存麦克风录制的 20 秒音频文件,我们看到确实保存了,且播放正常。

测试蓝牙

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
    }
};

void setup() {
  Serial.begin(115200);
  Serial.println("Scanning...");

  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);
}

void loop() {
   // 开始扫描蓝牙信号
  BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
  Serial.print("Devices found: ");
  Serial.println(foundDevices.getCount());
  Serial.println("Scan done!");
  pBLEScan->clearResults();
  delay(10000);
}

上述代码执行完毕会列出附近扫描到的蓝牙信号,打开 Serial Monitor,可以看到正常扫描得到了结果:

测试 WIFI

void setup() {
  Serial.begin(115200);
}

void loop() {
  // 开始扫描 WIFI 信号,返回附近的 WIFI 数量
  int n = WiFi.scanNetworks();
  Serial.println("scan done");
  if (n == 0) {
      Serial.println("no networks found");
  } else {
    Serial.print(n);
    Serial.println(" networks found");
    for (int i = 0; i < n; ++i) {
      // 打印 WIFI 信息
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.print(")");
      Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
      delay(10);
    }
  }
  Serial.println("");

  // 延迟 5s 进行下一次扫描
  delay(5000);
}

上述代码运行完毕会列出周围 WIFI 的信息,实际结果显示工作正常:

测试网络连接

void setup() {
    USE_SERIAL.begin(115200);

    wifiMulti.addAP("MY WIFI SSID", "PASSWORD");
}

void loop() {
    // 当 WIFI 连接上
    if((wifiMulti.run() == WL_CONNECTED)) {
        // 请求测试网页
        http.begin("http://example.com/index.html");

        int httpCode = http.GET();
        
        // 成功返回结果
        if(httpCode == HTTP_CODE_OK) {
            String payload = http.getString();
            USE_SERIAL.println(payload);
        }
        http.end();
    }

    delay(5000);
}

上述代码将请求测试网页,并返回测试网页的代码。从测试结果看功能正常:

测试大模型

最后,我们测试大模型是否能为盲人朋友识别路况,以最简单的图片中盲道被占的图片为例:

我们用 GPT-4o 来进行测试,结果如下:

我们看到大模型将道路中的摩托车、盲道上的铁架和石块都识别出来了,这是影响盲道畅通的关键因素。当然其中还有一些元素识别有问题,作为后续改进的项目。

总结:

本文对购买的 XIAO ESP32 S3 开发板,测试了摄像头、麦克风、蓝牙、WIFI、网络连接以及最后的大模型识别能力,发现基本功能满足需求。后续将以此为基础,进行该硬件的开发。

欢迎关注哦~

希望各位能提提意见,比如这个想法可行性、实用性,或者其它帮助完善的其它想法都可以的。