diff --git a/articles/20221005-d1-lab-step4-and-try-ubuntu-official.md b/articles/20221005-d1-lab-step4-and-try-ubuntu-official.md new file mode 100644 index 0000000000000000000000000000000000000000..8bfbec036c0f3d97d5130335caa557be8c797721 --- /dev/null +++ b/articles/20221005-d1-lab-step4-and-try-ubuntu-official.md @@ -0,0 +1,230 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1-rc3 - [spaces toc comments codeblock urls pangu autocorrect epw]
+> Author: Jinwen Zhou
+> Date: 2022/10/04
+> Revisor: Falcon
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Sponsor: PLCT Lab, ISCAS + +# D1 开发板实时人物检测推流的功能实现及官方 Ubuntu RISC-V 系统在小内存的 D1 哪吒开发板使能流程 + +## 前言 + +前面三篇文章介绍了 D1 开发板实现摄像头实时人物识别功能及 D1 开发板上使用 ffmpeg 进行视频推流的功能,此篇将二者进行整合,以实现一个完整可用功能:实时检测,当有人出现时将当前视频推流到服务器,可在客户端进行观看。此功能可用于对敏感地点的监控及记录。读者阅读完本文后,结合代码即可复现。 + +另外,本文记录了将官方适配的 Ubuntu 22.04 RISC-V 系统在 D1 哪吒开发板上的使用过程,由于官方适配的开发板为 2G 内存的型号,需要做一些改动才能起来。 + +## 整体逻辑设计 + +### 开发板逻辑 + +D1 开发板通过摄像头模块需要完成两个关键逻辑: +- 画面进行实时采集并对采集的图像或视频进行人物识别,能够识别到人并通知视频推流模块。 +- 能够在收到通知后开始同步录制并推送视频流到给定服务器地址。 + +以上两个关键逻辑在前两篇文章中已经分别实现,需将二者合并并处理合并后的一些细节即可完成预期目标。 + +[用 D1 进行图片采集和人体识别][1] + +[使用 ffmpeg 和 D1 开发板进行直播推流][2] + +### 服务器及客户端部署 + +服务器用安装 Ubuntu 22.04 server 的虚拟机进行模拟,客户端使用 Windows 上的 VLC media player 拉起目标服务器上的视频流,具体流程已在上一篇文章中叙述。需要补充说明一下网络配置:虚拟机中选择桥接网卡桥接到主机的以太网卡并将混杂模式选择为全部允许,再将虚拟机桥接网卡对应的 IP 地址和主机以太网卡的 IP 设置为同一网段,开发板网口也设置为此网段。如果开发板和主机无法 Ping 通,建议关闭主机防火墙。 + +## 具体实现的细节处理 + +以下描述设计及整合代码时两处关键细节,为此做了一些适应性调整。 + +### 处理器性能制约 + +D1 处理器为单核处理器,主频 1GHz。前面在使用 ffmpeg 进行视频录制、编码、推流实验时延迟很大,约为 20 秒左右。采用对视频直接进行深度学习处理开销对于本芯片来说压力较大。所以本实验采用间隔 1s 拍照,拍摄的图片使用 ncnn 进行人物识别的方式判断有没有人,而没有采用直接从视频中动态识别人物的方式。保证实时性的同时也能在没有人的时候让 CPU 负载不至于过大。 + +### 摄像头模块无法复用 + +实验中发现,摄像头模块在同一个进程打开设备(`open()`)并设置相关参数,使用完毕后,按照示例流程释放缓存,释放映射并关闭设备(`close()`)后,在此进程中再次进行打开设备设置参数流程会报错,无法进行再次拍摄。只有该进程退出后,才可以继续使用摄像头模块。判断应该是相关驱动的一个 Bug。并且在拍照进程打开摄像头后,就无法对此摄像头使用 ffmpeg 进行录制和推流。 + +针对以上问题,对代码逻辑重新进行设计: +- 拍照程序拍照后将图片保存固定路径后通知 ncnn 图片识别模块,若收到 ncnn 反馈到图片有人(`buffer=1`)的通知后再反馈给 ncnn 已收到(`buffer=2`)后退出程序。退出保证了摄像头可被推流进程使用。 + +- ncnn 程序对图片进行识别,若无人将结果(`buffer=0`)告知拍照程序即可。 + +- 若有人,通知拍照程序结果后,收到对方发来的确认(`buffer=2`)后,即启动一个子进程执行 ffmpeg 推流录制的脚本(脚本的内容即为上一篇推流的命令)。原进程计时,2 分钟后执行关闭 ffmpeg 的命令,并将拍照程序再次启动。 + +拍照模块相关逻辑代码如下: + +```c + v4l2_init(); + while(1) + { + v4l2Grab(); + buffer[0] = 1; + /* 通知 ncnn 模块已经采集到图片并存储 */ + datalen = write(fd_w,buffer,80); + if (datalen == -1) + continue; + + /* 等待 ncnn 发回来的识别结果 */ + if (read(fd_r,buffer,80)) + { + if (buffer[1] == 1) + { + printf("person!\n"); + buffer[0] = 2; + datalen = write(fd_w,buffer,80); + if (datalen == -1) + continue; + /* 主动退出,使摄像头可被推流模块用 */ + v4l2_close(); + return 0; + } + else + printf("no person!\n"); + } + } +``` + +ncnn 程序关键相关逻辑代码如下: + +```c +while(1) + { + memset(buffer,0,80); + /* 收取客户端发来的识别请求 */ + datalen = read(fd_r,buffer,80); + if(datalen ==-1) + continue; + if(buffer[0] == 1) + { + // start detect + if (argc != 2) + { + fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]); + return -1; + } + /* 摄像头模块采集图片的保存路径 */ + const char* imagepath = argv[1]; + + cv::Mat m = cv::imread(imagepath, 1); + if (m.empty()) + { + fprintf(stderr, "cv::imread %s failed\n", imagepath); + return -1; + } + + std::vector objects; + /* 使用 nanodet 框架进行识别,识别结果保存在 object 结构体中 */ + detect_nanodet(m, objects); + ret = is_person(objects); + buffer[1] = ret; + write(fd_w,buffer,80); + + } + + if(buffer[0] == 2) + { + pid = fork(); + if(pid) + { + + system("./video.sh"); // (ffmpeg -f video4linux2 -s 1280x720 -i /dev/video0 -f flv rtmp://192.168.56.103/live/live &) + exit(0); + } + + else + { + sleep(120); + system("killall -9 ffmpeg"); + + system("./cam1 &"); + } + + } +``` + +## 实验效果及展望 + +### 实验效果 + +按照之前文章所述编译流程对上述代码进行编译,并实际测试。 + +首先开启服务器程序和客户端程序,等待视频流。 + +接着在开发板执行启动 nanodet 程序: + +``` +# ./nanodet 1.jpeg& +``` + +再启动 cam 程序即可: + +``` +#./cam & +``` + +通过实验,上述程序满足预期功能,在有人出现时会推流 2 分钟的视频,可在客户端观看。2 分钟后,推流程序退出,会重新进行人物检测。 + +### 展望 + +本实验仅实现了功能,有很多可以优化的地方: + +- 实验中,可以看到推流延迟很大,约有 20 秒,实验用的 D1 开发板处理器单核以及其内存比较小制约了其性能。可以明显感受到用于视频的处理无论是处理器性能方面还是 ffmpeg 在该芯片上的优化都有很大进步空间。 + +- 客户端采用现成的拉流软件验证,若要有实际使用价值,需要在客户端增加视频保存回看功能。 + +- 图片的检测功能单一,本实践仅仅验证了 ncnn 的可用性。官方工具对算法模型的部署中关键的参数转换的工具支持希望能够更智能和平滑。 + +## Ubuntu RISC-V 官方系统的使用 + +Ubuntu 对哪吒 D1 开发板进行了官方适配,[Ubuntu 官方适配 的 RISC-V 版本链接][3]。直接拿来用是针对 2G 内存版本的。对 1G 和 512M 的内存的板子还需要删除 `cryptsetup-initramfs` 才能顺利启动。具体流程如下: + +- 将解压后的 img 文件,使用 dd 命令拷贝到 tf 卡,可以加上 status=progress 查看进度。这个 img 文件的 dd 速度比一般的镜像要慢很多,建议使用 USB3.0 的读卡器。 + +``` +$ xz -d jammy-preinstalled-server-riscv64+nezha.img.xz +$ dd if=jammy-preinstalled-server-riscv64+nezha.img of=/dev/YOUR_SDCARD_BLOCK_DEVICE status=progress +``` + +- 系统安装 qemu-user-static。 + +``` +sudo apt install qemu-user-static +``` + +- 拔下读卡器重新插入,会在 `/media/yourhostname/` 下有一个 `cloudimg-rootfs` 目录,此即为此系统的文件系统。需要一些挂载操作,这些挂载操作不可省略,否则后续步骤虽然可以执行但是最后制作的 SD 卡是无法启动的。 + +``` +cd /media/yourhostname/cloudimg-rootfs +mount -t proc /proc ./proc +mount -t sysfs /sys ./sys +mount -o bind /dev ./dev +mount -o bind /dev/pts ./dev/pts +mount -o bind /run ./run +``` + +- 启动该文件系统。 + +``` +sudo chroot /media/yourhostname/cloudimg-rootfs +``` + +- 卸载 `cryptsetup-initramfs`,这一步时间很长,包括重新打包的时间,耐心等待。 + +``` +sudo apt remove cryptsetup-initramfs +``` + +- 完成后输入 `exit` 退出卸载 SD 卡,即完成 Ubuntu 22.04 系统的制作。插入开发板卡槽上电即可,系统启动时间很长,约 3 分钟。 + +## 总结 + +本文对在 D1 哪吒上进行实时人物检测并推流的功能进行了总结和展望。并介绍了在 D1 哪吒开发板上使用官方 Ubuntu 系统的方法。 + +## 参考资料 + +1. [用 D1 进行图片采集和人体识别][1] +2. [使用 ffmpeg 和 D1 开发板进行直播推流][2] +3. [Ubuntu 官方适配 的 RISC-V 版本链接][3] + +[1]: https://gitee.com/tinylab/riscv-linux/blob/master/articles/20220809-d1-lab-step2.md +[2]: https://gitee.com/tinylab/riscv-linux/blob/master/articles/20220908-d1-lab-step3.md +[3]: https://ubuntu.com/download/risc-v