【智慧中控项目】

智慧中控

  • 前言
  • 一、搭建开发环境
    • 1.需要做什么?
      • 1.1 刷机和启动OrangePi Zero2(全志H616芯片)
      • 1.2 在PC上安装虚拟机VM(安装VirtualBox或VMware:这是常用的虚拟机软件工具)
      • 1.3 在虚拟机VM(VirtualBox或VMware)上安装Linux系统 (Ubuntu、Debian)
      • 1.4 安装Python、wiringOP和所需的库。
      • ——————————————————
      • 不用操作1.2、1.3、1.4,直接从1.1接上1.5。
      • 正常来说,在PC机上用虚拟机装linux环境开发是按照上面的顺序。但是MobaXterm软件可以通过SSH远程连接上嵌入式设备,直接就可以通过MobaXterm软件操作到香橙派的Linux 系统,直接在MobaXterm上进行香橙派的开发。
      • ——————————————————
      • 1.5 安装MobaXterm 软件,在PC机上远程连接到OrangePi Zero2(linux系统) 进行代码开发
        • 1.5.1其它企业开发嵌入式设备时,会使用到的方式
      • 1.6 安装wiringPi库——官方的外设SDK
    • 2.怎么做?
      • 2.1 硬件:Orangepi Zero2 全志H616开发板
      • 2.2 配套操作系统支持说明—Orangepi Zero2 全志H616开发板
      • 2.3 刷机和系统启动
        • 2.31 工具
        • 2.32 工具安装
        • 2.33 刷机和系统启动
        • 2.34 修改登陆密码
        • 2.35 连接网络,查看ip地址
        • 2.36 SSH登陆开发板
        • 2.37 搭建SSH服务器 —— 额外的知识点
        • ————————————
        • 到上面就已经结束了,下面是扩展内容
        • ————————————
        • 2.38 修改开发板内核启动日志级别
      • 2.4 安装wiringPi库——官方的外设SDK
  • 二、基于OrangePi Zero2的智能家居
    • 1. 需求及项目准备
    • 2. 开发过程
      • 2.0 登录阿里云,创建阿里云账户,创建AccessKey。用来链接阿里云,获取权限。
      • 2.1 把人的照片上传录进数据库
      • 2.2 安装阿里云视觉智能API——人脸识别的SDK(后面人脸识别的python代码导入的库会用到)
      • 2.3 使用pip命令之前先安装pip命令,
      • 2.4 把AccessKey的账号和密码提供给orangePi,让它能通过AccessKey账号链接到阿里云,使用权限。
      • 2.5 这里用官方提供的人脸搜索Python示例代码试试:
      • 2.6 执行后返回的结果是json格式的,结果如下:
      • 2.7 使用C语言去调用上面的人脸搜索Python代码
        • 2.71 封装人脸识别python代码
        • 2.72 C语言调用python
          • ① 搭建编译环境
          • ② C语言调用Python代码
          • ⑤ 封装成(face.c),创建(face.h头文件)提供接口,创建(main.c主程序)——最终的代码放到⑥
          • ⑥ 最终项目的结构和代码
          • 结构
          • 代码
          • main.c(主程序)
          • face.h(头文件接口)
          • face.c (C语言调用Python文件)
          • face.py(python人脸搜索代码)
    • 3. POSIX消息队列
      • 3.0 消息队列基础知识链接:[https://blog.csdn.net/Thenunaoer/article/details/143307134](https://blog.csdn.net/Thenunaoer/article/details/143307134)
      • 3.1 示例1:开两个线程,使用阻塞方式去读写消息队列的数据
        • 代码流程图:
        • 代码:
      • 3.2 示例2: (mq_notify、sigev_notify = SIGEV_THREAD)通过注册信号事件通知,使用异步通知的方式实现去读写消息队列的数据
        • 代码流程图:
        • 流程图说明:
        • 代码:
    • 4.先不交叉编译版本
      • 问题合集:


前言


一、搭建开发环境

我们需要在window系统的PC机上配置一个开发环境,以便与OrangePi和相关硬件组件进行交互。下面是详细步骤:

1.需要做什么?

OrangePi Zero2(全志H616芯片)开发板在使用前,需要先刷机
OrangePi Zero2(全志H616芯片) 系统启动
在PC上安装虚拟机VM(安装VirtualBox或VMware:这是常用的虚拟机软件工具)
在虚拟机VM(VirtualBox或VMware)上安装Linux系统 (Ubuntu、Debian)
安装Python、wiringOP和所需的库。
设置Socket编程进行远程控制。
配置阿里云SDK进行人脸识别。
使用Python脚本测试硬件连接。
makefile不知道要不要?

1.1 刷机和启动OrangePi Zero2(全志H616芯片)

就像买了电脑,出厂带有windows操作系统,才算是真正的电脑,
所以开发板需要烧写对应的系统固件,才能正常发挥作用。

1.2 在PC上安装虚拟机VM(安装VirtualBox或VMware:这是常用的虚拟机软件工具)

1.3 在虚拟机VM(VirtualBox或VMware)上安装Linux系统 (Ubuntu、Debian)

在Windows系统的PC机上实现基于OrangePi Zero2(全志H616芯片)的项目,OrangePi顾名思义就叫香橙派,为了顺口,下面就都叫做实现基于香橙派(全志H616芯片)项目了。
首先因为香橙派(全志H616芯片)运行的是Linux系统 (Debian, Ubuntu等,Debian是Ubuntu的爸巴),
所以我们要在PC机的window上安装虚拟机(相当于在电脑里虚拟出另外一台电脑,可以装其它的系统),然后在虚拟机里安装linux系统。
这样我们就可以在自己PC机的window环境中,并在这个环境中的虚拟机的Linux环境中开发Linux的项目了。
开发完项目,写完的代码,我们可以通过①串口烧录或者②SSH登录开发板的方式,将代码传给香橙派。
在这里插入图片描述

1.4 安装Python、wiringOP和所需的库。

开发香橙派(全志H616芯片)项目的过程中,我们在写智能中控的主代码执行逻辑时,我们的主代码需要调用到一些接口,来实现一些功能。所以我们会调用到一些库和函数,比如:

wiringOP库——这是最基本的外设库,提供了很多操作香橙派外设的功能接口。就比如香橙派的gpio口的控制,我们安装下载这个库,这样实现一些关于香橙派的gpio口还有其它外设的功能就不用自己写了。
在PC机(Window环境)的虚拟机(Linux环境)上安装wiringOP库,因为在虚拟机(Linux环境)开发过程中要调用到这个库。然后也要下载或传到香橙派的linux系统中,因为等我们在虚拟机中开发完并把代码传到香橙派时,香橙派用的也是这份代码,所以香橙派在执行主代码时候也需要调用到这个库。

安装Python——在PC机(Window环境)的虚拟机(Linux环境)上安装Python,同样也要在香橙派的linux系统中安装Python(在香橙派 3.0.6版本的镜像里已经默认自带了python3.10的版本,不需要安装,只需要后续安装下python3 dev即可)。
因为我们在linux开发过程中使用的是C语言,但是我们需要调用到阿里巴巴的图像识别和人脸识别的接口(这个接口支持Python和java语言,官网会提供Python和java语言的demo代码给我们去调用图像识别和人脸识别的接口)。
这里我们使用阿里巴巴提供的python语言demo代码去调用图像识别和人脸识别的接口,但是我们开发又是使用c语言,所以我们需要使用c语言代码去调用Python语言代码,让Python语言代码再去调用人脸识别的接口。所以我们需要安装Python开发环境。

——————————————————

不用操作1.2、1.3、1.4,直接从1.1接上1.5。

正常来说,在PC机上用虚拟机装linux环境开发是按照上面的顺序。但是MobaXterm软件可以通过SSH远程连接上嵌入式设备,直接就可以通过MobaXterm软件操作到香橙派的Linux 系统,直接在MobaXterm上进行香橙派的开发。

——————————————————

1.5 安装MobaXterm 软件,在PC机上远程连接到OrangePi Zero2(linux系统) 进行代码开发

直接下载安装就好了。
使用 MobaXterm 软件,就不需要在电脑上安装虚拟机来运行 Linux 环境了。使用MobaXterm软件通过SSH远程连接上嵌入式设备,就可以直接通过MobaXterm软件操作到香橙派的Linux 系统,直接在MobaXterm上进行香橙派的开发。

这句话有待考证,后面学完再回来看:
但是写完代码,可能香橙派(全志H616芯片)的CPU运算比较慢,编译代码会很忙,特别是项目特别大的时候。所以我们需要在用电脑的CPU运算,在电脑上进行编译项目代码(这里又涉及到交叉编译了,使用交叉编译工具链编译适用于嵌入式设备的代码,)。编译完后将生成的可执行文件传到香橙派(全志H616芯片)上运行。(除了香橙派,其它的嵌入式设备交叉编译也一样)

所以在你的智能家居项目中,如果 OrangePi Zero2 已经搭载了 Linux 系统,MobaXterm 就可以很方便地进行开发、文件传输和远程管理。如果需要本地编译代码,再传到 OrangePi 上,你可以考虑在 PC 上使用 Linux 或虚拟机做交叉编译。

用MobaXterm来开发基于OrangePi Zero2的智能家居项目,有以下好处:

  1. 远程连接:MobaXterm支持SSH、SFTP等协议,可以让你通过网络远程登录OrangePi设备,进行命令行操作或文件传输,而不必每次都直接接触硬件。

  2. 图形化文件管理:使用SFTP功能,你可以通过图形化界面在你的PC与OrangePi之间传输文件,省去使用命令行复制文件的繁琐步骤。

  3. 终端管理:它提供一个直观的终端窗口来输入Linux命令,如同在Linux机器本地操作一样,方便调试和配置你的OrangePi项目。

  4. 多任务支持:MobaXterm支持多标签页,可以同时管理多个连接或任务,提升你的工作效率,特别适合你同时处理多个设备或调试多个服务时。

  5. X11转发:MobaXterm可以通过SSH连接支持X11转发功能,也就是可以在本地Windows电脑上显示OrangePi上运行的图形界面程序。
    在这里插入图片描述

1.5.1其它企业开发嵌入式设备时,会使用到的方式

在企业中,开发嵌入式设备时,一般会使用以下方式:

  1. 远程开发:使用工具如 MobaXterm(这里使用的就是这个)、PuTTY 或 SSH,远程登录到嵌入式设备,直接在设备上进行开发和调试,特别是当设备已经安装了 Linux 系统时,这种方式比较常见。

  2. 交叉编译:在开发电脑上(通常是 Linux 环境)进行代码编写,然后使用交叉编译工具链编译适用于嵌入式设备的代码,最后将生成的可执行文件传到设备上运行。

  3. IDE 与调试工具:有些开发企业会使用专门的 IDE(集成开发环境)如 Eclipse、VS Code、或 JetBrains CLion 来进行代码编写和调试。这些 IDE 可以通过插件或扩展来支持嵌入式开发,还可以集成调试工具如 GDB。

  4. 硬件调试器:对于较底层的开发(如驱动程序开发),有些企业会使用硬件调试器(如 JTAG)直接连接到设备,以进行更加深入的调试和监控。

1.6 安装wiringPi库——官方的外设SDK

wiringOP库——这是最基本的外设库,提供了很多操作香橙派外设的功能接口。就比如香橙派的gpio口的控制,我们安装下载这个库,这样实现一些关于香橙派的gpio口还有其它外设的功能就不用自己写了。

2.怎么做?

2.1 硬件:Orangepi Zero2 全志H616开发板

在这里插入图片描述

2.2 配套操作系统支持说明—Orangepi Zero2 全志H616开发板

建议使用内核版本为linux4.9的镜像,5.13版本的还存在一些bug,它们的程序员没有写好。
这里改了,后续用的镜像内核版本是linux5.16版,因为它自带了python3.10,我们后面会用到python,使用直接安装这个linux5.16版的操作系统,我们就不用安装python了。镜像文件:Orangepizero2_3.0.6_ubuntu_jammy_desktop_xfce_linux5.16.17.img)
在这里插入图片描述

2.3 刷机和系统启动

就像买了电脑,出厂带有windows操作系统,才算是真正的电脑,
所以开发板需要烧写对应的系统固件,才能正常发挥作用。

2.31 工具

Orangepi Zero2 全志H616开发板
PC机
TF卡(也称 MicroSD卡)及读卡器
操作系统镜像
SDFormatter (TF卡的格式化工具)
Win32Diskimager (刷机工具)
USB转TTL(用于系统烧写后的串口登录开发板)

2.32 工具安装

SDFormatter傻瓜式安装,Win32Diskimager傻瓜式安装

2.33 刷机和系统启动

环境搭建版本:
课程使用的镜像是香橙派3.0.6版本(linux操作系统)
(备注:在香橙派 3.0.6版本的镜像里已经默认自带了python3.10的版本,不需要安装,只需要后续安装下python3 dev即可,所以后续统一采用Orangepizero2_3.0.6_ubuntu_jammy_desktop_xfce_linux5.16.17的系统镜像)

刷机过程:
①使用SDFormatter工具格式化 TF卡(也称 MicroSD卡)

  • 把 TF卡(也称 MicroSD卡)插入TF读卡器中,
  • 然后把TF读卡器通过USB口插入电脑,电脑读取到TF卡。
  • 打开SDFormatter工具,选择TF卡那个盘格式化就好了。
    在这里插入图片描述
    在这里插入图片描述

②使用Win32Diskimager (刷机工具),把香橙派3.0.6版本的镜像文件(linux操作系统)刷写入TF卡

  • 打开Win32Diskimager (刷机工具),
  • 然后选择镜像文件:香橙派3.0.6版本,镜像文件以.img结尾(Orangepizero2_3.0.6_ubuntu_jammy_desktop_xfce_linux5.16.17.img),
  • 选择设备:插在电脑上的TF卡的盘符,
  • 然后点击写入,就会把这个香橙派3.0.6的镜像文件写入这个TF卡中。
    在这里插入图片描述

③把TF卡插入香橙派中,这个时候TF卡里面是有linux操作系统的了。
在这里插入图片描述

④香橙派通过串口连接电脑,电脑通过MobaXterm软件观察香橙派的启动情况。

  • 安装CH340驱动,香橙派使用USB转TTL模块通过串口连接电脑。电脑插上后在设备管理器/端口可以看到USB CH340
    (因为USB转TTL模块芯片是CH340,所以电脑需要安装CH340驱动,这个驱动写好了协议转换,具体的知识,可以看串口的笔记)
    在这里插入图片描述

  • 接线:
    a.USB转TTL模块的GND接到开发板的GND上
    b.USB转TTL模块的RX接到开发板的TX上
    c.USB转TTL模块的TX接到开发板的RX上
    在这里插入图片描述

  • 接着电脑打开MobaXterm软件,通过MobaXterm软件就可以观察到板子的启动情况了。
    (使用MobaXterm免费好用,类似的工具还有Putty-相对太简陋,SecurityCRT老牌工具-需要付费或者破解)
    在这里插入图片描述

⑤把香橙派插上电就可以(MobaXterm软件可能会显示报错什么的,按R重启一下软件)。香橙派上电正常启动后,MobaXterm软件显示输入用户和密码,代表刷机成功并且系统成功启动。

  • 供电:
    在这里插入图片描述

  • 香橙派的LED灯测试说明:
    在这里插入图片描述

  • 香橙派上电正常启动后,MobaXterm软件显示输入用户和密码,代表刷机成功且系统成功启动:
    在这里插入图片描述

2.34 修改登陆密码

默认密码是orangepi容易写错,为了方便,我改成密码为1
在这里插入图片描述

2.35 连接网络,查看ip地址

命令扫描周围的可以连接的WIFI热点 nmcli dev wifi
命令接入网络 nmcli dev wifi connect YXS1005 password yuanxueshe1005
查看IP地址ip addr show wlan0 (ifconfig也可以)
在这里插入图片描述

2.36 SSH登陆开发板

这是企业开发调试必用方式,比串口来说不用接线,前提是连接上网络并获得板子的IP地址,且系统做了SSH的服务器。本镜像(加载进去的香橙派的linux操作系统)自带SSH服务器,所以直接通过mobaXterm软件用SSH连接的方式登陆开发板就行。
在这里插入图片描述

设置一下用来保护存储信息(开发过程中产生的信息、记录)的密码。
我这里好像设置为qwe123。
在这里插入图片描述

设置完后,出现如下的画面,把之前通过串口连接的界面关掉就好了,之后我们就使用SSH网络连接的方式进行开发香橙派。
在这里插入图片描述

2.37 搭建SSH服务器 —— 额外的知识点

如果镜像没有自带SSH服务器,那需要自己搭建,怎么搭建呢?

————————————
到上面就已经结束了,下面是扩展内容
————————————
2.38 修改开发板内核启动日志级别

为了方便之后调试,因为香橙派开机启动的时候,加载uboot后,会加载内核(linux操作系统),那为了看到内核这过程中有没有出问题,可以修改内核的日志级别,让他把日志打印出来显示给我们看到。就很清晰的知道内核有没有问题了。

①使用sudo超级用户权限打开/boot/orangepiEnv.txt,
sudo vi /boot/orangepiEnv.txt
②修改里面的verbosity(日志级别),还有把console(输出日志信息的控制台)改为serial(串口)输出:
③ :wq 冒号wq保存退出
在这里插入图片描述
④这时候如果想看有没有改成功,可以切换到串口终端显示界面,因为我们刚刚②把console(输出日志信息的控制台)改为serial(串口)输出了。然后sudo reboot命令重启操作系统一下。就看到操作系统(内核kerne)重启时,输出了一堆操作系统(内核kernel)的信息了。
在这里插入图片描述

2.4 安装wiringPi库——官方的外设SDK

①在香橙派的linux上输入命令下载:

git clone https://github.com/orangepi-xunlong/wiringOP //下载源码
cd wiringOP                                            //进入文件夹
sudo ./build clean                                     //清除编译信息
sudo ./build                                           //编译                 

在这里插入图片描述

如果下载不了就自己去GitHub上下载,然后上传到开发板

通过windows浏览器打开`https://github.com/orangepi-xunlong/wiringOP` 下载压缩包
把压缩包通过MobaXterm传到开发板
解压 unzip xxx.zipcd  xxxsudo ./buildgpio readall

②编译完后,通过gpio readall 查看各个gpio口 或者 gpio -v查看版本,有东西出来说明就编译成功了。
在这里插入图片描述

二、基于OrangePi Zero2的智能家居

1. 需求及项目准备

  • 语音接入控制各类家电,如客厅灯、卧室灯、风扇
  • 回顾二阶段的Socket编程,实现Sockect发送指令远程控制各类家电
  • 烟雾警报监测, 实时检查是否存在煤气泄漏或者火灾警情,当存在警情时及时触发蜂鸣器报警及语音播报
  • 控制人脸识别打开房门功能,并语音播报识别成功或者失败
  • 局域网实时视频监控
  • OLED屏实话显示当前主板温度、警情信息及控制指令信息

人脸识别使用阿里SDK支持Python和Java接口,目的是复习巩固C语言的Python调用(也有看你使用翔云的SDK去实现),
此接口是人工智能接口,阿里云识别模型是通过训练后的模型,精准度取决于训练程度,人工智能范畴在常规嵌入式设备负责执行居多,
说白了嵌入式设备负责数据采集,然后转发给人工智能识别后,拿到结果进行执行器动作。

2. 开发过程

2.0 登录阿里云,创建阿里云账户,创建AccessKey。用来链接阿里云,获取权限。

2.1 把人的照片上传录进数据库

2.2 安装阿里云视觉智能API——人脸识别的SDK(后面人脸识别的python代码导入的库会用到)

pip install alibabacloud_facebody20191230

2.3 使用pip命令之前先安装pip命令,

sudo apt install python3-pip (通过APT包管理器安装pip。当运行这个命令时,你需要有管理员权限,所以使用了 sudo)

2.4 把AccessKey的账号和密码提供给orangePi,让它能通过AccessKey账号链接到阿里云,使用权限。

  • vi打开bash的rc(Resource Configuration )资源配置
    vi ~/.bashrc

  • 在bashrc配置文件,最后的结尾添加导入环境变量:
    ALIBABA_CLOUD_ACCESS_KEY_ID和 ALIBABA_CLOUD_ACCESS_KEY_SECRET环境变量,
    在垃圾分类的项目里如果已经添加过就不需要添加了

export ALIBABA_CLOUD_ACCESS_KEY_ID="你的KEY_ID"
export ALIBABA_CLOUD_ACCESS_KEY_SECRET="你的KEY_SECRECT"

2.5 这里用官方提供的人脸搜索Python示例代码试试:

# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_facebody20191230import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import SearchFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptionsconfig = Config(# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='facebody.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)search_face_request = SearchFaceAdvanceRequest()
#场景一:文件在本地
stream0 = open(r'/tmp/SearchFace.jpg', 'rb') #把需要识别的照片放这个目录下
search_face_request.image_url_object = stream0#场景二:使用任意可访问的url
#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/facebody/SearchFace1.png'  #从url地址上读取照片
#img = urlopen(url).read()
#search_face_request.image_url_object = io.BytesIO(img)
search_face_request.db_name = 'default'
search_face_request.limit = 5runtime_option = RuntimeOptions()
try:# 初始化Clientclient = Client(config)response = client.search_face_advance(search_face_request, runtime_option)# 获取整体结果print(response.body)
except Exception as error:# 获取整体报错信息print(error)# 获取单个字段print(error.code)# tips: 可通过error.__dict__查看属性名称#关闭流
#stream0.close()
  • 代码保存成face.py文件
  • 把代码中场景二的部分注释掉,从本地(香橙派)获取图片
  • 把需要识别的照片放代码的这个目录下stream0 = open(r'/tmp/SearchFace.jpg', 'rb')
  • 搜索人脸的请求的数据库名称 改成 我们上传到阿里巴巴人脸识别数据库的名字default:
    search_face_request.db_name = 'default'
  • 在香橙派linux命令终端执行 python3 face.py
    在这里插入图片描述

2.6 执行后返回的结果是json格式的,结果如下:

在这份结果中,一般比对成功的Python字典数据里的score会有大于0.6的值,而比对失败score普遍低于0.1。
例如下面是比对成功的数据:

{'Data': 
{'MatchList': 
[
{'FaceItems': 
[
{'Confidence': 80.54945, 'DbName': 'default', 'EntityId': 'wwx', 'FaceId': '88665949', 'Score': 0.7572572231292725}, 
{'Confidence': 77.51004, 'DbName': 'default', 'EntityId': 'wwx', 'FaceId': '88665951', 'Score': 0.7193253040313721}, 
{'Confidence': 74.420425, 'DbName': 'default', 'EntityId': 'wwx', 'FaceId': '88665946', 'Score': 0.6665557622909546}, 
{'Confidence': 11.461451, 'DbName': 'default', 'EntityId': 'gyx', 'FaceId': '88657431', 'Score': 0.0663260966539383}, 
{'Confidence': 5.28706, 'DbName': 'default', 'EntityId': 'gyx', 'FaceId': '88657429', 'Score': 0.030595608055591583}
], 
'Location': {'Height': 527, 'Width': 405, 'X': 136, 'Y': 123}, 
'QualitieScore': 99.3521}
]
}, 
'RequestId': '6DE302BB130A-5D3C-B83D-0937D5A257FD'}

比对失败的数据则如下所示:

{'Data': {'MatchList': [{'FaceItems': [
{'Confidence': 6.137868, 'DbName': 'default', 'EntityId': 'gyx', 'FaceId': '88657429', 'Score': 0.03551913797855377}, 
{'Confidence': 2.9869182, 'DbName': 'default', 'EntityId': 'gyx', 'FaceId': '88657433', 'Score': 0.017284952104091644}, 
{'Confidence': 2.0808065, 'DbName': 'default', 'EntityId': 'gyx', 'FaceId': '88657431', 'Score': 0.01204138807952404}, 
{'Confidence': 0.71279377, 'DbName': 'default', 'EntityId': 'wwx', 'FaceId': '88657430', 'Score': 0.004124855622649193}, 
{'Confidence': 0.0, 'DbName': 'default', 'EntityId': 'wwx', 'FaceId': '88665951', 'Score': -0.09112970530986786}], 
'Location': {'Height': 257, 'Width': 173, 'X': 156, 'Y': 42}, 'QualitieScore': 99.673065}]}, 'RequestId': '62C20100-CCAC-5FE2-9BA6AE583F0056EF'}

因此,就可以利用获取的最大score的值判断是否大于0.6来判断是否比对成功。
返回数据的说明:

Data:这是一个对象,其中包含了匹配列表的信息。
MatchList:这是一个数组,其中包含了匹配的结果。每个元素都是一个对象,代表一个匹配项。
FaceItems:这是一个数组,其中包含了匹配项中所有人脸的信息。每个元素都是一个对象,包含了一些关于
该人脸的信息,如自信度(Confidence)、数据库名(DbName)、实体ID(EntityId)、面部ID
(FaceId)和分数(Score)。
Location:这是一个对象,包含了人脸在原始图像中的位置信息,包括宽度(Width)、高度(Height)、
左上角的x坐标(X)和y坐标(Y)。
QualitieScore:这是一个浮点数,表示了整个匹配过程的质量得分。

2.7 使用C语言去调用上面的人脸搜索Python代码

2.71 封装人脸识别python代码

把上面的人脸搜索的python代码封装成 def alibaba_face(): 函数,方便C程序代码去调用,修改python代码,我们只要获取返回结果中最大的score值,修改后人脸搜索的python代码如下:

# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_facebody20191230import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import SearchFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptionsconfig = Config(# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='facebody.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)def alibaba_face():search_face_request = SearchFaceAdvanceRequest()#场景一:文件在本地stream0 = open(r'/tmp/SearchFacew2.jpg', 'rb')search_face_request.image_url_object = stream0#场景二:使用任意可访问的url#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/facebody/SearchFace1.png'#img = urlopen(url).read()#search_face_request.image_url_object = io.BytesIO(img)search_face_request.db_name = 'default'search_face_request.limit = 5runtime_option = RuntimeOptions()try:# 初始化Clientclient = Client(config)response = client.search_face_advance(search_face_request, runtime_option)# 打印全部结果#print(response.body)# 获取结果中的MatchList列表match_list = response.body.to_map()['Data']['MatchList']# 获取结果中的MatchList列表中的FaceItems列表中的提取所有'Score'键的值,# 并将这些值存储在一个新的列表 scores 中。scores = [item['Score'] for item in match_list[0]['FaceItems']]  highest_score = max(scores)  # 获取最大的scores值value = round(highest_score, 2)  # scores值保留两位小数#print(match_list)#print(value)return valueexcept Exception as error:# 获取整体报错信息print(error)# 获取单个字段print(error.code)return 0.0# tips: 可通过error.__dict__查看属性名称#关闭流#stream0.close()if __name__ == "__main__":alibaba_face()
2.72 C语言调用python
① 搭建编译环境

科普知识点:
在写C语言调用Python代码时,我们会调用到很多函数,这些函数都在libpython3的 dev依赖库的库文件里面,所以需要安装libpython3的 dev依赖库,然后通过导入【依赖库的头文件】建立接口(API)来使用 【依赖库的库文件】中的函数。(依赖包/依赖库中有很多库文件和头文件,库文件里面有许多功能函数,我们通过头文件提供的接口(API)去引用这些功能函数)

所以通过C语言调用Python代码前,需要先安装libpython3的 dev依赖库(不同的ubuntu版本下,python版本,可能会有差异, 比如ubuntu 22.04里是libpython3.10-dev)。
首先可以通过以下命令验证是否是否已经存在python3的dev包

dpkg -l | grep libpython3

正常会有类似如下的输出,出现"libpython3"和 “dev”,如libpython3.10-dev即可:
在这里插入图片描述

如果没有, 可以通过apt命令安装相关的dev包:

sudo apt update
sudo apt install libpython3.10-dev
② C语言调用Python代码

参照之前5.3.3节的C语言调用有参python函数的做法,实现用C语言程序去调用上面封装的人脸搜索python代码,代码如下:

  • 这里先在face.c里写个main主函数先测试一下
  • 编译:gcc face.c -o face -I /usr/include/python3.10 -L /usr/lib/python3.10 -lpython3.10
    (gcc 源文件 -o 可执行文件 -l链接头文件路径 -L链接库路径 -链接库)
    这里的-I、-L、 -l可以看交叉编译这章的——Makefile文件结构解析)
  • 执行:./face
  • 执行结果如下:
    在这里插入图片描述
#include <Python.h>  //包含Python.h头文件,以便使用Python APIvoid face_init(void)
{// 初始化Python环境(初始化Python解释器)Py_Initialize();// 获取sys.path对象,将当前路径"."添加到sys.path中,使得Python可以在当前路径"."中查找模块// 导入Python的'sys'模块PyObject *sys = PyImport_ImportModule("sys");// 获取'sys'模块中的'path'属性PyObject *path = PyObject_GetAttrString(sys, "path");// 将"."添加到'sys.path'中,使得Python可以在当前目录查找模块PyList_Append(path, PyUnicode_FromString("."));
}void face_final(void)
{// 结束Python环境(释放Python解释器)Py_Finalize();
}double face_category(void)
{// 导入Python文件'face.py',并检查是否有错误PyObject *pModule = PyImport_ImportModule("face"); //1.导入我们要调用的python文件,face.py// 如果导入失败if (!pModule){// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: failed to load face.py\n");// 跳转到模块加载失败处理部分goto FAILED_MODULE;}// 获取'face.py'文件中的'alibabacloud_face'函数,并检查是否可调用PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_face"); //2.加载,加载这个python文件中的我们需要调用的功能// 如果获取函数失败if (!pFunc){// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: failed to load alibabacloud_face\n");// 跳转到函数加载失败处理部分goto FAILED_FUNC;}// 3.加载完了,我们就调用这个函数('alibabacloud_face'函数),并获取返回值PyObject *pValue = PyObject_CallObject(pFunc, NULL);// 如果函数调用失败if (!pValue){// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: function call failed\n");// 跳转到函数调用失败处理部分goto FAILED_VALUE;}// 4.从alibab_face函数返回值中,解析double浮点数返回值,将结果转换为C语言格式,// 并检查是否有错误,没有返回值时不需要调用。double result = 0.0;if (!PyArg_Parse(pValue, "d", &result)) {// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: parse failed");// 跳转到解析失败处理部分goto FAILED_RESULT;}//printf("result=%lf\n",result); //封装起来后,在主函数中打印//这是之前用来处理字符串的,// // 分配内存来存储解析得到的结果字符串// category = (char *)malloc(sizeof(char) * (strlen(result) + 1) );// // 初始化分配的内存// memset(category, 0, (strlen(result) + 1));// // 复制结果字符串到新分配的内存中// strncpy(category, result, (strlen(result) + 1));FAILED_RESULT:// 释放函数返回值对象Py_DECREF(pValue);
FAILED_VALUE:// 释放函数对象Py_DECREF(pFunc);
FAILED_FUNC:// 释放模块对象Py_DECREF(pModule);
FAILED_MODULE:// 返回原来的类别字符串return result;}//if..endif结构,可以用来注释,如果是0就不执行,如果是1就执行。
#if 1   
int main(int argc,char *argv[])
{double face_result = 0.0;face_init();face_result = face_category();printf("face_result=%lf\n",face_result);face_final();
}
#endif
⑤ 封装成(face.c),创建(face.h头文件)提供接口,创建(main.c主程序)——最终的代码放到⑥
  • 封装成(face.c文件),直接把face.c的main主函数注释掉,把if 1 设置为 if 0
  • 创建(face.h文件)提供接口
  • 创建(main.c主程序文件)通过(face.h文件)提供接口,去调用face.c中的函数
  • 执行gcc main.c face.c -o face2 -I /usr/include/python3.10 -L /usr/lib/python3.0/ -lpython3.10
    (gcc 源文件 -o 可执行文件 -l链接头文件路径 -L链接库路径 -链接库)
  • 执行结果
    在这里插入图片描述
⑥ 最终项目的结构和代码
结构

main.c通过导入"face.h"得到接口,去调用face.c,face.c再去调用face.py。
在这里插入图片描述

代码

main.c(主程序)
//main.c
#include <stdio.h>
#include"face.h"int main(int argc,char *argv[])
{double face_result = 0.0;face_init();face_result = face_category();printf("face_result=%lf\n",face_result);face_final();
}

face.h(头文件接口)
//face.h#ifndef __FACE__H#define __FACE__Hvoid face_init(void);void face_final(void);double face_category(void);#endif

face.c (C语言调用Python文件)
//face.c#include <Python.h>  //包含Python.h头文件,以便使用Python APIvoid face_init(void)
{// 初始化Python环境(初始化Python解释器)Py_Initialize();// 获取sys.path对象,将当前路径"."添加到sys.path中,使得Python可以在当前路径"."中查找模块// 导入Python的'sys'模块PyObject *sys = PyImport_ImportModule("sys");// 获取'sys'模块中的'path'属性PyObject *path = PyObject_GetAttrString(sys, "path");// 将"."添加到'sys.path'中,使得Python可以在当前目录查找模块PyList_Append(path, PyUnicode_FromString("."));
}void face_final(void)
{// 结束Python环境(释放Python解释器)Py_Finalize();
}double face_category(void)
{// 导入Python文件'face.py',并检查是否有错误PyObject *pModule = PyImport_ImportModule("face"); //1.导入我们要调用的python文件,face.py// 如果导入失败if (!pModule){// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: failed to load face.py\n");// 跳转到模块加载失败处理部分goto FAILED_MODULE;}// 获取'face.py'文件中的'alibabacloud_face'函数,并检查是否可调用PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_face"); //2.加载,加载这个python文件中的我们需要调用的功能// 如果获取函数失败if (!pFunc){// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: failed to load alibabacloud_face\n");// 跳转到函数加载失败处理部分goto FAILED_FUNC;}// 3.加载完了,我们就调用这个函数('alibabacloud_face'函数),并获取返回值PyObject *pValue = PyObject_CallObject(pFunc, NULL);// 如果函数调用失败if (!pValue){// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: function call failed\n");// 跳转到函数调用失败处理部分goto FAILED_VALUE;}// 4.从alibab_face函数返回值中,解析double浮点数返回值,将结果转换为C语言格式,// 并检查是否有错误,没有返回值时不需要调用。double result = 0.0;if (!PyArg_Parse(pValue, "d", &result)) {// 打印错误信息PyErr_Print();// 输出错误信息printf("Error: parse failed");// 跳转到解析失败处理部分goto FAILED_RESULT;}//printf("result=%lf\n",result); //封装起来后,在主函数中打印//这是之前用来处理字符串的,// // 分配内存来存储解析得到的结果字符串// category = (char *)malloc(sizeof(char) * (strlen(result) + 1) );// // 初始化分配的内存// memset(category, 0, (strlen(result) + 1));// // 复制结果字符串到新分配的内存中// strncpy(category, result, (strlen(result) + 1));FAILED_RESULT:// 释放函数返回值对象Py_DECREF(pValue);
FAILED_VALUE:// 释放函数对象Py_DECREF(pFunc);
FAILED_FUNC:// 释放模块对象Py_DECREF(pModule);
FAILED_MODULE:// 返回原来的类别字符串return result;}//if..endif结构,可以用来注释,如果是0就不执行,如果是1就执行。
#if 0   
int main(int argc,char *argv[])
{double face_result = 0.0;face_init();face_result = face_category();printf("face_result=%lf\n",face_result);face_final();
}
#endif

face.py(python人脸搜索代码)
#face.py# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_facebody20191230import os
import io
from urllib.request import urlopen
from alibabacloud_facebody20191230.client import Client
from alibabacloud_facebody20191230.models import SearchFaceAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptionsconfig = Config(# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html。# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),# 访问的域名endpoint='facebody.cn-shanghai.aliyuncs.com',# 访问的域名对应的regionregion_id='cn-shanghai'
)def alibaba_face():search_face_request = SearchFaceAdvanceRequest()#场景一:文件在本地stream0 = open(r'/tmp/SearchFacew2.jpg', 'rb')search_face_request.image_url_object = stream0#场景二:使用任意可访问的url#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/facebody/SearchFace1.png'#img = urlopen(url).read()#search_face_request.image_url_object = io.BytesIO(img)search_face_request.db_name = 'default'search_face_request.limit = 5runtime_option = RuntimeOptions()try:# 初始化Clientclient = Client(config)response = client.search_face_advance(search_face_request, runtime_option)# 打印全部结果#print(response.body)# 获取结果中的MatchList列表match_list = response.body.to_map()['Data']['MatchList']# 获取结果中的MatchList列表中的FaceItems列表中的提取所有'Score'键的值,# 并将这些值存储在一个新的列表 scores 中。scores = [item['Score'] for item in match_list[0]['FaceItems']]  highest_score = max(scores)  # 获取最大的scores值value = round(highest_score, 2)  # scores值保留两位小数#print(match_list)#print(value)return valueexcept Exception as error:# 获取整体报错信息print(error)# 获取单个字段print(error.code)return 0.0# tips: 可通过error.__dict__查看属性名称#关闭流#stream0.close()if __name__ == "__main__":alibaba_face()

3. POSIX消息队列

3.0 消息队列基础知识链接:https://blog.csdn.net/Thenunaoer/article/details/143307134

3.1 示例1:开两个线程,使用阻塞方式去读写消息队列的数据

代码流程图:

在这里插入图片描述


代码:
 // 包含所需的头文件  
#include <pthread.h>  // POSIX线程库  
#include <stdio.h>    // 标准输入输出库  
#include <stdlib.h>   // 标准库  
#include <unistd.h>   // UNIX标准库(sleep函数)  
#include <mqueue.h>   // POSIX消息队列库  
#include <string.h>   // 字符串处理库  // 定义消息队列的名称和要发送的消息  
#define QUEUE_NAME "/test_queue"  // 消息队列的名称  
#define MESSAGE "Hello, World!"   // 要发送的消息  // 全局的消息队列描述符  
mqd_t mqd;  // sender_thread函数:发送线程的主体  
void *sender_thread(void *arg) {    char message[] = MESSAGE;  // 创建要发送的消息的副本  printf("Sender thread started.\n");  // 打印发送线程开始的消息  sleep(10); //延时10秒,测试的时候看现象mq_send(mqd, message, strlen(message) + 1, 0);  // 发送消息到消息队列  printf("Message sent.\n");  // 打印消息已发送的消息  return NULL;  // 返回NULL,表示线程正常结束  
}  // receiver_thread函数:接收线程的主体  
void *receiver_thread(void *arg) {    char buffer[256];  // 创建用于接收消息的缓冲区  printf("Receiver thread started.\n");  // 打印接收线程开始的消息  sleep(20); //延时30秒,测试的时候看现象mq_receive(mqd, buffer, sizeof(buffer), NULL);  // 从消息队列接收消息  printf("Received message: %s\n", buffer);  // 打印已接收的消息  return NULL;  // 返回NULL,表示线程正常结束  
}  // main函数:程序的入口点  
int main() {    pthread_t sender, receiver;  // 创建发送和接收线程的标识符  struct mq_attr attr;  // 创建消息队列属性结构体变量  // 设置消息队列的属性值  attr.mq_flags = 0;  // 消息队列的标志位设置为0  attr.mq_maxmsg = 10;  // 消息队列的最大消息数设置为10  attr.mq_msgsize = 256;  // 消息队列的每个消息的最大大小设置为256字节  attr.mq_curmsgs = 0;  // 消息队列的当前消息数设置为0  // 打开或创建名为QUEUE_NAME的消息队列,并设置其属性为attr指定的值  mqd = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0666, &attr);    if (mqd == (mqd_t)-1) {  // 如果打开或创建失败,则打印错误信息并返回1  perror("mq_open");    return 1;    }  // 创建发送线程,如果创建失败,则打印错误信息并返回1  if (pthread_create(&sender, NULL, sender_thread, NULL) != 0) {    perror("pthread_create (sender)");    return 1;    }  // 创建接收线程,如果创建失败,则打印错误信息并返回1  if (pthread_create(&receiver, NULL, receiver_thread, NULL) != 0) {    perror("pthread_create (receiver)");    return 1;    }  // 等待发送线程结束,如果发送线程已经结束,则立即返回,否则阻塞等待其结束  pthread_join(sender, NULL);    // 等待接收线程结束,如果接收线程已经结束,则立即返回,否则阻塞等待其结束  pthread_join(receiver, NULL);    // 关闭已打开的消息队列描述符mq所引用的消息队列,并释放由该描述符占用的所有资源  mq_close(mqd);    sleep(25);//延时30秒,测试的时候看现象// 删除名为QUEUE_NAME的消息队列,并将其从系统中删除,如果成功则返回0,否则返回-1并设置errno以指示错误。mq_unlink(QUEUE_NAME);  // 删除消息队列return 0;  // 程序正常退出,返回0}

3.2 示例2: (mq_notify、sigev_notify = SIGEV_THREAD)通过注册信号事件通知,使用异步通知的方式实现去读写消息队列的数据

代码流程图:

完整版:
在这里插入图片描述


简单版:


+------------------------+
| 开始                   |
| (main 函数启动程序)    |
+------------------------+|v
+------------------------+
| 初始化消息队列         |
| - 设置最大消息数 10    |
| - 每条消息最大 256 字节 |
+------------------------+|v
+------------------------+
| 设置通知事件           |
| - 使用 SIGEV_THREAD    |
| - 注册 notify_thread   |
|   作为回调函数         |
+------------------------+|v
+------------------------+
| 创建发送线程           |
| - 创建 sender_thread   |
+------------------------+|v
+------------------------+
| 在发送线程中发送消息   |
| - 发送消息内容:        |
|   "mqueue, test!"      |
+------------------------+|v
+------------------------+
| 通知线程接收消息       |
| (notify_thread 触发)   |
+------------------------+|v
+------------------------+
| 处理接收的消息         |
| - 显示消息内容         |
| - 打印消息大小         |
+------------------------+|v
+------------------------+
| 重新注册通知事件       |
| - 再次调用 mq_notify   |
+------------------------+|v
+------------------------+
| 关闭消息队列           |
| 删除消息队列           |
+------------------------+|v
+------------------------+
| 程序结束               |
+------------------------+

流程图说明:
  1. 开始:程序启动,执行 main 函数。
  2. 初始化消息队列:定义消息队列的属性,设置最大消息数、每条消息大小等。
  3. 设置通知事件:将 notify_thread 设置为回调函数,通过 mq_notify 注册,当消息到达时触发。
  4. 创建发送线程:创建发送线程 sender_thread,准备发送消息。
  5. 发送消息sender_thread 线程向消息队列发送消息 “mqueue, test!”。
  6. 通知线程接收消息:当有消息到达时触发 notify_thread,接收消息。
  7. 死循环处理接收的消息notify_thread 中显示消息内容和大小。
  8. (这里没有用到,如果上面不是死循环接收消息,就需要重新注册通知事件,反复执行:有消息来了就获取消息)
    重新注册通知事件
    :重新调用 mq_notify 注册通知事件,以便再次触发。
  9. 关闭消息队列:关闭并删除消息队列,释放资源。
  10. 程序结束:程序执行完毕,退出。

代码:
// 引入消息队列的头文件,包含消息队列的基本操作函数
#include <mqueue.h> 
// 引入线程处理的头文件,用于创建和管理线程
#include <pthread.h>
// 引入标准输入输出的头文件,用于打印信息
#include <stdio.h>
// 引入错误处理的头文件,用于获取和处理错误码
#include <errno.h>
// 引入字符串处理的头文件,用于字符串操作
#include <string.h>
// 引入通用 UNIX I/O 操作的头文件,用于一些通用的操作函数
#include <unistd.h>
// 引入标准库函数的头文件,用于一些标准库函数
#include <stdlib.h>
// 引入信号处理的头文件,用于处理信号
#include <signal.h>// 以下是一些消息队列的基本操作函数的声明,但由于 #if 0,这部分代码不会被编译
#if 0 
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr attr );
// 打开或创建一个消息队列,返回消息队列描述符int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); 
// 向消息队列发送消息ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio); 
// 从消息队列接收消息int mq_close(mqd_t mqdes);
// 关闭消息队列int mq_unlink(const char *name);
// 删除消息队列struct mq_attr {long mq_flags;//阻塞标志, 0(阻塞)或O_NONBLOCKlong mq_maxmsg;//最大消息数long mq_msgsize;//每个消息最大大小long mq_curmsgs;//当前消息数
};
// 消息队列属性的结构体,定义了消息队列的一些属性union sigval {            /* 数据传递给通知 */int     sival_int;    /* 整数值 */void   *sival_ptr;    /* 指针值 */
};
// 传递给信号事件中的回调函数的参数,可以传递整数或指针类型的值struct sigevent {int    sigev_notify;  /* 通知方式 */int    sigev_signo;   /* 通知信号 */union sigval sigev_value;/* 数据传递给通知 */void (*sigev_notify_function) (union sigval);/* 用于线程通知的回调函数 */void  *sigev_notify_attributes;/* 用于线程通知的属性 */pid_t  sigev_notify_thread_id;/* 用于线程通知的目标线程ID */
};
// 用于设置信号事件的通知方式,包括回调函数和参数
#endif// 定义消息队列的名字
#define QUEQUE_NAME "/test_queue"
// 定义发送的消息内容
#define MESSAGE "mqueque,test!"
// 线程函数————发送数据
void *sender_thread(void *arg)
{   // 从传入的参数中获取消息队列描述符mqd_t mqd = *(mqd_t *)arg; int send_size = -1;        // 用于存储发送消息的返回值char message[] = MESSAGE;  // 发送的消息内容// 打印发送的消息内容和消息队列描述符printf("sender thread message=%s, mqd=%d\n", message, mqd);// 发送消息————使用 mq_send 函数来发送一个消息到指定的消息队列// strlen(message) + 1 是为了包括字符串末尾的 '\0'send_size = mq_send(mqd, message, strlen(message) + 1, 0);if (-1 == send_size)  //成功发送后,函数返回0,否则返回-1,并设置 errno 来表明错误的原因。{if (errno == EAGAIN){printf("message queque is full\n"); // 如果消息队列满了,打印提示}else{perror("mq_send"); // 如果发送失败,打印错误信息}}return NULL; // 线程结束
}// 线程函数————信号事件的回调函数
void notify_thread(union sigval sval)
{// 初始化消息队列描述符为 -1mqd_t mqd = -1; mqd = *((mqd_t *)sval.sival_ptr); // 从传入的参数中获取消息队列描述符char buffer[256]; // 定义一个缓冲区,用于存储接收到的消息memset(buffer,0,sizeof(buffer));//清空一下缓冲区ssize_t bytes_read; // 定义一个变量,用于存储接收到的消息的大小struct sigevent sev; // 定义一个结构体,用于重新注册消息队列的通知printf("notify_thread started, mqd=%d\n", mqd);// 打印提示信息,表明线程开始运行while (1){// 使用 q_receive函数 从消息队列中接收消息到buffer中// 并返回 收到的消息的大小 给bytes_readbytes_read = mq_receive(mqd, buffer, 256, NULL);if (bytes_read == -1){if (errno == EAGAIN){printf("queue is empty\n"); // 如果队列为空,打印提示break; // 跳出循环}else{perror("mq_receive"); // 如果接收失败,打印错误信息exit(1); // 退出程序}}// 如果接收成功,打印接收到的消息的大小和内容printf("read %ld bytes: %s\n", (long)bytes_read, buffer);}//上面死循环了,可以一直获取信息。下面不用重新注册了,也不会执行sev.sigev_notify = SIGEV_THREAD; // 设置通知方式为线程模式sev.sigev_value.sival_ptr = &mqd; // 设置传递给回调函数的参数,传递消息队列描述符的地址sev.sigev_notify_function = notify_thread; // 设置回调函数为 notify_threadsev.sigev_notify_attributes = NULL; // 设置线程属性为 NULLif (mq_notify(mqd, &sev) == -1){perror("mq_notify"); // 如果重新注册通知失败,打印错误信息exit(1); // 退出程序}
}// 以下是一个接收线程函数,但由于 #if 0,这部分代码不会被编译
#if 0
void *receiver_thread(void *arg)
{// 从传入的参数中获取消息队列描述符mqd_t mqd = *(mqd_t *)arg; ssize_t receiver_size = -1; // 用于存储接收消息的返回值char buffer[256]; // 定义一个缓冲区,用于存储接收到的消息printf("Receive thread start\n");receiver_size = mq_receive(mqd, buffer, sizeof(buffer), NULL);// 接收消息printf("receiver thread message=%s, mqd=%d, receiver_size=%ld\n", buffer, mqd, receiver_size);// 如果接收成功,打印接收到的消息的大小和内容return NULL; // 线程结束
}
#endifint main(int argc, char *argv[])
{// 定义发送线程和接收线程的IDpthread_t sender, receiver; //—————————一、给当前我们现在的进程创建消息队列mqd,并设置消息队列mqd的属性————————————// 初始化消息队列描述符为 -1mqd_t mqd = -1; // 定义消息队列属性结构体struct mq_attr attr; attr.mq_flags = 0; // 设置阻塞标志为 0(阻塞模式)attr.mq_maxmsg = 10; // 设置最大消息数为 10attr.mq_msgsize = 256; // 设置每个消息的最大大小为 256 字节attr.mq_curmsgs = 0; // 设置当前消息数为 0// 打开或创建消息队列,设置为读写模式,并传入消息队列属性mqd = mq_open(QUEQUE_NAME, O_CREAT | O_RDWR, 0666, &attr);if (mqd == (mqd_t)-1 ){perror("mq_open"); // 如果打开失败,打印错误信息return -1; // 返回错误码}// 定义一个结构体,用于设置信号事件的通知方式struct sigevent sev; sev.sigev_notify = SIGEV_THREAD; // 设置通知方式为线程模式sev.sigev_notify_function = notify_thread; // 设置回调函数为 notify_threadsev.sigev_notify_attributes = NULL; // 设置线程属性为 NULLsev.sigev_value.sival_ptr = &mqd; // 设置数据为消息队列描述符的地址/*mq_notify 函数用于通知进程当中的消息队列有新消息的到达。当消息队列中消息的数量从0变为1时,系统会向指定的进程发送一个实时信号,或者通过异步I/O通知机制通知进程。*///注册通知,给消息队列mqd注册通知方式为sevif (mq_notify(mqd, &sev) == -1){perror("mq_notify"); // 如果注册通知失败,打印错误信息exit(1); // 退出程序}//—————————二、创建发送线程:创建一个线程,执行发送函数,发送数据到消息队列中————————————if (pthread_create(&sender, NULL, sender_thread, (void *)&mqd) != 0){perror("pthread_create sender"); // 如果创建发送线程失败,打印错误信息return -1; // 返回错误码}// 以下是一个创建接收线程的语句,但由于 #if 0,这部分代码不会被编译#if 0if (pthread_create(&receiver, NULL, receiver_thread,  (void *)&mqd) != 0){perror("pthread_create receiver"); // 如果创建接收线程失败,打印错误信息return -1; // 返回错误码}#endif//——————————————————————三、等待线程结束————————————————————// 等待发送线程结束pthread_join(sender, NULL); sleep(5); // 等待一段时间,确保消息全被获取到,等notify_thread(union sigval sval)执行完// 等待接收线程结束// pthread_join(receiver, NULL); mq_close(mqd); // 关闭消息队列mq_unlink(QUEQUE_NAME); // 删除消息队列return 0; // 程序正常结束
}

4.先不交叉编译版本

思路:按照只能分类项目。不交叉编译时候的,不需要src和ins和3rd目录。
只用这个命令就可以了:

**垃圾桶网络编程部分:**
编译:gcc -o category *.c *.h -I /usr/include/python3.10/ -lwiringPi -lpython3.10
gcc -o category *.c *.h -I /usr/include/python3.10/ -lwiringPi -lpython3.10
执行sudo -E ./category

3rd目录:里面包括了交叉编译时。宿主虚拟机编译时需要的wiringPi库和pthony运行链接库。C调用PYthon啥的。。。但是现在我们不交叉编译,直接在嵌入式设备香橙派中编译代码,香橙派中有这些库了-lwiringPi -lpython3.10。

1.把功能代码都给封装好。新建了两个头文件:
gdevice.h:用来管理所有的设备节点
control.h:用来建立所有所有的监听节点。监听到了就进行相关的控制操作。

2.封装消息队列.c和.h文件,用来发送和接受消息:
msg_queue.c
msg_queue.h

问题合集:

voice_interface.c的 struct control *add_voice_to_ctrl_list(struct control *phead) 可能有点问题

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

“短线看涨”,上升周期中,抓以小波段行情,落袋为安

使用技巧 短线看涨指标属于副图公式&#xff0c;短线怎么操作&#xff1f;看蓝色短期安全线 这个公式主要是在上升周期中&#xff0c;抓以小波段行情为主&#xff0c;落袋为安 弱水三千 只取一瓢 公式 DIFM:(EMA(C,240)-EMA(C,520)); DEAM:EMA(DIFM,180); MACD&#xff08…

21_双端 diff 算法

目录 双端比较的原理非理想状况的处理方式添加新元素移除不存在的元素 在上一节中&#xff0c;我们实现了简单的 diff 算法&#xff0c;简单的 diff 算法利用 key 属性&#xff0c;尽可能的复用 DOM 元素&#xff0c;并通过移动 DOM 元素来完成更新&#xff0c;从而减少不断创建…

微服务实战系列之玩转Docker(十六)

导览 前言Q&#xff1a;基于容器云如何实现高可用的配置中心一、etcd入门1. 简介2. 特点 二、etcd实践1. 安装etcd镜像2. 创建etcd集群2.1 etcd-node12.2 etcd-node22.3 etcd-node3 3. 启动etcd集群 结语系列回顾 前言 Docker&#xff0c;一个宠儿&#xff0c;一个云原生领域的…

注册信息的提交

动态网页是指能够根据用户的操作或输入动态变化的网页。与静态网页相比&#xff0c;动态网页具有交互性和可变性。 一 动态网页概念 动态网页通常使用脚本语言&#xff08;如JavaScript&#xff09;与服务器进行交互&#xff0c;从服务器获取数据并动态更新网页内容。常见的动…

aws 部署测试环境服务+ip域名绑定

aws 部署springboot vue ip域名绑定域名 1.新建实例之后&#xff0c;作为测试环境开放mysql入出站规则&#xff0c;route53域名&#xff0c;红框中放入阿里云域名 1.设置出入站规则 实例应用安全组 2.mysql aws部署&#xff0c;redis,java环境&#xff0c;参见之前文章腾讯…

《数字图像处理基础》学习05-数字图像的灰度直方图

目录 一&#xff0c;数字图像的数值描述 &#xff11;&#xff0c;二值图像 &#xff12;&#xff0c;灰度图像 3&#xff0c;彩色图像 二&#xff0c;数字图像的灰度直方图 一&#xff0c;数字图像的数值描述 在之前的学习中&#xff0c;我知道了图像都是二维信息&…

书生大模型第四期 | L0G3000 git 基础知识

1、破冰行动 fork项目 PR链接&#xff1a;跳转访问 https://github.com/InternLM/Tutorial/pull/21632、构建个人项目 创建一个仓库保存LLM学习的笔记&#xff0c;以md文件为主 博客页面项目

使用 OpenTelemetry 定制跨度名称并丰富跨度而无需更改代码 - 第 1 部分

作者&#xff1a;来自 Elastic David Hope OpenTelemetry Collector 提供强大的功能&#xff0c;可以在遥测数据到达可观察性工具之前丰富和细化遥测数据。在这篇博文中&#xff0c;我们将探讨如何利用 Collector 在 Elastic Observability 中创建更有意义的 transaction 名称&…

成都睿明智科技有限公司正规吗靠谱吗?

在这个短视频风起云涌的时代&#xff0c;抖音电商以其独特的魅力&#xff0c;成为了无数商家竞相追逐的新蓝海。而在这片浩瀚的商海中&#xff0c;成都睿明智科技有限公司犹如一艘装备精良的航船&#xff0c;引领着众多企业破浪前行&#xff0c;探索抖音电商的无限可能。今天&a…

GHuNeRF: Generalizable Human NeRF from a Monocular Video

研究背景 研究问题&#xff1a;这篇文章要解决的问题是学习一个从单目视频中泛化的人类NeRF模型。尽管现有的泛化人类NeRF已经取得了令人印象深刻的成果&#xff0c;但它们需要多视图图像或视频&#xff0c;这在某些情况下可能不可用。此外&#xff0c;一些基于单目视频的人类…

中聚企服:打造智能企业服务助手,“中聚AI”解答一切企业难题

近日&#xff0c;一款专为企业用户设计的智能问答助手——“中聚AI”正式亮相市场。这款AI由中产聚融有限公司旗下的中聚企服团队自主研发&#xff0c;旨在帮助企业用户快速、高效地解答经营过程中的各种难题&#xff0c;覆盖从公司注册、财税规划到知识产权和资质办理等多领域…

手把手教你轻松掌握~Air780E软件UDP应用示例!快来看!

还不会的小伙伴看过来&#xff01;通过本文的介绍&#xff0c;相信大家已经掌握了Air780E模组UDP应用的基本操作和常见问题的解决方法。赶快动手实践吧&#xff0c;让你的项目更加高效稳定&#xff01; 1、UDP概述 UDP&#xff08;用户数据报协议&#xff0c;UserDatagramProt…

Win10搭建SFTP服务器

1、下载安装 Release v9.5.0.0p1-Beta PowerShell/Win32-OpenSSH GitHub 下载OpenSSH-Win64.zip 解压之后放入到&#xff1a;C:\Program Files (x86)\OpenSSH-Win64以管理员身份打开CMD进入到 C:\Program Files (x86)\OpenSSH-Win64 文件夹执行命令 powershell.exe -Exec…

1分钟解决Excel打开CSV文件出现乱码问题

一、编码问题 1、不同编码格式 CSV 文件有多种编码格式&#xff0c;如 UTF - 8、UTF - 16、ANSI 等。如果 CSV 文件是 UTF - 8 编码&#xff0c;而 Excel 默认使用的是 ANSI 编码打开&#xff0c;就可能出现乱码。例如&#xff0c;许多从网络应用程序或非 Windows 系统生成的 …

构建灵活、高效的HTTP/1.1应用:探索h11库

文章目录 构建灵活、高效的HTTP/1.1应用&#xff1a;探索h11库背景这个库是什么&#xff1f;如何安装这个库&#xff1f;库函数使用方法使用场景常见的Bug及解决方案总结 构建灵活、高效的HTTP/1.1应用&#xff1a;探索h11库 背景 在现代网络应用中&#xff0c;HTTP协议是基础…

【算法】C++深度优先搜索(DFS)全解析

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

汽车免拆诊断案例 | 2010款起亚赛拉图车发动机转速表指针不动

故障现象  一辆2010款起亚赛拉图车&#xff0c;搭载G4ED 发动机&#xff0c;累计行驶里程约为17.2万km。车主反映&#xff0c;车辆行驶正常&#xff0c;但组合仪表上的发动机转速表指针始终不动。 故障诊断  接车后进行路试&#xff0c;车速表、燃油存量表及发动机冷却温度…

【环境搭建】Apache ZooKeeper 3.8.4 Stable

软件环境 Ubuntu 20.04 、OpenJDK 11 OpenJDK 11&#xff08;如果已经安装&#xff0c;可以跳过这一步&#xff09; 安装OpenJDK 11&#xff1a; $ sudo apt-get update$ sudo apt-get install -y openjdk-11-jdk 设置 JAVA_HOME 环境变量&#xff1a; $ sudo gedit ~/.bash…

solid works下载

软件安装包下载解压打开 将软件安装包下载到电脑本地&#xff0c;使用解压工具进行解压打开&#xff08;下载解压安装全程关闭杀毒软件及防火墙&#xff09; 打开Crack文件夹 打开Crack文件夹进去 复制SolidWorks_Flexnet_Server文件夹 复制SolidWorks_Flexnet_Server文件夹到…

硅谷甄选(10)用户管理

用户管理模块 9.1 静态搭建 主要是el-form、el-pagination <template><el-card style"height: 80px"><el-form :inline"true" class"form"><el-form-item label"用户名:"><el-input placeholder"请…