Categories
程式開發

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統?


導語 | 所謂圖傳,就是把相機模組捕捉到的畫面,實時傳輸到另一個可接收該數據的設備上,並且在該設備上進行實時播放。本文將介紹如何基於VisionSeed 和Raspberry Pi(4B)搭建一套完整的圖傳系統,希望與大家一同交流。文章作者:毛江雲,騰訊優圖實驗室研發工程師。

http://mpvideo.qpic.cn/0bf2jibziaadfaagfy2wr5pvgswdsrfahfaa.f10002.mp4?dis_k=27248bd1e2107c7bcfb47d671741019f&dis_t=1600673227&vid=wxv_1507883883827150849

VisionSeed智能小車實際跑圈演示視頻

一、概念介紹

1.樹莓派

樹莓派[1]其實不用筆者過多介紹,這應該是做的最成功的開源硬件芯片,深受技術和數碼愛好者們的擁護。下圖摘自淘寶某店家的中文說明圖,總之第四代比第三代功能強了很多,而且好多接口都與時俱進了。

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 1

2. VisionSeed

視覺種子[2] 是騰訊優圖推出的一款具備AI 功能的攝像頭模組,產品如下圖所示。它的體型很小,有點類似Raspberry Pi Zero,不過麻雀雖小五臟俱全。

右邊是整塊VisionSeed 的核心模塊,包括2 個攝像頭(一個UVC 攝像頭,一個紅外攝像頭),剩下一整塊都是AI 計算單元;左邊是控制板塊,主要是對外的接口,如串口、TypeC 接口。兩塊直接通過FP C連接起來。

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 2

VisionSeed 模組可以搭載很多CV 的AI 能力,目前官方已經推出的有疲勞駕駛監測儀[3],筆者目前參加的智能小車就是正在孵化的另一個項目,期待越來越多的AI 愛好者們參與進來,把VisionSeed “玩出花”。

二、系統搭建

本文所介紹的是利用VisionSeed 和Raspberry Pi(4B) 搭建的一套 基於FFMPEG 編碼+SRS+WIFI 協議+RTMP 協議+FFPLAY 解碼播放 的完整圖傳系統,該實時圖傳全過程示意圖如下所示:

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 3

1. RTMP推流服務器

推流服務器怎麼選擇 ?其實推流服務器有很多種選型,具體該選擇哪種比較好?筆者結合自身經驗給出 nginx-rtmp 服務srs 服務 的使用心得和實際對比:

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 4

補充說明一下 延時 這塊:首先筆者給出的具體延遲時間並非真正服務器推流的延遲,而是端(VisionSeed 採集到視頻)到端(播放設備播放視頻)的延遲。

這中間涉及到的環節多且複雜:VisionSeed 的UVC 視頻採集,FFMPEG 編碼、推流服務器推流、FFPLAY 解碼、以及顯示器顯示。其中推流服務器推流還包括服務器內部buffer 緩存、網絡數據包拼接和組裝、以及網絡包的傳輸等。

2. FFMPEG編碼和推流

那麼,該 怎麼捕捉VisionSeed 攝像頭的視頻呢

VisionSeed 上的攝像頭是UVC。 紫外線 全稱 USB視頻班 ,是一種標準的USB 視頻設備協議,也就是傳說中的免驅攝像頭。也就是說,這款攝像頭可以通過USB 即插即用,不需要安裝驅動。

於是,我們用一根TypeC 數據線把VisionSeed 和樹莓派連接在一起。如下圖所示:

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 5

登錄Raspberry Pi,打開Terminal,查看UVC 的狀態:$ lsusb。如果出現下圖紅框的部分,就說明UVC 被系統識別了。

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 6

然後,查看UVC 被掛在哪個節點上:$ ls / dev / video *:

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 7

也可以用v4l2-ctl 進一步仔細查看/dev/video* 的信息,如命令v4l2-ctl –device=/dev/video0 –all 可以查看到攝像頭的細節信息,命令v4l2-ctl –list-devices查看系統中所有的設備等。

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 8

”Linux 系統一切皆文件“,也就是說,系統是直接把/dev/video0 當作文件來進行處理,也就是在ffmpeg 的-i 的參數。

那麼, 怎麼用FFMPEG 做編碼呢

雖然本地可以直接用ffplay 播放UVC 的原始流,但是要走RTMP 協議做圖傳的話,必須要使用編碼流。因此必須在搭載VisionSeed 的Raspberry Pi 上做編碼,然後推流出去。

FFMPEG CMD 功能強大:

  • -i :指定文件輸入路徑;
  • -an :指不處理音頻數據;
  • -vcodec:指定視頻codec;
  • -f:指定視頻輸出格式。

下述命令就是把從/dev/video0 獲取到的原始視頻流編碼成flv,並保存成本地文件test.flv。

ffmpeg -i /dev/video0 -an -vcodec h264 -f flv rtmp://localhost:${port}/${api}

綜上,FFMPEG 對UVC 原始視頻流做編碼就完成了。

3. FFPLAY 收流

必須要確保客戶端設備和推流端的Raspberry Pi 處於同一個局域網下,互相可以聯通。

筆者因為項目需要,客戶端也選擇了Raspberry Pi +顯示屏。但是實際上,選擇手上的筆記本或者台式機就可以,只要確保安裝了FFMPEG(FFMPEG、FFPLAY和FFPROBE是打包的)。

那麼, 如何用FFPLAY 實時顯示RTMP 視頻呢

筆者原本以為播放端出不了什麼問題,萬萬沒想到FFPLAY 的問題竟然無比的“坑”。首先,很多類似的網站教程提供的播放命令大多是:ffplay -i rtmp://${ip}:${港口}/${api}。

這個命令存在相當大的延時,導致筆者最開始就走錯了方向。 ffplay 內部有buffer,因此看到的播放畫面其實是好幾十秒之前的!

ffplay 播放時間長了會有累積延時,也就是越播放到後來,延時越大。並且畫面時不時出現卡頓、有時候還會發現畫面幀率不穩定,時快時慢。當推流端/服務端斷開時,ffplay畫面就卡住了,超過2 min 也並不會退出。

這些問題該怎樣解決呢?下文將會來詳細討論。

三、優化延時

按照以上的流程搭建好之後,就可以在客戶端上看到VisionSeed 的視頻畫面了。但是,延時巨大,主觀感受至少有10s的延遲,所以還需要進一步做優化。

1. FFMPEG 硬解碼

細心的同學在使用FFMPEG 做編碼的時候,應該發現實際編碼推流的幀率大約在18 左右,運行到後來大概穩定在10 左右,筆者這邊的情況如下圖所示:

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 9

但是,VisionSeed(關閉算法功能後)的原始視頻幀率是28 fps,分辨率是1280×720。由此可見Raspberry Pi 4B 的CPU 編碼速率跟不上,必須要優化。

雖然這個CMD 沒有開啟多線程,但是根據筆者經驗來看,即使多線程開滿了,也很難滿足性能要求。

要知道Raspberry Pi 4B 是有專用編解碼模塊的,官方號稱性能是[email protected] 的編碼能力,而端側開發就是要“榨乾”每一個芯片模組的過程。

查了資料後了解到,Raspberry Pi 的硬解碼支持 OPENMAX 標準[4],這是一種類似vaapi 等多媒體硬件加速的統一接口,因此可以直接用h264_max 來調用底層硬解碼,然後再推送到推流服務器上,命令如下:

ffmpeg -i /dev/video0 -an -vcodec h264_omx -f flv 
rtmp://localhost:${port}/${api}

此外,對實時性要求再高一點的同學,不妨再多了解些FFMPEG 的參數,參見 FFmpeg格式文檔[5]H.264視頻編碼指南[6],筆者下面摘錄一些跟效率相關的參數,大家可以選擇使用(如果有更多未列出來的,歡迎大家留言補充):

### 延时相关
- fflags nobuffer # 减少由于buffer带来的延时,能够做到即时处理。
- fflags flush_packets # 马上把packets刷出来。(实际好像没有对降低延时带来作用)
- analyzeduration ${整型值|时间} # 流分析时间,数值越长,得到的流信息越多、准确,但是延时上升,默认值是5秒。(对编码流会起作用,原始流应该没啥作用)
- max_delay ${整型值|时间} # 设置(解)封装的最大延时。(对封装格式的编码流有作用,原始流应该没啥作用)
- framerate ${整型值|时间} # 输入视频的码率,默认值是25。(建议可以用-re,这个是用输入视频的码率)

而筆者最後使用的命令如下:

ffmpeg -r 28 -fflags nobuffer -fflags flush_packets -i /dev/video0 -vf 
fps=fps=28 -an -vcodec h264_omx -preset slower -tune zerolatency -max_delay 10 
-r 28 -video_size 1280x720 -g 50 -b:v 8192k -f flv "rtmp://
{RTMPIP}:{RTMPPORT}/live/1"

2. 用srs 推流服務器,開啟優化參數

從最終結果上來看,替換了srs 服務器之後,時延確實比用nignx-rtmp 提升了400 ms。

但是這到底是因為srs 確實比nginx-rtmp 優秀呢,還是因為筆者打開nginx-rtmp 方式不正確,還有待討論。對這塊有了解的大佬們,歡迎留言告知,不勝感激!

修改srs.conf 如下:

listen              1935; # rtmp端口 !!可以修改为自己的端口号!!
max_connections     1000;
srs_log_tank        file;
srs_log_file        ./objs/srs.log;
http_api {
    enabled         on;
    listen          1985;
}
http_server {
    enabled         on;
    listen          80;
    dir             ./objs/nginx/html;
}
stats {
    network         0;
    disk            sda sdb xvda xvdb;
}
vhost __defaultVhost__ {
    #最小延迟打开,默认是打开的,该选项打开的时候,mr默认关闭。
    min_latency     on;
    #Merged-Read,针对RTMP协议,为了提高性能,SRS对于上行的read使用merged-read,即SRS在读写时一次读取N毫秒的数据
    mr {
        enabled     off;
        #默认350ms,范围[300-2000]
        #latency     350;
    }
    #Merged-Write,SRS永远使用Merged-Write,即一次发送N毫秒的包给客户端。这个算法可以将RTMP下行的效率提升5倍左右,范围[350-1800]
    mw_latency      100;
    #enabled         on;
    #https://github.com/simple-rtmp-server/srs/wiki/v2_CN_LowLatency#gop-cache
    gop_cache       off;
    #配置直播队列的长度,服务器会将数据放在直播队列中,如果超过这个长度就清空到最后一个I帧
    #https://github.com/simple-rtmp-server/srs/wiki/v2_CN_LowLatency#%E7%B4%AF%E7%A7%AF%E5%BB%B6%E8%BF%9F
    queue_length    10;
    #http_flv配置
    http_remux {
      enabled     on;
      mount [vhost]/[app]/[stream].flv;
      hstrs  on;
    }
}

配置修改之後的確實時性得到了很大的提升。

3. 優化FFPLAY

上文出現的問題,在這裡也為大家一一解答。

問題一: ffplay 內部有buffer,因此看到播放的畫面是好幾十秒之前的。

解決方法:關閉buffer!

參考 ffplay文檔[7],參數-fflags nobuffer(FFMPEG命令裡面也有這個參數)是最關鍵的。筆者最後採用了:

ffplay -autoexit -fflags nobuffer -fflags flush_packets -flags low_delay -
noframedrop -strict very -analyzeduration 600000 -i 
rtmp://192.168.1.1:2020/live/1

問題二: ffplay 播放時間長了會有累積延時,也就是越播放到後來,延時越大。並且畫面時不時出現卡頓、有時候還會發現畫面幀率不穩定,時快時慢。

解決方法:自從用了srs,累積延時的問題就沒有了。至於畫面不穩定的問題,可能和網絡有關,筆者後面也會提到怎麼在樹莓派上搭建無線AP 來提供專有無線局域網。

替換到無線AP 之後,畫面卡頓的情況會好很多。但是經過長時間的觀察,還是會有幀率不穩定的情況。

問題三 :當推流端/服務端斷開時,ffplay 畫面就卡主了!超過2 min 也並不會退出。

解決方法:這個其實就是FFPLAY 的bug !其實,ffplay 提供了幾個參數,一個是-autoexit,但是它對RTMP 還有RTSP 都不起作用,當流斷開或者網斷開的時候, ffplay 還是卡住的。

要想解決就必須修改源碼,重新編譯ffplay。修改辦法可以參考文檔[8]

另一個是-timeout 參數,但是一旦加上它,ffplay 就跑不起來,具體原因參考文章[9]

4. Raspberry Pi 上搭建無線AP

怎麼基於Raspberry Pi 搭建無線AP 是有官方教程的:如何將Raspberry Pi用作無線訪問點[10]。但是,官方教程是有坑的,下文將重點介紹哪些坑需要避開。

無線AP,全稱Wireless Access Point,其實就是常說的WIFI 熱點,生活中的路由器也是一種無線AP 設備。

在我們的環境中,可能會在沒有無線網環境下,甚至在網絡條件很糟糕的環境下。另外Raspberry Pi 4B 本身自帶了無線網卡,因此不妨用它搭建無線AP,客戶端直接接入它的網絡就可以接受它的流數據。

而且,從網絡鏈路上來說,無線AP 還能在原來基礎上減少路由器轉發的環節。網絡拓撲圖如下圖所示,優化的鏈路環節是把藍色虛線替換了紅色虛線和實線。

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 10

接下來,跟著官方教程一步步走:

(1)升級apt-get 工具

建議國內的小伙伴們替換一下源,筆者早就已經替換到了清華源,教程網上很多,推薦樹莓派3b 更換國內源[11]

/etc/apt/sources.list 修改為:

deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi
deb-src http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi

/etc/apt/sources.list.d/raspi.list修改為:

deb http://mirror.tuna.tsinghua.edu.cn/raspberrypi/ stretch main ui
deb-src http://mirror.tuna.tsinghua.edu.cn/raspberrypi/ stretch main ui

(2)安裝hostapd 和dnsmasq

hostapd 就是對外提供熱點的主要服務,dnsmasq 則是負責dns 和dhcp 作用的。如果操作完畢後,沒有搜索到自己設置的熱點WIFI,大概率是hostapd 的問題(原因:hostapd 沒有工作,因此沒有對外提供AP);如果搜到了熱點網絡,但是一直連不上的話,大概率是dnsmasq 的問題(原因:dnsmasq 沒有工作,沒辦法為客戶端分配ip)。

Raspberry Pi 為自己分為靜態IP。筆者這裡的設置如下:

interface wlan0
static ip_address=192.168.1.1/24 # 官方给的是192.168.0.10/24,这个自己灵活配置,这个是这台树莓派在它提供出去的AP网络里面的ip地址
denyinterfaces eth0
denyinterfaces wlan0

(3)配置DHCP

筆者這裡的配置如下:

interface=wlan0
  dhcp-range=192.168.1.6,192.168.1.12,255.255.255.0,24h # ip范围自定义就可以

(4)修改hostapd 配置

這一個步驟是最容易出問題的步驟,而且官方提供的配置在筆者的環境中並不能起作用。筆者給出自己的配置,並在註釋中說明為什麼這麼配置:

interface=wlan0
#bridge=br0    # 这个一定要去掉,因为笔者不需要做桥接(不需要外网)。
country_code=CN
hw_mode=a    # g-2.4GHZ;a-5GHZ
channel=149    # 5G的信道,网上有的写36有的是0各种,推荐查看下面的信道示意图
wmm_enabled=1
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
ssid=${YOUR_NETWORK_NAME} # 对外暴露的wifi名称,请注意不要和已有网络重名
wpa_passphrase=${YOUR_NETWORK_PSWD} # wifi的密码
ieee80211n=1
ieee80211d=1
ieee80211ac=1

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統? 11

其實做到這一步之後,基本就達到目標了。一個可用的無線AP 就搭建好了。客戶端只需要連接到剛剛設置的網絡,就可以和這台Raspberry Pi 通信。

終於,筆者把端到端的延時從十幾秒優化到400 ms 左右(一把辛酸淚)!最後,如果您有更多的優化方案,歡迎留言與我討論~

參考資料

[1] 樹莓派:

https://www.raspberrypi.org/

[2] VisionSeed:

https://visionseed.youtu.qq.com/#/home

[3] 疲勞駕駛監測儀:

https://zhuanlan.zhihu.com/p/77190381

[4] OPENMAX標準:

https://zh.wikipedia.org/wiki/OpenMAX

[5] FFmpeg格式文檔:

https://ffmpeg.org/ffmpeg-formats.html

[6] H.264視頻編碼指南:

https://trac.ffmpeg.org/wiki/Encode/H.264

[7] ffplay文檔:

https://ffmpeg.org/ffplay-all.html

[8] -autoexit參數修改參考:

http://ffmpeg.org/pipermail/ffmpeg-devel/2020-August/268749.html

[9] -timeout參數修改參考:

https://www.jianshu.com/p/e75e3f1fb6b0

[10] 如何將Raspberry Pi用作無線訪問點:

https://thepi.io/how-to-use-your-raspberry-pi-as-a-wireless-access-point/

[11] 樹莓派3b更換國內源:

https://my.oschina.net/TimeCarving/blog/1622950

本文轉載自公眾號雲加社區(ID:QcloudCommunity)。

原文鏈接

如何利用VisionSeed+樹莓派,實現智能小車實時圖傳系統?