语音模块&摄像头模块&阿里云结合
文章目录
- 语音模块&摄像头模块&阿里云结合
- 1、实现的功能
- 2、配置
- 2.1 软件环境
- 2.2 硬件配置
- 3、程序介绍
- 3.1 程序概况
- 3.2 语言模块SDK配置介绍
- 3.3 程序文件
- 3.3.1 开启摄像头的程序
- 3.3.2 云端识别函数( `Py ==> C` ) & 串口程序
- 3.3.3 结合 `main.c`
- 3.4 程序运行结果
1、实现的功能
- 可以通过语音模块使摄像头在抓拍垃圾图片,程序自动将垃圾传到云端识别,判断出是什么垃圾类型(干垃圾、湿垃圾、可回收垃圾、有害垃圾)(无法识别) 最后由语音模块输出相关信息
2、配置
2.1 软件环境
VS Code
MobaXterm
2.2 硬件配置
-
全志H616
-
SU-03T LD3320
-
语言模块要配置SDK,大家可以到网上搜一下,很简单的;我用的是智能公元的语音模块;我把我用的分享给大家;
-
语言模块SDK:链接: https://pan.baidu.com/s/1CjgIhW0AWQjXdEhXZiBIzw?pwd=2222 提取码: 2222 复制这段内容后打开百度网盘手机App,操作更方便哦
烧入工具:链接: https://pan.baidu.com/s/1JF2K_TbS_lg74YInBCCI2g?pwd=2222 提取码: 2222 复制这段内容后打开百度网盘手机App,操作更方便哦
-
- USB摄像头模块
在某宝随便每个USB摄像头模块就ok。
3、程序介绍
3.1 程序概况
garbage.py //云端提供的py程序,不用自己编写,改改参数,看得懂就ok
garbage.c & garbage.h //利用C语言调用py文件,并且进行了封装,方便main函数调用
>>> https://blog.csdn.net/Super_Fisher_man/article/details/137055641
uartTool.c & uartTool.h //串口通信程序
>>> https://blog.csdn.net/Super_Fisher_man/article/details/136662787
main.c //主函数,将两者结合
garbage.c & uartTool.c 在前面的文章都已经介绍过了,这边就不多加赘述
3.2 语言模块SDK配置介绍
3.3 程序文件
3.3.1 开启摄像头的程序
- 可以看看之前的blog
基于香橙派 调用USB摄像头 的两种方法(命令&脚本)-CSDN博客
# start.sh
# /home/orangepi/mjpg-streamer/mjpg-streamer-experimental
export LD_LIBRARY_PATH="$(pwd)"
#./mjpg_streamer -i "input_uvc.so --help"
./mjpg_streamer -i "./input_uvc.so -d /dev/video0 -u -f 30" -o "./output_http.so -w ./www"
# mjpg.sh
# /home/orangepi
#!/bin/bash
cd /home/orangepi/mjpg-streamer/mjpg-streamer-experimental
./start.sh
-
在清除了mjpg进程的前提下命令
-
>>> ps ax | grep mjpg >>> kill -9 ××× >>> ./mjpg.sh
3.3.2 云端识别函数( Py ==> C
) & 串口程序
- 云端调用的程序,只需改 #场景二 就ok
//garbage.py
# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_imagerecog20190930
import os
import io
from urllib.request import urlopen
from alibabacloud_imagerecog20190930.client import Client
from alibabacloud_imagerecog20190930.models import ClassifyingRubbishAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
config = 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='imagerecog.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
def alibaba_garbage(): # 封装成一个函数,方便其他文件调用
#场景一:文件在本地
# img = open(r'/home/orangepi/garbage/lj/test2.jpg','rb')
img = open(r'/tmp/garbage.jpg','rb')
#场景二:使用任意可访问的url
# url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/imagerecog/ClassifyingRubbish/ClassifyingRubbish1.jpg'
# img = io.BytesIO(urlopen(url).read())
classifying_rubbish_request = ClassifyingRubbishAdvanceRequest()
classifying_rubbish_request.image_urlobject = img
runtime = RuntimeOptions()
try:
# 初始化Client
client = Client(config)
response = client.classifying_rubbish_advance(classifying_rubbish_request, runtime)
# 获取整体结果
# print(response.body) # 默认
# print(response.body.to_map()) # 打印出body的类型
print(response.body.to_map()['Data']['Elements'][0]['Category']) # 打印出body的类型
return response.body.to_map()['Data']['Elements'][0]['Category']
# 提供调用方便
except Exception as error:
# 获取整体报错信息
# print(error)
# 获取单个字段
# print(error.code)
return '获取失败'
if __name__== "__main__":
alibaba_garbage()
Py ==> c
//garbage.h
#ifndef __GARBAGE__H
#define __GARBAGE__H
void garbage_init(void);//第一步做初始化,初始python的编译器包括导入当前路劲到sys.path里面
void garbage_final(void);
char *garbage_category(char *category);
//127.0.0.1
//通常被称为"本地回环地址"或"localhost";
//这个地址经常用于测试网络应用程序和诊断网络问题
//因为它确保数据能够在同一台计算机上进行循环传输,而无需真正通过网络传输。
#define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg" //抓拍命令
#define GARBAGE_FILE "/tmp/garbage.jpg"
#endif
// C语言引用Python文件
#if 0
1、包含Python.h头文件,以便使用Python API。
2、使用void Py_Initialize()初始化Python解释器,
3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject
*PyObject_GetAttrString(PyObject *o, const char *attr_name)获取sys.path对象,并利用
int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,以便加载
当前的Python模块(Python文件即python模块)。
4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否
有错误。
5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取
Python函数对象,并检查是否可调用。
+6、使用PyObject *Py_BuildValue(const char *format, ...)函数将C类型的数据结构转换成
Python对象,作为Python函数的参数,没有参数不需要调用
7、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用
Python函数,并获取返回值。
+8、使用int PyArg_Parse(PyObject *args, const char *format, ...)函数将返回值转换为C类
型,并检查是否有错误,没有返回值时不需要调用。
9、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
10、结束时调用void Py_Finalize()函数关闭Python解释器。
相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
https://docs.python.org/zh-cn/3/c-api/import.html
#endif
#include <Python.h>
#include "garbage.h"
void garbage_init(void)
{
Py_Initialize();
// 将当前路径添加到sys.path中
PyObject *sys = PyImport_ImportModule("sys");//初始化python解释器
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyUnicode_FromString("."));
}
void garbage_final(void)
{
//释放所有引用的Python对象
Py_Finalize();
}
char *garbage_category(char *category)
{
// 导入para模块
PyObject *pModule = PyImport_ImportModule("garbage");//引用的是本目录底下的garbage.py文件
if (!pModule)
{
PyErr_Print();
printf("Error: failed to load para.py\n");
goto FAILED_MODULE;
}
//获取 alibaba_garbage 函数对象
PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_garbage");//garbage.py文件里的这个函数 alibaba_garbage
if (!pFunc)
{
PyErr_Print();
printf("Error: failed to load say_funny\n");
goto FAILED_FUNC;
}
//调用alibaba_garbage函数并获取返回值
PyObject *pValue = PyObject_CallObject(pFunc, NULL);
if (!pValue)
{
PyErr_Print();
printf("Error: function call failed\n");
goto FAILED_VALUE;
}
//将返回值转换为C类型
char *result = NULL; //pValue(py) --> result(c)
if (!PyArg_Parse(pValue, "s", &result))//PyArg_Parse根据指定的格式字符串将 Python 对象转换为 C 变量
{
PyErr_Print();
printf("Error: parse failed\n");
goto FAILED_RESULT;
}
//打印返回值
//printf("pValue=%s\n", result);
category = (char*)malloc(sizeof(char)*(strlen(result)+1));//要留一个字节给 \0
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 category;
}
- 串口函数
//uartTool.h
#ifndef __UARTTOOL_H
#define __UARTTOOL_H
int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, unsigned const char *s, int len); //写数据
int serialGetstring (const int fd, unsigned char *buffer); //读数据
#define SERIAL_DEV "/dev/ttyS5" // 可代替设备节点
#define BAUD 115200 // 代替波特率
#endif
//防止头文件中的声明反复区声明
//uartTool.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "uartTool.h"//先到当前文件夹查看
#include "wiringSerial.h"
int myserialOpen (const char *device, const int baud)
{
struct termios options ;//这个结构体
speed_t myBaud ;
int status, fd ;
switch (baud)
{
case 9600: myBaud = B9600 ; break ;
case 115200: myBaud = B115200 ; break ;
default:
}
if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
return -1 ;
fcntl (fd, F_SETFL, O_RDWR) ;
// Get and modify current options:
tcgetattr (fd, &options) ;//get
//标准化的操作 一般都是固定的
cfmakeraw (&options) ;
cfsetispeed (&options, myBaud) ;
cfsetospeed (&options, myBaud) ;//波特率的设置
options.c_cflag |= (CLOCAL | CREAD) ;
options.c_cflag &= ~PARENB ;//奇偶校验位
options.c_cflag &= ~CSTOPB ;//ͣ停止位 一般都是一位的
options.c_cflag &= ~CSIZE ;
options.c_cflag |= CS8 ;//数据位 基本都是八位的
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
options.c_oflag &= ~OPOST ;
options.c_cc [VMIN] = 0 ;
options.c_cc [VTIME] = 100 ; // Ten seconds (100 deciseconds)
//标准化的操作
tcsetattr (fd, TCSANOW, &options) ;//写入内核
ioctl (fd, TIOCMGET, &status);//获得整型控制字TIOCMGET get出来
status |= TIOCM_DTR ;
status |= TIOCM_RTS ;//控制字加上标志位
ioctl (fd, TIOCMSET, &status);//写入内核
usleep (10000) ; // 10mS
return fd ;
}
//0XAA 0X55 0X46 0X00 0X55 0XAA
void serialSendstring (const int fd, const unsigned char *s, int len)//去写数据
{
int ret;
ret = write (fd, s, len);
if (ret < 0)
printf("Serial Putchar Error\n");
}
int serialGetstring (const int fd, unsigned char *buffer)
{
int n_read;
n_read = read(fd,buffer,6);
// printf("%d\n",fd);
return n_read;
}
3.3.3 结合 main.c
//main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "uartTool.h"
#include "garbage.h"
int main(int argc, char *argv[])
{
int serial_fd = -1;
int len = 0;
char *category = NULL;
unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
//初始化
garbage_init();
//打开设备
serial_fd = myserialOpen(SERIAL_DEV,BAUD);//可代替设备节点 & 波特率 返回值fd
if(serial_fd == -1)//是否打开成功
{
goto END;
}
while(1)
{
//获取(读)传过来的数据
len = serialGetstring(serial_fd,buffer); //P8 & P10 ==> TXD.5 & RXD.5
if(len>0 && buffer[2]==0x46) //0x46命令:识别垃圾类型
{
buffer[2]==0x00;
//define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg" //抓拍命令
system(WGET_CMD);//宏定义:拍照命令,garbage.h 文件中
// access函数是一个用于测试文件访问权限的函数,它通常用于检查某个文件是否可以被当前用户读取、写入或执行。
// 包括F_OK(检查文件是否存在)、R_OK(检查读权限)、W_OK(检查写权限)和X_OK(检查执行权限)
// 该函数返回值为0表示有权限,-1表示无权限或发生错误。
if(0 == access(GARBAGE_FILE, F_OK))//GARBAGE_FILE 宏定义 识别到的垃圾图片是否存在
{
category = garbage_category(category);//调用阿里云接口判断垃圾类型
if(strstr(category,"干垃圾"))
{
buffer[2] = 0x41;//赋值
}
else if(strstr(category,"湿垃圾"))
{
buffer[2] = 0x42;
}
else if(strstr(category,"可回收垃圾"))
{
buffer[2] = 0x43;
}
else if(strstr(category,"有害垃圾"))
{
buffer[2] = 0x44;
}
else
{
buffer[2] = 0x45;// 未识别成功
}
}
else
{
buffer[2] = 0x45;
}
serialSendstring(serial_fd,buffer,6);//写数据,回传给语音模块
buffer[2]=0x00;//将buffer[2]清零
remove(GARBAGE_FILE);//garbage.h 的宏定义 把识别到的垃圾的图片清除掉
}
}
END:
garbage_final; //释放所有引用的Python对象
return 0;
}
3.4 程序运行结果
- 先杀进程
- 打开摄像头
-
显示视频内容、抓拍
-
执行结果
语言会自动回复,该垃圾是什么类型
欢迎大家一起交流讨论