避撞功能是 Jetbot 一開(kāi)始最令人矚目的功能,因?yàn)檫@是所有小車都必須具備的最基本“自我保護(hù)”的能力,而 Jetbot 沒(méi)有任何距離傳感,只憑著一個(gè) CSI 攝像頭就能完成這項(xiàng)任務(wù),對(duì)很多人來(lái)說(shuō)是一件蠻神奇的事情,Jetbot 是如何識(shí)別與周邊物體的距離,來(lái)決定是繼續(xù)前進(jìn)還是得轉(zhuǎn)向?
這里請(qǐng)大家先沉淀一下來(lái)思考,人腦是如何學(xué)習(xí)判斷前方的路是可以繼續(xù)前進(jìn)?或是有障礙物、坑洞必須轉(zhuǎn)向?請(qǐng)先忘記您的成年人身份,試著模擬剛學(xué)會(huì)爬行的小嬰兒,如何逐步學(xué)習(xí)并建立這方面的“認(rèn)知系統(tǒng)”呢?小嬰兒對(duì)前方的信息來(lái)源,有以下三個(gè)特點(diǎn):
只有視覺(jué)(眼睛)的輸入
沒(méi)有距離的概念
缺乏物件的分類
在沒(méi)有其他干預(yù)的狀況下,小嬰兒必定得經(jīng)過(guò)不斷地碰撞與摔倒之后,自身防御系統(tǒng)會(huì)逐步學(xué)習(xí)并修正決策機(jī)制,這是動(dòng)物界最原始的學(xué)習(xí)機(jī)制。小嬰兒在這個(gè)過(guò)程所接收的信息,就是沒(méi)有距離、沒(méi)有物件類別的最基本“圖像”而已。
到目前為止,Jetbot 的運(yùn)作邏輯是最接近人類行進(jìn)思維的一套智能無(wú)人車,因?yàn)槲覀儾⒉恍枰ヅ袛嗯c障礙物(或坑洞)之間的確實(shí)距離是多近,也不需要分辨前面障礙物是什么東西,就能下達(dá)“前進(jìn)”或“轉(zhuǎn)向”的決策。
當(dāng)我們安裝好 Jetbot 智能車與系統(tǒng)軟件之后,接下去就是為每個(gè)特定功能添加“深度學(xué)習(xí)”的智能識(shí)別技術(shù)進(jìn)去。例如這個(gè)避障的應(yīng)用就只使用到最基礎(chǔ)的“圖像識(shí)別”能力,將 CSI 鏡頭的每幀畫(huà)面識(shí)別出“無(wú)阻礙(free)”與“有阻礙(blocked)”兩種狀態(tài),然后發(fā)出對(duì)應(yīng)指令去驅(qū)動(dòng)電機(jī)執(zhí)行運(yùn)動(dòng)。
任何要添加深度學(xué)習(xí)的智能識(shí)別功能,都必須執(zhí)行以下三個(gè)步驟:
數(shù)據(jù)收集與整理
模型訓(xùn)練
執(zhí)行識(shí)別
這個(gè)避障功能的實(shí)驗(yàn)代碼在 Jetbot 的 notebooks/collision_avoidance 下,里面有 8 個(gè) .ipynb 文件,包括 1 個(gè) data_collecton.ipynb、3 個(gè) train_modelxxx.ipynb 與 4 個(gè) live_demoxxx.ipynb,分別對(duì)應(yīng)上面所說(shuō)的三個(gè)步驟。不過(guò)這些實(shí)驗(yàn)代碼不需要全都用上,這里以 data_collecton.ipynb、train_model.ipynb 與 live_demo.ipynb 這三個(gè)最基本的代碼來(lái)做說(shuō)明。
礙于篇幅問(wèn)題,本文先帶著大家運(yùn)行 live_demo.ipynb 代碼,去體驗(yàn)一下 Jetbot 的避障功能,畢竟前面花了這么多時(shí)間與精力所組裝的系統(tǒng),先跑起來(lái)能獲得一些成就感之后,在下一篇文章里再說(shuō)明比較枯燥的“數(shù)據(jù)收集與整理”、“模型訓(xùn)練”兩大步驟,這樣才算完成整個(gè)流程。
為了讓大家能夠先行體驗(yàn),這里提供原創(chuàng)團(tuán)隊(duì)預(yù)訓(xùn)練的 best_model.pth 模型的鏈接,模型的訓(xùn)練方式會(huì)在下一篇文章里面說(shuō)明,這里只管下載到 collision_avoidance 目錄下使用就行。文件鏈接如下:https://drive.google.com/file/d/1UsRax8bR3R-e-0-80KfH2zAt-IyRPtnW/view
由于我們未得到原創(chuàng)團(tuán)隊(duì)的授權(quán),不能擅自下載這個(gè)存放在 Google 網(wǎng)盤(pán)上的文件,再分享給讀者下載,請(qǐng)大家能夠理解,這需要讀者請(qǐng)自行設(shè)法下載!
接下來(lái)開(kāi)啟 notebooks/collision_avoidance/live_demo.ipynb 工作腳本,逐步執(zhí)行就能讓 Jetbot 小車執(zhí)行避障的功能。這里面主要分為以下三大部分:
1. 加載訓(xùn)練的模型:
這里關(guān)于深度學(xué)習(xí)的部分,全部使用 PyTorch 這個(gè)輕量級(jí)的框架,對(duì)于不熟悉的讀者來(lái)說(shuō),一開(kāi)始的兩行代碼可能就已經(jīng)會(huì)產(chǎn)生不小的困擾,現(xiàn)在就簡(jiǎn)單地逐行說(shuō)明:
model = torchvision.models.alexnet(pretrained=False) |
torchvision 是 PyTorch 里面專門(mén)用在視覺(jué)應(yīng)用的深度學(xué)習(xí)庫(kù)。
由于 PyTorch 內(nèi)建支持很多常用的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),這里要使用深度卷積網(wǎng)絡(luò)始祖的 AlexNet,是因?yàn)榍懊嫣峁┫螺d連接的 best_model.pth 就是以 AlexNet 進(jìn)行訓(xùn)練的圖像識(shí)別模型,因此這里對(duì)應(yīng)地用 torchvision.models.alexnet 函數(shù)來(lái)創(chuàng)建 model 對(duì)象。
由于現(xiàn)在是要執(zhí)行推理任務(wù)而不是做訓(xùn)練,因此 pretrained=FALSE
model.classifier[6] = torch.nn.Linear(model.classifier[6].in_features, 2) |
這行代碼是至關(guān)重要的,首先在這行代碼上方加入“print(model.classifier)”,執(zhí)行后會(huì)看到以下的數(shù)據(jù)結(jié)構(gòu):
Sequential( (0):Dropout(p=0.5, inplace=False) (1):Linear(in_features=9216, out_features=4096, bias=True) (2):ReLU(inplace=True) (3):Dropout(p=0.5, inplace=False) (4):Linear(in_features=4096, out_features=4096, bias=True) (5):ReLU(inplace=True) (6):Linear(in_features=4096, out_features=1000, bias=True) ) |
這是 torchvision 里為 AlexNet 神經(jīng)網(wǎng)絡(luò)所預(yù)先定義的圖像分類功能的結(jié)構(gòu),其中 classifier[6].out_features 是模型最終的輸出數(shù)量,也就是分類數(shù)量。
因?yàn)?AlexNet 這個(gè)“深度神經(jīng)網(wǎng)絡(luò)”的鼻祖,是在 2012 年 ILSVRC 競(jìng)賽中以 1000 分類的 ImageNet 數(shù)據(jù)集作為測(cè)試標(biāo),一舉拔得頭籌而開(kāi)創(chuàng)“深度學(xué)習(xí)”新時(shí)代,這個(gè) 1000 分類的 AlexNet 圖像分類模型便成為這個(gè)領(lǐng)域的經(jīng)典之作,因此在標(biāo)準(zhǔn)的 AlexNet 模型中就保留“1000”這個(gè)數(shù)字作為基準(zhǔn)。
在這個(gè)避障應(yīng)用中只使用“free”與“blockerd”兩個(gè)分類,因此需將 classifier[6] 的輸出類別數(shù)量調(diào)整為 2。請(qǐng)?jiān)诖a下方加入“print(model.classifier[6])”指令,打印出修改后的內(nèi)容,會(huì)看到“out_features”的值已經(jīng)變成 2。
注意:每種神經(jīng)網(wǎng)絡(luò)的處理方式是不同的,必須根據(jù)PyTorch的定義進(jìn)行調(diào)整。
接下去三行代碼就是將模型文件加載進(jìn)來(lái),然后存到 CUDA 設(shè)備去,相對(duì)直觀:
model.load_state_dict(torch.load('best_model.pth')) device = torch.device('cuda') model = model.to(device) |
2. 圖像格式轉(zhuǎn)換與正規(guī)化處理:
這幾乎是所有視覺(jué)類深度學(xué)習(xí)應(yīng)用中不可或缺的步驟,比較繁瑣的部分是不同神經(jīng)網(wǎng)絡(luò)存在細(xì)節(jié)上的差異,不過(guò)總的來(lái)說(shuō)都脫離不了以下部分:
(1) 顏色空間轉(zhuǎn)換:所有神經(jīng)網(wǎng)絡(luò)都有自己定義的顏色空間格式,這里的 AlexNet 接受 RGB 數(shù)據(jù),而 CSI 攝像頭的格式為 BGR,這樣就必須進(jìn)行格式轉(zhuǎn)換。這部分的處理幾乎都使用 OpenCV、numpy、PIL 這些強(qiáng)大的圖像處理庫(kù)就可以,下面這行代碼就是執(zhí)行這個(gè)功能。
x = cv2.cvtColor(x, cv2.COLOR_BGR2RGB) |
(2) 張量順序轉(zhuǎn)換:將 HWC 順序轉(zhuǎn)換成 CHW 順序,下面指令就是執(zhí)行順序調(diào)整:
x = x.transpose((2, 0, 1)) |
(3) 正規(guī)化(normalization)處理:透過(guò)減去數(shù)據(jù)對(duì)應(yīng)維度的統(tǒng)計(jì)平均值,消除公共部分以凸顯個(gè)體之間的差異和特征的一種平穩(wěn)的分布計(jì)算。下面使用到的 [0.485, 0.456, 0.406]、[0.229, 0.224, 0.225] 兩組數(shù)據(jù),是業(yè)界經(jīng)過(guò)公認(rèn)的經(jīng)驗(yàn)數(shù)據(jù)。
mean = 255.0 * np.array([0.485, 0.456, 0.406]) stdev = 255.0 * np.array([0.229, 0.224, 0.225]) |
以上就是針對(duì)讀入圖像與模型之間對(duì)應(yīng)的一些轉(zhuǎn)換與計(jì)算的過(guò)程。
3. 創(chuàng)建控制元件并與攝像頭進(jìn)行關(guān)聯(lián):
這里使用的 traitlets、IPython.display、ipwidgets.wiegets 與 jetbot 的 Camera 庫(kù),在前面的文章里都說(shuō)明過(guò),比較重要的代碼如下:
(1) blocked_slider:用于顯示所獲取圖像是“blocked”類的幾率
blocked_slider = widgets.FloatSlider(description='blocked', min=0.0, max=1.0, orientation='vertical') |
(2)speed_slider:用于調(diào)整 Jetbot 小車行進(jìn)速度比
speed_slider = widgets.FloatSlider(description='speed', min=0.0, max=0.5, value=0.0, step=0.01, orientation='horizontal') |
(3)camera_link:將攝像頭獲取圖像與 image 變量進(jìn)行關(guān)聯(lián),并執(zhí)行格式轉(zhuǎn)換,才能在下方“display”指令之后,將攝像頭圖像動(dòng)態(tài)地在 Jupyter 里顯示。
camera_link = traitlets.dlink((camera, 'value'), (image, 'value'), transform=bgr8_to_jpeg) |
執(zhí)行這個(gè)階段代碼之后,下面應(yīng)該會(huì)出現(xiàn)如下圖左方的顯示框,試著在鏡頭前晃動(dòng)手,看看畫(huà)面內(nèi)容是否產(chǎn)生變化?顯示框右邊與下方分別出現(xiàn)“blocked”與“speed”兩個(gè)滑塊,就是前面代碼所建立的小工具。
由于后面會(huì)使用到這個(gè) “speed” 滑塊對(duì) Jetbot 進(jìn)行速度調(diào)整,并且我們也希望能實(shí)時(shí)觀察到攝像頭的畫(huà)面,因此建議用鼠標(biāo)在畫(huà)面上點(diǎn)擊右鍵,點(diǎn)選上圖右方 “Create New View for Output” 去創(chuàng)建另一個(gè)獨(dú)立輸出框,然后進(jìn)行位置調(diào)整如下圖,這樣就方便后續(xù)的操作。
4. 將控制元件與網(wǎng)絡(luò)模型、機(jī)電控制進(jìn)行結(jié)合:
這是整個(gè)應(yīng)用中最核心的整合與計(jì)算過(guò)程,雖然代碼量不多,但信息量卻非常大,現(xiàn)在將這部分切割成幾個(gè)小塊來(lái)進(jìn)行說(shuō)明。
(1) 獲取圖像進(jìn)行識(shí)別:
def update(change): x = change['new'] x= preprocess(x) y= model(x) 。。。 update({'new': camera.value}) |
這里首先定義 “update(change)”,在最下方用”update()”進(jìn)行調(diào)用。
在“update({'new': camera.value})” 里使用{key:value}對(duì)的方式,將 camera.value 圖像內(nèi)容透過(guò) change['new'] 傳給 x 變量;
將 x 變量傳入進(jìn)行前面定義的 preprocess() 格式轉(zhuǎn)換與正規(guī)化處理;
y 是 model(x) 推理計(jì)算所得出來(lái)“blocked”與“free”兩個(gè)類的個(gè)別置信度,例如為[-0.9425, 0.4077];
(2) 將置信度轉(zhuǎn)換成[0,1]范圍的值:
y= F.softmax(y, dim=1) prob_blocked= float(y.flatten()[0]) blocked_slider.value= prob_blocked |
這里調(diào)用 torch.nn.functional.softmax 函數(shù),將所有類置信度的總和調(diào)整為1,如此一來(lái)前面的[-0.9425, 0.4077]就轉(zhuǎn)換成[0.2058, 0.7942];
作為行進(jìn)的決策判斷,我們只要在兩個(gè)類別中挑選任何一個(gè)都可以,這里的代碼以“blocked”類幾率值作為判斷的依據(jù),因此取 float(y.flatten()[0]) 的值,如果改用“free”的幾率,就取 float(y.flatten()[1]) 的值。
然后將這個(gè)值同時(shí)也傳給 blocked_slider.value,現(xiàn)在看看前面輸出的 blocked 滑塊的值是否跟著產(chǎn)生變化!
(3)用 prob_blocked 值控制 Jetbot 行進(jìn):
ifprob_blocked < 0.5: robot.forward(speed_slider.value) else: robot.left(speed_slider.value) |
這里設(shè)定以 0.5 的幾率值為上限,當(dāng) prob_blocked < 0.5 時(shí)就前進(jìn),否則就原地左轉(zhuǎn),當(dāng)然您也可以改成往右轉(zhuǎn)。
Jetbot 的行進(jìn)速度由“speed_slider.value”變量所控制,這個(gè)數(shù)值得透過(guò)前面輸出畫(huà)面的 “speed” 滑塊去調(diào)整速度,最高值可以到 0.5,這是前面創(chuàng)建滑塊時(shí)就定義的。
執(zhí)行到這里的時(shí)候,正常狀況應(yīng)該如下:
攝像頭傳回的畫(huà)面是實(shí)時(shí)更新;
“blocked”滑塊固定在某個(gè)值;
現(xiàn)在調(diào)整“speed”滑塊的值并不會(huì)讓電機(jī)開(kāi)始轉(zhuǎn)動(dòng)。
現(xiàn)在可以將 Jetbot 小車放到您安排的執(zhí)行場(chǎng)地上,在執(zhí)行下一個(gè)步驟之前,建議透過(guò)“speed”滑塊將速度控制在 0.25 以下,避免啟動(dòng)后造成 Jetbot 小車爆沖。
5. 啟動(dòng)攝像頭的動(dòng)態(tài)關(guān)聯(lián):
這里其實(shí)就只有下面這一道指令:
camera.observe(update, names='value') |
這是由 jetbot 所提供的函數(shù),將 camera.value 與前面定義的 update(change) 進(jìn)行動(dòng)態(tài)連接上,現(xiàn)在 Jetbot 小車就應(yīng)該開(kāi)始行動(dòng)了,攝像頭里的畫(huà)面也在不停更新,右方“blocked”滑塊的值也在不斷跳動(dòng)(更新),現(xiàn)在試著調(diào)整“speed”滑塊,是不是就能改變行進(jìn)的速度了!
好了,現(xiàn)在就可以看看您 Jetbot 小車的避障功能執(zhí)行的如何?如果想停止工作的話,就繼續(xù)往下執(zhí)行暫停的指令就可以。
最后需要說(shuō)明的,假如您的避障功能執(zhí)行的不是太好,例如無(wú)法順利識(shí)別一些障礙物或坑洞的話,通常是因?yàn)槟臏y(cè)試場(chǎng)所或者使用的攝像頭規(guī)格(廣角),與原廠提供的模型數(shù)據(jù)有比較大的差異,甚至場(chǎng)地明暗度也會(huì)有影響,如果測(cè)試效果不如預(yù)期的話,就得自己重頭收集數(shù)據(jù)并重新訓(xùn)練模型,這才是解決問(wèn)題的根本之道。
原文標(biāo)題:NVIDIA Jetson Nano 2GB 系列文章(49):智能避撞之現(xiàn)場(chǎng)演示
文章出處:【微信公眾號(hào):NVIDIA英偉達(dá)企業(yè)解決方案】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
審核編輯:湯梓紅
-
NVIDIA
+關(guān)注
關(guān)注
14文章
4994瀏覽量
103153 -
鏡頭
+關(guān)注
關(guān)注
2文章
506瀏覽量
25656 -
智能識(shí)別
+關(guān)注
關(guān)注
0文章
201瀏覽量
18128
原文標(biāo)題:NVIDIA Jetson Nano 2GB 系列文章(49):智能避撞之現(xiàn)場(chǎng)演示
文章出處:【微信號(hào):NVIDIA-Enterprise,微信公眾號(hào):NVIDIA英偉達(dá)企業(yè)解決方案】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論