北京网站设计联系方式,辉县网站建设,劳务公司怎么注册,唐山制作网站的一、增加垃圾桶开关盖功能
环境准备
二、PWM 频率的公式 三、pthread_detach分离线程#xff0c;使其在退出时能够自动释放资源 四、具体代码实现
图像识别数据及调试信息wget-log打印日志文件
五、增加OLED显示功能 六、功能点实现语音交互视频
一、增加垃圾桶开关盖功能…一、增加垃圾桶开关盖功能
环境准备
二、PWM 频率的公式 三、pthread_detach分离线程使其在退出时能够自动释放资源 四、具体代码实现
图像识别数据及调试信息wget-log打印日志文件
五、增加OLED显示功能 六、功能点实现语音交互视频
一、增加垃圾桶开关盖功能
实现功能使用语音模块和摄像头在香橙派上做垃圾智能分类识别 同时根据识别结果开关不同的垃圾桶的盖子。
环境准备
在《语音模块和阿里云图像识别结合》搭建环境的基础上 接上用于开关盖的舵机(舵机模块可以直接粘在垃圾桶内侧当前代码里仅用了2个舵机用于示例代码的编写可以自行多购买3个垃圾桶和舵机用于区分4垃圾类型接线位置如下
实物图 二、PWM 频率的公式
这个 PWM 频率的公式可以更详细地表示为 P W M f r e q 1 × 1 0 6 pulse-width × range \\{PWMfreq} \frac{1 \times 10^6}{\text{pulse-width} \times \text{range}} \ PWMfreqpulse-width×range1×106
其中
(\text{PWMfreq}) 是 PWM 的频率赫兹。(1 \times 10^6) 是为了将频率从赫兹Hz转换为微秒μs。(\text{pulse-width}) 是每个 PWM 脉冲的宽度微秒。(\text{range}) 是 PWM 的范围即 PWM 值的最大范围。
这个公式的基本思想是PWM 的频率与脉冲宽度和范围有关。脉冲宽度表示每个 PWM 脉冲的持续时间而范围表示 PWM 值的最大范围。通过调整这两个参数可以控制 PWM 的频率。
三、pthread_detach分离线程使其在退出时能够自动释放资源
pthread_detach 函数是 POSIX 线程库提供的一个函数用于将一个线程标记为可被回收的。标记为可被回收的线程在退出时会自动释放其占用的系统资源无需等待其他线程调用 pthread_join。
具体来说当一个线程被标记为可被回收时其退出状态会自动被收回。这对于那些不需要其他线程等待其结束的线程是有用的因为它允许主线程或其他线程继续执行而无需等待这个线程的完成。
#include pthread.hint pthread_detach(pthread_t thread);pthread_detach 的参数是一个线程标识符pthread_t 类型的变量它表示要被标记为可被回收的线程。如果线程标识符为 thread 的线程处于 joinable 状态那么它会被标记为可被回收并且在线程退出时其资源将被自动释放。如果线程已经处于 detached 状态或者线程标识符不对应一个现存的线程pthread_detach 函数将返回适当的错误码。
示例用法
#include pthread.hvoid *thread_function(void *arg) {// 线程的执行体// ...return NULL;
}int main() {pthread_t my_thread;// 创建线程if (pthread_create(my_thread, NULL, thread_function, NULL) ! 0) {// 线程创建失败处理return 1;}// 将线程标记为可被回收if (pthread_detach(my_thread) ! 0) {// 线程标记失败处理return 1;}// 主线程继续执行而不用等待子线程的结束// ...return 0;
}在这个例子中my_thread 线程被创建后立即被标记为可被回收主线程可以继续执行而不用等待 my_thread 线程的完成。 总结 你可以将这种机制称为“分离线程”或“分离父子线程”。当你将一个线程标记为可被回收这个线程就不再和主线程形成关联主线程不需要显式地等待它的结束。这样的线程就像“自洁”一样它在结束时会自动释放资源。 这种机制对于那些主线程不关心其返回值也不需要等待其结束的辅助线程是非常有用的。这样主线程和辅助线程可以并行执行提高了程序的性能。 四、具体代码实现
增加用于实现开光盖驱动舵机的源码文件pwm.c:
#include wiringPi.h
#include softPwm.h
#include pwm.h// 根据PWM 频率公式PWMfreq 1 x 10^6 / (100 x range) 。
// 要得到PWM频率为50Hz则range设置周期分为200步周期20ms控制精度相比硬件PWM较低。// 设置指定PWM引脚的输出实现模拟PWM
void pwm_write(int pwm_pin)
{pinMode(pwm_pin, OUTPUT);softPwmCreate(pwm_pin, 0, 200); // 创建软件PWM初始占空比为0%范围为0到200softPwmWrite(pwm_pin, 10); // 设置占空比为10% 45度delay(1000); // 延时1秒softPwmStop(pwm_pin); // 停止软件PWM
}// 停止指定PWM引脚的输出
void pwm_stop(int pwm_pin)
{pinMode(pwm_pin, OUTPUT);softPwmCreate(pwm_pin, 0, 200); // 创建软件PWM初始占空比为0%范围为0到200softPwmWrite(pwm_pin, 5); // 设置占空比为5% 0度delay(1000); // 延时1秒softPwmStop(pwm_pin); // 停止软件PWM
}pwm.h代码
#ifndef __PWM__H
#define __PWM__H#define PWM_GARBAGE 7 // 干垃圾
#define PWM_RECOVERABLE_GARBAGE 5 // 可回收垃圾
#define PWM_WET_GARBAGE 8 // 湿垃圾
#define PWM_HAZARDOUS_GARBAGE 9 // 有害垃圾void pwm_write(int pwm_pin); // 设置指定PWM引脚的输出
void pwm_stop(int pwm_pin); // 停止指定PWM引脚的输出#endif修改main.c代码调整整体main函数的代码架构利用多线程实现具体的功能用到了线程里的条件变量控制线程间的数据同步
#include stdio.h
#include string.h
#include stdlib.h
#include unistd.h
#include errno.h
#include wiringPi.h
#include pthread.h#include uartTool.h
#include garbage.h
#include pwm.hint serial_fd -1; // 串口文件描述符
pthread_cond_t cond; // 条件变量用于线程之间的条件同步
pthread_mutex_t mutex; // 互斥锁用于线程之间的互斥访问// 判断进程是否在运行
static int detect_process(const char * process_name)
{int n -1; // 存储进程PID默认为-1FILE *strm;char buf[128] {0}; // 缓冲区// 构造命令字符串通过ps命令查找进程sprintf(buf, ps -ax | grep %s|grep -v grep, process_name);// 使用popen执行命令并读取输出if ((strm popen(buf, r)) ! NULL) {if (fgets(buf, sizeof(buf), strm) ! NULL) {printf(buf %s\n, buf); //打印缓存区的内容n atoi(buf); // 将进程ID字符串转换为整数printf(n %d\n, n); // 打印下进程的PID}}else {return -1; // popen失败} pclose(strm); // 关闭popen打开的文件流return n;
}// 获取语音线程
void *pget_voice(void *arg)
{unsigned char buffer[6] {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};int len 0;printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);// 串口未打开退出线程if (-1 serial_fd) {printf(%s|%s|%d: open serial failed\n, __FILE__, __func__, __LINE__);pthread_exit(0);}printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);// 循环读取串口数据while (1) {len my_serialGetstring(serial_fd, buffer);printf(%s|%s|%d, len %d\n, __FILE__, __func__, __LINE__, len);// 检测到特定数据发出信号唤醒其他线程if (len 0 buffer[2] 0x46) {printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);pthread_mutex_lock(mutex);buffer[2] 0x00;pthread_cond_signal(cond);pthread_mutex_unlock(mutex);system(WGET_CMD);}}pthread_exit(0);
}// 发送语音线程
void *psend_voice(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer (unsigned char *)arg;// 串口未打开退出线程if (-1 serial_fd) {printf(%s|%s|%d: open serial failed\n, __FILE__, __func__, __LINE__);pthread_exit(0);}// buffer不为空时通过串口发送数据(分类结果)if (NULL ! buffer) {my_serialSendstring(serial_fd, buffer, 6);}pthread_exit(0);
}// 控制垃圾桶线程
void *popen_trash_can(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer (unsigned char *)arg;// 根据垃圾类型控制PWMif (buffer[2] 0x43) { // 可回收垃圾printf(%s|%s|%d: buffer[2] 0x%x\n, __FILE__, __func__, __LINE__, buffer[2]);pwm_write(PWM_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}else if (buffer[2] 0x41) { // 干垃圾printf(%s|%s|%d: buffer[2] 0x%x\n, __FILE__, __func__, __LINE__, buffer[2]);pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}else if (buffer[2] 0x42) { // 湿垃圾printf(%s|%s|%d: buffer[2]0x%x\n, __FILE__, __func__, __LINE__,buffer[2]);pwm_write(PWM_WET_GARBAGE);delay(2000);pwm_stop(PWM_WET_GARBAGE);}else if (buffer[2] 0x44) { // 有害垃圾printf(%s|%s|%d: buffer[2]0x%x\n, __FILE__, __func__, __LINE__,buffer[2]);pwm_stop(PWM_HAZARDOUS_GARBAGE);delay(2000);pwm_write(PWM_HAZARDOUS_GARBAGE);}pthread_exit(0);
}// 垃圾分类线程
void *pcategory(void *arg)
{unsigned char buffer[6] {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};char *category NULL;pthread_t send_voice_tid, trash_tid;while (1) {printf(%s|%s|%d: \n, __FILE__, __func__, __LINE__);pthread_mutex_lock(mutex);pthread_cond_wait(cond, mutex);pthread_mutex_unlock(mutex);printf(%s|%s|%d: \n, __FILE__, __func__, __LINE__);buffer[2] 0x00;// 在执行wget命令之前添加调试输出printf(Executing wget command...\n);// 使用系统命令拍照system(WGET_CMD);// 在执行wget命令之后添加调试输出printf(Wget command executed.\n);// 判断垃圾种类if (0 access(GARBAGE_FILE, F_OK)) {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; // 识别失败}// 开垃圾桶开关pthread_create(trash_tid, NULL, psend_voice, (void *)buffer);// 开语音播报线程pthread_create(send_voice_tid, NULL, popen_trash_can, (void *)buffer);// buffer[2] 0x00;// 删除拍照文件remove(GARBAGE_FILE); }pthread_exit(0);
}int main(int argc, char *argv[])
{int ret -1;int len 0;char *category NULL;pthread_t get_voice_tid, category_tid;wiringPiSetup();// 初始化串口和垃圾分类模块garbage_init ();// 用于判断mjpg_streamer服务是否已经启动ret detect_process (mjpg_streamer);if (-1 ret) {printf(detect process failed\n);goto END;}// 打开串口serial_fd my_serialOpen (SERIAL_DEV, BAUD);if (-1 serial_fd) {printf(open serial failed\n);goto END;}// 开语音线程printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);pthread_create(get_voice_tid, NULL, pget_voice, NULL);// 开阿里云交互线程printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);pthread_create(category_tid, NULL, pcategory, NULL);// 创建互斥锁和条件变量pthread_join(get_voice_tid, NULL);pthread_join(category_tid, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(mutex);pthread_cond_destroy(cond);// 关闭串口close(serial_fd);
END:// 释放垃圾分类资源garbage_final();return 0;
}图像识别数据及调试信息 wget-log打印日志文件 wget-log 文件名通常是 wget 命令行工具的默认日志文件名用于记录 wget 下载命令执行过程中的信息、警告和错误。wget 是一个用于在命令行中下载文件的工具而 wget-log 文件则用于记录执行 wget 命令时产生的输出。
如果你在使用类似如下的 wget 命令
wget [URL]wget 默认会将日志输出到 wget-log 文件中。如果你希望更改日志文件的名称可以使用 -o 选项例如
wget -o mylog.txt [URL]上述命令将日志输出到名为 mylog.txt 的文件中。因此wget-log 文件的生成通常取决于 wget 命令的使用方式。
阿里云的相关操作比如通过 wget 下载文件也可能产生 wget-log 文件具体情况可能取决于你执行的命令和阿里云环境的设置。如果有特定的 wget 命令或阿里云操作你可以提供更多的上下文以便我更好地理解你的问题。
五、增加OLED显示功能
环境配置
cat /boot/orangepiEnv.txt
ls -a /dev/i2c-3《OLED屏应用-IIC协议》直接添在garbage项目中添加2个OLED实现代码文件
myoled.h
#include errno.h
#include string.h
#include stdio.h
#include stdlib.h
#include time.h
#include stdint.h#include oled.h
#include font.h#ifndef __MYOLED__H
#define __MYOLED__Hint myoled_init(void);
int oled_show(void *arg);#endifmyoled.c:
#include myoled.h#define FILENAME /dev/i2c-3static struct display_info disp;// 在 OLED 上显示垃圾分类结果
int oled_show(void *arg)
{unsigned char *buffer (unsigned char *)arg;// 在 OLED 上显示提示信息oled_putstrto(disp, 0, 91, THis garbage is:);disp.font font2;// 根据垃圾类型显示相应信息switch(buffer[2]){case 0x41:oled_putstrto(disp, 0, 20, Dry_garbage);break;case 0x42:oled_putstrto(disp, 0, 20, Wet_garbage);break;case 0x43:oled_putstrto(disp, 0, 20, Recycle_garbage);break;case 0x44:oled_putstrto(disp, 0, 20, Hazardous_garbage);break;case 0x45:oled_putstrto(disp, 0, 20, recognition failed);break;}disp.font font2;// 发送显示缓冲区到 OLEDoled_send_buffer(disp);return 0;
}// 初始化 OLED
int myoled_init(void)
{int e;disp.address OLED_I2C_ADDR;disp.font font2;// 打开 OLED 设备文件e oled_open(disp, FILENAME);// 初始化 OLEDe oled_init(disp);return e;
}然后修改下main.c文件 增加OLED线程用于显示识别后的垃圾类型:
#include stdio.h
#include string.h
#include stdlib.h
#include unistd.h
#include errno.h
#include wiringPi.h
#include pthread.h#include uartTool.h
#include garbage.h
#include pwm.h
#include myoled.hint serial_fd -1; // 串口文件描述符
pthread_cond_t cond; // 条件变量用于线程之间的条件同步
pthread_mutex_t mutex; // 互斥锁用于线程之间的互斥访问// 判断进程是否在运行
static int detect_process(const char * process_name)
{int n -1; // 存储进程PID默认为-1FILE *strm;char buf[128] {0}; // 缓冲区// 构造命令字符串通过ps命令查找进程sprintf(buf, ps -ax | grep %s|grep -v grep, process_name);// 使用popen执行命令并读取输出if ((strm popen(buf, r)) ! NULL) {if (fgets(buf, sizeof(buf), strm) ! NULL) {printf(buf %s\n, buf); //打印缓存区的内容n atoi(buf); // 将进程ID字符串转换为整数printf(n %d\n, n); // 打印下进程的PID}}else {return -1; // popen失败} pclose(strm); // 关闭popen打开的文件流return n;
}// 获取语音线程
void *pget_voice(void *arg)
{unsigned char buffer[6] {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};int len 0;printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);// 串口未打开退出线程if (-1 serial_fd) {printf(%s|%s|%d: open serial failed\n, __FILE__, __func__, __LINE__);pthread_exit(0);}printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);// 循环读取串口数据while (1) {len my_serialGetstring(serial_fd, buffer);printf(%s|%s|%d, len %d\n, __FILE__, __func__, __LINE__, len);// 检测到特定数据发出信号唤醒其他线程if (len 0 buffer[2] 0x46) {printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);pthread_mutex_lock(mutex);buffer[2] 0x00;pthread_cond_signal(cond);pthread_mutex_unlock(mutex);system(WGET_CMD);}}pthread_exit(0);
}// 发送语音线程
void *psend_voice(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer (unsigned char *)arg;// 串口未打开退出线程if (-1 serial_fd) {printf(%s|%s|%d: open serial failed\n, __FILE__, __func__, __LINE__);pthread_exit(0);}// buffer不为空时通过串口发送数据(分类结果)if (NULL ! buffer) {my_serialSendstring(serial_fd, buffer, 6);}pthread_exit(0);
}// 控制垃圾桶线程
void *popen_trash_can(void *arg)
{pthread_detach(pthread_self());unsigned char *buffer (unsigned char *)arg;// 根据垃圾类型控制PWMif (buffer[2] 0x43) { // 可回收垃圾printf(%s|%s|%d: buffer[2] 0x%x\n, __FILE__, __func__, __LINE__, buffer[2]);pwm_write(PWM_RECOVERABLE_GARBAGE);delay(2000);pwm_stop(PWM_RECOVERABLE_GARBAGE);}else if (buffer[2] 0x41) { // 干垃圾printf(%s|%s|%d: buffer[2] 0x%x\n, __FILE__, __func__, __LINE__, buffer[2]);pwm_write(PWM_GARBAGE);delay(2000);pwm_stop(PWM_GARBAGE);}else if (buffer[2] 0x42) { // 湿垃圾printf(%s|%s|%d: buffer[2]0x%x\n, __FILE__, __func__, __LINE__,buffer[2]);pwm_write(PWM_WET_GARBAGE);delay(2000);pwm_stop(PWM_WET_GARBAGE);}else if (buffer[2] 0x44) { // 有害垃圾printf(%s|%s|%d: buffer[2]0x%x\n, __FILE__, __func__, __LINE__,buffer[2]);pwm_stop(PWM_HAZARDOUS_GARBAGE);delay(2000);pwm_write(PWM_HAZARDOUS_GARBAGE);}pthread_exit(0);
}// 在线程中显示 OLED
void *poled_show(void *arg)
{// 分离线程使其在退出时能够自动释放资源pthread_detach(pthread_self());// 初始化 OLEDmyoled_init();// 在 OLED 上显示垃圾分类结果oled_show(arg);// 退出线程 pthread_exit(0);
}// 垃圾分类线程
void *pcategory(void *arg)
{unsigned char buffer[6] {0xAA, 0x55, 0x00, 0x00, 0X55, 0xAA};char *category NULL;pthread_t send_voice_tid, trash_tid, oled_tid;while (1) {printf(%s|%s|%d: \n, __FILE__, __func__, __LINE__);pthread_mutex_lock(mutex);pthread_cond_wait(cond, mutex);pthread_mutex_unlock(mutex);printf(%s|%s|%d: \n, __FILE__, __func__, __LINE__);buffer[2] 0x00;// 在执行wget命令之前添加调试输出printf(Executing wget command...\n);// 使用系统命令拍照system(WGET_CMD);// 在执行wget命令之后添加调试输出printf(Wget command executed.\n);// 判断垃圾种类if (0 access(GARBAGE_FILE, F_OK)) {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; // 识别失败}// 开垃圾桶开关pthread_create(trash_tid, NULL, psend_voice, (void *)buffer);// 开语音播报线程pthread_create(send_voice_tid, NULL, popen_trash_can, (void *)buffer);//oled显示线程pthread_create(oled_tid, NULL, poled_show, (void *)buffer);// buffer[2] 0x00;// 删除拍照文件remove(GARBAGE_FILE); }pthread_exit(0);
}int main(int argc, char *argv[])
{int ret -1;int len 0;char *category NULL;pthread_t get_voice_tid, category_tid;wiringPiSetup();// 初始化串口和垃圾分类模块garbage_init ();// 用于判断mjpg_streamer服务是否已经启动ret detect_process (mjpg_streamer);if (-1 ret) {printf(detect process failed\n);goto END;}// 打开串口serial_fd my_serialOpen (SERIAL_DEV, BAUD);if (-1 serial_fd) {printf(open serial failed\n);goto END;}// 开语音线程printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);pthread_create(get_voice_tid, NULL, pget_voice, NULL);// 开阿里云交互线程printf(%s|%s|%d\n, __FILE__, __func__, __LINE__);pthread_create(category_tid, NULL, pcategory, NULL);// 创建互斥锁和条件变量pthread_join(get_voice_tid, NULL);pthread_join(category_tid, NULL);// 销毁互斥锁和条件变量pthread_mutex_destroy(mutex);pthread_cond_destroy(cond);// 关闭串口close(serial_fd);
END:// 释放垃圾分类资源garbage_final();return 0;
}六、功能点实现语音交互视频 垃圾分类