在最開始介紹 Jetbot 的時候有提到,這套智慧小車只使用一個 CSI 攝像頭作為全部輸入的裝置,因為這種裝置的體積輕巧、功耗較低,並且 NVIDIA Jetson 系列針對 CSI 攝像頭提供了一組 Camera SubSystem 來提高效率,非常適合用在智慧車方案上的影片影象輸入。下表是各種攝像頭的簡單比較表,這樣就能一目瞭然地理解 Jetbot 為何挑選 CSI 攝像頭作為輸入裝置。
首先要提示的,Jetson Nano(含2GB)所支援的是 CSI-2 版本介面,早期用在樹莓派上 50 元以下的 CSI 介面攝像頭是不能使用的,主要以 IMX219 晶片的攝像頭為主,價格大約在 100 元以上,因此請勿選購錯誤。
本文並不花時間去說明 CSI 攝像頭的工作原理,主要配合 Jetbot 的安裝,以及執行最簡單指令去確認您手上的攝像頭是否良好可用,否則等整機都裝好之後再進行測試,如果遇到不良的攝像頭還得再拆卸下來,是一件頗為浪費時間的事情。
- 將CSI攝像頭裝到Jetson Nano(含2GB)上
這種介面並不支援即插即用(PnP, Plug and Play)功能,不能像 USB 攝像頭可以隨時插入 Jetson Nano(含2GB),必須在開機之前就先裝好。如果開機後測試發現不成功,就得關機後再檢查是否有什麼地方安裝不正確?或者接觸不良的問題,就便利性而言不如 USB 攝像頭那麼順手。
安裝 CSI 攝像頭的介面如下圖左方的卡座,要拔取卡筍進行安裝時千萬小心力度,這個塑膠件比較脆弱,一不小心弄斷了,不僅無法再安裝 CSI 攝像頭,也嚴重影響 Jetson Nano(含2GB)的質保,需要非常小心處理!
卡座上透過一根專屬的軟排線(如上圖右)與 CSI 攝像頭進行連線,這個軟排線是有方向性的,如上圖右所顯示,軟排線有金屬針腳的一面要朝內(核心模組)方向,將排線完整塞進卡座之後,再將卡筍往下壓,確保完全壓倒底,否則攝像頭可能因為排線接觸不良而無法正常工作。
請參考“菜鳥手冊(2):給 Jetson Nano 安裝 CSI 攝像頭”(https://cloud.tencent.com/developer/article/1421907)一文,有非常詳盡的步驟與動態簡圖能完整表達過程。總而言之,這個攝像頭的安裝有些細微之處,需要細心處理。一旦安裝測試好之後,也儘量不要拆卸。
- 測試CSI攝像頭的執行
由於 Jetbot 會關閉 Ubuntu 圖形桌面,因此過去所學到呼叫 nvarguscamera 指令,去啟動 CSI 攝像頭並在圖形桌面上顯示的方法,在這裡都不能適用,但是 v4l2-ctl 工具還是能做最基本的檢測。
下面的測試都在 Jetbot 上的 Jupyter 環境上進行,也順便講解一下 Jupyter 上呼叫攝像頭與顯示內容的方法,先熟悉一下這方面的使用是很重要,因為後面所有實驗都要用到 CSI 攝像頭。
現在從 PC 上的瀏覽器輸入 “:8888” 進入 Jetbot 的 Jupyter 操作介面,然後開啟一個命令終端(如下圖):
現在先用 v4l2-ctl 工具檢查一下 CSI 攝像頭的狀況,請在終端輸入以下指令:
# 如果是Docker版,請先安裝v4l-utils工具
apt install v4l-utils
# 執行檢測指令
v4l2-ctl --list-devices
# 如果檢測到 /dev/video0,繼續檢測這個裝置的細部引數
v4l2-ctl --device=/dev/video0 --all
細部引數就不花時間說明,主要讓大家知道當有需要的時候,就可以用 v4l2-ctl 這個工具檢視細節。
接下去開始用 Jupyter 的程式碼,來測試 CSI 攝像頭的工作狀況。首先建立一個新的“Notebook”(如下圖步驟),這是 Jupyter 的工作區域。
每次新開的 Notebook 都是如下圖的狀況,會有一個[ ] 與一個方框:
現在就將下面程式碼複製到在 Notebook 的方框內裡,由於前面“v4l2-ctl”檢測到這個攝像頭的寬高為(1640, 1232),因此在程式碼中將攝像頭與 widgets 的圖形尺寸都設為這兩個數字:
from jetbot import Camera, bgr8_to_jpeg
import ipywidgets.widgets as widgets
from IPython.display import display
import traitlets
camera = Camera.instance(width=1640, height=1232)
image = widgets.Image(format='jpeg', width=1640, height=1232)
camera_link = traitlets.dlink((camera, 'value'), (image, 'value'), transform=bgr8_to_jpeg)
display(image)
然後按一下“Ctrl-Enter”組合鍵就能執行,正常的話就能在下方看到一個方框顯示攝像頭所捕捉的動態畫面,這樣就完成 CSI 攝像頭的測試。顯示有點小延遲是正常的,畢竟這裡選擇的是(1640, 1232)尺寸,試試將尺寸都縮小到 1/2 之後,是不是就流暢的多?
如果要停止攝像頭播放,就用上面的“+”號新增一個“指令格”,然後在裡面輸入:
camera_link.unlink()
然後用“Ctrl-Enter”執行這個指令,這時候顯示畫面就處於凍結狀態,不會播放顯示器所捕捉的內容。
假如想要讓攝像頭再次執行播放,就在下面再新增一個“指令格”,輸入:
camera_link.link()
按下“Ctrl-Enter”執行,就會看到顯示框裡的內容又開始執行動態播放了。
- 程式碼解說
由於 Jupyter 並不支援 OpenGL 功能,無法像圖形桌面那樣直接播放影片,因此在這裡需要使用一些固定的技巧,將影片轉成圖形方式去顯示,上面這段程式碼可以說是 Jetbot 所有實驗啟動 CSI 攝像頭的標準內容。
這個程式碼中有幾個關鍵的部分:
1. jetbot的Camera模組:
早期 Jetbot 的攝像頭是基於 NVIDIA 的 JetCam 專案,提供對 CSI 與 USB 兩類攝像頭的支援,不過最新版本中彷彿只對 CSI 攝像頭提供支援,並且將底層的程式碼放在 jetbot/camera 下面的幾隻 .py 裡面。
這使得呼叫方式變得非常簡單,只要將寬與高提供給 Camera.instance (寬,高)就可以,不需要再指定攝像頭的編號。
2. ipywidgets:
這個模組為 jupyter 提供非常好用的“互動式小部件”,為原本只提供靜態顯示的教學環境,注入非常生動與多樣化互動的功能。
熟悉圖形化開發工具的朋友就應該知道,很多這類工具會提供下拉式選項、滑塊調整、複選框、文字框這類的小部件,可以組建較為複雜的儀表盤。
在後面控制 Jetbot 車輪的實驗中會使用到多種這類小部件,這裡只是用到比較簡單的 widgets.image 功能,設定輸出為“jpeg”格式影象。
3. iPython.display:
這是一個 python 互動式介面裡的顯示功能,這裡就是將 widget.image 輸出的 jpeg 影象顯示在 Jupyter。
4. traitlets:
如果從整個英文字去翻譯,這個庫就被稱為“叛徒庫”,其實是蠻奇怪的東西。但如果將這個字切割成“trait”與“-let”組合,就能解釋成“小特徵”的含義,例如 booklet 是小冊子、eaglet 是小老鷹的意思。
這個庫的功能非常強大,除了能為我們處理“型別檢查”之外,還能為動態計算提供預設值、修改屬性之後發出更改的事件訊號、處理屬性值之間的互動影響,為我們簡化很多複雜的交錯關係。
在上述程式碼中“camera_link=traitlets.dlink((camera, 'value'), (image, 'value'), transform=bgr8_to_jpeg)”,將攝像頭讀取的影象與 widgets.image 所建立的 image 物件產生動態關聯,並將攝像頭圖形執行“bgr8_to_jpeg”轉換,存放到 image 裡,最後再由 display(image)顯示出來。
至於 camera_link 物件就能在後面透過.link()與.unlink()執行開關人任務,以實現“暫停”與“再啟動”的功能。
這樣我們對 Jetbot 攝像頭的呼叫與使用,就有個初步的瞭解,在後面實驗中就能更清楚的感受到這些功能的使用。