1. 自動(dòng)駕駛感知算法及AidLux相關(guān)方案介紹
1.1自動(dòng)駕駛
自動(dòng)駕駛汽車,又稱無人駕駛車、電腦駕駛車、無人車、自駕車,是一種需要駕駛員輔助駕駛或者完全不需要操控的車輛。作為自動(dòng)化載具,自動(dòng)駕駛汽車可以不需要人類操作即能感知環(huán)境及導(dǎo)航。
1.2 自動(dòng)駕駛系統(tǒng)的組成部分
1.2.1 環(huán)境感知系統(tǒng)
1.2.2 決策系統(tǒng)
1.3 安卓端部署平臺(tái)AidLux
AidLux平臺(tái)可快速部署在ARM64位智能設(shè)備上,手機(jī)也能變成邊緣計(jì)算設(shè)備,當(dāng)服務(wù)器使用、做測(cè)試、做練習(xí)。后續(xù)換設(shè)備落實(shí)實(shí)際項(xiàng)目,直接遷移,不需要重復(fù)開發(fā)。
2. 基于YOLOP的全景感知系統(tǒng)講解與實(shí)戰(zhàn)應(yīng)用
2.1 YOLOP算法介紹
YOLOP能同時(shí)處理目標(biāo)檢測(cè)、可行駛區(qū)域分割、車道線檢測(cè)三個(gè)感知任務(wù),并速度優(yōu)異,保持較好精度進(jìn)行工作,代碼開源。它是華中科技大學(xué)--王興剛團(tuán)隊(duì)在全景感知方面提出的模型。
模型結(jié)構(gòu)包括1個(gè)encoder+3個(gè)分離的decoder,其中encoder包括backbone和neck,3個(gè)decoder分別完成車輛檢測(cè)、車道線檢測(cè)、可行駛區(qū)域分割任務(wù)
encoder:主干網(wǎng)絡(luò)(CSPDarknet),和neck結(jié)構(gòu)(SPP+FPN)
decoder:1個(gè)檢測(cè)頭和2個(gè)分割,兩個(gè)分割頭都是使用FPN的最底層特征圖(w/8,h/8,256)作為輸入,進(jìn)行3次最近鄰上采樣,最終輸出(W, H, 2)的特征圖。
2.2 AutoDL云端YOLOP模型訓(xùn)練
2.2.1 下載BDD100K數(shù)據(jù)集
2.2.2 將項(xiàng)目和數(shù)據(jù)集上傳到AutoDL平臺(tái)
2.2.3 訓(xùn)練YOLOP
執(zhí)行命令:pip install -r requirements.txt
安裝依賴包
單GPU訓(xùn)練:
python tools/train.py
多GPU訓(xùn)練:
python -m torch.distributed.launch --nproc_per_node=N tools/train.py #N:GPU數(shù)量
推理
python tools/demo.py --weights weights/End-to-end.pth --source inference/videos
3. 智能預(yù)警在AidLux平臺(tái)上的部署與應(yīng)用
3.1 YOLOP模型onnx轉(zhuǎn)換部署
1.使用課程代碼轉(zhuǎn)換為onnx
執(zhí)行命令:
python export_onnx.py --height 640 --width 640
執(zhí)行完成后,會(huì)在weights文件夾下生成轉(zhuǎn)換成功的onnx模型
onnx轉(zhuǎn)換核心api:
x = torch.onnx.export(model, # 網(wǎng)絡(luò)模型
torch.randn(1, 3, 224, 224), # 用于確定輸入大小和類型,其中的值可以是隨機(jī)的。
export_onnx_file, # 輸出onnx的名稱
verbose=False, # 是否以字符串的形式顯示計(jì)算圖
input_names=["input"], # 輸入節(jié)點(diǎn)的名稱,可以是一個(gè)list
output_names=["output"], # 輸出節(jié)點(diǎn)的名稱
opset_version=10, # onnx支持使用的operator set
do_constant_folding=True, # 是否壓縮變量
# 設(shè)置動(dòng)態(tài)維度, 此處指明Input節(jié)點(diǎn)的第0維度可變,命名為batch_size
dynamic_axes={"input":{0: "batch_size", 2:"h", 3: "w"}, "output": {0: "batch_size"}}
)
2. AidLux模型轉(zhuǎn)換工具-AIMO
AI Model Optimizer--AIMO, 是一個(gè)簡(jiǎn)單、快速、精度損失小的模型轉(zhuǎn)換平臺(tái)。
AIMO旨在幫助用戶能夠在邊緣端芯片上無精度損失的快速遷移、部署和運(yùn)行各種機(jī)器學(xué)習(xí)模型。
平臺(tái)地址:http://117.176.129.180:21115/
體驗(yàn)賬號(hào):AIMOTC001
賬號(hào)密碼:AIMOTC001
3.2 YOLOP模型在AidLux上部署和應(yīng)用
3.2.1 AidLux簡(jiǎn)介
AidLux是一個(gè)構(gòu)建在ARM硬件上,基于創(chuàng)新性跨Android/鴻蒙+Linux融合系統(tǒng)環(huán)境的智能物聯(lián)網(wǎng)(AIOT應(yīng)用開發(fā)和部署平臺(tái))。
AidLux軟件使用非常方便,可以安裝在手機(jī)、PAD、ARM開發(fā)板等邊緣設(shè)備上,而且使用AidLux開發(fā)的過程中,既能支持在邊緣設(shè)備的本機(jī)開發(fā),也支持通過web瀏覽器訪問邊緣端桌面進(jìn)行開發(fā)。
各大應(yīng)用商城都能下載AidLux,在手機(jī)商城搜索、下載安裝AidLux。
3.2.2 連接AidLux
?將手機(jī)的wifi網(wǎng)絡(luò)和電腦的網(wǎng)絡(luò)連接到一起,打開安裝好的手機(jī)上的AidLux軟件,點(diǎn)擊第一排第二個(gè)Cloud_ip。手機(jī)界面上會(huì)跳出可以在電腦上登錄的IP地址,在電腦的瀏覽器上,隨便出入一個(gè)IP,即可將手機(jī)的系統(tǒng)投影到電腦上,連接上后就可以利用手機(jī)的算力進(jìn)行模型推理了。
3.2.3 上傳項(xiàng)目代碼到AidLux
1.點(diǎn)擊文件瀏覽器,打開文件管理頁面
2.找到home文件夾,并雙擊進(jìn)入此文件夾
3.點(diǎn)擊右上角往上的箭頭;再選擇Folder,將前面YOLOP的文件夾上傳到home文件夾內(nèi)。(也可以直接將文件夾拖進(jìn)目錄下。)
3.2.4 安裝環(huán)境
1.打開終端,切換到項(xiàng)目目錄
2.執(zhí)行命令:pip install -r requirements.txt安裝依賴環(huán)境
3.安裝pytorch、torchvision、onnxruntime
pip install torch == 1.8.1==0.9.0 -i https://pypi.mirrors.ustc.edu.cn/simple/
pip install onnxruntime -i https://pypi.mirrors.ustc.edu.cn.simple/
4 運(yùn)行demo.py
驗(yàn)證推理效果,執(zhí)行命令:
python tools/demo.py --source inference/images
3.3 智能預(yù)警系統(tǒng)代碼實(shí)戰(zhàn)
智能預(yù)警系統(tǒng)包含3個(gè)任務(wù):
目標(biāo)檢測(cè),可行駛區(qū)域檢測(cè),車道線檢測(cè)
傳感器:前視相機(jī)
目標(biāo)檢測(cè)任務(wù):檢測(cè)車輛
可行駛區(qū)域檢測(cè):主要是hi檢測(cè)可以行駛的區(qū)域,為自動(dòng)駕駛提供路徑規(guī)劃輔助
車道線檢測(cè):一種環(huán)境感知應(yīng)用,目的是通過車載相機(jī)或激光雷達(dá)來檢測(cè)車道線。
智能預(yù)警流程
1.輸入
讀取視頻圖像作為輸入,圖像尺寸1920*1080
2.預(yù)處理
2.1 將輸入尺寸1920*1080 resize + padding到640*640
2.2 歸一化
2.3 640*640*3 --> 1*3*640*640
3. 使用onnx模型進(jìn)行推理
讀取模型-->準(zhǔn)備數(shù)據(jù)-->推理
得到det_out,da_seg_out, ll_seg_out,shape分別為:(1,n,6) (1,2,640,640) (1,2,640,640)
4.后處理
4.1 將檢測(cè)結(jié)果,可行駛區(qū)域檢測(cè)結(jié)果,車道線檢測(cè)結(jié)果,合并到一張圖像上,分別用不同的顏色標(biāo)記出來
4.2 將檢測(cè)的幀數(shù),幀率,車輛數(shù)等信息顯示在圖像上
5.輸出
獲取最終融合的圖像,并保存成視頻,圖像尺寸、幀率、編碼是原視頻尺寸、幀率和編碼。
完整的預(yù)警代碼
import cv2
import time
import torch
import numpy as np
import onnxruntime as ort
from PIL import Image, ImageDraw, ImageFont
from lib.core.general import non_max_suppression
onnx_path = "weights/yolop-640-640.onnx"
def resize_unscale(img, new_shape=(640, 640), color=114):
shape = img.shape[:2] # current shape [height, width]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
canvas = np.zeros((new_shape[0], new_shape[1], 3))
canvas.fill(color)
# Scale ratio (new / old) new_shape(h,w)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
# Compute padding
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r)) # w,h
new_unpad_w = new_unpad[0]
new_unpad_h = new_unpad[1]
pad_w, pad_h = new_shape[1] - new_unpad_w, new_shape[0] - new_unpad_h # wh padding
dw = pad_w // 2 # divide padding into 2 sides
dh = pad_h // 2
if shape[::-1] != new_unpad: # resize
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_AREA)
canvas[dh:dh + new_unpad_h, dw:dw + new_unpad_w, :] = img
return canvas, r, dw, dh, new_unpad_w, new_unpad_h # (dw,dh)
def cv2AddChineseText(img, text, position, textColor=(0, 0, 255), textSize=10):
if (isinstance(img, np.ndarray)): # 判斷是否OpenCV圖片類型
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 創(chuàng)建一個(gè)可以在給定圖像上繪圖的對(duì)象
draw = ImageDraw.Draw(img)
# 字體的格式
fontStyle = ImageFont.truetype(
"simsun.ttc", textSize, encoding="utf-8")
# 繪制文本
draw.text(position, text, textColor, font=fontStyle)
# 轉(zhuǎn)換回OpenCV格式
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
def infer(ori_img, img, r, dw, dh, new_unpad_w, new_unpad_h):
ort_session = ort.InferenceSession(onnx_path)
t0 = time.time()
# inference: (1,n,6) (1,2,640,640) (1,2,640,640)
det_out, da_seg_out, ll_seg_out = ort_session.run(
['det_out', 'drive_area_seg', 'lane_line_seg'],
input_feed={"images": img}
)
seconds = time.time() - t0
fps = "%.2f fps" %(1 / seconds) # 幀率
det_out = torch.from_numpy(det_out).float()
boxes = non_max_suppression(det_out)[0] # [n,6] [x1,y1,x2,y2,conf,cls]
boxes = boxes.cpu().numpy().astype(np.float32)
if boxes.shape[0] == 0:
print("no bounding boxes detected.")
return None
# scale coords to original size.
boxes[:, 0] -= dw
boxes[:, 1] -= dh
boxes[:, 2] -= dw
boxes[:, 3] -= dh
boxes[:, :4] /= r
print(f"detect {boxes.shape[0]} bounding boxes.")
img_det = ori_img[:, :, ::-1].copy()
for i in range(boxes.shape[0]):
x1, y1, x2, y2, conf, label = boxes[i]
x1, y1, x2, y2, label = int(x1), int(y1), int(x2), int(y2), int(label)
img_det = cv2.rectangle(img_det, (x1, y1), (x2, y2), (0, 255, 0), 2, 2)
# select da & ll segment area.
da_seg_out = da_seg_out[:, :, dh:dh + new_unpad_h, dw:dw + new_unpad_w]
ll_seg_out = ll_seg_out[:, :, dh:dh + new_unpad_h, dw:dw + new_unpad_w]
da_seg_mask = np.argmax(da_seg_out, axis=1)[0]
ll_seg_mask = np.argmax(ll_seg_out, axis=1)[0]
color_area = np.zeros((new_unpad_h, new_unpad_w, 3), dtype=np.uint8)
color_area[da_seg_mask == 1] = [0, 255, 0]
color_area[ll_seg_mask == 1] = [0, 0, 255]
color_seg = color_area
return img_det, boxes, color_seg, fps
def main(source, save_path):
cap = cv2.VideoCapture(source)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 獲取視頻的寬度
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 獲取視頻的高度
fps = cap.get(cv2.CAP_PROP_FPS) #
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
#fourcc = int(cap.get(cv2.CAP_PROP_FOURCC)) # 視頻的編碼
#定義視頻對(duì)象輸出
writer = cv2.VideoWriter(save_path, fourcc, fps, (width, height))
# 檢查是否導(dǎo)入視頻成功
if not cap.isOpened():
print("視頻無法打開")
exit()
frame_id = 0
while True:
ret, frame = cap.read()
if not ret:
print("視頻推理完畢...")
break
frame_id += 1
# if frame_id % 3 != 0:
# continue
canvas, r, dw, dh, new_unpad_w, new_unpad_h = resize_unscale(frame, (640, 640))
img = canvas.copy().astype(np.float32) # (3,640,640) RGB
img /= 255.0
img[:, :, 0] -= 0.485
img[:, :, 1] -= 0.456
img[:, :, 2] -= 0.406
img[:, :, 0] /= 0.229
img[:, :, 1] /= 0.224
img[:, :, 2] /= 0.225
img = img.transpose(2, 0, 1)
[]() img = np.expand_dims(img, 0) # (1, 3,640,640)
# 推理
img_det, boxes, color_seg, fps = infer(frame, img, r, dw, dh, new_unpad_w, new_unpad_h)
if img_det is None:
continue
color_mask = np.mean(color_seg, 2)
img_merge = canvas[dh:dh + new_unpad_h, dw:dw + new_unpad_w, :]
# merge: resize to original size
img_merge[color_mask != 0] = \
img_merge[color_mask != 0] * 0.5 + color_seg[color_mask != 0] * 0.5
img_merge = img_merge.astype(np.uint8)
img_merge = cv2.resize(img_merge, (width, height),
interpolation=cv2.INTER_LINEAR)
img_merge = cv2AddChineseText(img_merge, f'幀數(shù):{frame_id} 幀率:{fps} 前方共有 {boxes.shape[0]} 輛車...',
(100, 50), textColor=(0, 0, 255), textSize=30)
img_merge = cv2AddChineseText(img_merge, '前方綠色區(qū)域?yàn)榭尚旭倕^(qū)域,紅色為檢出的車道線...',
(100, 100), textColor=(0, 0, 255), textSize=30)
for i in range(boxes.shape[0]):
x1, y1, x2, y2, conf, label = boxes[i]
x1, y1, x2, y2, label = int(x1), int(y1), int(x2), int(y2), int(label)
img_merge = cv2.rectangle(img_merge, (x1, y1), (x2, y2), (0, 255, 0), 2, 2)
# cv2.imshow('img_merge', img_merge)
# cv2.waitKey(0)
writer.write(img_merge)
cap.release() # 釋放攝像頭
writer.release() # 可以實(shí)現(xiàn)預(yù)覽
cv2.destroyAllWindows()
if __name__=="__main__":
source = 'inference/videos/1.mp4'
save_path = '/home/AidLux_Course/YOLOP/inference/output/test.mp4'
main(source, save_path)
代碼運(yùn)行結(jié)果:
4.總結(jié)文章來源:http://www.zghlxwxcb.cn/news/detail-541245.html
感謝成都阿加犀公司舉辦的訓(xùn)練營(yíng)課程,讓筆者能夠?qū)W習(xí)自動(dòng)駕駛的基礎(chǔ)知識(shí),以及自動(dòng)駕駛算法在移動(dòng)端的部署。文章來源地址http://www.zghlxwxcb.cn/news/detail-541245.html
到了這里,關(guān)于基于AidLux的自動(dòng)駕駛智能預(yù)警應(yīng)用方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!