試:MIPI CSI-2 Virtual Channel機(jī)制與實(shí)戰(zhàn))
最近在調(diào)一塊Jetson Orin NX開發(fā)板遇到一個(gè)挺有意思的現(xiàn)象板子接了兩路MIPI攝像頭/dev/video0注冊正常/dev/video1卻經(jīng)常時(shí)有時(shí)無。最開始以為是硬件接觸不良查來查去最后發(fā)現(xiàn)是Virtual Channel的配置問題。如果你也在Jetson上做多路視覺采集或者看過設(shè)備樹和media-ctl -p打印后一頭霧水這篇文章應(yīng)該對你有用。我不會把Virtual Channel Driver當(dāng)成一個(gè)高深莫測的模塊來寫而是按我自己的理解從硬件通道、內(nèi)核驅(qū)動(dòng)、用戶態(tài)配置和實(shí)際排錯(cuò)四個(gè)層面把它拆開講清楚。這篇文章適合兩類人一類是用Jetson做多路相機(jī)采集但又沒系統(tǒng)看過驅(qū)動(dòng)鏈路的開發(fā)者另一類是遇到video0、video1節(jié)點(diǎn)亂跳或者虛擬顯示配置不生效想搞清楚背后機(jī)制的嵌入式Linux工程師。1. 一個(gè)物理連接塞進(jìn)多路數(shù)據(jù)MIPI CSI-2的Virtual Channel機(jī)制1.1 先從CSI-2物理層說起MIPI CSI-2是Jetson板載攝像頭接口最常用的協(xié)議它對應(yīng)的是板上那排FPC排線座Sensor通過排線連接到SoC的CSI控制器。很多人把CSI接口理解成“一根線傳一路攝像頭”這么理解在低速時(shí)代沒問題但在多路高清攝像頭場景下就有點(diǎn)不夠了。在CSI-2協(xié)議里物理通道和邏輯通道是兩個(gè)不同層級的概念。物理層看的是差分信號線lane一條CSI接口由時(shí)鐘lane和若干數(shù)據(jù)lane組成數(shù)據(jù)lane數(shù)量通常是1、2或4條這個(gè)數(shù)量決定了帶寬上限。而邏輯層有一個(gè)機(jī)制叫Virtual Channel簡稱VC在數(shù)據(jù)包頭部有一個(gè)2bit的VC ID取值0到3。接收端拿到數(shù)據(jù)包后根據(jù)這個(gè)ID把數(shù)據(jù)分發(fā)到不同的邏輯通道所以同一條物理CSI接口最多能傳4路獨(dú)立的視頻流。用大白話說這就好比一條物理公路CSI接口路上跑著4輛不同顏色的車VC 0/1/2/3每輛車車頭上都寫著目的地編號收費(fèi)站CSI控制器看一眼編號就把車分到對應(yīng)車道。1.2 “虛擬通道”虛擬的到底是什么Virtual Channel“虛擬”的是數(shù)據(jù)流歸屬而不是物理連接。Sensor端在發(fā)送數(shù)據(jù)的時(shí)候會在每個(gè)幀的包頭上打上自己的VC ID接收端CSI控制器解析包頭按VC ID把數(shù)據(jù)放到對應(yīng)的FIFO里。Jetson的驅(qū)動(dòng)再把每個(gè)FIFO對應(yīng)成一個(gè)獨(dú)立的V4L2 video節(jié)點(diǎn)這就是你在用戶空間看到的/dev/video0、/dev/video1。這帶來一個(gè)很直接的影響在硬件上幾路攝像頭可以共用同一個(gè)CSI端口只需要把多路Sensor的信號通過Deserializer解串器匯流到同一條CSI鏈路上。很多車載和工業(yè)視覺方案里一個(gè)CSI口接4路甚至8路攝像頭靠的就是VC機(jī)制。Jetson的驅(qū)動(dòng)里每個(gè)VC對應(yīng)一個(gè)獨(dú)立的channel驅(qū)動(dòng)層層往下注冊時(shí)會把它們拆成不同的視頻設(shè)備。實(shí)際項(xiàng)目中還有個(gè)非常容易踩的坑VC ID必須與Sensor側(cè)的配置一致。有些Sensor的VC ID是通過硬件引腳配置的有些是通過I2C寄存器配置的如果Sensor實(shí)際發(fā)出的VC ID和驅(qū)動(dòng)里設(shè)置的不一致表現(xiàn)就是video0能采到圖但畫面是花的或者干脆不出流。1.3 為什么這個(gè)機(jī)制對Jetson尤其重要Jetson的CSI接口數(shù)量是有限的以O(shè)rin NX為例可用CSI端口就那么幾個(gè)。如果每個(gè)攝像頭都獨(dú)占一個(gè)物理接口那最多只能接四路左右。但很多場景比如無人機(jī)環(huán)繞視覺、機(jī)器人頭部多目、VR手勢識別需要6路、8路甚至更多攝像頭這時(shí)候就必須借助VC在一路物理CSI上串多路Sensor。另外Jetson的ISP和VIVideo Input硬件在設(shè)計(jì)上也是按channel來管理的每個(gè)channel有獨(dú)立的內(nèi)存地址和buffer管理邏輯。這也是為什么在用戶空間每個(gè)VC看起來就像一路完全獨(dú)立的攝像頭打開、出流、關(guān)閉互不影響。驅(qū)動(dòng)層面實(shí)際做的事情就是把硬件上按VC劃分好的通路通過V4L2框架暴露給應(yīng)用。2. 從硬件到節(jié)點(diǎn)Jetson媒體子系統(tǒng)對虛擬通道的逐層映射2.1 Jeston采集鏈路里都有哪些角色一套標(biāo)準(zhǔn)的Jetson攝像頭采集鏈路涉及的角色比很多人想象得多。最前端是Sensor本身它通過I2C和GPIO與SoC通信然后是CSI控制器負(fù)責(zé)接收MIPI數(shù)據(jù)包做VC識別、數(shù)據(jù)解析再往后是VIVideo Input負(fù)責(zé)把數(shù)據(jù)搬運(yùn)到內(nèi)存最后是ISPImage Signal Processor負(fù)責(zé)做Bayer去馬賽克、降噪、色彩校正等圖像處理。在內(nèi)核驅(qū)動(dòng)層面每一個(gè)角色都是一個(gè)獨(dú)立的內(nèi)核模塊。Sensor對應(yīng)tegra-camera-platform下的驅(qū)動(dòng)CSI和VI對應(yīng)tegra-vi4、tegra-capture等模塊。這些模塊在設(shè)備樹里通過端口連接關(guān)系組成一張圖media graph用戶空間的media-ctl工具就是用來查看和操作這張圖的。Virtual Channel Driver這個(gè)名字嚴(yán)格說不是指某一個(gè)單一驅(qū)動(dòng)文件而是指一整套讓虛擬通道生效的軟件機(jī)制從設(shè)備樹的VC ID聲明、到CSI驅(qū)動(dòng)的通道解析、再到VI驅(qū)動(dòng)的buffer管理串起來才是完整的Virtual Channel Driver。2.2 media graph理解虛擬通道的最佳入口在Jetson上做多路攝像頭開發(fā)第一時(shí)間應(yīng)該打開的就是media graph。執(zhí)行media-ctl -p /dev/media0會看到一長串拓?fù)湫畔⒗锩鏁谐鏊凶缘膶?shí)體entity和它們之間的連接link。每個(gè)Sensor是一個(gè)entityCSI控制器是另一個(gè)entityVI接收端又是幾個(gè)entity它們之間通過pad連接。我剛開始調(diào)多路攝像頭時(shí)不理解為什么video0不能直接對應(yīng)某個(gè)Sensor后來看media graph才明白video0只是整個(gè)流水線的入口真正決定數(shù)據(jù)從哪里來的是它下游的subdev鏈路。具體來說一個(gè)V4L2 video節(jié)點(diǎn)后面通常會掛一個(gè)VI的subdevVI再連到CSI控制器CSI再連到某個(gè)Sensor。數(shù)據(jù)是逐級往上流的每一級都有自己的格式配置。在media graph里虛擬通道的體現(xiàn)就是CSI控制器有多個(gè)sink pad每個(gè)sink pad對應(yīng)一個(gè)VC ID。比如CSI控制器有4個(gè)sink pad分別連接四個(gè)來自不同VC ID的Sensor entity那么你就能在拓?fù)淅锴宄吹矫總€(gè)VC被路由到了哪個(gè)video節(jié)點(diǎn)。這是調(diào)試多路攝像頭時(shí)最有價(jià)值的參考信息。2.3 用戶空間的video節(jié)點(diǎn)是如何一一對應(yīng)的很多人以為/dev/video0、/dev/video1這些節(jié)點(diǎn)號是固定的其實(shí)不是。在Jetson上video節(jié)點(diǎn)號是由驅(qū)動(dòng)注冊順序決定的而注冊順序又和設(shè)備樹里節(jié)點(diǎn)的排列順序、模塊加載順序有關(guān)。如果你改過設(shè)備樹、重新編譯過內(nèi)核或者換了SDK版本節(jié)點(diǎn)號完全可能變。真正穩(wěn)定的是節(jié)點(diǎn)對應(yīng)的名字或者media graph里的拓?fù)潢P(guān)系。通過v4l2-ctl --list-devices可以看到每個(gè)video節(jié)點(diǎn)的名字比如vi-output idx 0。判斷一個(gè)節(jié)點(diǎn)到底對應(yīng)哪個(gè)VC最靠譜的方法是看media-ctl -p里面從該video節(jié)點(diǎn)往下沿著鏈路找到末端Sensor的名稱和地址。這里有個(gè)經(jīng)驗(yàn)在實(shí)際項(xiàng)目中不要依賴/dev/video0這樣的硬編碼而是應(yīng)該通過/sys/class/video4linux/下面的符號鏈接或者media graph里的名字來匹配設(shè)備。Jetson上不同的JetPack版本對節(jié)點(diǎn)的命名規(guī)則都有過調(diào)整硬編碼節(jié)點(diǎn)號是給自己埋坑。3. 調(diào)試虛擬通道必須掌握的三條命令以及我翻車過的兩次3.1 確認(rèn)當(dāng)前拓?fù)鋗edia-ctl在Jetson上調(diào)試任何與視頻通道相關(guān)的問題第一步永遠(yuǎn)是查看media graph。執(zhí)行sudo media-ctl -p /dev/media0這條命令會打印完整的實(shí)體和pad信息。重點(diǎn)看CSI控制器的sink pad連接到了哪些Sensor實(shí)體每個(gè)Sensor的名稱后面會帶上I2C地址。比如- entity 7: imx219 6-0010 (1 pad, 1 link)這個(gè)信息說明在I2C總線6地址0x10上有一顆IMX219 Sensor。如果你是第一次接觸這個(gè)工具建議先用-p參數(shù)看一下所有實(shí)體之間的上下游關(guān)系再結(jié)合設(shè)備樹確認(rèn)每個(gè)Sensor配置的VC ID。拓?fù)淝逦撕竺婧芏鄦栴}都不用猜。3.2 查看和設(shè)置格式v4l2-ctlv4l2-ctl是用來配置和控制video節(jié)點(diǎn)的工具。多路虛擬通道調(diào)試中最常用的子命令包括v4l2-ctl --list-devices v4l2-ctl --list-formats-ext -d /dev/video0 v4l2-ctl --set-fmt-videowidth1920,height1080,pixelformatNV12 -d /dev/video0 v4l2-ctl --stream-mmap --stream-count1 -d /dev/video0有一個(gè)很關(guān)鍵的細(xì)節(jié)在虛擬通道架構(gòu)下光設(shè)置video節(jié)點(diǎn)的格式還不夠sensor的subdev格式也要單獨(dú)設(shè)置。因?yàn)関ideo節(jié)點(diǎn)的格式?jīng)Q定的是VI輸出到內(nèi)存的格式而Sensor的格式?jīng)Q定的是CSI鏈路上傳輸?shù)腞AW數(shù)據(jù)格式。兩層格式脫節(jié)最典型的表現(xiàn)是v4l2-ctl --stream-mmap能出流但畫面顏色完全不對或者圖像邊緣有奇怪的綠色條紋。正確的設(shè)置方式是通過media-ctl設(shè)置subdev的format比如media-ctl -V imx219 6-0010:0[fmt:SRGGB10_1X10/1920x1080]然后再用v4l2-ctl設(shè)置video節(jié)點(diǎn)的格式。很多項(xiàng)目跑不起來就是因?yàn)橹慌淞藇ideo節(jié)點(diǎn)沒配subdev。3.3 翻車案例一只開一個(gè)channel另一個(gè)不出流我有一次調(diào)試雙目相機(jī)兩個(gè)sensor配置了不同的VC ID設(shè)備樹也加好了。上電后兩個(gè)video節(jié)點(diǎn)都注冊出來了但只有video0能出流video1的STREAMON一直超時(shí)。用dmesg看只有一行CSI相關(guān)的超時(shí)日志沒有任何更詳細(xì)的錯(cuò)誤。排查過程其實(shí)很曲折。我先懷疑是sensor配置問題用I2C工具讀sensor寄存器確認(rèn)兩個(gè)sensor都在正常輸出。然后懷疑是VC ID不匹配又去看了設(shè)備樹配置發(fā)現(xiàn)兩個(gè)節(jié)點(diǎn)配置的VC分別是0和1沒有寫錯(cuò)。最后拿著放大鏡檢查FPC排線才看到第二個(gè)sensor有一根差分信號線的焊點(diǎn)虛焊導(dǎo)致同步信號異常。這個(gè)問題的教訓(xùn)是在虛擬通道架構(gòu)下兩條通道共用物理鏈路一條鏈路的信號完整性問題會直接影響另一條通道的同步。排線虛焊、線序錯(cuò)誤這類硬件問題在單攝方案里可能只是畫面異常但在多路VC方案里往往表現(xiàn)為另一路完全不出流排查難度大得多。3.4 翻車案例二media graph里出現(xiàn)了兩個(gè)相同的sensor名字另一個(gè)坑來自設(shè)備樹配置。我新加到一塊板子的設(shè)備樹時(shí)不小心把同一個(gè)sensor節(jié)點(diǎn)復(fù)制了兩份I2C地址和VC ID都完全相同只是名字改了后綴。編譯、燒錄一切正常但運(yùn)行media-ctl -p時(shí)發(fā)現(xiàn)graph里出現(xiàn)了兩個(gè)拓?fù)渫耆粯拥膃ntity分別接在CSI控制器的不同pad上。看起來好像沒問題但實(shí)際采集時(shí)兩個(gè)video節(jié)點(diǎn)出的畫面都來自同一顆sensor而且由于VC沖突兩個(gè)節(jié)點(diǎn)都時(shí)而花屏。我一開始還懷疑是驅(qū)動(dòng)注冊邏輯的問題后來反復(fù)核實(shí)才發(fā)現(xiàn)是設(shè)備樹里重復(fù)定義了節(jié)點(diǎn)。在Jetson的L4T內(nèi)核中sensor的I2C地址加上VC ID才是唯一的標(biāo)識。寫設(shè)備樹時(shí)這兩項(xiàng)必須跟實(shí)際硬件一一對應(yīng)不能出現(xiàn)兩個(gè)節(jié)點(diǎn)指向同一個(gè)硬件通路的情況。經(jīng)驗(yàn)是寫完之后先在/proc/device-tree下面檢查對應(yīng)節(jié)點(diǎn)的內(nèi)容確認(rèn)I2C地址和vc值再跑media-ctl -p做二次確認(rèn)。4. 設(shè)備樹、驅(qū)動(dòng)加載與常見告警虛擬通道驅(qū)動(dòng)在系統(tǒng)里的落地方式4.1 設(shè)備樹里怎么聲明一個(gè)虛擬通道Jetson的攝像頭設(shè)備樹配置遵循標(biāo)準(zhǔn)V4L2設(shè)備樹綁定。每個(gè)sensor節(jié)點(diǎn)需要聲明regI2C地址、reset-gpios、mclk等屬性同時(shí)還要在port節(jié)點(diǎn)中聲明它連接到的CSI控制器端口。虛擬通道的ID在子節(jié)點(diǎn)中通常通過一個(gè)屬性來指定比如sensor1a { reg 0x1a; vc-id 0; port { sensor_out: endpoint { remote-endpoint csi_in0; }; }; };第二個(gè)sensor則是sensor1c { reg 0x1c; vc-id 1; port { sensor_out2: endpoint { remote-endpoint csi_in1; }; }; };CSI控制器節(jié)點(diǎn)里對應(yīng)地要有多個(gè)sink端點(diǎn)分別指向不同VC的sensor endpoint。這種聲明方式讓驅(qū)動(dòng)在初始化時(shí)就能知道哪個(gè)sensor掛在哪個(gè)CSI端口、用的是哪個(gè)VC ID、對應(yīng)的遠(yuǎn)端端點(diǎn)是誰。修改設(shè)備樹之后很多人會忘記一件事Jetson的設(shè)備樹是編譯成dtb文件燒錄的僅修改源碼還不夠要用dtc工具或JetPack自帶的編譯腳本重新生成dtb再燒到boot分區(qū)。我在早期調(diào)試時(shí)經(jīng)常改了設(shè)備樹但沒燒錄導(dǎo)致怎么查都跟源碼對不上。4.2 nvidia-uvm加載沖突和虛擬通道沒有直接關(guān)系但經(jīng)常一起出現(xiàn)在Jetson上做開發(fā)幾乎所有人都會遇到一條報(bào)錯(cuò)信息An nvidia kernel module nvidia-uvm appears to be already loaded in your kernel這個(gè)信息跟Virtual Channel Driver沒什么關(guān)系它說的是NVIDIA的UVMUnified Virtual Memory模塊已經(jīng)被加載了。但在實(shí)際項(xiàng)目中這個(gè)報(bào)錯(cuò)會干擾你判斷驅(qū)動(dòng)加載狀態(tài)因?yàn)槟憧赡転榱伺挪関ideo0不出流去了解驅(qū)動(dòng)模塊結(jié)果看到滿屏的UVM警告以為哪個(gè)驅(qū)動(dòng)加載順序出問題了。UVM模塊的作用是讓GPU和CPU共享虛擬內(nèi)存地址空間它由nvidia.ko在啟動(dòng)時(shí)自動(dòng)加載并在CUDA運(yùn)行時(shí)被復(fù)用。如果你試圖在系統(tǒng)已經(jīng)運(yùn)行的情況下手動(dòng)modprobe nvidia-uvm就會看到這個(gè)提示。解決辦法很簡單要么不管它因?yàn)楣δ苷R创_認(rèn)運(yùn)行環(huán)境干凈后的狀態(tài)在干凈的L4T系統(tǒng)里它會在正常啟動(dòng)流程中自動(dòng)加載。排查多路攝像頭問題時(shí)判斷驅(qū)動(dòng)模塊的方法應(yīng)該是lsmod | grep tegra lsmod | grep video lsmod | grep nvidia而不是盯著nvidia-uvm報(bào)錯(cuò)看。tegra-capture、tegra-vi4這些模塊加載正常與否才是虛擬通道出流的關(guān)鍵。4.3 驅(qū)動(dòng)加載順序與節(jié)點(diǎn)命名的不確定性Jetson的攝像頭相關(guān)內(nèi)核模塊之間是有依賴關(guān)系的。從底層往上大致是tegra-camera-platform管理sensor平臺設(shè)備→tegra-vi4VI控制器驅(qū)動(dòng)→tegra-capture視頻采集驅(qū)動(dòng)→nvgpu/nvhostGPU和圖形相關(guān)視平臺而定。模塊加載順序會影響/dev/videoX節(jié)點(diǎn)編號的分配順序。在標(biāo)準(zhǔn)L4T內(nèi)核里這些模塊通常在啟動(dòng)階段由udev按依賴關(guān)系自動(dòng)加載。但由于platform設(shè)備的注冊順序和設(shè)備樹中節(jié)點(diǎn)的排列順序有關(guān)如果你在設(shè)備樹中新加了一個(gè)sensor節(jié)點(diǎn)放在最前面那么它可能會被注冊為video0原有的video0變成video1這是正常現(xiàn)象。開發(fā)階段我強(qiáng)烈建議做兩件事第一應(yīng)用層通過設(shè)備名稱或media graph來匹配設(shè)備而不是硬編碼節(jié)點(diǎn)號第二在發(fā)布版本里固定設(shè)備樹和內(nèi)核版本不要隨意升級JetPack或者更換dtb否則節(jié)點(diǎn)編號很可能改變影響系統(tǒng)穩(wěn)定性。4.4 lspci查不到NVIDIA設(shè)備那不是Bug很多人拿到Jetson Orin NX后習(xí)慣性地執(zhí)行l(wèi)spci | grep -i nvidia結(jié)果發(fā)現(xiàn)什么都沒有于是懷疑驅(qū)動(dòng)沒裝好。其實(shí)Tegra系列芯片是SoC架構(gòu)GPU、ISP、視頻編解碼器都集成在同一顆芯片里不經(jīng)過PCIe總線。lspci只能看到PCIe外設(shè)比如NVMe SSD、WiFi網(wǎng)卡、外接的獨(dú)立GPU查不到SoC內(nèi)部集成的NVIDIA設(shè)備。這個(gè)在X86平臺上習(xí)慣了的查驅(qū)動(dòng)方式放到Jetson上并不適用。Jetson上檢查GPU和顯示驅(qū)動(dòng)是否正確加載正確做法是ls /dev/nvidia-uvm ls /dev/nvidiactl cat /proc/driver/nvidia/version另外也要提到一點(diǎn)熱詞里有“nvidia control panel下載”這樣的詞但在Jetson上并沒有X86平臺那種獨(dú)立的NVIDIA控制面板顯示配置是通過xrandr、/etc/X11/xorg.conf以及設(shè)備樹里的display節(jié)點(diǎn)來管理的。把X86的使用習(xí)慣投射到Jetson上容易繞彎路。5. 虛擬顯示與多通道帶寬規(guī)劃把Virtual Channel理解用在實(shí)戰(zhàn)里5.1 無頭設(shè)備的Xorg虛擬顯示和內(nèi)核Virtual Channel不是一回事Jetson Orin NX開發(fā)套件本身不帶顯示輸出是很常見的很多人在無頭環(huán)境下想要一個(gè)虛擬顯示器來做遠(yuǎn)程桌面或者跑GUI程序于是在Xorg里配置xserver-xorg-video-dummy或者使用Xvfb。這套方案在用戶空間實(shí)現(xiàn)了一個(gè)“假顯示器”讓應(yīng)用程序認(rèn)為存在一個(gè)顯示器但它和本文講的Virtual Channel Driver沒有關(guān)系。一個(gè)是內(nèi)核空間的視頻采集通道一個(gè)是用戶空間的虛擬顯示設(shè)備。在Jetson上設(shè)置Xorg虛擬顯示的正確姿勢是修改/etc/X11/xorg.conf加一個(gè)dummy驅(qū)動(dòng)的Display段。比如Section Device Identifier dummy Driver dummy VideoRam 32768 EndSection同時(shí)聲明一個(gè)合適的分辨率范圍。這套方案跑GStreamer、OpenGL等圖形應(yīng)用都沒有問題但它消耗的是CPU和內(nèi)存資源不經(jīng)過Jetson的顯示控制器DC硬件模塊。這個(gè)區(qū)分很重要因?yàn)樵谡{(diào)試多路攝像頭時(shí)你很可能會用到虛擬顯示來跑GUI工具看畫面。如果理解錯(cuò)了層面就容易出現(xiàn)“為什么我配置了Virtual Channel但虛擬顯示不工作”這種跨層的困惑。5.2 多路虛擬通道場景下的帶寬估算規(guī)劃多路攝像頭方案時(shí)很多人只關(guān)注CSI端口的數(shù)量忽略了帶寬預(yù)算結(jié)果接上之后發(fā)現(xiàn)高幀率跑不滿。MIPI CSI-2鏈路的帶寬是有限的每路lane的速率取決于sensor輸出、Deserializer和SoC的CSI控制器能力。以常見的1080P60 RAW10為例單路數(shù)據(jù)速率大約是像素時(shí)鐘 × 位深 1920 × 1080 × 60 × 10 bit ≈ 1.2 Gbps這個(gè)速率需要至少1條數(shù)據(jù)lane按1.5Gbps/lane計(jì)算才勉強(qiáng)跑得動(dòng)但實(shí)際項(xiàng)目中一般留20%左右余量所以單路1080P60 RAW10至少配2 lane比較穩(wěn)妥。4路這樣的流加起來接近5 Gbps對CSI控制器的總帶寬和內(nèi)存帶寬都是不小壓力。下面是我在項(xiàng)目中常用的參考表分辨率幀率位深單路數(shù)據(jù)率4路總速率建議CSI數(shù)據(jù)lane1280x72030RAW100.27 Gbps約1.1 Gbps1 lane/路1920x108030RAW100.60 Gbps約2.4 Gbps2 lane/路1920x108060RAW101.20 Gbps約4.8 Gbps2 lane/路3840x216030RAW102.40 Gbps約9.6 Gbps4 lane/路數(shù)據(jù)率只是傳輸層帶寬到了VI寫入內(nèi)存還會占用系統(tǒng)總線和內(nèi)存帶寬多路同時(shí)開啟時(shí)內(nèi)存帶寬往往成為瓶頸。我在Orin NX上跑4路720P60的時(shí)候CPU占用不高但內(nèi)存帶寬占用已經(jīng)比較明顯同時(shí)跑ISP和編解碼任務(wù)時(shí)會出現(xiàn)幀率抖動(dòng)。5.3 多路開啟時(shí)的推薦流程與避坑習(xí)慣在Jetson上開啟多路虛擬通道采集我自己的習(xí)慣是啟動(dòng)后先看dmesg | grep -E tegra|vi|csi|imx確認(rèn)所有sensor都被正確探測和注冊再跑media-ctl -p確認(rèn)拓?fù)渫暾總€(gè)VC ID都對應(yīng)正確的sensor然后逐個(gè)通道單獨(dú)出流確認(rèn)每一路本身正常最后再同時(shí)開啟所有通道觀察是否有格式不匹配、帶寬不足導(dǎo)致的丟幀。多路同時(shí)開啟時(shí)最容易出的問題是某一路的圖像參數(shù)沒有設(shè)置完整。例如給兩個(gè)通道設(shè)置了不同的分辨率或幀率而CSI控制器還按同一個(gè)格式在解析數(shù)據(jù)就會出現(xiàn)畫面撕裂。另外所有通道的sensor幀率最好保持一致尤其是對同一顆Deserializer輸出來的多路流幀率不一致會導(dǎo)致同步信號互相干擾。還有一個(gè)小習(xí)慣不要在多路出流過程中反復(fù)開關(guān)單個(gè)video節(jié)點(diǎn)而不關(guān)閉其他節(jié)點(diǎn)這會讓VI的buffer分配狀態(tài)變得混亂偶爾會導(dǎo)致內(nèi)存泄漏。正確做法是先整體下電關(guān)閉所有stream再修改配置再整體上電。6. 回顧一次多路虛擬通道的完整調(diào)試過程為了幫你把這些知識點(diǎn)串起來我復(fù)盤一次實(shí)際調(diào)過的四路攝像頭項(xiàng)目看看Virtual Channel問題到底長什么樣。6.1 癥狀第四路圖像偶爾不刷新項(xiàng)目用了一顆四路Deserializer通過CSI接入Jetson Orin NX四路VC分別配置為0/1/2/3。剛接好時(shí)一切正常四路都能出流。但跑了一個(gè)小時(shí)后第四路圖像開始偶發(fā)不刷新大約每幾十秒卡一幀隨后自動(dòng)恢復(fù)。一開始我懷疑是sensor過熱但讀sensor溫度完全正常。后來懷疑是CSI鏈路抗干擾能力不足查了FPC排線的屏蔽也正常。最后用media-ctl -p盯拓?fù)鋾r(shí)發(fā)現(xiàn)問題第四路sensor的VC ID在設(shè)備樹里寫成了0和第一路重復(fù)了。按理說VC沖突會直接導(dǎo)致無法出流但實(shí)際表現(xiàn)是偶發(fā)卡頓這是因?yàn)镈eserializer內(nèi)部對同ID的多路流做了某種仲裁導(dǎo)致了不穩(wěn)定的調(diào)度行為而不是完全報(bào)錯(cuò)。6.2 定位鏈路從應(yīng)用到驅(qū)動(dòng)的逐層排除這次問題的定位過程耗時(shí)最長的環(huán)節(jié)其實(shí)是確認(rèn)“第四路到底走的是哪條通路”。我在應(yīng)用層用v4l2-ctl直接看video節(jié)點(diǎn)信息發(fā)現(xiàn)第四路對應(yīng)的video節(jié)點(diǎn)名和拓?fù)淅锏哪硹l鏈路對不上。后面對照設(shè)備樹源碼才發(fā)現(xiàn)VC ID配重了。這讓我養(yǎng)成了一個(gè)習(xí)慣任何時(shí)候懷疑多路攝像頭有問題先做三件事——讀設(shè)備樹vc配置、跑media-ctl -p、看dmesg的錯(cuò)誤日志。順序不能亂因?yàn)殄e(cuò)誤日志里報(bào)出的實(shí)體名稱能幫助你快速定位到底是哪一環(huán)出了問題而不是漫無目的地檢查硬件。6.3 修復(fù)與驗(yàn)證把第四路sensor的vc-id改成3重新編譯dtb燒錄重啟。之后用media-ctl -p確認(rèn)四個(gè)entity分別對應(yīng)四個(gè)VC再用v4l2-ctl對所有通道做了2小時(shí)壓力出流沒有再出現(xiàn)丟幀或卡頓。這次調(diào)試的經(jīng)驗(yàn)對后續(xù)項(xiàng)目很有幫助。Virtual Channel Driver本身并不復(fù)雜它就是一套把MIPI CSI-2的VC機(jī)制暴露成多個(gè)V4L2視頻設(shè)備的驅(qū)動(dòng)框架。但因?yàn)殒溌烽L、層級多任何一層的配置錯(cuò)誤都會表現(xiàn)為出流異常排查起來特別考驗(yàn)對整條鏈路的理解。我現(xiàn)在做Jetson上的多路采集第一步永遠(yuǎn)是打開media graph和設(shè)備樹把通道身份確認(rèn)清楚再做應(yīng)用層開發(fā)這套習(xí)慣幫我省掉了大量返工時(shí)間。