• 方案介紹
    • 一、前言
    • 二、主控板介紹
    • 三、搭建開發(fā)環(huán)境
    • 四、安裝Qt開發(fā)環(huán)境
    • 五、開發(fā)板初步測試
    • 五、測溫項目開發(fā)
    • 六、總結(jié)
  • 附件下載
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

基于香橙派 AIpro設(shè)計的醫(yī)院人臉紅外測溫系統(tǒng)(從0開始開發(fā))

17小時前
491
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

更多詳細資料請聯(lián)系.docx

共1個文件

一、前言

在公共衛(wèi)生事件頻發(fā)的當下,尤其是在全球性疫情爆發(fā)后,國家對公共空間的健康監(jiān)測和管理提出了更高的要求。醫(yī)院、疾病防控中心和發(fā)熱門診作為疫情防控的第一線,需要高效且精準地對進出人員進行健康篩查,以防止病毒傳播,保障醫(yī)護人員及患者的安全。傳統(tǒng)的手動體溫檢測方式不僅效率低下,而且存在交叉感染的風(fēng)險,開發(fā)一種能夠自動、快速、準確地進行人體溫度監(jiān)測與身份識別的系統(tǒng)顯得非常的重要。

當前文章會完整的介紹,如何采用香橙派AIpro設(shè)計出一套醫(yī)院人臉紅外測溫系統(tǒng)。香橙派AIpro是一款高性價比的邊緣計算設(shè)備,搭載了昇騰 AI 處理器,可提供 8TOPS INT8 的計算能力,能夠運行Ubuntu 22.04操作系統(tǒng),這為部署復(fù)雜的深度學(xué)習(xí)算法提供了硬件基礎(chǔ)。本系統(tǒng)利用OpenCV和SSD算法模型進行人臉檢測,通過各方面的模型訓(xùn)練,能確保在復(fù)雜光線和遮擋條件下仍能有效識別個體;結(jié)合紅外測溫技術(shù),可以非接觸式地測量額頭溫度,避免了傳統(tǒng)接觸式測文章溫可能帶來的衛(wèi)生問題。

考慮到環(huán)境因素對測溫結(jié)果的影響,系統(tǒng)還配備了溫濕度傳感器,以實時監(jiān)測并校準測溫數(shù)據(jù)。為了實現(xiàn)數(shù)據(jù)的實時監(jiān)控與分析,系統(tǒng)通過MQTT協(xié)議將收集到的信息上傳至華為云物聯(lián)網(wǎng)云平臺,便于遠程監(jiān)控和數(shù)據(jù)分析,有助于疫情趨勢的預(yù)測和資源的合理調(diào)配。

本項目整體提供了一個智能化、自動化的人臉識別與體溫監(jiān)測解決方案,以提高公共衛(wèi)生領(lǐng)域的響應(yīng)速度和防控效率,減少人力資源的投入,同時降低潛在的感染風(fēng)險,為構(gòu)建安全健康的醫(yī)療環(huán)境貢獻力量。

本項目在完成最終的功能開發(fā)前,會先單個完成模塊的功能開發(fā),實現(xiàn)了單個模塊功能之后,最終在整體合在一起實現(xiàn)最終的項目開發(fā)。

整體項目會從搭建環(huán)境開始, 一步一步實現(xiàn)最終的項目效果。

image-20240714173609690

下面的開發(fā)出來的最終人臉檢測設(shè)備最終設(shè)計效果:

image-20240713185744390

image-20240713184816028

二、主控板介紹

香橙派 AIpro開發(fā)板是香橙派聯(lián)合華為精心打造的高性能 AI 開發(fā)板,搭載了昇騰 AI 處理器,可提供 8TOPS INT8 的計算能力,內(nèi)存提供了 8GB 和 16GB兩種版本??梢詫崿F(xiàn)圖像、視頻等多種數(shù)據(jù)分析與推理計算,可廣泛用于教育、機器人、無人機等場景。

下面是香橙派 AIpro開發(fā)板的配置說明:

模塊 規(guī)格
昇騰 AI 處理器 4 核 64 位 Arm 處理器 + AI 處理器
AI 算力 半精度(FP16):4 TFLOPS
整數(shù)精度(INT8):8 TOPS
內(nèi)存 類型:LPDDR4X 容量:8GB 或 16GB
存儲 板載 32MB 的 SPI Flash
Micro SD 卡插槽
eMMC 插座:可外接 eMMC 模塊
M.2 M-Key 接口:可接 2280 規(guī)格的 NVMe SSD 或 SATA SSD
以太網(wǎng) 支持 10/100/1000Mbps
板載 PHY 芯片:RTL8211F
Wi-Fi+藍牙 支持 2.4G 和 5G 雙頻 WIFI
BT4.2
模組:歐智通 62221BUUC
USB 2 個 USB3.0 Host 接口
1 個 Type-C 接口(只支持 USB3.0,不支持 USB2.0
攝像頭 2 個 MIPI CSI 2 Lane 接口
顯示 2 個 HDMI 接口 1 個 MIPI DSI 2 Lane 接口
音頻 一個 3.5mm 耳機孔,支持音頻輸入輸出
2 個 HDMI 音頻輸出
40 pin 擴展口 用于擴展 UART、I2C、SPI、PWMGPIO 等接口
按鍵 一個復(fù)位鍵,一個關(guān)機鍵,一個升級按鍵
撥碼開關(guān) 2 個撥碼開關(guān):用于控制 SD 卡、eMMC 和 SSD 啟動選項
電源 支持 Type-C 供電,20V PD-65W 適配器
LED 燈 一個電源指示燈和一個軟件可控指示燈
風(fēng)扇接口 4pin,0.8mm 間距,用于接 12V 風(fēng)扇,支持 PWM 控制
電池接口 2pin,2.54mm 間距,用于接 3 串電池,支持快充
調(diào)試串口 Micro USB 接口的調(diào)試串口
支持的操作系統(tǒng) Ubuntu 22.04 和 openEuler 22.03
外觀規(guī)格介紹 產(chǎn)品尺寸:107*68mm 重量:82g

下面是香橙派 AIpro開發(fā)板的功能模塊介紹:

image-20240713184019802

image-20240713233418646

三、搭建開發(fā)環(huán)境

3.1 準備需要的配件

(1)準備一張至少32G的TFT卡,用來燒寫系統(tǒng)。

(2)準備一個讀卡器,方便插入TFT卡,好方便插入到電腦上拷貝系統(tǒng)

(3)香橙派 AIpro 主板一個

(4)一根網(wǎng)線(方便插路由器上與香橙派 AIpro 連接)

(5)一根type-C的電源線 + 電源插頭(3A電流),這個主板買回來是帶了電源的。 也可以用自己Android手機的數(shù)據(jù)線就行,拿手機充電器供電,因為目前Android手機電源線都是都是type-C 也支持快充的,電流也是滿足需求的。

(6)一個USB攝像頭,用于后續(xù)項目開發(fā)里獲取周圍的實時圖像,識別人臉。 (項目開發(fā)需要使用)

(7)一個串口協(xié)議的紅外測溫傳感器,用于后續(xù)項目開發(fā)里測量體溫。(項目開發(fā)需要使用)

(8)一個外放音箱,支持3.5mm的耳機插孔,方便后續(xù)項目開發(fā)里播放語音提示。(項目開發(fā)需要使用)

(9)一塊顯示屏(這個不是必須的,可以直接Windows遠程桌面訪問系統(tǒng),對前期開發(fā)來說沒有任何影響,只要做成最終的產(chǎn)品才需要配屏幕)。

3.2 開發(fā)板實物圖

拿回來的香橙派 AIpro 開發(fā)板實物是這樣的。

image-20240713193221825

image-20240713193411984

3.3 下載開發(fā)板資料

拿到板子之后,第一件事肯定是先去官網(wǎng)下載板子對應(yīng)的相關(guān)的資料。比如:用戶手冊、系統(tǒng)鏡像、原理圖、開發(fā)工具什么的。

官網(wǎng)地址:http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-AIpro.html

翻到下面,找到資料下載地址,直接下載就行,下載會跳轉(zhuǎn)到網(wǎng)盤。

image-20240713193822577

系統(tǒng)鏡像我選擇的 ubuntu22.04。

image-20240713193900175

資料下載下來之后,可以看到有一份官方的說明文檔,指導(dǎo)板子的基本使用。如何燒寫系統(tǒng),如何啟動系統(tǒng)等等。

image-20240713194235872

3.4 下載系統(tǒng)燒寫工具

鏈接:https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Atlas%20200I%20DK%20A2/DevKit/tools/latest/Ascend-devkit-imager_latest_win-x86_64.exe

下載下來之后,直接雙擊正常安裝,安裝好之后打開的界面如下。 (選擇本地制作)

image-20240713194924116

然后將 TF卡通過讀卡器插到電腦上,準備燒寫系統(tǒng)(就算有些電腦自帶了TF卡的插槽也建議用USB讀卡器,這個電腦自帶的TF卡槽燒寫系統(tǒng)無法啟動)。 TF卡的容量至少要32G,最好是64G。

image-20240713195607403

選擇要燒寫的系統(tǒng)鏡像文件(就是剛剛通過網(wǎng)盤下載的ubuntu系統(tǒng)鏡像)。

image-20240713195815959

然后點擊燒錄鏡像。

image-20240713200023532

彈出提示框,選擇確認。

image-20240713200052668

然后可以看到,系統(tǒng)正在燒寫中了,精心等待即可。

image-20240713200113792

燒錄成功之后,會彈窗提示彈出SD卡。

image-20240713210720997

TF卡從電腦上彈出,拔掉就行了。

image-20240713210748529

image-20240713193457415

3.5 設(shè)置開發(fā)板啟動模式

香橙派 AIpro開發(fā)板支持從 TF 卡、eMMC 和 SSD(支持 NVMe SSD 和 SATA SSD)啟動。

具體從哪個設(shè)備啟動是由開發(fā)板背面的兩個撥碼(BOOT1 和 BOOT2)開關(guān)來控制的。

image-20240713211059551

BOOT1 和 BOOT2 兩個撥碼開關(guān)都支持左右兩種設(shè)置狀態(tài),所以總共有 4 種設(shè)置狀態(tài),開發(fā)板目前只使用了其中的三種。不同的設(shè)置狀態(tài)對應(yīng)的啟動設(shè)備如下表所示:

image-20240713211152112

BOOT1 和 BOOT2 兩個撥碼開關(guān) 全部撥到靠右的位置就可以選擇從TF卡啟動了。

3.6 啟動系統(tǒng)

【1】將燒寫好的TF卡插在板子上。

image-20240713211425933

【2】插好網(wǎng)線。

網(wǎng)線一端接開發(fā)板的網(wǎng)口,一端接路由器,自己的筆記本電腦也是接的同一個路由器,讓板子與電腦在同一個局域網(wǎng)內(nèi),方便接下來遠程登錄開發(fā)板的系統(tǒng)。

image-20240713211553314

image-20240713213052792

【3】插好電源

板子是沒有電源開關(guān)的,電源線插好之后,系統(tǒng)就啟動了。剛啟動的時候風(fēng)扇的聲音會比較大,等待幾秒就正常了。

按下開發(fā)板左上角的PWR_OFF可以關(guān)閉系統(tǒng),點擊旁邊的RESET可以重啟系統(tǒng)。

image-20240713211543200

3.7 SSH遠程登錄系統(tǒng)

系統(tǒng)啟動之后,會自動請求路由器分配IP地址,我們只需要登錄到開發(fā)板連接的路由器后臺,就可以看到新接入的設(shè)備。

我的用是小米路由器。直接在瀏覽器里輸入:192.168.31.1 即可進入到路由器的后臺終端。

從下面圖片里可以看到,香橙派 AIpro 已經(jīng)分配到IP地址了,192.168.31.136。

image-20240713223645033

為了方便遠程登錄到系統(tǒng)終端,可以下載安裝一個FinalShell 軟件。

下載地址:https://www.hostbuf.com/t/988.html

軟件安裝打開后,建立一個新的SSH鏈接,具體看下圖的操作。

image-20240713224703844

這里面的主機IP地址就是從路由器后臺看到的,分配給香橙派 AIpro開發(fā)板的IP地址。 端口號是固定的22,這是SSH協(xié)議的固定端口。

用戶名是root,密碼是:Mind@123 這是燒寫的香橙派 AIpro系統(tǒng)固定的用戶名和密碼,也就是系統(tǒng)內(nèi)置的。

雙擊剛才建立好的鏈接,就可以登錄到系統(tǒng)終端。 進去終端之后基本上就可以進行正常的開發(fā)了。

image-20240713225229527

3.8 安裝xdrp工具

為了方便圖形化方式開發(fā),可以使用windows系統(tǒng)通過遠程桌面登錄香橙派 AIpro,就可以看到界面了,不過需要先安裝工具。

進入到香橙派 AIpro終端之后,輸入安裝命令:

sudo apt-get install xrdp

按下回車之后,會彈出確認窗口。輸入 y之后,按下回車,繼續(xù)安裝。

下面是完整的命令安裝過程: 按順序執(zhí)行就行了。

#安裝xrdp
sudo apt-get install xrdp
#安裝vnc4server
sudo apt-get install vnc4server tightvncserver
#安裝xubuntu-desktop
sudo apt-get install xubuntu-desktop
#向xsession中寫入xfce4-session
echo “xfce4-session” >~/.xsession
#開啟xrdp服務(wù)
sudo service xrdp restart

注意,如果之后斷電了,遠程桌面無法鏈接上??梢韵刃遁dxrdp,再重新安裝即可。

sudo apt-get remove --purge xrdp

3.9 Window遠程登錄

在windows上打開運行命令的窗口,輸入mstsc來打開遠程桌面。

image-20240713225908482

輸入mstsc,點擊確定。

image-20240713225941156

彈出窗口后,填入IP地址(這就是你的香橙派 AIpro 開發(fā)板的IP地址),點擊連接。

image-20240713230025713

正常登錄之后,就可以看到遠程桌面的界面了。

image-20240713230144585

輸入賬號和密碼。

用戶名是root,密碼是:Mind@123 這是燒寫的香橙派 AIpro系統(tǒng)固定的用戶名和密碼,也就是系統(tǒng)內(nèi)置的。

登錄之后的界面:

image-20240713231948302

image-20240713232200871

好了。接下來就可以進行項目的正式開發(fā)了。

3.10 取消自動休眠

Ubuntu桌面鏡像會自動休眠,輸入以下指令禁用休眠。

sudo systemctl status sleep.target
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

四、安裝Qt開發(fā)環(huán)境

因為最終的項目需要使用界面,我的項目準備采用Qt進行開發(fā),這一章節(jié)進行安裝Qt開發(fā)環(huán)境。

4.1 安裝qtcreator

在命令行終端分別輸入以下命令安裝qtcreator:

root@orangepiaipro:~# sudo apt-get update
root@orangepiaipro:~# sudo apt-get install qtcreator
root@orangepiaipro:~# sudo apt-get install qtmultimedia5-dev
root@orangepiaipro:~# sudo apt-get install libqt5serialport5-dev

image-20240714000307615

4.2 啟動qtcreator

安裝好之后,就可以看到Qt軟件了,點擊即可打開。

image-20240714000353422

這是啟動之后的效果。

image-20240714000438029

4.3 配置編譯器

默認安裝Qt之后,編譯器是配置錯誤,無法正常使用。

【1】打開設(shè)置頁面

image-20240714001025326

【2】選擇編譯器套件

image-20240714001100905

【3】配置編譯器套件。

將編譯器改為GCC

image-20240714000644413

4.4 新建工程Qt環(huán)境

【1】新建工程

image-20240714001302590

image-20240714001327984

【2】設(shè)置工程名稱和路徑(自己單獨建立一個文件夾存放工程)

image-20240714001351170

image-20240714001442561

【3】選擇繼承的基類

image-20240714001509921

image-20240714001548171

【4】選擇編譯器套件

image-20240714001610071

【5】創(chuàng)建完成

image-20240714001639213

這就是創(chuàng)建好的工程。

image-20240714001706696

【6】點擊左下角綠色三角形編譯運行。

下面是正常運行的效果,已經(jīng)彈窗窗口,說明Qt的環(huán)境已經(jīng)OK了。

image-20240714001757927

image-20240714001912150

五、開發(fā)板初步測試

為了了解下開發(fā)板本身的性能,先采用開發(fā)板開發(fā)一些小項目測試測試效果。

現(xiàn)在系統(tǒng)根目錄創(chuàng)建一個work目錄,方便存放接下來的項目文件。

image-20240714131053362

5.1 項目1:開發(fā)一個基于HTTP協(xié)議的網(wǎng)絡(luò)攝像頭

【1】項目介紹

本項目主要采用C語言開發(fā),實現(xiàn)了一個網(wǎng)絡(luò)攝像頭項目,在橙派 AIpro開發(fā)板上實現(xiàn)了一個HTTP服務(wù)器,,處理瀏覽器的請求,當瀏覽器訪問過來時,就將本地采集到的攝像頭畫面發(fā)送給瀏覽器,與瀏覽器建立長連接通信,直接傳輸JPG圖片,實現(xiàn)攝像頭畫面實時顯示效果。支持登錄頁面,做了一個賬號登錄界面,訪問服務(wù)器之后需要輸入賬號密碼才可以正常進入服務(wù)器查看共享的畫面。如果分享個攝像頭畫面,那是非常的方便的,想要查看分享的攝像頭畫面只需要瀏覽器里輸入服務(wù)器的IP地址登錄進去就可以看畫面了。

通過本項目的測試,可以了解到USB攝像頭的讀取效果,網(wǎng)絡(luò)傳輸?shù)男Ч?為后續(xù)的其他項目開發(fā)做一個參考。

基于香橙派 AIpro實現(xiàn)的網(wǎng)絡(luò)攝像頭項目-效果

【2】編寫項目代碼

項目開發(fā),代碼編寫先在Windows下進行,開發(fā)完畢,再拷貝到橙派 AIpro開發(fā)板上。

這是在Windows下開發(fā)好的項目代碼:

image-20240714131824132

【3】上傳項目代碼

打開FinalShell終端,可以直接將開發(fā)好的項目源碼,整個目錄上傳到香橙派 AIpro系統(tǒng)。

image-20240714131909001

通過FinalShell終端可以很方便的將香橙派 AIpro系統(tǒng)文件下載到本地,也可以將本地的文件很方便的上傳上去。在開發(fā)項目的階段是很方便的。

image-20240714132128503

【4】插入USB攝像頭

將USB攝像頭插入到開發(fā)板的USB口,然后ls /dev/video* 查看攝像頭的設(shè)備節(jié)點,確定攝像頭是否識別成功。

image-20240714135456599

【4】編譯運行項目

項目里已經(jīng)構(gòu)建好了Makefile文件,直接make就可以編譯。

image-20240714135115279

編譯之后,運行項目。

(base) root@orangepiaipro:~/work/http_camera# make
(base) root@orangepiaipro:~/work/http_camera# ./http_app 
./server <server_port> </dev/videoX>
(base) root@orangepiaipro:~/work/http_camera# ./http_app 666 /dev/video0 

運行命令的含義:./http_app 666 /dev/video0 666表示服務(wù)器的端口號。 /dev/video0是攝像頭的端口號。

【5】瀏覽器訪問

在自己電腦瀏覽器地址欄里輸入:http://192.168.31.136:666

就可以看到登錄頁面。

image-20240714135834059

登錄成功之后,可以看到攝像頭的實時畫面。

image-20240714135805025

5.2 項目2: 基于華為云設(shè)計的智能家居控制系統(tǒng)

【1】項目介紹

基于香橙派 AIpro開發(fā)板設(shè)計的智能家居控制系統(tǒng),通過MQTT協(xié)議連接華為云物聯(lián)網(wǎng)云平臺;通過DHT11傳感器讀取環(huán)境溫濕度,將數(shù)據(jù)上傳到華為云物聯(lián)網(wǎng)云平臺。在華為云云平臺上也可以遠程控制硬件端連接的LED燈,控制3種顏色顯示。

通過本項目的完整開發(fā)測試,可以掌握香橙派的GPIO口的基本使用以及網(wǎng)絡(luò)的測試。為后續(xù)的其他項目開發(fā)做一個參考測試。

【2】安裝wiringPi

(1)安裝 wiringOP 前,請先確保 Linux 系統(tǒng)中存在/etc/orangepi-release 這個配置文件,里面的內(nèi)容為:BOARD=orangepiaipro。

(base) root@orangepiaipro:~/work/# cat /etc/orangepi-release
BOARD=orangepiaipro

(2)如果 Linux 中沒有/etc/orangepi-release 這個配置文件,可以使用下面的命令創(chuàng)建一個。

(base) root@orangepiaipro:~/work/# echo "BOARD=orangepiaipro" | sudo tee /etc/orangepi-release

(3)下載 wiringOP 的代碼。

(base) root@orangepiaipro:~/work/# sudo apt-get update
(base) root@orangepiaipro:~/work/# sudo apt-get install -y git
(base) root@orangepiaipro:~/work/# git clone https://github.com/orangepi-xunlong/wiringOP.git -b next

(4)然后編譯安裝 wiringOP。

(base) root@orangepiaipro:~/work/# sudo apt-get install -y gcc make build-essential
(base) root@orangepiaipro:~/work/# cd wiringOP
(base) root@orangepiaipro:~/work/wiringOP# sudo ./build clean
(base) root@orangepiaipro:~/work/wiringOP# sudo ./build

(5)編譯完之后,可以看到生成的文件

image-20240714144232399

image-20240714144341068

查看GPIO口信息。

(base) root@orangepiaipro:~/work/wiringOP# cd gpio/
(base) root@orangepiaipro:~/work/wiringOP/gpio# ./gpio readall
       
 +------+-----+----------+--------+---+  AI PRO  +---+--------+----------+-----+------+
 | GPIO | wPi |   Name   |  Mode  | V | Physical | V |  Mode  | Name     | wPi | GPIO |
 +------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+
 |      |     |     3.3V |        |   |  1 || 2  |   |        | 5V       |     |      |
 |   76 |   0 |     SDA7 |    OFF | 0 |  3 || 4  |   |        | 5V       |     |      |
 |   75 |   1 |     SCL7 |    OFF | 0 |  5 || 6  |   |        | GND      |     |      |
 |  226 |   2 | GPIO7_02 |    OFF | 0 |  7 || 8  | 0 | OFF    | UTXD0    | 3   | 14   |
 |      |     |      GND |        |   |  9 || 10 | 0 | OFF    | URXD0    | 4   | 15   |
 |   82 |   5 | GPIO2_18 |    OFF | 0 | 11 || 12 | 0 | OFF    | GPIO7_03 | 6   | 227  |
 |   38 |   7 | GPIO1_06 |     IN | 1 | 13 || 14 |   |        | GND      |     |      |
 |   79 |   8 | GPIO2_15 |     IN | 1 | 15 || 16 | 1 | IN     | GPIO2_16 | 9   | 80   |
 |      |     |     3.3V |        |   | 17 || 18 | 0 | IN     | GPIO0_25 | 10  | 25   |
 |   91 |  11 | SPI0_SD0 |    OFF | 0 | 19 || 20 |   |        | GND      |     |      |
 |   92 |  12 | SPI0_SDI |    OFF | 0 | 21 || 22 | 1 | IN     | GPIO0_02 | 13  | 2    |
 |   89 |  14 | SPI0_CLK |    OFF | 0 | 23 || 24 | 0 | OFF    | SPI0_CS  | 15  | 90   |
 |      |     |      GND |        |   | 25 || 26 | 0 | IN     | GPIO2_19 | 16  | 83   |
 |      |     |     SDA6 |        |   | 27 || 28 |   |        | SCL6     |     |      |
 |  231 |  17 |    URXD7 |    OFF | 0 | 29 || 30 |   |        | GND      |     |      |
 |   84 |  18 | GPIO2_20 |     IN | 0 | 31 || 32 | 0 | IN     | GPIO1_01 | 19  | 35   |
 |  128 |  20 | GPIO4_00 |     IN | 1 | 33 || 34 |   |        | GND      |     |      |
 |  228 |  21 | GPIO7_04 |    OFF | 0 | 35 || 36 | 0 | OFF    | GPIO2_17 | 22  | 81   |
 |    3 |  23 | GPIO0_03 |     IN | 1 | 37 || 38 | 0 | IN     | GPIO7_06 | 24  | 230  |
 |      |     |      GND |        |   | 39 || 40 | 0 | OFF    | GPIO7_05 | 25  | 229  |
 +------+-----+----------+--------+---+----++----+---+--------+----------+-----+------+
 | GPIO | wPi |   Name   |  Mode  | V | Physical | V |  Mode  | Name     | wPi | GPIO |
 +------+-----+----------+--------+---+  AI PRO  +---+--------+----------+-----+------+

可以通過命令行測試GPIO口:

(base) root@orangepiaipro:~/work# ./gpio mode 2 out   設(shè)置 wPi  為2的這個IO口為輸出模式
(base) root@orangepiaipro:~/work# ./gpio write 2 0    設(shè)置 wPi  為2的這個IO口輸出0 (低電平)
(base) root@orangepiaipro:~/work# ./gpio write 2 1    設(shè)置 wPi  為2的這個IO口輸出1 (高電平

【3】GPIO口布局

image-20240714145819245

【4】控制LED燈

image-20240714154247320

通過香橙派 AIpro實現(xiàn)三色燈的控制(GPIO口控制效果)

編寫的測試代碼:

#include <stdio.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <string.h>
/*控制繼電器高低電平亮燈*/

#define LEDG 0
#define LEDB 1
#define LEDR 2


int main()
{
	wiringPiSetup();  //置引腳編號方式為wiringPi編碼
	pinMode(LEDG,OUTPUT);
	pinMode(LEDB,OUTPUT);
	pinMode(LEDR,OUTPUT);


	while(1)
	{
		//全部關(guān)閉
		digitalWrite(LEDG,LOW);
		digitalWrite(LEDB,LOW);
		digitalWrite(LEDR,LOW);
		//亮藍色
		digitalWrite(LEDG,HIGH);
		sleep(1);
		
		
		//全部關(guān)閉
		digitalWrite(LEDG,LOW);
		digitalWrite(LEDB,LOW);
		digitalWrite(LEDR,LOW);
		//亮綠色
		digitalWrite(LEDB,HIGH);
		sleep(1);
		
		
		//全部關(guān)閉
		digitalWrite(LEDG,LOW);
		digitalWrite(LEDB,LOW);
		digitalWrite(LEDR,LOW);
		//亮紅色
		digitalWrite(LEDR,HIGH);
		sleep(1);
		
		
		
	}
	
     return 0;
}

編譯代碼:

(base) root@orangepiaipro:~/work# gcc led.c -lwiringPi

運行代碼:

(base) root@orangepiaipro:~/work# ./a.out 

運行效果:

image-20240714154950089

【5】DHT11溫濕度傳感器數(shù)據(jù)讀取

image-20240714155252689

代碼:

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
 
//編譯:gcc -Wall -o dht11 dht11.c -lwiringPi -o app
 
 
typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;
 
#define HIGH_TIME 32
 
int pinNumber = 5;
uint32 databuf;
  
uint8 readSensorData(void)
{
    uint8 crc; 
    uint8 i;
  
    pinMode(pinNumber, OUTPUT); // set mode to output
    digitalWrite(pinNumber, 0); // output a high level 
    delay(25);
    digitalWrite(pinNumber, 1); // output a low level 
    pinMode(pinNumber, INPUT); // set mode to input
    pullUpDnControl(pinNumber, PUD_UP);
 
    delayMicroseconds(27);
    if (digitalRead(pinNumber) == 0) //SENSOR ANS
    {
        while (!digitalRead(pinNumber))
            ; //wait to high
 
        for (i = 0; i < 32; i++)
        {
            while (digitalRead(pinNumber))
                ; //data clock start
            while (!digitalRead(pinNumber))
                ; //data start
            delayMicroseconds(HIGH_TIME);
            databuf *= 2;
            if (digitalRead(pinNumber) == 1) //1
            {
                databuf++;
            }
        }
 
        for (i = 0; i < 8; i++)
        {
            while (digitalRead(pinNumber))
                ; //data clock start
            while (!digitalRead(pinNumber))
                ; //data start
            delayMicroseconds(HIGH_TIME);
            crc *= 2;  
            if (digitalRead(pinNumber) == 1) //1
            {
                crc++;
            }
        }
        return 1;
    }
    else
    {
        return 0;
    }
}
  
int main(void)
{
    printf("PIN:%dn", pinNumber);
 
    wiringPiSetup();  //置引腳編號方式為wiringPi編碼
   
    pinMode(pinNumber, OUTPUT); // set mode to output
    digitalWrite(pinNumber, 1); // output a high level 
 
    printf("Starting...n");
    while (1) 
    {
        pinMode(pinNumber, OUTPUT); // set mode to output
        digitalWrite(pinNumber, 1); // output a high level 
        delay(3000);
        if (readSensorData())
        {
            printf("Sensor data read ok!n");
            printf("RH:%d.%dn", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); 
            printf("TMP:%d.%dn", (databuf >> 8) & 0xff, databuf & 0xff);
            databuf = 0;
        }
        else
        {
            printf("Sensor dosent ans!n");
            databuf = 0;
        }
    }
    return 0;
}

編譯代碼:

(base) root@orangepiaipro:~/work# gcc dht11.c -lwiringPi

運行代碼:

(base) root@orangepiaipro:~/work# ./a.out 
PIN:5
Starting...
RH:68.4
TMP:30.4
RH:68.5
TMP:30.2
RH:68.2
TMP:30.4
RH:68.1
TMP:30.3
RH:68.1
TMP:30.6
RH:68.1
TMP:30.4

實物圖:

image-20240714155731504

【6】注冊華為云設(shè)備

華為云物聯(lián)網(wǎng)平臺的整體就不再詳細展示了,可以直接看視頻。

B站的視頻鏈接:https://www.bilibili.com/video/BV1mr421c75S

手把手講解華為云物聯(lián)網(wǎng)云平臺的使用以及應(yīng)用側(cè)的開發(fā)(2024最新版)

(1)注冊產(chǎn)品

image-20240714162050418

(2)注冊設(shè)備

image-20240714160920091

(3)創(chuàng)建命令

image-20240714161446789

image-20240714162113037

(4)得到MQTT三元組

IP地址:117.78.5.125
端口號:1883

ClientId   6693872aa559ef6226685350_dev1_0_0_2024071408
Username   6693872aa559ef6226685350_dev1
Password   8ce1b26a6fac2c52402d2911a9a951efe6026f71041037a963bebdd4a099190f

訂閱主題:$oc/devices/6693872aa559ef6226685350_dev1/sys/messages/down

發(fā)布主題:$oc/devices/6693872aa559ef6226685350_dev1/sys/properties/report
發(fā)布數(shù)據(jù):{"services": [{"service_id": "stm32","properties":{"DHT11_T":23,"DHT11_H":80}}]}

【7】編寫整體項目

接下來就編寫代碼,連接華為云物聯(lián)網(wǎng)平臺,完成數(shù)據(jù)上傳。 將采集的溫濕度數(shù)據(jù)上傳到華為云物聯(lián)網(wǎng)云平臺。 同時支持在華為云物聯(lián)網(wǎng)平臺下發(fā)命令遠程控制設(shè)備端的LED燈。

代碼是采用純C語言編寫,實現(xiàn)了MQTT協(xié)議,完成了與物聯(lián)網(wǎng)云平臺交互。

關(guān)于MQTT協(xié)議的整體編寫過程,可以直接看視頻:https://www.bilibili.com/video/BV1BN4y1Y7cf

從0開始編寫MQTT協(xié)議代碼連接標準MQTT服務(wù)器(精講MQTT協(xié)議)

(1)這是寫好的項目代碼

image-20240714163848551

image-20240714164238391

完整的代碼:

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <poll.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include "mqtt.h"
#include "main.h"
#include <netdb.h>
#include <stdio.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <string.h>


/*控制繼電器高低電平亮燈*/

#define LEDG 0
#define LEDB 1
#define LEDR 2



//服務(wù)器IP
#define SERVER_IP "117.78.5.125"
#define SERVER_PORT 1883 //端口號

//MQTT三元組
#define ClientID "6693872aa559ef6226685350_dev1_0_0_2024071408"
#define Username "6693872aa559ef6226685350_dev1"
#define Password "8ce1b26a6fac2c52402d2911a9a951efe6026f71041037a963bebdd4a099190f"http://密文 

//訂閱主題:
#define SET_TOPIC  "$oc/devices/6693872aa559ef6226685350_dev1/sys/messages/down"http://訂閱
//發(fā)布主題:
#define POST_TOPIC "$oc/devices/6693872aa559ef6226685350_dev1/sys/properties/report"http://發(fā)布


char mqtt_message[1024*1024];//上報數(shù)據(jù)緩存區(qū)
char request_id[100];
char mqtt_cmd_message[100];
char mqtt_cmd_data[100];

int sockfd;
/*獲取平臺下發(fā)數(shù)據(jù)*/
void *pth_work_func(void *arg)
{
     char buff[1024];
     int size=0;
     int i=0;
     while(1)
    {
        size=Client_GetData(buff);
		printf("size=%drn",size);
        if(size<0)break;
        for(i=0;i<size;i++)
        {
            printf("%c ",buff[i]);
        }
		buff[size]='?';
		if(size>5)
		{
			printf("%srn",buff+5);
			
			if(strstr((char*)&buff[5],"sys/commands/request_id="))
			{
				char *p=NULL;
				p=strstr((char*)&buff[5],"request_id");
				if(p)
				{        
					//解析數(shù)據(jù)
					//$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/request_id=5f359b5c-542f-460e-9f51-85e82150ff4a{"service_id":"gps"} 
					strncpy(request_id,p,47);      
				}
				
				//上報數(shù)據(jù)
				sprintf(mqtt_cmd_message,"{"result_code":0,"response_name":"COMMAND_RESPONSE","paras":{"result":"success"}}");
				
				sprintf(mqtt_cmd_data,"$oc/devices/6693872aa559ef6226685350_dev1/sys/commands/response/%s",
				request_id);
				
				MQTT_PublishData(mqtt_cmd_data,mqtt_cmd_message,0);
				
				printf("應(yīng)答-發(fā)布主題:%srn",mqtt_cmd_data);
				printf("應(yīng)答-發(fā)布數(shù)據(jù):%srn",mqtt_cmd_message);
			} 
			if(strstr((char*)&buff[5],""LED_SW":1")) 
			{
				//全部關(guān)閉
				digitalWrite(LEDG,LOW);
				digitalWrite(LEDB,LOW);
				digitalWrite(LEDR,LOW);
				//亮藍色
				digitalWrite(LEDG,HIGH);
			}
			if(strstr((char*)&buff[5],""LED_SW":2")) 
			{
				//全部關(guān)閉
				digitalWrite(LEDG,LOW);
				digitalWrite(LEDB,LOW);
				digitalWrite(LEDR,LOW);
				//亮綠色
				digitalWrite(LEDB,HIGH);
			} 
			if(strstr((char*)&buff[5],""LED_SW":3")) 
			{
				//全部關(guān)閉
				digitalWrite(LEDG,LOW);
				digitalWrite(LEDB,LOW);
				digitalWrite(LEDR,LOW);
				//亮紅色
				digitalWrite(LEDR,HIGH);
			} 
			if(strstr((char*)&buff[5],""LED_SW":0")) 
			{
				//全部關(guān)閉
				digitalWrite(LEDG,LOW);
				digitalWrite(LEDB,LOW);
				digitalWrite(LEDR,LOW);

			} 
		}
        printf("rn");
    }
}
 
 
/*信號處理函數(shù)*/
 void signal_func(int sig)
{
	//printf("捕獲的信號:%dn",sig);
	if(sig==SIGALRM)
	{
        MQTT_SentHeart();//心跳包
		alarm(5);
	}
}



typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;
 
#define HIGH_TIME 32
 
int pinNumber = 5;
uint32 databuf;
  
uint8 readSensorData(void)
{
    uint8 crc; 
    uint8 i;
  
    pinMode(pinNumber, OUTPUT); // set mode to output
    digitalWrite(pinNumber, 0); // output a high level 
    delay(25);
    digitalWrite(pinNumber, 1); // output a low level 
    pinMode(pinNumber, INPUT); // set mode to input
    pullUpDnControl(pinNumber, PUD_UP);
 
    delayMicroseconds(27);
    if (digitalRead(pinNumber) == 0) //SENSOR ANS
    {
        while (!digitalRead(pinNumber))
            ; //wait to high
 
        for (i = 0; i < 32; i++)
        {
            while (digitalRead(pinNumber))
                ; //data clock start
            while (!digitalRead(pinNumber))
                ; //data start
            delayMicroseconds(HIGH_TIME);
            databuf *= 2;
            if (digitalRead(pinNumber) == 1) //1
            {
                databuf++;
            }
        }
 
        for (i = 0; i < 8; i++)
        {
            while (digitalRead(pinNumber))
                ; //data clock start
            while (!digitalRead(pinNumber))
                ; //data start
            delayMicroseconds(HIGH_TIME);
            crc *= 2;  
            if (digitalRead(pinNumber) == 1) //1
            {
                crc++;
            }
        }
        return 1;
    }
    else
    {
        return 0;
    }
}
  


unsigned int DHT11_T;// 	環(huán)境溫度
unsigned int DHT11_H;//	環(huán)境濕度


int main()
{
	
	wiringPiSetup();  //置引腳編號方式為wiringPi編碼
	pinMode(LEDG,OUTPUT);
	pinMode(LEDB,OUTPUT);
	pinMode(LEDR,OUTPUT);
	
	
	//DHT11溫濕度初始化
	pinMode(pinNumber, OUTPUT); // set mode to output
    digitalWrite(pinNumber, 1); // output a high level 
	
    int stat;
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd==-1)
    {
        printf("網(wǎng)絡(luò)套接字打開失敗n");
        return 0;
    }
    signal(SIGPIPE,SIG_IGN);/*忽略SIGPIPE信號*/
    signal(SIGALRM,signal_func);/*鬧鐘信號*/
	
	
    /*連接服務(wù)器*/
     struct sockaddr_in addr;
     addr.sin_family=AF_INET;//IPV4
     addr.sin_port=htons(SERVER_PORT);/*端口號*/
     addr.sin_addr.s_addr=inet_addr(SERVER_IP);//inet_addr(ip);//服務(wù)器IP
     if(connect(sockfd, (struct sockaddr *)&addr,sizeof(struct sockaddr_in))==0)
     {
        printf("server connect okn");
		 
		MQTT_Init();
		  
         while(1)
         {
           
            /*登錄服務(wù)器*/
            if(MQTT_Connect(ClientID,Username,Password)==0)
            {
                break;
            }
            sleep(1);
            printf("server connect ....n");
         }
        printf("MQTT_Connect OKrn");
        //訂閱物聯(lián)網(wǎng)平臺數(shù)據(jù)
        stat=MQTT_SubscribeTopic(SET_TOPIC,1,1);
        if(stat)
        {
            close(sockfd);
            printf("MQTT_SubscribeTopic ERRORrn");  
            exit(0);
        }
		printf("MQTT_SubscribeTopic okrn");
        /*創(chuàng)建線程*/
        pthread_t id;
        pthread_create(&id, NULL,pth_work_func,NULL);
        pthread_detach(id);//設(shè)置分離屬性
       
	   
	   //發(fā)送心跳包
	   // alarm(5);//鬧鐘函數(shù),時間到達會產(chǎn)生SIGALRM信號

        while(1)
        {
			//讀取DHT11溫濕度數(shù)據(jù)
			pinMode(pinNumber, OUTPUT); // set mode to output
			digitalWrite(pinNumber, 1); // output a high level 
			delay(3000);
			if (readSensorData())
			{
				printf("DHT11 Sensor data read ok!n");
				printf("RH:%d.%dn", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); 
				printf("TMP:%d.%dn", (databuf >> 8) & 0xff, databuf & 0xff);
				
				
				//溫度整數(shù)部分
				DHT11_H=((databuf >> 24) & 0xff);
				printf("DHT11_T:%drn",DHT11_T);
				//濕度整數(shù)部分
				DHT11_T=((databuf >> 8) & 0xff);
				printf("DHT11_H:%drn",DHT11_H);
				
				databuf = 0;
				
			}
			else
			{
				printf("Sensor dosent ans!n");
				databuf = 0;
			}
		

			
			//組合傳感器狀態(tài)數(shù)據(jù)
			sprintf(mqtt_message,"{"services": [{"service_id": "stm32","properties":{"DHT11_T":%d,"DHT11_H":%d}}]}",DHT11_T,DHT11_H);//溫度
            
			//上報數(shù)據(jù)
			MQTT_PublishData(POST_TOPIC,mqtt_message,0);
			printf("MQTT_PublishData....rn");
            sleep(2);
        }
     }
}

(2)這是編譯運行后的效果

image-20240714163715479

(3)在華為云物聯(lián)網(wǎng)平臺后臺,可以看到設(shè)備已經(jīng)在線了,同時也實時收到設(shè)備端上傳的數(shù)據(jù)。

image-20240714164311508

(4)下發(fā)命令測試。 通過命令下發(fā)控制設(shè)備端的LED燈。

image-20240714164457620

5.3 項目3:OpenCV+卷積神經(jīng)網(wǎng)絡(luò)實現(xiàn)人臉識別

本項目通過OpenCV加載訓(xùn)練好的SSD模型,實現(xiàn)人臉檢測,能夠在圖像中找到并標記出人臉的位置和置信度。

通過本項目,可以驗證整個系統(tǒng)的算法運行速度。為后續(xù)的項目開發(fā)做參考。

(1)安裝python (燒寫的系統(tǒng)本身自帶了完整的Python環(huán)境,可以不需要安裝,如果沒有才需要安裝)

sudo apt update
sudo apt install python3

(2)編寫代碼加載模型識別人臉

import cv2
import numpy as np
import time

prototxt_path = "./deploy.prototxt.txt"
model_path = "./res10_300x300_ssd_iter_140000_fp16.caffemodel"
image_path = "6.jpg"

# 加載模型
model = cv2.dnn.readNetFromCaffe(prototxt_path, model_path)

# 讀取圖像
image = cv2.imread(image_path)
h, w = image.shape[:2]

# 準備模型輸入的 blob
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0), swapRB=False)

# 設(shè)置 blob 作為模型的輸入
model.setInput(blob)

# 進行推斷并獲取輸出
start_time = time.time()
output = model.forward()
end_time = time.time()

# 遍歷檢測結(jié)果
font_scale = 1.0
for i in range(output.shape[2]):
    confidence = output[0, 0, i, 2]

    # 通過置信度閾值過濾弱檢測結(jié)果
    if confidence > 0.5:
        box = output[0, 0, i, 3:7] * np.array([w, h, w, h])
        (start_x, start_y, end_x, end_y) = box.astype("int")

        # 繪制邊界框和置信度
        cv2.rectangle(image, (start_x, start_y), (end_x, end_y), (255, 0, 0), 2)
        text = f"{confidence * 100:.2f}%"
        cv2.putText(image, text, (start_x, start_y - 10), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 0, 0), 2)

# 顯示和保存帶有檢測結(jié)果的圖像
cv2.imwrite("beauty_detected.jpg", image)

# 輸出識別耗時
print(f"識別耗時:{end_time - start_time:.3f} 秒")

(3)運行效果

image-20240714171723373

(4)將圖片下載下來打開

image-20240714171744817

5.4 項目4:OpenCV+YOLOv3實現(xiàn)目標檢測

本項目通過OpenCV加載YOLOV3官方的模型,實現(xiàn)目標。

通過本項目,可以驗證整個系統(tǒng)的算法運行速度。為后續(xù)的項目開發(fā)做參考。

實現(xiàn)代碼:

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>

#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>

using namespace std;
using namespace cv;
using namespace cv::dnn;
void image_detection();


String yolo_cfg = "./yolov3.cfg";
String yolo_model = "./yolov3.weights";

int main(int argc, char** argv)
{
	image_detection();
}

void image_detection() {
	//加載網(wǎng)絡(luò)模型
	Net net = readNetFromDarknet(yolo_cfg, yolo_model);

	//net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
	net.setPreferableTarget(DNN_TARGET_CPU);
	std::vector<String> outNames = net.getUnconnectedOutLayersNames();
	for (int i = 0; i < outNames.size(); i++) {
		printf("output layer name : %sn", outNames[i].c_str());
	}

	vector<string> classNamesVec;
	ifstream classNamesFile("./coco.names");
	if (classNamesFile.is_open())
	{
		string className = "";
		while (std::getline(classNamesFile, className))
			classNamesVec.push_back(className);
	}

	// 加載圖像 
	Mat frame = imread("6.jpg");
	Mat inputBlob = blobFromImage(frame, 1 / 255.F, Size(416, 416), Scalar(), true, false);
	net.setInput(inputBlob);

	// 檢測
	std::vector<Mat> outs;
	net.forward(outs, outNames);
	vector<double> layersTimings;
	double freq = getTickFrequency() / 1000;
	double time = net.getPerfProfile(layersTimings) / freq;
	ostringstream ss;
	ss << "detection time: " << time << " ms";
	putText(frame, ss.str(), Point(20, 20), 0, 0.5, Scalar(0, 0, 255));
	vector<Rect> boxes;
	vector<int> classIds;
	vector<float> confidences;
	for (size_t i = 0; i < outs.size(); ++i)
	{
		// Network produces output blob with a shape NxC where N is a number of
		// detected objects and C is a number of classes + 4 where the first 4
		// numbers are [center_x, center_y, width, height]
		float* data = (float*)outs[i].data;
		for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)
		{
			Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
			Point classIdPoint;
			double confidence;
			minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
			if (confidence > 0.5)
			{
				int centerX = (int)(data[0] * frame.cols);
				int centerY = (int)(data[1] * frame.rows);
				int width = (int)(data[2] * frame.cols);
				int height = (int)(data[3] * frame.rows);
				int left = centerX - width / 2;
				int top = centerY - height / 2;

				classIds.push_back(classIdPoint.x);
				confidences.push_back((float)confidence);
				boxes.push_back(Rect(left, top, width, height));
			}
		}
	}

	vector<int> indices;
	NMSBoxes(boxes, confidences, 0.5, 0.2, indices);
	for (size_t i = 0; i < indices.size(); ++i)
	{
		int idx = indices[i];
		Rect box = boxes[idx];
		String className = classNamesVec[classIds[idx]];
		putText(frame, className.c_str(), box.tl(), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255, 0, 0), 2, 8);
		rectangle(frame, box, Scalar(0, 0, 255), 2, 8, 0);
	}

     # 保存結(jié)果圖片
    cv2.imwrite('detections.jpg', frame)
	waitKey(0);
	return;
}

將識別的圖片結(jié)果拷貝下來,查看效果。

image-20240714180036886

五、測溫項目開發(fā)

5.1 外設(shè)模塊選型

需要用到的傳感器如下:

(1)LU90614非接觸式紅外測溫模塊(串口協(xié)議),用于測量體溫。

(2)DHT11溫濕度傳感器,用于測量環(huán)境的溫濕度。

(3)USB攝像頭,用于捕獲圖像,檢測人臉。

(4)一塊香橙派 AIpro主控板。

(5)一個三色LED燈,用于顯示檢測的體溫狀態(tài)。 紅、綠、藍 三種顏色。

5.2 整體的項目代碼

整體項目是采用Qt開發(fā)的,因為需要通過顯示屏展示界面,在界面上顯示人臉的識別效果,溫度測量效果等信息。

【1】技術(shù)實現(xiàn)方式說明

(1)這里面的LU90614非接觸式紅外測溫模塊 采用USB-TTL模塊接入系統(tǒng)的。在/dev目錄下的節(jié)點是ttyUSB0。沒有使用開發(fā)板本身的IO口。

(2)本項目是先在Windows下開發(fā)完成后,再上傳到香橙派 AIpro開發(fā)板運行,在香橙派 AIpro里安裝了Qt的開發(fā)環(huán)境。

(3)體溫傳感器的串口數(shù)據(jù)讀取,沒有采用Qt本身的串口接口,而是采用了標準Linux下的方式讀取串口數(shù)據(jù)。

(4)攝像頭的采集沒有采用Qt的內(nèi)置接口,而是采用了Linux下V4L2框架完成的圖像采集。

(5)MQTT協(xié)議沒有采用第三方庫,是自己基于Linux下的socket,從0開始編寫的。

(6)顯示屏采用HDMI接口的7寸顯示屏。作為整個項目的界面終端。

【2】攝像頭圖像采集代碼

下面是采集USB實時畫面的代碼。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>

#define WIDTH 640
#define HEIGHT 480

struct buffer {
    void *start;
    size_t length;
};

int pthread_run()
{
    int fd;
    struct v4l2_format fmt;
    struct v4l2_requestbuffers req;
    struct v4l2_buffer buf;
    enum v4l2_buf_type type;
    struct buffer *buffers;
    unsigned char *rgb888_buffer;
    
    // 打開攝像頭設(shè)備
    fd = open("/dev/video0", O_RDWR);
    if (fd == -1) {
        perror("打開/dev/video0失敗");
        return 1;
    }
    
    // 設(shè)置格式
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = WIDTH;
    fmt.fmt.pix.height = HEIGHT;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // 使用YUYV格式,常見于USB攝像頭
    fmt.fmt.pix.field = V4L2_FIELD_NONE;
    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
        perror("設(shè)置格式失敗");
        close(fd);
        return 1;
    }
    
    // 請求緩沖區(qū)
    memset(&req, 0, sizeof(req));
    req.count = 1; // 緩沖區(qū)數(shù)量
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
        perror("請求緩沖區(qū)失敗");
        close(fd);
        return 1;
    }
    
    // 分配并映射緩沖區(qū)
    buffers = calloc(req.count, sizeof(*buffers));
    if (!buffers) {
        perror("分配緩沖區(qū)內(nèi)存失敗");
        close(fd);
        return 1;
    }
    
    // 查詢緩沖區(qū)
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 0;
    if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
        perror("查詢緩沖區(qū)失敗");
        close(fd);
        return 1;
    }
    
    // 內(nèi)存映射
    buffers[0].length = buf.length;
    buffers[0].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
    if (buffers[0].start == MAP_FAILED) {
        perror("內(nèi)存映射失敗");
        close(fd);
        return 1;
    }
    
    // 開始流式傳輸
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        perror("開始流式傳輸失敗");
        close(fd);
        return 1;
    }
    
    // 捕獲循環(huán)(示例:捕獲一幀)
    while (1) {
        // 入隊緩沖區(qū)
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            perror("入隊緩沖區(qū)失敗");
            close(fd);
            return 1;
        }
        
        // 出隊緩沖區(qū)
        if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
            perror("出隊緩沖區(qū)失敗");
            close(fd);
            return 1;
        }
        
        // 處理幀(轉(zhuǎn)換為RGB888格式)
        // 示例:將YUYV轉(zhuǎn)換為RGB888
        rgb888_buffer = (unsigned char *)malloc(WIDTH * HEIGHT * 3);
        for (int i = 0, j = 0; i < WIDTH * HEIGHT * 2; i += 4, j += 6) {
            // YUYV到RGB888的簡化轉(zhuǎn)換(實際應(yīng)用中可能需要更復(fù)雜的算法)
            unsigned char Y0 = ((unsigned char *)buffers[0].start)[i + 0];
            unsigned char U = ((unsigned char *)buffers[0].start)[i + 1];
            unsigned char Y1 = ((unsigned char *)buffers[0].start)[i + 2];
            unsigned char V = ((unsigned char *)buffers[0].start)[i + 3];
            
            rgb888_buffer[j + 0] = Y0 + 1.402 * (V - 128); // 紅色分量
            rgb888_buffer[j + 1] = Y0 - 0.344 * (U - 128) - 0.714 * (V - 128); // 綠色分量
            rgb888_buffer[j + 2] = Y0 + 1.772 * (U - 128); // 藍色分量
            
            rgb888_buffer[j + 3] = Y1 + 1.402 * (V - 128); // 紅色分量
            rgb888_buffer[j + 4] = Y1 - 0.344 * (U - 128) - 0.714 * (V - 128); // 綠色分量
            rgb888_buffer[j + 5] = Y1 + 1.772 * (U - 128); // 藍色分量
        }
        
        // 使用rgb888_buffer進行進一步處理(如保存到文件、顯示)
        // 示例:保存到文件
        FILE *fp = fopen("frame.rgb", "wb");
        if (fp) {
            fwrite(rgb888_buffer, 1, WIDTH * HEIGHT * 3, fp);
            fclose(fp);
        }
        
        free(rgb888_buffer);
        break; // 示例中僅處理一幀,所以退出循環(huán)
    }
    
    // 停止流式傳輸
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
        perror("停止流式傳輸失敗");
        close(fd);
        return 1;
    }
    
    // 解除內(nèi)存映射
    munmap(buffers[0].start, buf.length);
    
    // 清理和關(guān)閉
    free(buffers);
    close(fd);
    
    return 0;
}

【3】體溫數(shù)據(jù)采集(串口)

下面是采集體溫傳感器的代碼。

image-20240714182058655

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/select.h>
#include <poll.h>

#define SERIAL_DEVICE "/dev/ttyUSB0"
#define BAUDRATE B9600

int temp_read_pthread() {
    int fd;
    char *sendbuf = "xFAxC5xBF"; // 發(fā)送體溫模式指令
    char recvbuf[8];
    struct termios options;
    struct pollfd pfd;
    int timeout = 1000; // 超時時間,單位毫秒

    // 打開串口設(shè)備文件
    if ((fd = open(SERIAL_DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) {
        perror("open serial port failed");
        exit(1);
    }

    // 設(shè)置串口參數(shù)
    tcgetattr(fd, &options);
    cfmakeraw(&options);
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~CSIZE;
    options.c_cflag |= CS8;
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_iflag &= ~(IXON | IXOFF | IXANY);
    options.c_oflag &= ~OPOST;
    cfsetispeed(&options, BAUDRATE);
    cfsetospeed(&options, BAUDRATE);
    tcsetattr(fd, TCSANOW, &options);

    // 初始化poll結(jié)構(gòu)體
    pfd.fd = fd;
    pfd.events = POLLIN;

    // 發(fā)送命令
    write(fd, sendbuf, 3);

    while (1) {
        // 使用poll等待數(shù)據(jù)
        if (poll(&pfd, 1, timeout) > 0) {
            int nread = read(fd, recvbuf, sizeof(recvbuf));
            if (nread > 0) {
                printf("Received data: ");
                for (int i = 0; i < nread; i++) {
                    printf("%02x ", recvbuf[i]);
                }
                printf("n");
            } else {
                printf("Read error: %sn", strerror(errno));
            }
        } else {
            printf("No data received in %d msn", timeout);
        }
    }

    close(fd);
    return 0;
}

【4】人臉識別圖像處理

下面是完成人臉識別處理的代碼。

#include "image_handle.h"
#pragma execution_character_set("utf-8")

//關(guān)閉線程
void ImageHandle::close()
{
    run_flag=0;
    this->quit();
    this->wait();
}



//線程執(zhí)行函數(shù)
void ImageHandle::run()
{
    QImage use_image;

    while(run_flag)
    {
        //如果沒有圖像可以處理
        if(start_run==0)
        {
            //休眠100毫秒
            msleep(100);
            continue;
        }
        //表示已經(jīng)處理過
        start_run=0;

        //表示開始處理圖像
        Handle_flag=1;

        //調(diào)用圖像處理算法 對 image  的圖像進行處理
        //1. 人臉識別
        opencv_face(m_image);

        //處理完畢之后
        //將圖像傳出去給UI界面顯示
        emit HandleSend(m_image);

        //處理完畢
        Handle_flag=0;
    }
}



//傳入待處理的圖片數(shù)據(jù)
void ImageHandle::SetImage(QImage &image)
{
    if(Handle_flag==0)
    {
        start_run=1; //表示有圖像可以處理了
        //保存待處理的原圖像
        m_image=image;
    }
}



void printMatInfo(const cv::Mat& mat)
{
    QTextStream out(stdout);
    out << "Type: " << mat.type() << endl;
    out << "Channels: " << mat.channels() << endl;
    out << "Size: " << mat.size().width << "x" << mat.size().height << endl;
    out << "Depth: " << mat.depth() << endl;
    out << "Element Size: " << mat.elemSize() << " bytes" << endl;
    out << "Total Size: " << mat.total() * mat.elemSize() << " bytes" << endl;
}


bool saveMatToFile(const cv::Mat& mat, const std::string& filename)
{
    // 將cv::Mat保存為圖像文件
    bool success = cv::imwrite(filename, mat);

    if (!success) {
        // 保存失敗時輸出錯誤信息
        std::cerr << "Failed to save image: " << filename << std::endl;
    }

    return success;
}


// 繪制馬賽克
void drawMosaic(Mat& image, Rect roi) {
    // 將人臉區(qū)域縮小為一定比例,以增加馬賽克效果
    Rect smallRoi = roi;
    smallRoi.x += smallRoi.width * 0.1;
    smallRoi.y += smallRoi.height * 0.1;
    smallRoi.width -= smallRoi.width * 0.2;
    smallRoi.height -= smallRoi.height * 0.2;

    // 對縮小后的人臉區(qū)域進行馬賽克處理
    Mat mosaic = image(smallRoi);
    //可以調(diào)整數(shù)字,調(diào)整馬賽克的像素大小
    resize(mosaic, mosaic, Size(smallRoi.width / 20, smallRoi.height / 20), INTER_NEAREST);
    resize(mosaic, image(smallRoi), smallRoi.size(), 0, 0, INTER_NEAREST);
}

#include "widget.h"

//人臉檢測代碼
void ImageHandle::opencv_face(QImage qImage)
{
    QTime time;
    time.start();

    //(1)包含必要的頭文件和命名空間:

    //(2)加載人臉檢測模型
    std::string cnn_file_path= OpenCV_CNN_MODEL_FILE_PATH;  //CNN模型文件路徑
    std::string prototxt_path = cnn_file_path+"/deploy.prototxt.txt";
    std::string model_path = cnn_file_path+"/res10_300x300_ssd_iter_140000_fp16.caffemodel";
    cv::dnn::Net model = cv::dnn::readNetFromCaffe(prototxt_path, model_path);

    //(3)加載圖片:
    //Mat frame = imread("D:1.png");  // 替換為你的圖片路徑

    Mat frame = QImage_to_cvMat(qImage);
    if (frame.empty())
    {
        ss_log_text("待識別的圖片加載失敗...n");
        // 處理圖片加載失敗的情況
        return;
    }

    int h = frame.rows;
    int w = frame.cols;
    cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, cv::Size(300, 300), cv::Scalar(104.0, 177.0, 123.0));


    //(4)進行人臉檢測:
    model.setInput(blob);
    cv::Mat output = model.forward();
    cv::Mat detectionMat(output.size[2], output.size[3], CV_32F, output.ptr<float>());

    //(5)給每個檢測到的人臉繪制馬賽克:

    int face_number=0;

    for (int i = 0; i < detectionMat.rows; ++i) {
        float confidence = detectionMat.at<float>(i, 2);
        if (confidence > 0.5) {

            //記錄人臉數(shù)量
            face_number++;

            int start_x = static_cast<int>(detectionMat.at<float>(i, 3) * w);
            int start_y = static_cast<int>(detectionMat.at<float>(i, 4) * h);
            int end_x = static_cast<int>(detectionMat.at<float>(i, 5) * w);
            int end_y = static_cast<int>(detectionMat.at<float>(i, 6) * h);

            // 馬賽克處理
            cv::Rect roi(start_x, start_y, end_x - start_x, end_y - start_y);
            cv::Mat face_roi = frame(roi);
            cv::resize(face_roi, face_roi, cv::Size(), 0.05, 0.05, cv::INTER_LINEAR);
            cv::resize(face_roi, frame(roi), roi.size(), 0, 0, cv::INTER_NEAREST);

            // 繪制邊框和文字
            cv::rectangle(frame, cv::Point(start_x, start_y), cv::Point(end_x, end_y), cv::Scalar(255, 0, 0), 2);
            std::ostringstream ss;
            ss << confidence * 100 << "%";
            cv::putText(frame, ss.str(), cv::Point(start_x, start_y - 5), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 0, 0), 2);
        }
    }

    //傳遞出人臉數(shù)量
    emit ss_face_number(face_number);

    // 在圖像上顯示識別消耗的時間
    std::ostringstream time_ss;
    time_ss << "Time: " << time.elapsed() << " ms";
    cv::putText(frame, time_ss.str(), cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 0, 0), 2);


    //轉(zhuǎn)為QImage
    QImage out_image=Mat_to_QImage(frame);

    ss_log_text(tr("耗時:%1 msn").arg(time.elapsed()));

    //qDebug()<<"子線程:"<<QThread::currentThread();

    //保存結(jié)果
    m_image=out_image.copy();
}

QImage convertToRGB888(const QImage& image)
{
    if (image.format() == QImage::Format_RGB888) {
        return image;  // Already in RGB888 format
    }

    QImage convertedImage = image.convertToFormat(QImage::Format_RGB888);
    return convertedImage;
}


//可以用。 OpenCV4.0已測試。
//
Mat ImageHandle::QImage_to_cvMat(QImage image)
{
    cv::Mat mat;
        //qDebug() << image.format();
        switch(image.format())
        {
        case QImage::Format_ARGB32:
        case QImage::Format_RGB32:
        case QImage::Format_ARGB32_Premultiplied:
            mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
            break;
        case QImage::Format_RGB888:
            mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());

            //opencv 3.x以及一下,顏色用 CV_BGR2RGB
            cv::cvtColor(mat, mat, COLOR_BGR2RGB);
            break;
        case QImage::Format_Indexed8:
            mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
            break;
        }
        return mat;
}



QImage ImageHandle::Mat_to_QImage(Mat mat)
{
#if 0
    QImage image;

   // 檢查矩陣是否有效
   if (!mat.empty()) {
       // 創(chuàng)建QImage對象,并分配內(nèi)存
       image = QImage(mat.cols, mat.rows, QImage::Format_ARGB32);

       // 根據(jù)Mat的類型和通道數(shù)來設(shè)置Qt圖像格式
       switch (mat.type()) {
           case CV_8UC4:
               image = QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_ARGB32);
               break;

           case CV_8UC3:
               image = QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888);
               break;

           case CV_8UC1:
               image = QImage(mat.data, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_Indexed8);
               break;
       }

       // 對象回收
       if (image.format() != QImage::Format_RGB32) {
           image = image.convertToFormat(QImage::Format_RGB32);
       }
   }
#else
    // Check if the image is valid
    if (mat.empty())
        return QImage();

    // Convert the image color space
    cv::Mat rgbMat;
    cv::cvtColor(mat, rgbMat, cv::COLOR_BGR2RGB);

    // Create the QImage
    QImage image(rgbMat.data, rgbMat.cols, rgbMat.rows, static_cast<int>(rgbMat.step), QImage::Format_RGB888);

#endif
   return image.copy();
}

?

六、總結(jié)

本項目利用香橙派 AIpro開發(fā)了一個創(chuàng)新的健康監(jiān)測解決方案,能夠提升醫(yī)院、疾病防控中心和發(fā)熱門診等關(guān)鍵場所的公共衛(wèi)生管理水平。系統(tǒng)利用香橙派AIpro的強大計算能力,搭載Ubuntu 22.04操作系統(tǒng),實現(xiàn)了高效的人臉識別與非接觸式體溫測量功能,顯著增強了疾病早期預(yù)警和控制的能力。

通過整體項目開發(fā)完成后,這塊基于香橙派 AIpro的性能是完全滿足了要求;運行了10幾個小時, 整個板子不發(fā)燙,只是啟動的時候風(fēng)扇有明顯噪聲,正常進入系統(tǒng)之后,風(fēng)扇的聲音就正常,基本處于靜音狀態(tài)。 板子構(gòu)造小巧,很容易集成,進行項目開發(fā)

  • 更多詳細資料請聯(lián)系.docx
    下載

相關(guān)推薦