基于香橙派 AIpro设计的医院人脸红外测温系统(从0开始开发)

文章目录

  • 一、前言
  • 二、主控板介绍
  • 三、搭建开发环境
    • 3.1 准备需要的配件
    • 3.2 开发板实物图
    • 3.3 下载开发板资料
    • 3.4 下载系统烧写工具
    • 3.5 设置开发板启动模式
    • 3.6 启动系统
    • 3.7 SSH远程登录系统
    • 3.8 安装xdrp工具
    • 3.9 Window远程登录
    • 3.10 取消自动休眠
  • 四、安装Qt开发环境
    • 4.1 安装qtcreator
    • 4.2 启动qtcreator
    • 4.3 配置编译器
    • 4.4 新建工程Qt环境
  • 五、开发板初步测试
    • 5.1 项目1:开发一个基于HTTP协议的网络摄像头
      • 【1】项目介绍
      • 【2】编写项目代码
      • 【3】上传项目代码
      • 【4】插入USB摄像头
      • 【4】编译运行项目
      • 【5】浏览器访问
    • 5.2 项目2: 基于华为云设计的智能家居控制系统
      • 【1】项目介绍
      • 【2】安装wiringPi
      • 【3】GPIO口布局
      • 【4】控制LED灯
      • 【5】DHT11温湿度传感器数据读取
      • 【6】注册华为云设备
      • 【7】编写整体项目
    • 5.3 项目3:OpenCV+卷积神经网络实现人脸识别
    • 5.4 项目4:OpenCV+YOLOv3实现目标检测
  • 五、测温项目开发
    • 5.1 外设模块选型
    • 5.2 整体的项目代码
      • 【1】技术实现方式说明
      • 【2】摄像头图像采集代码
      • 【3】体温数据采集(串口)
      • 【4】人脸识别图像处理
  • 六、总结

一、前言

在公共卫生事件频发的当下,尤其是在全球性疫情爆发后,国家对公共空间的健康监测和管理提出了更高的要求。医院、疾病防控中心和发热门诊作为疫情防控的第一线,需要高效且精准地对进出人员进行健康筛查,以防止病毒传播,保障医护人员及患者的安全。传统的手动体温检测方式不仅效率低下,而且存在交叉感染的风险,开发一种能够自动、快速、准确地进行人体温度监测与身份识别的系统显得非常的重要。

当前文章会完整的介绍,如何采用香橙派AIpro设计出一套医院人脸红外测温系统。香橙派AIpro是一款高性价比的边缘计算设备,搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,能够运行Ubuntu 22.04操作系统,这为部署复杂的深度学习算法提供了硬件基础。本系统利用OpenCV和SSD算法模型进行人脸检测,通过各方面的模型训练,能确保在复杂光线和遮挡条件下仍能有效识别个体;结合红外测温技术,可以非接触式地测量额头温度,避免了传统接触式测文章温可能带来的卫生问题。

考虑到环境因素对测温结果的影响,系统还配备了温湿度传感器,以实时监测并校准测温数据。为了实现数据的实时监控与分析,系统通过MQTT协议将收集到的信息上传至华为云物联网云平台,便于远程监控和数据分析,有助于疫情趋势的预测和资源的合理调配。

本项目整体提供了一个智能化、自动化的人脸识别与体温监测解决方案,以提高公共卫生领域的响应速度和防控效率,减少人力资源的投入,同时降低潜在的感染风险,为构建安全健康的医疗环境贡献力量。

本项目在完成最终的功能开发前,会先单个完成模块的功能开发,实现了单个模块功能之后,最终在整体合在一起实现最终的项目开发。

整体项目会从搭建环境开始, 一步一步实现最终的项目效果。

image-20240714173609690

下面的开发出来的最终人脸检测设备最终设计效果:

image-20240713185744390

image-20240713184816028

二、主控板介绍

香橙派 AIpro开发板是香橙派联合华为精心打造的高性能 AI 开发板,搭载了昇腾 AI 处理器,可提供 8TOPS INT8 的计算能力,内存提供了 8GB 和 16GB两种版本。可以实现图像、视频等多种数据分析与推理计算,可广泛用于教育、机器人、无人机等场景。

下面是香橙派 AIpro开发板的配置说明:

模块规格
昇腾 AI 处理器4 核 64 位 Arm 处理器 + AI 处理器
AI 算力半精度(FP16):4 TFLOPS
整数精度(INT8):8 TOPS
内存类型:LPDDR4X 容量:8GB 或 16GB
存储板载 32MB 的 SPI Flash
Micro SD 卡插槽
eMMC 插座:可外接 eMMC 模块
M.2 M-Key 接口:可接 2280 规格的 NVMe SSD 或 SATA SSD
以太网支持 10/100/1000Mbps
板载 PHY 芯片:RTL8211F
Wi-Fi+蓝牙支持 2.4G 和 5G 双频 WIFI
BT4.2
模组:欧智通 62221BUUC
USB2 个 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、PWM 和 GPIO 等接口
按键一个复位键,一个关机键,一个升级按键
拨码开关2 个拨码开关:用于控制 SD 卡、eMMC 和 SSD 启动选项
电源支持 Type-C 供电,20V PD-65W 适配器
LED 灯一个电源指示灯和一个软件可控指示灯
风扇接口4pin,0.8mm 间距,用于接 12V 风扇,支持 PWM 控制
电池接口2pin,2.54mm 间距,用于接 3 串电池,支持快充
调试串口Micro USB 接口的调试串口
支持的操作系统Ubuntu 22.04 和 openEuler 22.03
外观规格介绍产品尺寸:107*68mm 重量:82g

下面是香橙派 AIpro开发板的功能模块介绍:

image-20240713184019802

image-20240713233418646

三、搭建开发环境

3.1 准备需要的配件

(1)准备一张至少32G的TFT卡,用来烧写系统。

(2)准备一个读卡器,方便插入TFT卡,好方便插入到电脑上拷贝系统

(3)香橙派 AIpro 主板一个

(4)一根网线(方便插路由器上与香橙派 AIpro 连接)

(5)一根type-C的电源线 + 电源插头(3A电流),这个主板买回来是带了电源的。 也可以用自己Android手机的数据线就行,拿手机充电器供电,因为目前Android手机电源线都是都是type-C 也支持快充的,电流也是满足需求的。

(6)一个USB摄像头,用于后续项目开发里获取周围的实时图像,识别人脸。 (项目开发需要使用)

(7)一个串口协议的红外测温传感器,用于后续项目开发里测量体温。(项目开发需要使用)

(8)一个外放音箱,支持3.5mm的耳机插孔,方便后续项目开发里播放语音提示。(项目开发需要使用)

(9)一块显示屏(这个不是必须的,可以直接Windows远程桌面访问系统,对前期开发来说没有任何影响,只要做成最终的产品才需要配屏幕)。

3.2 开发板实物图

拿回来的香橙派 AIpro 开发板实物是这样的。

image-20240713193221825

image-20240713193411984

3.3 下载开发板资料

拿到板子之后,第一件事肯定是先去官网下载板子对应的相关的资料。比如:用户手册、系统镜像、原理图、开发工具什么的。

官网地址:http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-AIpro.html

翻到下面,找到资料下载地址,直接下载就行,下载会跳转到网盘。

image-20240713193822577

系统镜像我选择的 ubuntu22.04

image-20240713193900175

资料下载下来之后,可以看到有一份官方的说明文档,指导板子的基本使用。如何烧写系统,如何启动系统等等。

image-20240713194235872

3.4 下载系统烧写工具

链接: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卡通过读卡器插到电脑上,准备烧写系统(就算有些电脑自带了TF卡的插槽也建议用USB读卡器,这个电脑自带的TF卡槽烧写系统无法启动)。 TF卡的容量至少要32G,最好是64G。

image-20240713195607403

选择要烧写的系统镜像文件(就是刚刚通过网盘下载的ubuntu系统镜像)。

image-20240713195815959

然后点击烧录镜像。

image-20240713200023532

弹出提示框,选择确认

image-20240713200052668

然后可以看到,系统正在烧写中了,精心等待即可。

image-20240713200113792

烧录成功之后,会弹窗提示弹出SD卡

image-20240713210720997

TF卡从电脑上弹出,拔掉就行了。

image-20240713210748529

image-20240713193457415

3.5 设置开发板启动模式

香橙派 AIpro开发板支持从 TF 卡、eMMC 和 SSD(支持 NVMe SSD 和 SATA SSD)启动。

具体从哪个设备启动是由开发板背面的两个拨码(BOOT1 和 BOOT2)开关来控制的。

image-20240713211059551

BOOT1 和 BOOT2 两个拨码开关都支持左右两种设置状态,所以总共有 4 种设置状态,开发板目前只使用了其中的三种。不同的设置状态对应的启动设备如下表所示:

image-20240713211152112

BOOT1 和 BOOT2 两个拨码开关 全部拨到靠右的位置就可以选择从TF卡启动了。

3.6 启动系统

【1】将烧写好的TF卡插在板子上。

image-20240713211425933

【2】插好网线。

网线一端接开发板的网口,一端接路由器,自己的笔记本电脑也是接的同一个路由器,让板子与电脑在同一个局域网内,方便接下来远程登录开发板的系统。

image-20240713211553314

image-20240713213052792

【3】插好电源

板子是没有电源开关的,电源线插好之后,系统就启动了。刚启动的时候风扇的声音会比较大,等待几秒就正常了。

按下开发板左上角的PWR_OFF可以关闭系统,点击旁边的RESET可以重启系统。

image-20240713211543200

3.7 SSH远程登录系统

系统启动之后,会自动请求路由器分配IP地址,我们只需要登录到开发板连接的路由器后台,就可以看到新接入的设备。

我的用是小米路由器。直接在浏览器里输入:192.168.31.1 即可进入到路由器的后台终端。

从下面图片里可以看到,香橙派 AIpro 已经分配到IP地址了,192.168.31.136

image-20240713223645033

为了方便远程登录到系统终端,可以下载安装一个FinalShell 软件。

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

软件安装打开后,建立一个新的SSH链接,具体看下图的操作。

image-20240713224703844

这里面的主机IP地址就是从路由器后台看到的,分配给香橙派 AIpro开发板的IP地址。 端口号是固定的22,这是SSH协议的固定端口。

用户名是root,密码是:Mind@123 这是烧写的香橙派 AIpro系统固定的用户名和密码,也就是系统内置的。

双击刚才建立好的链接,就可以登录到系统终端。 进去终端之后基本上就可以进行正常的开发了。

image-20240713225229527

3.8 安装xdrp工具

为了方便图形化方式开发,可以使用windows系统通过远程桌面登录香橙派 AIpro,就可以看到界面了,不过需要先安装工具。

进入到香橙派 AIpro终端之后,输入安装命令:

sudo apt-get install xrdp

按下回车之后,会弹出确认窗口。输入 y之后,按下回车,继续安装。

下面是完整的命令安装过程: 按顺序执行就行了。

#安装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服务
sudo service xrdp restart

注意,如果之后断电了,远程桌面无法链接上。可以先卸载xrdp,再重新安装即可。

sudo apt-get remove --purge xrdp

3.9 Window远程登录

在windows上打开运行命令的窗口,输入mstsc来打开远程桌面。

image-20240713225908482

输入mstsc,点击确定。

image-20240713225941156

弹出窗口后,填入IP地址(这就是你的香橙派 AIpro 开发板的IP地址),点击连接。

image-20240713230025713

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

image-20240713230144585

输入账号和密码。

用户名是root,密码是:Mind@123 这是烧写的香橙派 AIpro系统固定的用户名和密码,也就是系统内置的。

登录之后的界面:

image-20240713231948302

image-20240713232200871

好了。接下来就可以进行项目的正式开发了。

3.10 取消自动休眠

Ubuntu桌面镜像会自动休眠,输入以下指令禁用休眠。

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

四、安装Qt开发环境

因为最终的项目需要使用界面,我的项目准备采用Qt进行开发,这一章节进行安装Qt开发环境。

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】打开设置页面

image-20240714001025326

【2】选择编译器套件

image-20240714001100905

【3】配置编译器套件。

将编译器改为GCC

image-20240714000644413

4.4 新建工程Qt环境

【1】新建工程

image-20240714001302590

image-20240714001327984

【2】设置工程名称和路径(自己单独建立一个文件夹存放工程)

image-20240714001351170

image-20240714001442561

【3】选择继承的基类

image-20240714001509921

image-20240714001548171

【4】选择编译器套件

image-20240714001610071

【5】创建完成

image-20240714001639213

这就是创建好的工程。

image-20240714001706696

【6】点击左下角绿色三角形编译运行。

下面是正常运行的效果,已经弹窗窗口,说明Qt的环境已经OK了。

image-20240714001757927

image-20240714001912150

五、开发板初步测试

为了了解下开发板本身的性能,先采用开发板开发一些小项目测试测试效果。

现在系统根目录创建一个work目录,方便存放接下来的项目文件。

image-20240714131053362

5.1 项目1:开发一个基于HTTP协议的网络摄像头

【1】项目介绍

本项目主要采用C语言开发,实现了一个网络摄像头项目,在橙派 AIpro开发板上实现了一个HTTP服务器,,处理浏览器的请求,当浏览器访问过来时,就将本地采集到的摄像头画面发送给浏览器,与浏览器建立长连接通信,直接传输JPG图片,实现摄像头画面实时显示效果。支持登录页面,做了一个账号登录界面,访问服务器之后需要输入账号密码才可以正常进入服务器查看共享的画面。如果分享个摄像头画面,那是非常的方便的,想要查看分享的摄像头画面只需要浏览器里输入服务器的IP地址登录进去就可以看画面了。

通过本项目的测试,可以了解到USB摄像头的读取效果,网络传输的效果。 为后续的其他项目开发做一个参考。

基于香橙派 AIpro实现的网络摄像头项目-效果

【2】编写项目代码

项目开发,代码编写先在Windows下进行,开发完毕,再拷贝到橙派 AIpro开发板上。

这是在Windows下开发好的项目代码:

image-20240714131824132

【3】上传项目代码

打开FinalShell终端,可以直接将开发好的项目源码,整个目录上传到香橙派 AIpro系统。

image-20240714131909001

通过FinalShell终端可以很方便的将香橙派 AIpro系统文件下载到本地,也可以将本地的文件很方便的上传上去。在开发项目的阶段是很方便的。

image-20240714132128503

【4】插入USB摄像头

将USB摄像头插入到开发板的USB口,然后ls /dev/video* 查看摄像头的设备节点,确定摄像头是否识别成功。

image-20240714135456599

【4】编译运行项目

项目里已经构建好了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表示服务器的端口号。 /dev/video0是摄像头的端口号。

【5】浏览器访问

在自己电脑浏览器地址栏里输入:http://192.168.31.136:666

就可以看到登录页面。

image-20240714135834059

登录成功之后,可以看到摄像头的实时画面。

image-20240714135805025

5.2 项目2: 基于华为云设计的智能家居控制系统

【1】项目介绍

基于香橙派 AIpro开发板设计的智能家居控制系统,通过MQTT协议连接华为云物联网云平台;通过DHT11传感器读取环境温湿度,将数据上传到华为云物联网云平台。在华为云云平台上也可以远程控制硬件端连接的LED灯,控制3种颜色显示。

通过本项目的完整开发测试,可以掌握香橙派的GPIO口的基本使用以及网络的测试。为后续的其他项目开发做一个参考测试。

【2】安装wiringPi

(1)安装 wiringOP 前,请先确保 Linux 系统中存在/etc/orangepi-release 这个配置文件,里面的内容为:BOARD=orangepiaipro。

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

(2)如果 Linux 中没有/etc/orangepi-release 这个配置文件,可以使用下面的命令创建一个。

(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   设置 wPi  为2的这个IO口为输出模式
(base) root@orangepiaipro:~/work# ./gpio write 2 0    设置 wPi  为2的这个IO口输出0 (低电平)
(base) root@orangepiaipro:~/work# ./gpio write 2 1    设置 wPi  为2的这个IO口输出1 (高电平)

【3】GPIO口布局

image-20240714145819245

【4】控制LED灯

image-20240714154247320

通过香橙派 AIpro实现三色灯的控制(GPIO口控制效果)

编写的测试代码:

#include <stdio.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <string.h>
/*控制继电器高低电平亮灯*/#define LEDG 0
#define LEDB 1
#define LEDR 2int main()
{wiringPiSetup();  //置引脚编号方式为wiringPi编码pinMode(LEDG,OUTPUT);pinMode(LEDB,OUTPUT);pinMode(LEDR,OUTPUT);while(1){//全部关闭digitalWrite(LEDG,LOW);digitalWrite(LEDB,LOW);digitalWrite(LEDR,LOW);//亮蓝色digitalWrite(LEDG,HIGH);sleep(1);//全部关闭digitalWrite(LEDG,LOW);digitalWrite(LEDB,LOW);digitalWrite(LEDR,LOW);//亮绿色digitalWrite(LEDB,HIGH);sleep(1);//全部关闭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温湿度传感器数据读取

image-20240714155252689

代码:

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>//编译:gcc -Wall -o dht11 dht11.c -lwiringPi -o apptypedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;#define HIGH_TIME 32int pinNumber = 5;
uint32 databuf;uint8 readSensorData(void)
{uint8 crc; uint8 i;pinMode(pinNumber, OUTPUT); // set mode to outputdigitalWrite(pinNumber, 0); // output a high level delay(25);digitalWrite(pinNumber, 1); // output a low level pinMode(pinNumber, INPUT); // set mode to inputpullUpDnControl(pinNumber, PUD_UP);delayMicroseconds(27);if (digitalRead(pinNumber) == 0) //SENSOR ANS{while (!digitalRead(pinNumber)); //wait to highfor (i = 0; i < 32; i++){while (digitalRead(pinNumber)); //data clock startwhile (!digitalRead(pinNumber)); //data startdelayMicroseconds(HIGH_TIME);databuf *= 2;if (digitalRead(pinNumber) == 1) //1{databuf++;}}for (i = 0; i < 8; i++){while (digitalRead(pinNumber)); //data clock startwhile (!digitalRead(pinNumber)); //data startdelayMicroseconds(HIGH_TIME);crc *= 2;  if (digitalRead(pinNumber) == 1) //1{crc++;}}return 1;}else{return 0;}
}int main(void)
{printf("PIN:%d\n", pinNumber);wiringPiSetup();  //置引脚编号方式为wiringPi编码pinMode(pinNumber, OUTPUT); // set mode to outputdigitalWrite(pinNumber, 1); // output a high level printf("Starting...\n");while (1) {pinMode(pinNumber, OUTPUT); // set mode to outputdigitalWrite(pinNumber, 1); // output a high level delay(3000);if (readSensorData()){printf("Sensor data read ok!\n");printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); printf("TMP:%d.%d\n", (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】注册华为云设备

华为云物联网平台的整体就不再详细展示了,可以直接看视频。

B站的视频链接:https://www.bilibili.com/video/BV1mr421c75S

手把手讲解华为云物联网云平台的使用以及应用侧的开发(2024最新版)

(1)注册产品

image-20240714162050418

(2)注册设备

image-20240714160920091

(3)创建命令

image-20240714161446789

image-20240714162113037

(4)得到MQTT三元组

IP地址:117.78.5.125
端口号:1883ClientId   6693872aa559ef6226685350_dev1_0_0_2024071408
Username   6693872aa559ef6226685350_dev1
Password   8ce1b26a6fac2c52402d2911a9a951efe6026f71041037a963bebdd4a099190f订阅主题:$oc/devices/6693872aa559ef6226685350_dev1/sys/messages/down发布主题:$oc/devices/6693872aa559ef6226685350_dev1/sys/properties/report
发布数据:{"services": [{"service_id": "stm32","properties":{"DHT11_T":23,"DHT11_H":80}}]}

【7】编写整体项目

接下来就编写代码,连接华为云物联网平台,完成数据上传。 将采集的温湿度数据上传到华为云物联网云平台。 同时支持在华为云物联网平台下发命令远程控制设备端的LED灯。

代码是采用纯C语言编写,实现了MQTT协议,完成了与物联网云平台交互。

关于MQTT协议的整体编写过程,可以直接看视频:https://www.bilibili.com/video/BV1BN4y1Y7cf

从0开始编写MQTT协议代码连接标准MQTT服务器(精讲MQTT协议)

(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//服务器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"//密文 //订阅主题:
#define SET_TOPIC  "$oc/devices/6693872aa559ef6226685350_dev1/sys/messages/down"//订阅
//发布主题:
#define POST_TOPIC "$oc/devices/6693872aa559ef6226685350_dev1/sys/properties/report"//发布char mqtt_message[1024*1024];//上报数据缓存区
char request_id[100];
char mqtt_cmd_message[100];
char mqtt_cmd_data[100];int sockfd;
/*获取平台下发数据*/
void *pth_work_func(void *arg)
{char buff[1024];int size=0;int i=0;while(1){size=Client_GetData(buff);printf("size=%d\r\n",size);if(size<0)break;for(i=0;i<size;i++){printf("%c ",buff[i]);}buff[size]='\0';if(size>5){printf("%s\r\n",buff+5);if(strstr((char*)&buff[5],"sys/commands/request_id=")){char *p=NULL;p=strstr((char*)&buff[5],"request_id");if(p){        //解析数据//$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/get/request_id=5f359b5c-542f-460e-9f51-85e82150ff4a{"service_id":"gps"} strncpy(request_id,p,47);      }//上报数据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("应答-发布主题:%s\r\n",mqtt_cmd_data);printf("应答-发布数据:%s\r\n",mqtt_cmd_message);} if(strstr((char*)&buff[5],"\"LED_SW\":1")) {//全部关闭digitalWrite(LEDG,LOW);digitalWrite(LEDB,LOW);digitalWrite(LEDR,LOW);//亮蓝色digitalWrite(LEDG,HIGH);}if(strstr((char*)&buff[5],"\"LED_SW\":2")) {//全部关闭digitalWrite(LEDG,LOW);digitalWrite(LEDB,LOW);digitalWrite(LEDR,LOW);//亮绿色digitalWrite(LEDB,HIGH);} if(strstr((char*)&buff[5],"\"LED_SW\":3")) {//全部关闭digitalWrite(LEDG,LOW);digitalWrite(LEDB,LOW);digitalWrite(LEDR,LOW);//亮红色digitalWrite(LEDR,HIGH);} if(strstr((char*)&buff[5],"\"LED_SW\":0")) {//全部关闭digitalWrite(LEDG,LOW);digitalWrite(LEDB,LOW);digitalWrite(LEDR,LOW);} }printf("\r\n");}
}/*信号处理函数*/void signal_func(int sig)
{//printf("捕获的信号:%d\n",sig);if(sig==SIGALRM){MQTT_SentHeart();//心跳包alarm(5);}
}typedef unsigned char uint8;
typedef unsigned int  uint16;
typedef unsigned long uint32;#define HIGH_TIME 32int pinNumber = 5;
uint32 databuf;uint8 readSensorData(void)
{uint8 crc; uint8 i;pinMode(pinNumber, OUTPUT); // set mode to outputdigitalWrite(pinNumber, 0); // output a high level delay(25);digitalWrite(pinNumber, 1); // output a low level pinMode(pinNumber, INPUT); // set mode to inputpullUpDnControl(pinNumber, PUD_UP);delayMicroseconds(27);if (digitalRead(pinNumber) == 0) //SENSOR ANS{while (!digitalRead(pinNumber)); //wait to highfor (i = 0; i < 32; i++){while (digitalRead(pinNumber)); //data clock startwhile (!digitalRead(pinNumber)); //data startdelayMicroseconds(HIGH_TIME);databuf *= 2;if (digitalRead(pinNumber) == 1) //1{databuf++;}}for (i = 0; i < 8; i++){while (digitalRead(pinNumber)); //data clock startwhile (!digitalRead(pinNumber)); //data startdelayMicroseconds(HIGH_TIME);crc *= 2;  if (digitalRead(pinNumber) == 1) //1{crc++;}}return 1;}else{return 0;}
}unsigned int DHT11_T;// 	环境温度
unsigned int DHT11_H;//	环境湿度int main()
{wiringPiSetup();  //置引脚编号方式为wiringPi编码pinMode(LEDG,OUTPUT);pinMode(LEDB,OUTPUT);pinMode(LEDR,OUTPUT);//DHT11温湿度初始化pinMode(pinNumber, OUTPUT); // set mode to outputdigitalWrite(pinNumber, 1); // output a high level int stat;sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){printf("网络套接字打开失败\n");return 0;}signal(SIGPIPE,SIG_IGN);/*忽略SIGPIPE信号*/signal(SIGALRM,signal_func);/*闹钟信号*//*连接服务器*/struct sockaddr_in addr;addr.sin_family=AF_INET;//IPV4addr.sin_port=htons(SERVER_PORT);/*端口号*/addr.sin_addr.s_addr=inet_addr(SERVER_IP);//inet_addr(ip);//服务器IPif(connect(sockfd, (struct sockaddr *)&addr,sizeof(struct sockaddr_in))==0){printf("server connect ok\n");MQTT_Init();while(1){/*登录服务器*/if(MQTT_Connect(ClientID,Username,Password)==0){break;}sleep(1);printf("server connect ....\n");}printf("MQTT_Connect OK\r\n");//订阅物联网平台数据stat=MQTT_SubscribeTopic(SET_TOPIC,1,1);if(stat){close(sockfd);printf("MQTT_SubscribeTopic ERROR\r\n");  exit(0);}printf("MQTT_SubscribeTopic ok\r\n");/*创建线程*/pthread_t id;pthread_create(&id, NULL,pth_work_func,NULL);pthread_detach(id);//设置分离属性//发送心跳包// alarm(5);//闹钟函数,时间到达会产生SIGALRM信号while(1){//读取DHT11温湿度数据pinMode(pinNumber, OUTPUT); // set mode to outputdigitalWrite(pinNumber, 1); // output a high level delay(3000);if (readSensorData()){printf("DHT11 Sensor data read ok!\n");printf("RH:%d.%d\n", (databuf >> 24) & 0xff, (databuf >> 16) & 0xff); printf("TMP:%d.%d\n", (databuf >> 8) & 0xff, databuf & 0xff);//温度整数部分DHT11_H=((databuf >> 24) & 0xff);printf("DHT11_T:%d\r\n",DHT11_T);//湿度整数部分DHT11_T=((databuf >> 8) & 0xff);printf("DHT11_H:%d\r\n",DHT11_H);databuf = 0;}else{printf("Sensor dosent ans!\n");databuf = 0;}//组合传感器状态数据sprintf(mqtt_message,"{\"services\": [{\"service_id\": \"stm32\",\"properties\":{\"DHT11_T\":%d,\"DHT11_H\":%d}}]}",DHT11_T,DHT11_H);//温度//上报数据MQTT_PublishData(POST_TOPIC,mqtt_message,0);printf("MQTT_PublishData....\r\n");sleep(2);}}
}

(2)这是编译运行后的效果

image-20240714163715479

(3)在华为云物联网平台后台,可以看到设备已经在线了,同时也实时收到设备端上传的数据。

image-20240714164311508

(4)下发命令测试。 通过命令下发控制设备端的LED灯。

image-20240714164457620

5.3 项目3:OpenCV+卷积神经网络实现人脸识别

本项目通过OpenCV加载训练好的SSD模型,实现人脸检测,能够在图像中找到并标记出人脸的位置和置信度。

通过本项目,可以验证整个系统的算法运行速度。为后续的项目开发做参考。

(1)安装python (烧写的系统本身自带了完整的Python环境,可以不需要安装,如果没有才需要安装)

sudo apt update
sudo apt install python3

(2)编写代码加载模型识别人脸

import cv2
import numpy as np
import timeprototxt_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)# 设置 blob 作为模型的输入
model.setInput(blob)# 进行推断并获取输出
start_time = time.time()
output = model.forward()
end_time = time.time()# 遍历检测结果
font_scale = 1.0
for i in range(output.shape[2]):confidence = output[0, 0, i, 2]# 通过置信度阈值过滤弱检测结果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)# 显示和保存带有检测结果的图像
cv2.imwrite("beauty_detected.jpg", image)# 输出识别耗时
print(f"识别耗时:{end_time - start_time:.3f} 秒")

(3)运行效果

image-20240714171723373

(4)将图片下载下来打开

image-20240714171744817

5.4 项目4:OpenCV+YOLOv3实现目标检测

本项目通过OpenCV加载YOLOV3官方的模型,实现目标。

通过本项目,可以验证整个系统的算法运行速度。为后续的项目开发做参考。

实现代码:

#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() {//加载网络模型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 : %s\n", 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);}# 保存结果图片cv2.imwrite('detections.jpg', frame)waitKey(0);return;
}

将识别的图片结果拷贝下来,查看效果。

image-20240714180036886

五、测温项目开发

5.1 外设模块选型

需要用到的传感器如下:

(1)LU90614非接触式红外测温模块(串口协议),用于测量体温。

(2)DHT11温湿度传感器,用于测量环境的温湿度。

(3)USB摄像头,用于捕获图像,检测人脸。

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

(5)一个三色LED灯,用于显示检测的体温状态。 红、绿、蓝 三种颜色。

5.2 整体的项目代码

整体项目是采用Qt开发的,因为需要通过显示屏展示界面,在界面上显示人脸的识别效果,温度测量效果等信息。

【1】技术实现方式说明

(1)这里面的LU90614非接触式红外测温模块 采用USB-TTL模块接入系统的。在/dev目录下的节点是ttyUSB0。没有使用开发板本身的IO口。

(2)本项目是先在Windows下开发完成后,再上传到香橙派 AIpro开发板运行,在香橙派 AIpro里安装了Qt的开发环境。

(3)体温传感器的串口数据读取,没有采用Qt本身的串口接口,而是采用了标准Linux下的方式读取串口数据。

(4)摄像头的采集没有采用Qt的内置接口,而是采用了Linux下V4L2框架完成的图像采集。

(5)MQTT协议没有采用第三方库,是自己基于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 480struct 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;// 打开摄像头设备fd = open("/dev/video0", O_RDWR);if (fd == -1) {perror("打开/dev/video0失败");return 1;}// 设置格式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("设置格式失败");close(fd);return 1;}// 请求缓冲区memset(&req, 0, sizeof(req));req.count = 1; // 缓冲区数量req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {perror("请求缓冲区失败");close(fd);return 1;}// 分配并映射缓冲区buffers = calloc(req.count, sizeof(*buffers));if (!buffers) {perror("分配缓冲区内存失败");close(fd);return 1;}// 查询缓冲区buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 0;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {perror("查询缓冲区失败");close(fd);return 1;}// 内存映射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("内存映射失败");close(fd);return 1;}// 开始流式传输type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {perror("开始流式传输失败");close(fd);return 1;}// 捕获循环(示例:捕获一帧)while (1) {// 入队缓冲区if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {perror("入队缓冲区失败");close(fd);return 1;}// 出队缓冲区if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {perror("出队缓冲区失败");close(fd);return 1;}// 处理帧(转换为RGB888格式)// 示例:将YUYV转换为RGB888rgb888_buffer = (unsigned char *)malloc(WIDTH * HEIGHT * 3);for (int i = 0, j = 0; i < WIDTH * HEIGHT * 2; i += 4, j += 6) {// YUYV到RGB888的简化转换(实际应用中可能需要更复杂的算法)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; // 示例中仅处理一帧,所以退出循环}// 停止流式传输type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {perror("停止流式传输失败");close(fd);return 1;}// 解除内存映射munmap(buffers[0].start, buf.length);// 清理和关闭free(buffers);close(fd);return 0;
}

【3】体温数据采集(串口)

下面是采集体温传感器的代码。

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 B9600int temp_read_pthread() {int fd;char *sendbuf = "\xFA\xC5\xBF"; // 发送体温模式指令char recvbuf[8];struct termios options;struct pollfd pfd;int timeout = 1000; // 超时时间,单位毫秒// 打开串口设备文件if ((fd = open(SERIAL_DEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) {perror("open serial port failed");exit(1);}// 设置串口参数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结构体pfd.fd = fd;pfd.events = POLLIN;// 发送命令write(fd, sendbuf, 3);while (1) {// 使用poll等待数据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: %s\n", strerror(errno));}} else {printf("No data received in %d ms\n", timeout);}}close(fd);return 0;
}

【4】人脸识别图像处理

下面是完成人脸识别处理的代码。

#include "image_handle.h"
#pragma execution_character_set("utf-8")//关闭线程
void ImageHandle::close()
{run_flag=0;this->quit();this->wait();
}//线程执行函数
void ImageHandle::run()
{QImage use_image;while(run_flag){//如果没有图像可以处理if(start_run==0){//休眠100毫秒msleep(100);continue;}//表示已经处理过start_run=0;//表示开始处理图像Handle_flag=1;//调用图像处理算法 对 image  的图像进行处理//1. 人脸识别opencv_face(m_image);//处理完毕之后//将图像传出去给UI界面显示emit HandleSend(m_image);//处理完毕Handle_flag=0;}
}//传入待处理的图片数据
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) {// 将人脸区域缩小为一定比例,以增加马赛克效果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;// 对缩小后的人脸区域进行马赛克处理Mat mosaic = image(smallRoi);//可以调整数字,调整马赛克的像素大小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) {//记录人脸数量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);}}//传递出人脸数量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);//转为QImageQImage out_image=Mat_to_QImage(frame);ss_log_text(tr("耗时:%1 ms\n").arg(time.elapsed()));//qDebug()<<"子线程:"<<QThread::currentThread();//保存结果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_BGR2RGBcv::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 0QImage image;// 检查矩阵是否有效if (!mat.empty()) {// 创建QImage对象,并分配内存image = QImage(mat.cols, mat.rows, QImage::Format_ARGB32);// 根据Mat的类型和通道数来设置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 validif (mat.empty())return QImage();// Convert the image color spacecv::Mat rgbMat;cv::cvtColor(mat, rgbMat, cv::COLOR_BGR2RGB);// Create the QImageQImage image(rgbMat.data, rgbMat.cols, rgbMat.rows, static_cast<int>(rgbMat.step), QImage::Format_RGB888);#endifreturn image.copy();
}

六、总结

本项目利用香橙派 AIpro开发了一个创新的健康监测解决方案,能够提升医院、疾病防控中心和发热门诊等关键场所的公共卫生管理水平。系统利用香橙派AIpro的强大计算能力,搭载Ubuntu 22.04操作系统,实现了高效的人脸识别与非接触式体温测量功能,显著增强了疾病早期预警和控制的能力。

通过整体项目开发完成后,这块基于香橙派 AIpro的性能是完全满足了要求;运行了10几个小时, 整个板子不发烫,只是启动的时候风扇有明显噪声,正常进入系统之后,风扇的声音就正常,基本处于静音状态。 板子构造小巧,很容易集成,进行项目开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/377305.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu系统安装mysql之后进行远程连接

1.首先要配置数据库允许进行远程连接 1.1 打开MySQL配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf1.2 修改 bind-address 行 #按i进入插入模式 bind-address 0.0.0.0 #按 Esc 键退出插入模式。 #输入:wq 然后按 Enter 保存并退…

MySQL第八次作业

一、备份与恢复作业&#xff1a; 创库,建表&#xff1a; CREATE DATABASE booksDB; use booksDB; CREATE TABLE books ( bk_id INT NOT NULL PRIMARY KEY, bk_title VARCHAR(50) NOT NULL, copyright YEAR NOT NULL ); CREATE TABLE authors …

在uniapp中如何使用地图

1&#xff0c;技术选择 最好是使用webview html形式加载&#xff0c;避免打包app时的地图加载问题 2&#xff0c;webview使用 使用webview必须按照官方文档,官网地址&#xff1a;https://uniapp.dcloud.net.cn/component/web-view.html <template><view><!…

MATLAB激光通信和-积消息传递算法(Python图形模型算法)模拟调制

&#x1f3af;要点 &#x1f3af;概率论和图论数学形式和图结构 | &#x1f3af;数学形式、图结构和代码验证贝叶斯分类器算法&#xff1a;&#x1f58a;多类型&#xff1a;朴素贝叶斯&#xff0c;求和朴素贝叶斯、高斯朴素贝叶斯、树增强贝叶斯、贝叶斯网络增强贝叶斯和半朴素…

STM32对数码管显示的控制

1、在项目开发过程中会遇到STM32控制的数码管显示应用&#xff0c;这里以四位共阴极数码管显示控制为例讲解&#xff1b;这里采用的控制芯片为STM32F103RCT6。 2、首先要确定数码管的段选的8个引脚连接的单片机的引脚是哪8个&#xff0c;然后确认位选的4个引脚连接的单片机的4…

MVC 生成验证码

在mvc 出现之前 生成验证码思路 在一个html页面上&#xff0c;生成一个验证码&#xff0c;在把这个页面嵌入到需要验证码的页面中。 JS生成验证码 <script type"text/javascript">jQuery(function ($) {/**生成一个随机数**/function randomNum(min, max) {…

登录/注册

目录 1.HTML 2.CSS 3.JS 4.资源 5.运行结果 6.下载链接 7.注意事项 1.HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(八)-通过无人机进行无线接入

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

基于Rspack实现大仓应用构建提效实践|得物技术

一、实践背景 随着项目的逐步迭代&#xff0c;代码量和依赖的逐渐增长&#xff0c;应用的构建速度逐步进入缓慢期。以目前所在团队的业务应用来看&#xff08;使用webpack构建&#xff09;&#xff0c;应用整体构建耗时已经普遍偏高&#xff0c;影响日常开发测试的使用效率&am…

GUI界面开发之tkinter(一)

Tkinter是一个内置的Python库&#xff0c;用于创建图形用户界面&#xff08;GUI&#xff09;。它提供了一组工具和小部件&#xff0c;用于创建窗口、对话框、按钮、菜单和其他GUI元素。 在本篇文章中&#xff0c;主要介绍了窗口等知识点。 大家好&#xff01;我是码银&#x1…

【高中数学/幂函数】比较a=2^0.3,b=3^0.2,c=7^0.1的大小

【问题】 比较a2^0.3,b3^0.2,c7^0.1的大小 【解答】 a2^0.32^3/10(2^3)^1/108^1/10 b3^0.23^2/10(3^2)^1/109^1/10 c7^0.17^1/10 由于yx^1/10在x正半轴是增函数&#xff0c;底数大的得数就大。 因为9>8>7,所以b>a>c 【图像】 在图像上绘出曲线yx^1/10&…

springcolud学习01

创建项目 修改pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.o…

【游戏引擎之路】登神长阶(七)——x86汇编学习:凡做难事,必有所得

5月20日-6月4日&#xff1a;攻克2D物理引擎。 6月4日-6月13日&#xff1a;攻克《3D数学基础》。 6月13日-6月20日&#xff1a;攻克《3D图形教程》。 6月21日-6月22日&#xff1a;攻克《Raycasting游戏教程》。 6月23日-7月1日&#xff1a;攻克《Windows游戏编程大师技巧》。 7月…

如何指定多块GPU卡进行训练-数据并行

训练代码&#xff1a; train.py import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, Dataset import torch.nn.functional as F# 假设我们有一个简单的文本数据集 class TextDataset(Dataset):def __init__(self, te…

jmeter分布式(四)

一、gui jmeter的gui主要用来调试脚本 1、先gui创建脚本 先做一个脚本 演示&#xff1a;如何做混合场景的脚本&#xff1f; 用211的业务比例 ①启动数据库服务 数据库服务&#xff1a;包括mysql、redis mysql端口默认3306 netstat -lntp | grep 3306处于监听状态&#xf…

【C++】—— 初识C++

【C】—— 初识C 一、什么是 C二、C 的发展历史三、C 版本更新四、C 的重要性五、C 在工作领域中的运用六、C 书籍推荐&#xff1a; 一、什么是 C C语言 是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要…

使用嵌入式知识打造智能手环:nRF52蓝牙开发实战(C++/BLE/传感器)

项目概述 现代人越来越注重健康管理&#xff0c;智能穿戴设备应运而生。本项目旨在利用低功耗蓝牙芯片nRF52832&#xff0c;结合加速度计、心率传感器、陀螺仪等传感器&#xff0c;开发一款功能完善、性能稳定的智能运动手环。该手环能够实时采集用户的运动数据和生理指标&…

vue3 - vue项目自动检测更新

vue3 GitHub Demo 地址 vue3在线预览 vue2 GitHub Demo 地址 vue2 在线预览 web项目当页面检测到需要更新&#xff0c;然后弹框提示是否更新&#xff08;刷新页面&#xff09;这种可以通过纯前端实现也可以通过接口实现 接口实现&#xff1a;通过调用接口轮询和本地的版本号比…

护网HW面试——redis利用方式即复现

参考&#xff1a;https://xz.aliyun.com/t/13071 面试中经常会问到ssrf的打法&#xff0c;讲到ssrf那么就会讲到配合打内网的redis&#xff0c;本篇就介绍redis的打法。 未授权 原理&#xff1a; Redis默认情况下&#xff0c;会绑定在0.0.0.0:6379&#xff0c;如果没有采用相关…

FPGA设计之跨时钟域(CDC)设计篇(1)----亚稳态到底是什么?

1、什么是亚稳态? 在数字电路中,如果数据传输时不满足触发器FF的建立时间要求Tsu和保持时间要求Th,就可能产生亚稳态(Metastability),此时触发器的输出端(Q端)在有效时钟沿之后比较长的一段时间都会处于不确定的状态(在0和1之间振荡),而不是等于数据输入端(D端)的…