0 想法
在野外进行视频流或者图片传输时,可以选择利用备用电源驱动一个路由器来构建一个局域网用于传输需求。但小安派在是可以创建AP进行通信的(WIFI->CreateWiFiAP),且官方示例中的AmebaNN->RTSPFaceDetection还是建议使用自己的wifi。那突发奇想,是不是可以将其两个结合起来呢?这样在没有wifi的地方也可以用手机连接小安派的AP进行视频传输。
先说结论:芯片发热属于正常范围(较烫),体感温度约55℃±5℃(没专业设备,不好意思)。利用开发板做RTSP服务端还是太高估了(也可能是我还没有掌握精髓),延迟很大,存在失真可能性,速度卡成ppt了(传输1920 x 1080,检测576 x 320)。
// Channel 0 : 1920 x 1080 30FPS H264
// Channel 1 : 1280 x 720 30FPS H264
#define NNWIDTH 576
#define NNHEIGHT 320
VideoSetting config(VIDEO_FHD, 30, VIDEO_H264, 0); //Channel 0 1920 x 1080
VideoSetting configNN(NNWIDTH, NNHEIGHT, 10, VIDEO_RGB, 0);
)
1 做法
材料
小安派BW21-CVB-Kit官方套装(GC2053摄像头,天线,开发板)
Android / IOS手机(软件V7RC)
代码
/*
Example guide:
https://www.amebaiot.com/en/amebapro2-arduino-neuralnework-face-detection/
NN Model Selection
Select Neural Network(NN) task and models using modelSelect(nntask, objdetmodel, facedetmodel, facerecogmodel).
Replace with NA_MODEL if they are not necessary for your selected NN Task.
NN task
=======
OBJECT_DETECTION/ FACE_DETECTION/ FACE_RECOGNITION
Models
=======
YOLOv3 model DEFAULT_YOLOV3TINY / CUSTOMIZED_YOLOV3TINY
YOLOv4 model DEFAULT_YOLOV4TINY / CUSTOMIZED_YOLOV4TINY
YOLOv7 model DEFAULT_YOLOV7TINY / CUSTOMIZED_YOLOV7TINY
SCRFD model DEFAULT_SCRFD / CUSTOMIZED_SCRFD
MobileFaceNet model DEFAULT_MOBILEFACENET/ CUSTOMIZED_MOBILEFACENET
No model NA_MODEL
*/
#include "WiFi.h"
#include "StreamIO.h"
#include "VideoStream.h"
#include "RTSP.h"
#include "NNFaceDetection.h"
#include "VideoStreamOverlay.h"
#define CHANNEL 0
#define CHANNELNN 3
// Lower resolution for NN processing
#define NNWIDTH 576
#define NNHEIGHT 320
VideoSetting config(VIDEO_FHD, 30, VIDEO_H264, 0);
VideoSetting configNN(NNWIDTH, NNHEIGHT, 10, VIDEO_RGB, 0);
NNFaceDetection facedet;
RTSP rtsp;
StreamIO videoStreamer(1, 1);
StreamIO videoStreamerNN(1, 1);
IPAddress ip;
int rtsp_portnum;
// #define MANUAL_INPUT
#ifdef MANUAL_INPUT // Initialise ssid string, pwd string, and serial_in object
// Initialise strings
String str_ssid, str_pass, str_channel;
#endif
// UTF-8 encoding can also be used for SSID with emoji characters
// Emoji characters can be converted into UTF-8 at https://mothereff.in/utf-8
// char ssid[] = "\xe2\x9c\x8c\xef\xb8\x8f Ameba \xe2\x9c\x8c\xef\xb8\x8f";
char ssid[] = "AP_Network_SSID"; // Set the AP SSID
char pass[] = "AP_Password"; // Set the AP password
char channel[] = "1"; // Set the AP channel
int status = WL_IDLE_STATUS; // Indicator of Wifi status
int ssid_status = 0; // Set SSID status, 1 hidden, 0 not hidden
void setup()
{
// Initialize serial and wait for port to open:
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
// attempt to start AP:
while (status != WL_CONNECTED) {
#ifdef MANUAL_INPUT
Serial.println("Enter your ssid");
while (Serial.available() == 0) {
}
str_ssid = Serial.readString();
str_ssid.trim();
Serial.print("SSID entered: ");
Serial.println(str_ssid);
Serial.println("Enter your password");
while (Serial.available() == 0) {
}
str_pass = Serial.readString();
str_pass.trim();
if (str_pass.length() != 0) { // user has entered data
while (str_pass.length() < 8) { // to catch pwd<8 exception
Serial.println("Password cannot be less than 8 characters! Try again");
while (Serial.available() == 0) {
}
str_pass = Serial.readString();
str_pass.trim();
}
Serial.print("Password entered: ");
Serial.println(str_pass);
}
Serial.println("Enter your channel number");
int checker = 0;
while (1) {
while (Serial.available() == 0) {
}
str_channel = Serial.readString();
str_channel.trim();
checker = str_channel.toInt();
if (str_channel == (String(checker))) {
break;
}
Serial.println("channel should be a number!");
}
Serial.print("channel entered: ");
Serial.println(str_channel);
#endif
Serial.print("Attempting to start AP with SSID: ");
#ifndef MANUAL_INPUT
Serial.println(ssid);
status = WiFi.apbegin(ssid, pass, channel, ssid_status);
#else
char ssid_cust[str_ssid.length() + 1];
char pass_cust[str_pass.length() + 1];
char channel_cust[str_channel.length() + 1];
strcpy(ssid_cust, str_ssid.c_str());
strcpy(pass_cust, str_pass.c_str());
strcpy(channel_cust, str_channel.c_str());
Serial.println(str_ssid.c_str());
status = WiFi.apbegin(ssid_cust, pass_cust, channel_cust, ssid_status);
str_ssid = str_pass = str_channel = "";
#endif
delay(1000);
}
// AP MODE already started:
Serial.println("AP mode already started");
Serial.println();
printWifiData();
printCurrentNet();
ip = WiFi.localIP();
// Configure camera video channels with video format information
// Adjust the bitrate based on your WiFi network quality
config.setBitrate(2 * 1024 * 1024); // Recommend to use 2Mbps for RTSP streaming to prevent network congestion
Camera.configVideoChannel(CHANNEL, config);
Camera.configVideoChannel(CHANNELNN, configNN);
Camera.videoInit();
// Configure RTSP with corresponding video format information
rtsp.configVideo(config);
rtsp.begin();
rtsp_portnum = rtsp.getPort();
// Configure face detection with corresponding video format information
// Select Neural Network(NN) task and models
facedet.configVideo(configNN);
facedet.setResultCallback(FDPostProcess);
facedet.modelSelect(FACE_DETECTION, NA_MODEL, DEFAULT_SCRFD, NA_MODEL);
facedet.begin();
// Configure StreamIO object to stream data from video channel to RTSP
videoStreamer.registerInput(Camera.getStream(CHANNEL));
videoStreamer.registerOutput(rtsp);
if (videoStreamer.begin() != 0) {
Serial.println("StreamIO link start failed");
}
// Start data stream from video channel
Camera.channelBegin(CHANNEL);
// Configure StreamIO object to stream data from RGB video channel to face detection
videoStreamerNN.registerInput(Camera.getStream(CHANNELNN));
videoStreamerNN.setStackSize();
videoStreamerNN.setTaskPriority();
videoStreamerNN.registerOutput(facedet);
if (videoStreamerNN.begin() != 0) {
Serial.println("StreamIO link start failed");
}
// Start video channel for NN
Camera.channelBegin(CHANNELNN);
// Start OSD drawing on RTSP video channel
OSD.configVideo(CHANNEL, config);
OSD.begin();
}
void loop()
{
// Do nothing
}
// User callback function for post processing of face detection results
void FDPostProcess(std::vector<FaceDetectionResult> results)
{
int count = 0;
uint16_t im_h = config.height();
uint16_t im_w = config.width();
Serial.print("Network URL for RTSP Streaming: ");
Serial.print("rtsp://");
Serial.print(ip);
Serial.print(":");
Serial.println(rtsp_portnum);
Serial.println(" ");
printf("Total number of faces detected = %d\r\n", facedet.getResultCount());
OSD.createBitmap(CHANNEL);
if (facedet.getResultCount() > 0) {
for (int i = 0; i < facedet.getResultCount(); i++) {
FaceDetectionResult item = results[i];
// Result coordinates are floats ranging from 0.00 to 1.00
// Multiply with RTSP resolution to get coordinates in pixels
int xmin = (int)(item.xMin() * im_w);
int xmax = (int)(item.xMax() * im_w);
int ymin = (int)(item.yMin() * im_h);
int ymax = (int)(item.yMax() * im_h);
// Draw boundary box
printf("Face %ld confidence %d:\t%d %d %d %d\n\r", i, item.score(), xmin, xmax, ymin, ymax);
OSD.drawRect(CHANNEL, xmin, ymin, xmax, ymax, 3, OSD_COLOR_WHITE);
// Print identification text above boundary box
char text_str[40];
snprintf(text_str, sizeof(text_str), "%s %d", item.name(), item.score());
OSD.drawText(CHANNEL, xmin, ymin - OSD.getTextHeight(CHANNEL), text_str, OSD_COLOR_CYAN);
// Draw facial feature points
for (int j = 0; j < 5; j++) {
int x = (int)(item.xFeature(j) * im_w);
int y = (int)(item.yFeature(j) * im_h);
OSD.drawPoint(CHANNEL, x, y, 8, OSD_COLOR_RED);
count++;
if (count == MAX_FACE_DET) {
goto OSDUpdate;
}
}
}
}
OSDUpdate:
OSD.update(CHANNEL);
}
void printWifiData()
{
// print your WiFi IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print your subnet mask:
IPAddress subnet = WiFi.subnetMask();
Serial.print("NetMask: ");
Serial.println(subnet);
// print your gateway address:
IPAddress gateway = WiFi.gatewayIP();
Serial.print("Gateway: ");
Serial.println(gateway);
Serial.println();
}
void printCurrentNet()
{
// print the SSID of the AP:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print the MAC address of AP:
byte bssid[6];
WiFi.BSSID(bssid);
Serial.print("BSSID: ");
Serial.print(bssid[0], HEX);
Serial.print(":");
Serial.print(bssid[1], HEX);
Serial.print(":");
Serial.print(bssid[2], HEX);
Serial.print(":");
Serial.print(bssid[3], HEX);
Serial.print(":");
Serial.print(bssid[4], HEX);
Serial.print(":");
Serial.println(bssid[5], HEX);
// print the encryption type:
byte encryption = WiFi.encryptionType();
Serial.print("Encryption Type:");
Serial.println(encryption, HEX);
Serial.println();
}
烧录
按住boot键,按下reset键并松掉,这时候就可以松掉boot键了,点击编译并上传。静静等待上传成功。
运行
手机连接wifi,你也可以使用#define MANUAL_INPUT 改成手动输入wifi名称和密码。
wifi名字:AP_Network_SSID
wifi密码:AP_Password
V7RC设置RTSP地址和端口,默认rtsp://192.168.1.1:554。

视频展示效果

2 畅想
要是可以接多个摄像头或者安信可的雷达模块,那是不是就可以测距了。但目前最主要的还是要提高帧率。
3 结论
可以但没必要。小安派BW21-CVB-Kit整体的可玩性还是比较高的。后续有新想法会继续更新。