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