[Day 31] 番外篇─如何将OV7670 + BLE Sense连到Edge Impulse取像

虽然铁人赛已暂告一个段落,但在[Day 27] Edge Impulse + BLE Sense实现影像分类(上)有提到采购的OV760(无FIFO)摄影机模组尚未到手,所以只能暂以预先准备好的影像集上传来做实验。上周拿到後就急忙进行实验,结果一波N折,处处碰壁,直到昨晚(2021/10/20)才算初步搞定,所以今天补上这篇方便大家可以测试一下。

原先Edge Impulse官网推荐Arduino Nano 33 BLE Sense(以下简称BLE Sense)搭配OV7675来做视觉相关应用。而OV7670(无FIFO)摄影机模组取像规格相近,也满常用於Arduino其它产品上,价格相当便宜,且使用M12镜头可手动调整焦距,更方便应用於各种视觉应用,所以决定改用OV7670来连接BLE Sense再上传到Edge Impulse。

如果大家用Google搜寻BLE Sense如何连接到OV7670,大概通常都会找到这篇Machine vision with low-cost camera modules,本来想跟着做一遍就收工,无奈老天给了我更多的考验,以下就让我缓缓道来到底遇到什麽问题吧。

硬体接线

首先当然要把OV7670和BLE Sense连接起来,通常网路上的范例都是用面包板和杜邦线连结,为了後续实验更方便,这里采用直接焊洞洞板的方式完成,并令摄影机模组板和BLE Sense板呈90度连接,方便更替,大家可自行决定用那种方式连接。完整接线图如图31-1所示,或参考Table 33-1。在[Day 27]有提及OV7675的PEN/RST(Pin 19), PWDN/PDN(Pin20)是对应到OV7670的Pin 17, 18,但经查阅相关资料後得知,不接亦可。

Tabel 33-1 OV7670和Arduino Nano 33 BLE Sense接线对照表

OV7670 Pin Name OV7670 Pin Number BLE Sense Pin Name 备注
3.3V 01 3.3V
GND 02 GND 任一个GND皆可
SCL 03 SCL/A5 (P0.02)
SDA 04 SDA/A4 (P0.31)
VS 05 D8 (P0.21)
HS 06 A1 (P0.05)
PCLK 07 A0 (P0.04)
XCLK 08 D9 (P0.27)
D7 09 D4 (P1.15)
D6 10 D8 (P1.14)
D5 11 D5 (P1.13)
D4 12 D3 (P1.12)
D3 13 D2 (P1.11)
D2 14 D0/RX (P1.10)
D1 15 D1/TX (P1.03)
D0 16 D10 (P1.02)
RESET 17 A2 (P0.30) 可不接
PWDN 18 A3 (P0.29) 可不接

Arduino Nano 33 BLE Sense连接OV7670摄影机模组(无FIFO)之参考线路图
Fig. 31-1 Arduino Nano 33 BLE Sense连接OV7670摄影机模组(无FIFO)之参考线路图。(OmniXRI整理绘制, 2021/10/21)

Arduino测试程序

由於BLE Sense上并没有显示元件(如LCD),所以由OV7670取得的影像必须以二进制(16bit, RGB565)格式数值,经由虚拟串列埠(Virtual COM)传送到电脑上显示。为了测试OV7670是否能正确取像,首先要在Arduino IDE安装必要程序库。点击主选单[草稿码]-[汇入程序库]-[管理程序库...],再输入「OV7670」,选择「Arduino_OV767x」,按下安装即可。

接着点击主选单[档案]-[范例],结果发现「Arduino_OV767x」竟然被归类在不相容的函式库中,正在一头雾水时,回头检查「开发板」设定时,发现原先选用的是「Arduino Mbed OS Nano Boards」下的「Arduino Nano 33 BLE」,而另一个「Arduino Mbed OS Boards」下也有一个同名的「Arduino Nano 33 BLE」,经更换成後者後,「Arduino_OV767x」就正确回到「第三方程序库的范例」中了。点击「CameraCaptureRawBytes」就可开启测试OV7670的范例了。完整程序如图Fig. 31-2所示。

Arduino IDE OV7670程序库安装及开启范例
Fig. 31-2 Arduino IDE OV7670程序库安装及开启范例。(OmniXRI整理绘制, 2021/10/21)

/*
  OV767X - Camera Capture Raw Bytes 

  点击Arduino IDE主选单[范例]-[Arduino_OV767x]-[CameraCaptureRawBytes]得到此范例
  
  电路连接:
    - Arduino Nano 33 BLE board
    - OV7670 camera module:
      - 3.3 connected to 3.3
      - GND connected GND
      - SIOC connected to A5
      - SIOD connected to A4
      - VSYNC connected to 8
      - HREF connected to A1
      - PCLK connected to A0
      - XCLK connected to 9
      - D7 connected to 4
      - D6 connected to 6
      - D5 connected to 5
      - D4 connected to 3
      - D3 connected to 2
      - D2 connected to 0 / RX
      - D1 connected to 1 / TX
      - D0 connected to 10
*/

#include <Arduino_OV767X.h> // 导入OV767x程序库头文件

int bytesPerFrame; // 宣告一张影像所需Byte数量
byte data[320 * 240 * 2]; // 宣告存放QVGA解析度RGB565格式(16bit)之彩色影像之缓冲区

// 设定脚位用途及模组初始化(只在电源启动或重置时执行一次)
void setup() {
  Serial.begin(9600); // 宣告虚拟串列埠传输速度为9600bps,可自行调整。
  while (!Serial); // 若开启不成功就一直等待。

  // 初始化摄影机模组为QVGA解析度(320x240),RGB565彩色格式,取像速度为1秒1张。
  // 解析度可自行调整为VGA(640x480), QVGA(320x240), QQVGA(160x120), CIF(352x240), QCIF(176x144)。
  // 彩色影像格式可自行调整为YUV422, RGB444, RGB565, GRAYSCALE(8bit灰阶)。
  // 取像速度可设定为 1, 5, 10, 20FPS, 但由於BLE Sense CPU速度仅有64MHz,所以建议设定为1FPS为佳,以免来不及处理。
  if (!Camera.begin(QVGA, RGB565, 1)) {
    Serial.println("Failed to initialize camera!"); // 若初始化失败则回传错误讯息
    while (1); // 令程序卡在这一行,表示程序结束。
  }

  bytesPerFrame = Camera.width() * Camera.height() * Camera.bytesPerPixel();  // 依实际初始化结果计算出所需回传的Byte数量。

  // Optionally, enable the test pattern for testing
  // Camera.testPattern(); // 选择性输出,若删除注解符号,则会一直输出八色彩条图,不理会实际摄影机模组拍摄到的内容。可作为测试传输用。
}

// 设定无穷循环程序 (会一直依序重覆执行)
void loop() {
  Camera.readFrame(data); // 从摄影机读取一个影格资料

  Serial.write(data, bytesPerFrame); // 从虚拟串列埠回传读到的影像二进制资料
}

原则上,以上程序可直接烧录到BLE Sense中,不用修改。这只是为了测试硬体线路用,不用太在意取像速度只有1 FPS。

Processing测试程序

刚才有提到,BLE Sense并没有显示功能,而透过虚拟串列埠传送出来的值,也不是电脑显示用的格式(如BMP),所以需要有另一个程序来解读,这里推荐使用Processing来接收并显示。Processing是一种开源的程序语言,专门用来创作电子艺术和视觉互动设计。其架构是建立在Java语言之上,其整合开发环境(IDE)操作上很像Arduino IDE。

Processing不需要安装,只需到下载页面下载适合的作业系统的版本即可,最新的版本为4.0 beta2,亦可选用3.x版稳定版本。下载完成後只需解压缩,不必安装,直接执行Processing.exe(Windows版)即可。这里并没有要教大家如何写程序,只需把下列范例程序复制贴上即可。其主要操作介面如图Fig. 31-3所示。

其中有几个小地方要手动修改一下。

  • 影像格式:cameraWidth, cameraHeight, cameraBytesPerPixel等变数要符合BLE Sense上传之格式。
  • 显示视窗:size(window_Width, window_Height)要符合BLE Sense上传之尺寸。
  • 虚拟串列埠:myPort = new Serial(this, "COM3", 9600); 请依不同作业系统、埠号及传输速度自行修改,这里以Windows, COM3, 9600bps为例。
/*
  This sketch reads a raw Stream of RGB565 pixels
  from the Serial port and displays the frame on
  the window.

  Use with the Examples -> CameraCaptureRawBytes Arduino sketch.

  This example code is in the public domain.
  范例程序来源:https://raw.githubusercontent.com/arduino-libraries/Arduino_OV767X/master/extras/CameraVisualizerRawBytes/CameraVisualizerRawBytes.pde
*/

import processing.serial.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

Serial myPort; // 宣告一串列埠

// 以下宣告值必须符合BLE Sense上传的影像大小
final int cameraWidth = 320;       // 摄影机取得影像宽度
final int cameraHeight = 240;      // 摄影机取得影像高度
final int cameraBytesPerPixel = 2; // 影像像素占用Byte数
final int bytesPerFrame = cameraWidth * cameraHeight * cameraBytesPerPixel; // 影格影像所需Byte数量

PImage myImage; // 宣告一个显示用影像
byte[] frameBuffer = new byte[bytesPerFrame]; // 宣告影像资料缓冲区

// 设定系统相关参数,只执行一次
void setup()
{
  size(320, 240); // 指定显示视窗尺寸,需搭配取像尺寸修改

  // if you have only ONE serial port active
  //myPort = new Serial(this, Serial.list()[0], 9600);          // if you have only ONE serial port active

  // 请依不同作业系统、埠号及传输速度自行修改,这里以Windows, COM3, 9600bps为例
  myPort = new Serial(this, "COM3", 9600);                    // Windows
  //myPort = new Serial(this, "/dev/ttyACM0", 9600);          // Linux
  //myPort = new Serial(this, "/dev/cu.usbmodem14401", 9600); // Mac

  // 等待接收到足够的Byte数量
  myPort.buffer(bytesPerFrame); 

  myImage = createImage(cameraWidth, cameraHeight, RGB); // 创建一张RGB格式影像
}

// 绘图函式,更新接收到的资料到视窗上
void draw()
{
  image(myImage, 0, 0); // 绘制影像到视窗上
}

// 处理串列埠事件
void serialEvent(Serial myPort) {
  // 从串列埠读取原始资料到缓冲区
  myPort.readBytes(frameBuffer); 

  // 处理原始资料透过缓冲区
  ByteBuffer bb = ByteBuffer.wrap(frameBuffer); 
  bb.order(ByteOrder.BIG_ENDIAN);

  int i = 0;

  // 当资料还没读完
  while (bb.hasRemaining()) {
    // 读取一个像素资料(16bit, RBG565)
    short p = bb.getShort();

    // 将RGB565格式转换成RGB888(24bit)格式
    int r = ((p >> 11) & 0x1f) << 3;
    int g = ((p >> 5) & 0x3f) << 2;
    int b = ((p >> 0) & 0x1f) << 3;

    // 将转换好的像素颜色绘到影像指定位置
    myImage .pixels[i++] = color(r, g, b);
  }
 myImage .updatePixels(); // 更新影像中像素内容
}

原本以为贴上程序,按下左上角【Play】键就能看到完美影像,没想到只得到一片黑呼呼的视窗,检查了老半天,还是找不出问题,最後不得已只好换上另一个OV7670,结果就有一堆奇怪的方块产生,感觉好像有拍到影像,但却很破碎。於是把BLE Sense的「Camera.testPattern();」那行注解取消掉来检查,照道理应该会得到如图Fig. 31-3右上的彩条图,但却得到右下角那个歪斜的影像,感觉上好像是资料每隔一段时间就落後一些时间造成。经上网努力查找,最後得到一个解决方式,就是移到Ubuntu(Linux)下测试就不会有这个问题产生了。果然,在Ubuntu上安装完Processing并执行同一段程序就OK了。不过第一颗OV7670还是黑画面,猜想可能已经报销了。於是把BLE Sense的程序改回,重新把「Camera.testPattern();」加上注解。终於可以在Processing上看到OV7670取到的影像了。但不知为何取得的影像转了90度,只好先将就点用。

Processing显示BLE Sense上传之资料
Fig. 31-3 Processing显示BLE Sense上传之资料。(OmniXRI整理绘制, 2021/10/21)

Edge Impulse连结及测试

完成上面测试後,依Edge Impulse官网「Adding sight to your sensors」说明,应该把Edge Impulse提供的标准韧体(arduino-nano-33-ble-sense.ino.bin)使用「flash_windows.bat」(Windows版本)重新烧回BLE Sense开发板就可以了。但执行「edge-impulse-daemon --clean」将开发板连线後,但进到「资料撷取(Data Acquisition)」页面後,在「感测器(Sensor)」栏位,却只能看到「Build-in Accelerometer」和「Build-in Micphone」,没看到摄影机相关选项。

在网路上查找了许久一直没有答案,结果不小心在某个教学影片中发现,「arduino-nano-33-ble-sense.ino.bin」的日期是2021/9,而我的却是2021/5的版本。於是死马当活马医,重新下载官网提供的韧体,再次烧录并启动,果然,「感测器(Sensor)」栏位多了两个选项「Camera (160x120)」、「Camera (128x96)」,且萤幕上也能呈现摄影机即时拍到的内容,虽然Processing在Windows上依旧不正常,但不影响取像结果。当按下【Start Sampling】钮,果然可以拍下影像,并上传到系统,如此就能建立实拍的资料集了。

Edge Impulse连接OV7670取像结果
Fig. 31-4 Edge Impulse连接OV7670取像结果。(OmniXRI整理绘制, 2021/10/21)

小结

虽然经历OV7670损坏一组、Processing在Windows上无法正常显示BLE Sense上传影像及Edge Impulse Firmware版本不对等问题,花了一个多星期终於搞定,还好不是在比赛期间,不然为了这个问题无法完赛,就有点可惜了。希望藉由这篇文章可以补齐原来[Day 27]、[Day 28]还没说完的故事。

参考连结

[Day 27] Edge Impulse + BLE Sense实现影像分类(上)
[Day 28] Edge Impulse + BLE Sense实现影像分类(下)
Edge Impulse Document - Tutorials - Adding sight to your sensors
Edge Impulse Document - Development Boards - Arduino Nano 33 BLE Sense
Machine vision with low-cost camera modules


<<:  “Work Smart” vs “Work Hard”? (单选题)

>>:  操作授权 (Authorization to Operate:ATO)

卡夫卡的藏书阁【Book17】- Kafka - KafkaJS 生产者 - 5

“He is terribly afraid of dying because he hasn’t...

进击的软件工程师之路-软件战斗营 第十九周

学习进度 Android Studio (以下功能皆为自学) RelativeLayout Date...

什麽是ERP? ERP的重要性是甚麽?

ERP是「 企业资源计划 」的缩写 (Enterprise resource planning ER...

APP 开发 组别

APP 开发 组别 https://wolkesau.medium.com/app-开发-组别-49...

Day-19 Button

本篇内容要介绍Button元件, 除了认识Button的语法、属性外, 同时也要为按钮设置监听及触发...