【ARM-Linux篇】项目:智能分类垃圾桶

一、项目概述

借助阿里云图像识别垃圾分类算法,利用语音模块播报垃圾物品类型,并且在OLED显示垃圾物品类型,能够根据垃圾类型开关不同类型垃圾桶(干垃圾、湿垃圾、可回收垃圾、有害垃圾)。

前期准备:参考阿里云垃圾识别方案香橙派使用摄像头语音模块配置

二、软件流程图

三、项目代码

uart.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 "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 ;
    }
    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) ;
    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);
    status |= TIOCM_DTR ;
    status |= TIOCM_RTS ;
    ioctl (fd, TIOCMSET, &status);
    usleep (10000) ; // 10mS
    return fd ;
}

void serialSendstring (const int fd, const unsigned char *s,int len)
{
    int ret;
    ret = write (fd, s, len);
    if (ret < 0)
    printf("Serial Puts Error\n");
}

int serialGetstring (const int fd, unsigned char *buffer)
{
    int n_read;
    n_read = read(fd, buffer, 32);
    return n_read;
}

uart.h

#ifndef __UARTTOOL_H
#define __UARTTOOL_H

int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, const unsigned char *s,int len);
int serialGetstring (const int fd, unsigned char *buffer);

#define SERIAL_DEV "/dev/ttyS5"
#define BAUD 115200

#endif

garbage.py

调用阿里云Python垃圾分类代码

# -*- 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'/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.to_map()['Data']['Elements'][0]['Category'])
      return response.body.to_map()['Data']['Elements'][0]['Category']
    except Exception as error:
      return '获取失败'

if __name__=="__main__":
  alibaba_garbage()

garbage.c

用C语言调用阿里云Python接口

#include <Python.h>
#include "garbage.h"

void garbage_init(void)
{
    Py_Initialize();
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyUnicode_FromString("."));
}

void garbage_final(void)
{
    Py_Finalize();
}

char *garbage_category(char *category)
{
    PyObject *pModule = PyImport_ImportModule("garbage");
    if (!pModule){
        PyErr_Print();
        printf("Error: failed to load garbage.py\n");
        goto FAILED_MODULE;
    }
    PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_garbage");
    if (!pFunc){
        PyErr_Print();
        printf("Error: failed to load alibaba_garbage\n");
        goto FAILED_FUNC;
    }
    PyObject *pValue = PyObject_CallObject(pFunc, NULL);
    if (!pValue){
        PyErr_Print();
        printf("Error: function call failed\n");
        goto FAILED_VALUE;
    }
    char *result = NULL;
    if (!PyArg_Parse(pValue, "s", &result)){
        PyErr_Print();
        printf("Error: parse failed");
        goto FAILED_RESULT;
    }
    printf("result=%s\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 category;
}

garbage.h 

#ifndef __GARBAGE__H
#define __GARBAGE__H

void garbage_init(void);
void garbage_final(void);
char *garbage_category(char *category);

#define WGET_CMD "wget http://192.168.0.113:8080/?action=snapshot -O /tmp/garbage.jpg"
#define GARBAGE_FILE "/tmp/garbage.jpg"

#endif

pwm.c

使用pwm,控制不同类型垃圾桶开关盖

#include <wiringPi.h>
#include <softPwm.h>

//根据公式:PWMfreq = 1 x 10^6 / (100 x range) ,要得到PWM频率为50Hz,则range为200,即周期分为200步,控制精度相比硬件PWM较低。

void pwm_write(int pwm_pin)
{
    pinMode(pwm_pin, OUTPUT);
    softPwmCreate(pwm_pin,0,200);// range设置周期分为200步, 周期20ms
    softPwmWrite(pwm_pin,10);//1ms 45度
    delay(1000);
    softPwmStop(pwm_pin);
}

void pwm_stop(int pwm_pin)
{
    pinMode(pwm_pin, OUTPUT);
    softPwmCreate(pwm_pin,0,200);// range设置周期分为200步, 周期20ms
    softPwmWrite(pwm_pin,5);//0.5ms 0度
    delay(1000);
    softPwmStop(pwm_pin);
}

pwm.h

#ifndef __PWM_H
#define __PWM_H

#define PWM_DRY_GARBAGE 5
#define PWM_WET_GARBAGE 6 
#define PWM_RECYCLABLE_GARBAGE 7
#define PWM_HARMFUL_GARBAGE 8

void pwm_write(int pwm_pin);
void pwm_stop(int pwm_pin);

#endif

myoled.c

进行oled显示屏显示垃圾类型

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>

#include "oled.h"
#include "font.h"
#include "myoled.h"

#define FILENAME "/dev/i2c-3"
static struct display_info disp;

int oled_show(void *arg)
{
    unsigned char *buffer = (unsigned char *)arg;
    oled_putstrto(&disp, 0, 9+1, "This garbage is:");
    disp.font = font2;
    switch(buffer[2]){
        case 0x41:
            oled_putstrto(&disp, 0, 20, "dry waste");
        break;
        case 0x42:
            oled_putstrto(&disp, 0, 20, "wet waste");
        break;
        case 0x43:
            oled_putstrto(&disp, 0, 20, "recyclable waste");
        break;
        case 0x44:
            oled_putstrto(&disp, 0, 20, "harm waste");
        break;
        case 0x45:
            oled_putstrto(&disp, 0, 20, "recognition failed");
        break;
    }
    disp.font = font2;
    oled_send_buffer(&disp);
}   
int myoled_init(void) 
{
    int e;

    disp.address = OLED_I2C_ADDR;
    disp.font = font2;

    e = oled_open(&disp, FILENAME);
    e = oled_init(&disp);

    return e;
}

 myoled.h

#ifndef __MYOLED_H
#define __MYOLED_H

int myoled_init(void);
int oled_show(void *arg);

#endif

socket.c

加入网络控制,可以使用上位机控制进行垃圾识别

#include "socket.h"

int socket_init(const char *ipaddr,const char *ipport)
{
    int s_fd = -1;
    int ret = -1;
    struct sockaddr_in s_addr;

    memset(&s_addr,0,sizeof(struct sockaddr_in));
    //1.socket
    s_fd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == s_fd){
        perror("socket");
        return -1;
    }

    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(atoi(ipport));
    inet_aton(ipaddr,&s_addr.sin_addr);
    //2.bind
    ret = bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
    if(-1 == ret){
        perror("bind");
        return -1;
    }
    //3. listen
    ret = listen(s_fd,1);
    if(-1 == ret){
        perror("listen");
        return -1;
    }

    return s_fd; 
}

socket.h

#ifndef __SOCKET_H
#define __SOCKET_H

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>

#define IPADDR "192.168.0.113"
#define IPPORT "8192"

int socket_init(const char *ipaddr,const char *ipport);

#endif

 main.c

主函数实现逻辑

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>
#include <pthread.h>

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

int serial_fd = -1;
pthread_cond_t cond;
pthread_mutex_t mutex;

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){
        if(fgets(buf, sizeof(buf), strm) != NULL){
            printf("buf=%s\n",buf);
            n = atoi(buf);
            printf("n=%d\n",n);
        }
    }else{
        return -1;
    }

    pclose(strm);
    return n;
}

void *pget_voice(void *arg)
{
    int len = 0;
    //定义默认数据
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};
    if(-1 == serial_fd){
        printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);
        pthread_exit(0);
    }
    while(1){
        //监听语音模块串口输出
        len = 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);
        }
    }
    pthread_exit(0);
}

void *popen_trash_can(void *arg)
{
    pthread_detach(pthread_self());
    unsigned char *buffer = (unsigned char*)arg;

    //开关垃圾桶
    if(buffer[2] == 0x41){ 
        pwm_write(PWM_DRY_GARBAGE);
        delay(2000);
        pwm_stop(PWM_DRY_GARBAGE);
    }else if(buffer[2] == 0x42){
        pwm_write(PWM_WET_GARBAGE);
        delay(2000);
        pwm_stop(PWM_WET_GARBAGE);
    }else if(buffer[2] == 0x43){
        pwm_write(PWM_RECYCLABLE_GARBAGE);
        delay(2000);
        pwm_stop(PWM_RECYCLABLE_GARBAGE);
    }else if(buffer[2] == 0x44){
        pwm_write(PWM_HARMFUL_GARBAGE);
        delay(2000);
        pwm_stop(PWM_HARMFUL_GARBAGE);
    }else if(buffer[2] == 0x45){
        pwm_stop(PWM_DRY_GARBAGE);
        pwm_stop(PWM_WET_GARBAGE);
        pwm_stop(PWM_RECYCLABLE_GARBAGE);
        pwm_stop(PWM_HARMFUL_GARBAGE);
    }
    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);
    }
    if(NULL != buffer){
        //把收到的数据回传给语音模块,播报垃圾类型
        serialSendstring(serial_fd,buffer,6);
    }
    pthread_exit(0);
}

void *poled_show(void *arg)
{
    pthread_detach(pthread_self());//父子线程分离
    myoled_init();
    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;
        //拍照
        system(WGET_CMD);
        //判断照片是否存在
        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,popen_trash_can,(void *)buffer);
        //开语音播报线程
        pthread_create(&send_voice_tid,NULL,psend_voice,(void *)buffer);
        //oled显示线程
        pthread_create(&oled_tid,NULL,poled_show,(void *)buffer);
        //清理图片缓存
        remove(GARBAGE_FILE);
    }
    pthread_exit(0);
}

void *pget_socket(void *arg)
{
    int s_fd = -1;
    int c_fd = -1;
    int n_read = -1;
    char buffer[6];

    struct sockaddr_in c_addr;

    memset(&c_addr,0,sizeof(struct sockaddr_in));

    s_fd = socket_init(IPADDR,IPPORT);
    printf("%s|%s|%d:s_fd=%d\n", __FILE__, __func__, __LINE__, s_fd);
    if(-1 == s_fd){
        pthread_exit(0); 
    }
    int clen = sizeof(struct sockaddr_in);
    while(1){
        c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);

        int keepalive = 1;// 开启TCP KeepAlive功能
        int keepidle = 5;// tcp_keepalive_tim 5s内没收到数据开始发送心跳包
        int keepcnt = 3; // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
        int keepintvl = 3; // tcp_keepalive_intvl 每3s发送一次心跳包

        setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,sizeof(keepalive));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof(keepidle));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof(keepcnt));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof(keepintvl));

        printf("%s|%s|%d:Accept a conection from %s:%d\n",__FILE__,__func__,__LINE__,inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
        if(-1 == c_fd){
            perror("accept");
            continue;
        }
        while(1){
            memset(buffer,0,sizeof(buffer));
            n_read = recv(c_fd,buffer,sizeof(buffer),0);
            printf("%s|%s|%d:nread=%d,buffer=%s\n",__FILE__,__func__,__LINE__,n_read,buffer);
            if(n_read > 0){
                if(strstr(buffer,"open")){
                    pthread_mutex_lock(&mutex);
                    pthread_cond_signal(&cond);
                    pthread_mutex_unlock(&mutex);
                }
            }else if(0 == n_read || -1 == n_read){
                break;
            }
        }
        close(c_fd);
    }

    pthread_exit(0); 
}

int main(int argc,char *argv[])
{
    int len = 0;
    char *category = NULL;
    int ret = -1;
    pthread_t get_voice_tid,category_tid,get_socket_tid;

    //定义默认数据
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};
    //初始化wiringPi
    wiringPiSetup();
    //初始化阿里云接口
    garbage_init();
    //判断mjpg_streamer服务是否开启
    ret = detect_process("mjpg_streamer");
    if(-1 == ret){
        printf("detect process failed\n");
        goto END;
    }
    //打开语音模块设备
    serial_fd = myserialOpen(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(&get_socket_tid,NULL,pget_socket,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_join(get_socket_tid,NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    close(serial_fd);
END:
    garbage_final();
    return 0;
}

•编译

gcc -o garbage *.c *.h -Ⅰ /usr/include/python3.10 -lwiringPi -lpython3.10

•运行

sudo -E ./garbage

四、项目实现

(正在剪辑ing)

  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿gao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值