垃圾分类子项目2 - 加入舵机控制

观前提醒: 本文建立子在上一篇的基础上实现,加入了舵机开盖功能

上一篇: 垃圾分类子项目-识别垃圾类型-CSDN博客



固定摄像头设备号:

为什么要设置

我们在拔插摄像团队的时候经常遇到摄像头设备号变动的情况,这就导致我们之前设置好的开机自启脚本失效

具体参考大佬的这篇博文: 【linux】linux下摄像头设置固定的设备名-udev-CSDN博客

以下是我自己的设备号标注,不必观看

^第一步查看外设的设备号,主设备号:辅助设备号。使用命令:lsusb

Bus 002 Device 003: ID 1e45:8022 Suyin HD Camera

1e45:8022

1e45:8022

查看usb设备的信息
^^^^使用如下命令:udevadm info -a -p /sys/class/video4linux/video0

3.3 编辑规则
^^^^进入/etc/udev/rules.d/文件夹下,新建video.rules文件,文件内容如下:

KERNEL=="video*" , ATTRS{idVendor}== "32e4", ATTRS{idProduct}=="9422", ATTR{index}=="0",MODE:="0777", SYMLINK+="came0"
KERNEL=="video*" , ATTRS{idVendor}== "32e4", ATTRS{idProduct}=="9422", ATTR{index}=="1",MODE:="0777", SYMLINK+="came1

摄像头的USB 接口:

2-1:1.0

KERNELS=="2-1:1.0"


========================================================

主程序代码:

编译命令:


gcc -o test *c *h -I /usr/include/python3.10/ -l python3.10 -l wiringPi

运行:

-E --  保留当前用户的 环境变量
sudo -E ./test 

部分问题解决:

pinMode: You have not called one of the wiringPiSetup   functions, so I'm aborting your program before it crashes anyway.
翻译:  pinMode:您还没有调用wiringPiSetup函数之一,因此我将在您的程序崩溃之前中止它。

解决方式: 在main 函数前面初始化部分加入:  wiringPiSetup();

问题2: 另一个舵机不动
原因: buffer[2] 的复位位置,放下了子线程后面 -- 父子线程是并行的,父线程调用的给buffer[2] 复位,子线程就处理不到buffer[2] 的实际值了
解决方法: 换个位置 -- 放到创建子线程前 -- > 这段代码是父线程单独先执行的 具体可以放到 拍照前面

cpu优化:
问题3:CPU  占用率过高
原因:vscode 远程连接编辑  产生打的node 服务  -- 导致CPU 占有率比较高
解决方法: vscode 上编译完,之后关闭vscode ,在mobaxtrem 里面打开

屏蔽 代码新思路: 

#if0 // 条件一直是不成立,一直不执行 -- 屏蔽
//需要屏蔽的代码
#endif


 

====================================


case1: 实现基本的图像识别控制 舵机 -- 垃圾桶开盖雏形


 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // access()
#include <error.h>  // remove()
#include <wiringPi.h> 
#include <softPwm.h>

#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"

static int detect_process(const char *process_name) // 判断进程是否在运行
{
    int n = -1;
    FILE *strm;
    char buf[128] = {0};
    sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name); // 组合进程名字,为完整命令
    if ((strm = popen(buf, "r")) != NULL)                        // 通过 popen 的 方式去执行
    {
        if (fgets(buf, sizeof(buf), strm) != NULL) // 执行完后 判断是否能拿到正确的进程号,空格分开,第一个字符串就是进程号
        {
            n = atoi(buf); // 拿到就放回 进程号,不然  返回 -1
        }
    }
    else
    {
        return -1; // 执行失败
    }
    pclose(strm);
    return n;
}


int main(int argc, char **argv)
{
    int serial_fd = -1;
    int len = 0;
    int ret = -1;
    char *category = NULL;
    unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] --  关联垃圾类型
    wiringPiSetup();
    garbage_init();                                                 // 先初始化 阿里云接口
    ret = detect_process("mjpg_streamer");                          // 用于判断mjpg_streamer服务是否已经启动
    if (-1 == ret)
    {
        puts("detect process failed");
        goto END;
    }

    serial_fd = myserialOpen(SERIAL_DEV, BAUD); // 初始化串口,打开串口设备(语言模块)

    if (serial_fd == -1)
    { // 初始化串口失败
        goto END;
    }

    while (1) // 等待语言输入
    {
        len = serialGetstring(serial_fd, buffer); // 通过串口获得语言输入

        printf("lend=%d, buf[2]=0x%x\n", len, buffer[2]);
        if (len > 0 && buffer[2] == 0x46) // 判断是否 需要启动识别
        {
            buffer[2] = 0x00; // 判断完后 恢复,方便下次判断
            system(WGET_CMD); // 拍照

            if (access(GARBAGE_FILE, F_OK) == 0) // 判断 文件存在
            {
                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;
                //}
            }
          printf("buffer[2] = 0x%x\n",buffer[2]);  
          serialSendstring(serial_fd, buffer, 6); // 将识别到的数据发送到串口,回传给语音模块
          
          if(buffer[2] == 0x43){
          pwm_write(PWM_GARBAGE1);
          delay (5000); //开盖5s
          pwm_stop(PWM_GARBAGE1); //停止写入波形
          }
          
          else if(buffer[2] == 0x45){
          printf("start\n");
          pwm_write(PWM_GARBAGE2);
          delay (5000); //开盖5s
          pwm_stop(PWM_GARBAGE2); //停止写入波形
          }
          
          
          buffer[2] = 0x00;                       // 发送完后,一堆有效数据位清零,方便下一次调用
          remove(GARBAGE_FILE);                   // 清理缓存 删除刚刚拍摄的图片,避免对下一次拍摄造成干扰

        }
    }
    close(serial_fd); // 关闭串口文件描述符  fd
    END:
        garbage_final();
        return 0;
    }

============================================

常用出错判断; -- 方便定位出错位置

     if(-1 == serial_fd)
     {
       printf("%s | %s | %s:open serial failed\n",__FILE__,__func__,__LINE__);//三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 -  138
       pthread_exit(0); //串口打开失败 -->退出
     }

格式化操作:

alt + shift + 左键  拉下来 就能删掉n列 -- 实现 n 列的缩进 

优化: 不用join 来等待线程退出
 pthread_detach(pthread_self()); // pthread_self -- 拿到自己的线程id --> 与父进程分离,不然开盖等待时间太长影响下一次识别
   
main 函数里的变量和全局变量尽量不要重复,不然肯呢个造成调用错误  

-------------------------


====================================================


case2 - 使用线程优化代码: 


思路: 


在main 里面创建两个线程: 语音线程  和  阿里云交互线程                                                         语音线程 - 负责判断是否有语言命令输入

 阿里云交互线程:  下面有两个线程:舵机控制线程  和  语言播报线程
    这样就有三个线程同时执行了:

      主线程: 处理接收到的信号,进行拍照 和 图像识别,判断垃圾类型

      舵机控制线程:根据 主线程得到的垃圾类型,使用对应舵机(打开对应垃圾桶)
      语音播报线程: 根据主线程得到垃圾类型,播报对应语言

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // access()
#include <error.h>  // remove()
#include <wiringPi.h>
#include <softPwm.h>
#include <pthread.h>

#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"

static int detect_process(const char *process_name) // 判断进程是否在运行
{
    int n = -1;
    FILE *strm;
    char buf[128] = {0};
    sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name); // 组合进程名字,为完整命令
    if ((strm = popen(buf, "r")) != NULL)                        // 通过 popen 的 方式去执行
    {
        if (fgets(buf, sizeof(buf), strm) != NULL) // 执行完后 判断是否能拿到正确的进程号,空格分开,第一个字符串就是进程号
        {
            n = atoi(buf); // 拿到就放回 进程号,不然  返回 -1
        }
    }
    else
    {
        return -1; // 执行失败
    }
    pclose(strm);
    return n;
}



int serial_fd = -1;    // 线程调用 -- 定义为全局
pthread_cond_t cond;   // 设置条件变量
pthread_mutex_t mutex; //  设置线程锁

void *pget_voice(void *arg) // 语言播放线程函数
{
    int len = 0;
    unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] --  关联垃圾类型
    if (-1 == serial_fd)
    {
        printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 -  138
        pthread_exit(0);                                                           // 串口打开失败 -->退出
    }

    while (1)
    {
        len = serialGetstring(serial_fd, buffer); // 通过串口获得语言输入
        if (len > 0 && buffer[2] == 0x46)         // 判断是否 接到识别指令
        {
            pthread_mutex_lock(&mutex);   //  先上锁,保证后面执行这块不会被打断
            buffer[2] = 0x00;             // 判断完后的复位
            pthread_cond_signal(&cond);   // 发送信号,告诉阿里云线程开始识别了
            pthread_mutex_unlock(&mutex); // 解锁,与上锁包含的代码块执行的时候不会被打断
        }
    }

    pthread_exit(0);
}

void *popen_trash_can(void *arg) // 开盖
{ 
    pthread_detach(pthread_self());
    unsigned char *buffer = (unsigned char *)arg;
 
      if(buffer[2] == 0x43){
      pwm_write(PWM_GARBAGE1);
      delay (2000); //开盖5s
      pwm_stop(PWM_GARBAGE1); //停止写入波形
      }
      else if(buffer[2] != 0x45){
      pwm_write(PWM_GARBAGE2);
      delay (2000); //开盖5s
      pwm_stop(PWM_GARBAGE2); //停止写入波形
      }
     pthread_exit(0); 
}

void *psend_voice(void *arg) // 发送语言播报
{
     pthread_detach(pthread_self()); // pthread_self -- 拿到自己的线程id --> 与父进程分离,不然开盖等待时间太长影响下一次识别

    unsigned char *buffer = (unsigned char *)arg;
     if (-1 == serial_fd) //判断串口是否打开
    {
        printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 -  138
        pthread_exit(0);                                                           // 串口打开失败 -->退出
    }
   
    printf("buffer[2] = 0x%x\n",buffer[2]);  
    if(NULL!=buffer) //有数据
        serialSendstring(serial_fd, buffer, 6); // 将识别到的数据发送到串口,回传给语音模块,语言模块收到数据后进行相应输出 -- 实现语言播报
          


     pthread_exit(0); 
}

void *pcategory(void *arg) // 阿里云 -- 垃圾类型识别线程函数
{

    unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] --  关联垃圾类型
    char *category = NULL;
    pthread_t send_voice_tid,trash_tid;
    
    while (1)
    {
        pthread_mutex_lock(&mutex);       // 拿锁
        pthread_cond_wait(&cond, &mutex); // 等待 接受到信号 -- 才能开始识别
        pthread_mutex_unlock(&mutex);
         // 开始识别  
         buffer[2] = 0x00;  // 拍照前复位         
        system(WGET_CMD); // 拍照
 
        if (access(GARBAGE_FILE, F_OK) == 0) // 判断 文件存在
        {
            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, popen_trash_can, (void*)buffer);

    // 创建语音播报线程
    pthread_create(&send_voice_tid, NULL,  psend_voice, (void*)buffer);

         // buffer[2] = 0x00;                       // 发送完后,一堆有效数据位清零,方便下一次调用
          remove(GARBAGE_FILE);                   // 清理缓存 删除刚刚拍摄的图片,避免对下一次拍摄造成干扰
    }
    pthread_exit(0);
}

int main(int argc, char **argv)
{

    int len = 0;
    int ret = -1;
    char *category = NULL;
    pthread_t get_voice_tid, category_tid; // 创建线程id

    unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA}; // 初始化 buffer[2] --  关联垃圾类型
    wiringPiSetup(); // 初始化wiringPi库
    garbage_init();  // 初始化 阿里云接口
    ret = detect_process("mjpg_streamer"); // 用于判断mjpg_streamer服务是否已经启动
    if (-1 == ret)
    {
        puts("detect process failed");
        goto END;
    }

    serial_fd = myserialOpen(SERIAL_DEV, BAUD); // 初始化串口,打开串口设备(语言模块)

    if (serial_fd == -1)
    { // 初始化串口失败
        goto END;
    }
    // 创建语言线程  -- 注意第一个参数类型是指针变量 pthread_t *
    pthread_create(&get_voice_tid, NULL, pcategory, NULL);

    // 创建阿里云交互线程
    pthread_create(&category_tid, NULL, pget_voice, NULL);

    // 第二个参数表示接收到的返回值 -- 没有就NULL
    pthread_join(get_voice_tid, NULL); // 等待线程退出
    pthread_join(category_tid, NULL);

    pthread_mutex_destroy(&mutex); // 释放锁
    pthread_cond_destroy(&cond);  // 释放条件变量
    close(serial_fd); // 关闭串口文件描述符  fd
END:
    garbage_final();

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1、k8s问题pod从service中剔除

一、起因 redis原来由两服务器的集群变为三服务器的集群&#xff0c;通过statefulset扩展了两节点&#xff0c;并把redis-app-0和redis-app-3从集群中去除&#xff0c;但是由于service路由后端不变&#xff0c;导致程序连接后端仍然可能到redis-app-0和redis-app-3 二、处理 …

【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效,描边及阴影特效

前言 【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效&#xff0c;描边及阴影特效一、UGUI特效插件&#xff1a;UIEffect1.1 介绍 二、组件属性面板三、代码操作组件 总结 前言 在Unity中 UGUI 的使用几乎是必不可少的&#xff0c;虽然也有NGUI、FGUI等可以使用&#xff…

Spring Kafka——基于 Spring Kafka 实现动态管理 Kafka 连接和 topic 的监听

文章目录 使用 Spring Kafka 动态管理 Kafka 连接和主题监听1. 前言2. 简单的消费程序配置3. Spring Kafka 主要的相关类的说明4. KafkaListener 注解的加载执行流程解析5. 动态监听消费订阅的设计与实现 使用 Spring Kafka 动态管理 Kafka 连接和主题监听 文章内容较长&#x…

Error opening file a bytes-like object is required,not ‘NoneType‘

错误显示&#xff0c;打开的是一个无效路径的文件 查看json文件内容&#xff0c;索引的路径与json文件保存的路径不同 方法&#xff1a;使用python脚本统一修改json文件路径 import json import os import argparse import cv2 from tqdm import tqdm import numpy as np impo…

组合优于继承:什么情况下可以使用继承?

C设计模式专栏&#xff1a;http://t.csdnimg.cn/8Ulj3 目录 1.引言 2.为什么不推荐使用继承 3.相比继承&#xff0c;组合有哪些优势 4.如何决定是使用组合还是使用继承 1.引言 面向对象编程中有一条经典的设计原则:组合优于继承&#xff0c;也常被描述为多用组合&#xff0…

JavaScript原理篇——深入理解作用域、作用域链、闭包、this指向

执行上下文描述了代码执行时的环境&#xff0c;包括变量对象、作用域链和 this 值&#xff1b;而作用域则决定了变量和函数的可访问性范围&#xff0c;分为全局作用域和局部作用域。 变量对象用于存储变量和函数声明&#xff1a;是与执行上下文相关联的数据结构&#xff0c;用于…

USB设备的音频类UAC

一、UAC简介 UAC&#xff08;USB Audio Class&#xff09;是USB设备的音频类&#xff0c;它定义了USB音频设备与主机计算机通信的方式。UAC标准是USB规范的一部分&#xff0c;并受到各种操作系统&#xff08;包括Windows、macOS和Linux&#xff09;的支持。 UAC是基于libusb,实…

代码随想录算法训练营第五十一天| 309.最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费,总结

题目与题解 参考资料&#xff1a;买卖股票总结 309.最佳买卖股票时机含冷冻期 题目链接&#xff1a;309.最佳买卖股票时机含冷冻期 代码随想录题解&#xff1a;309.最佳买卖股票时机含冷冻期 视频讲解&#xff1a;动态规划来决定最佳时机&#xff0c;这次有冷冻期&#xff01;|…

【自然语言处理】InstructGPT、GPT-4 概述

InstructGPT官方论文地址&#xff1a;https://arxiv.org/pdf/2203.02155.pdf GPT-4 Technical Report&#xff1a;https://arxiv.org/pdf/2303.08774.pdf GPT-4&#xff1a;GPT-4 目录 1 InstructGPT 2 GPT-4 1 InstructGPT 在了解ChatGPT之前&#xff0c;我们先看看Instr…

k8s pod 无法启动一直ContainerCreating

情况如下&#xff0c;更新 pod 时&#xff0c;一直在ContainerCreating 查看详细信息如下 Failed to create pod sandbox: rpc error: code Unknown desc [failed to set up sandbox container “334d991a478b9640c66c67b46305122d7f0eefc98b2b4e671301f1981d9b9bc6” networ…

Jsoncpp搭建交叉编译环境(移植到arm)

1. 官网下载源码 github地址&#xff1a;GitHub - open-source-parsers/jsoncpp at update 2. 交叉编译环境 当前平台/开发平台-编译环境&#xff1a; [rootlocalroot ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalroot ~]# uname -a Lin…

Django框架之Django安装与使用

一、Django框架下载 首先我们需要先确定好自己电脑上的python解释器环境&#xff0c;否则会导致后面项目所需要的库安装不了以及项目无法运行的问题。 要下载Django并开始使用它&#xff0c;你可以按照以下步骤进行&#xff1a; 1、安装Python 首先&#xff0c;确保你的计算…

C/C++开发,opencv-ml库学习,支持向量机(SVM)应用

目录 一、OpenCV支持向量机&#xff08;SVM&#xff09;模块 1.1 openCV的机器学习库 1.2 SVM&#xff08;支持向量机&#xff09;模块 1.3 支持向量机&#xff08;SVM&#xff09;应用步骤 二、支持向量机&#xff08;SVM&#xff09;应用示例 2.1 训练及验证数据获取 2…

报错:OpenGL.error.NullFunctionError: Attempt to call an undefined function”

文件我已经上传 CSDN默认就是收费的 我修改不了 免费链接在文中 请寻找 OpenGL.error.NullFunctionError: Attempt to call an undefined function” 环境陈述: windows11 AMD-R9 python版本3.9.9 背景: 通过pip安装pip install PyOpenGL安装PyOpenGL模块后 运行出现的问题…

NLP Step by Step -- How to use pipeline

正如我们在摸鱼有一手&#xff1a;NLP step by step -- 了解Transformer中看到的那样&#xff0c;Transformers模型通常非常大。对于数以百万计到数千万计数十亿的参数&#xff0c;训练和部署这些模型是一项复杂的任务。此外&#xff0c;由于几乎每天都在发布新模型&#xff0c…

数据挖掘实验一

一、实验环境及背景 使用软件&#xff1a; Anaconda3 Jupyter Notebook 实验内容&#xff1a; 1.使用Tushare或者其他手段获取任意两支股票近三个月的交易数据。做出收盘价的变动图像。2.使用Pandas_datareader获取世界银行数据库中美国&#xff08;USA&#xff09;、瑞典&…

Windows电脑中护眼(夜间)模式的开启异常

我的电脑是联想小新16pro&#xff0c;Windows11版本。之前一直可以正常使用夜间模式&#xff0c;但是经过一次电脑的版本更新之后&#xff0c;我重启电脑发现我的夜间模式不能使用了。明明显示开启状态&#xff0c;但是却不能使用&#xff0c;电脑还是无法显示夜间模式。 询问…

基于Spring Boot的考研资讯平台设计与实现

基于Spring Boot的考研资讯平台设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 系统功能界面图&#xff0c;在系统首页可以查看首页、考…

【Qt QML】TabBar的用法

Qt Quick中的TabBar提供了一个基于选项卡的导航模型。TabBar由TabButton控件填充&#xff0c;并且可以与任何提供currentIndex属性的布局或容器控件一起使用&#xff0c;例如StackLayout或SwipeView。 import QtQuick import QtQuick.Controls import QtQuick.LayoutsWindow …

FPGA实现AXI4总线的读写_如何写axi4逻辑

FPGA实现AXI4总线的读写_如何写axi4逻辑 一、AXI4 接口描述 通道信号源信号描述全局信号aclk主机全局时钟aresetn主机全局复位&#xff0c;低有效写通道地址与控制信号通道M_AXI_WR_awid[3:0]主机写地址ID&#xff0c;用来标志一组写信号M_AXI_WR_awaddr[31:0]主机写地址&…