基于树莓派的智能垃圾分类系统

一、概述

创新实验课程做的东西。

1-系统示意图

2-功能说明

  1. 人把垃圾放到托盘上,触发光敏电阻引脚电平变化,然后软件触发相机拍照;
  2. 利用百度AI提供的图像识别SDK识别照片中物体名称;
  3. 利用天行数据提供的api接口查询物体对应的垃圾类型;
  4. 控制步进电机带动垃圾桶转到对应位置;
  5. 控制舵机带动托盘投放垃圾;
  6. 利用超声波测量出另一侧的桶的剩余容量;
  7. 将剩余容量信息打包成一帧数据发送到后台;
  8. 后台利用数据帧更新本地的数据(一个json文件);
  9. 提供给环卫部门一个Web端,用浏览器在地图上获取所有垃圾桶的用量信息;

3-Web示意图

在这里插入图片描述

4-垃圾桶成品示意图

相机,托盘,光敏电阻开关懒得往上固定了,画在图上示意一下。

在这里插入图片描述

5-主要构件示意图

在这里插入图片描述

二、树莓派与外设的连接和驱动

树莓派的引脚有不同的编码方式,由于我主要用的是C语言,所以用的wiringPi库,故应该使用WiringPi下的编码。

引脚约定:

树莓派引脚(WiringPi Pin)外设引脚导线颜色
22ULN2003_IN1橙色
23ULN2003_IN2黄色
24ULN2003_IN3绿色
25ULN2003_IN4蓝色
21光敏模块的DO黄色
4HC-SR04_Trig黄色
5HC-SR04_Echo绿色
26舵机的PWM输入橙色

1-光敏电阻触发相机拍照

对应文件:

  • photosensitive_camera.cppphotosensitive_camera.h

功能:

  • 当人把垃圾放到托盘上后,相机自动拍照,得到垃圾的照片。

实现原理:

  • 当光照正常时,光敏电阻模块DO输出为0,一旦被遮挡住(人把垃圾放上去),DO输出为1。利用死循环将程序阻塞在DO==0上,并在后面进行消抖判断。
  • 人把垃圾放上去,光敏电阻输出DO==1后,跳出死循环,进行拍照。
  • 由于我并没有找到如何用C语言控制树莓派拍照的函数调用(可能并没有),但是有控制树莓派拍照的shell命令,故采取了一种相对折中的做法:fork出一个子进程,然后用execl函数替换掉子进程的进程映像为/usr/bin/raspistill(控制树莓派拍照的shell命令),后面加上合适的参数,这样就相当于在程序中执行shell命令,实现了拍照功能。

2-步进电机驱动模块

对应文件:

  • step_motor.cppstep_motor.h

功能:

  • 得到垃圾类型(映射为4个整数)后,控制步进电机带动四个桶转动一定角度,使对应的垃圾类型的桶对准托盘下方。

实现原理:

  • 向ULN2003的IN1~IN4分时写入对应的电平即可,具体见步进电机的驱动原理。另外,步进电机最好用12V驱动(3根锂电池串联,与信号共地),扭矩大,转速高,又快又猛。

3-舵机驱动模块

对应文件:

  • steering_engin.cppsteering_engin.h

功能:

  • 带动托盘投放垃圾

实现原理:

4-超声波测距模块HC-SR04

对应文件:

  • ultrasonic_ranging.cppultrasonic_ranging.h

功能:

  • 测距。

实现原理:

  • 参考源码和HS-SR04的原理

三、图像识别部分

对应文件:

  • waste_sorting.cppwaste_sorting.h

1-实现思路概述

When it comes to 图像识别,首先想到的肯定是深度学习,但是我并不会,只懂个其中的皮毛,所以采用了一种更为简单的解决方案:使用BaiduAI开放平台提供的图像识别接口同时配合天行数据提供的垃圾分类查询接口实现图像识别并分类。因此这部分的功能就是读入一张垃圾的图片,输出它属于什么垃圾(用一个整数表示)。

代码实现在waste_sorting.cpp文件中,下面结合头文件waste_sorting.h中的函数声明简单说明其原理:

#ifndef __WASTE_SORTING_H__
#define __WASTE_SORTING_H__

#include <vector>
#include <string>
#include <iostream>

/*
   获取垃圾类型的函数:传入识别出来的物体种类(包含所有可能结果的string数组),返回其中最有可能所属的垃圾类型,约定垃圾类型如下:

   0-可回收
   1-有害
   2-厨余(湿)
   3-其他(干)

   -1表示出错,并打印到stderr中
*/
int getGarbageCategoryByNames(std::vector<std::string>* objectNames);
int getGarbageNamesByImage(const char* fileName, std::vector<std::string>* objectNames);

#endif

函数getGarbageNamesByImage的第一个参数fileName是图片的文件名,这个函数会调用BaiduAI的图像识别SDK中的aip::Imageclassify类下的advanced_general方法来获取图像中的物体类型,返回结果是一个JSON对象,格式如下(我输入了一张蓄电池的图片):

{
	"error_code" : null,
	"log_id" : 1376707039807406080,
	"result" : 
	[
		{
			"keyword" : "蓄电池",
			"root" : "商品-电子原器件",
			"score" : 0.82018500000000005
		},
		{
			"keyword" : "车载电源",
			"root" : "商品-机器设备",
			"score" : 0.58906800000000004
		},
		{
			"keyword" : "包装袋/盒",
			"root" : "非自然图像-图像素材",
			"score" : 0.38386300000000001
		},
		{
			"keyword" : "冷冻油",
			"root" : "商品-原材料",
			"score" : 0.2089
		},
		{
			"keyword" : "电能表",
			"root" : "商品-仪表",
			"score" : 0.039449999999999999
		}
	],
	"result_num" : 5
}

相关字段含义应该很清楚,可参阅api文档。

接下来解析这个JSON对象,拿到所有的"keyword"(实验中发现"score"最高结果的也可能是错的,出于容错性的考虑,所有的"keyword"都带走),存到第二个参数objectNames指向的一个字符串数组中去(传出参数)。这样函数调用者就能拿到图片中物体的所有可能类型了。

接下来将objectNames传给函数getGarbageCategoryByNames,它会将其中的每个字符串一一取出,写到post请求的参数中去,然后逐一向天行数据api发送HTTPpost请求查询这个物体的垃圾类型,一旦查询成功,就break,返回垃圾类型。四种垃圾类型用四个整数表示,映射关系在上面的注释里。

2-BaiduAI开放平台的使用

1-开发包使用步骤
  1. 官方网站下载C++ SDK压缩包。
  2. 将下载的aip-cpp-sdk-version.zip解压,其中文件为包含实现代码的头文件。
  3. 安装依赖库libcurl(需要支持https),openssl,jsoncpp(>1.6.2版本,0.x版本将不被支持)。
  4. 编译工程时添加 C++11 支持 (gcc/clang 添加编译参数 -std=c++11),添加第三方库链接参数 lcurl, lcrypto, ljsoncpp。
  5. 在源码中include imageclassify.h ,引入压缩包中的头文件以使用aip命名空间下的类和方法。
2.安装相关的库

如果库的安装或使用有错误,直接给树莓派重装系统吧。

libcurl库

这是一个可以发送很多网络请求的C库,利用它可以发送http请求,模拟浏览器的行为;

安装方法:

首先到官网下载源码包:https://curl.haxx.se/download.html

解压、进入主目录进行安装(经典三部曲):

tar -zxvf curl-7.51.0.tar.gz
 
sudo ./configure
 
sudo make
 
sudo make install

头文件目录:

动态库目录:

关于如何使用curl库,这里有详细的视频教程:https://www.bilibili.com/video/BV1o7411w75c

jsoncpp库

这是用C++编写的json库,可以用它来解析json字符串,操纵json对象等;

安装方法:直接命令sudo apt-get install libjsoncpp-dev

头文件目录:

动态库目录:

有一说一,他这个BaiduAI提供的SDK中json头文件引用的目录是错的,他直接#include<json/json.h>,少了一层jsoncpp,在前面都加上就好了。顺便记录一下gcc的头文件搜索顺序:

  • 由参数-I指定的路径(指定路径有多个路径时,按指定路径的顺序搜索)
  • 然后找gcc的环境变量 C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH
  • 再找内定目录
    /usr/include
    /usr/local/include
    /usr/lib/gcc-lib/i386-linux/2.95.2/include
    /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3
    /usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include
openssl库

负责https的吧,跟安全有关;

安装方法:

sudo apt-get install openssl

sudo apt-get install libssl-dev

头文件目录:

四、服务端部分

这部分不是跑在树莓派上的,是跑在本地机器,或者正儿八经服务器上的(简单考虑只在局域网内模拟),TCPServerWebServer这两个服务程序相配合:TCPServer接收来自树莓派端封装了垃圾桶最新使用余量状态信息(usage)的报文,启动一个线程,将其解析为JSON对象,并写入到allTrashesInfo.json文件中去。

WebServer在前端js脚本调用百度地图api,负责向用户在地图上展示垃圾桶的位置usage以及是否已满,后端基于NodeJS实现,很简单。

1-目录结构

在这里插入图片描述

2-目录中各个文件作用

1-allTrashesInfo.json

这个文件存储了一个json数组(对象),里面的每个对象都包装了一个垃圾桶的信息,格式是这样的:

   {
      "id" : 0,
      "location" : [ 120.3502, 30.3202 ],
      "recyleBitMap" : 1,
      "usage" : [ 0.2999, 0.5, 0.1000, 0.9000 ]
   }

各个键值对儿的解读:

  • id:垃圾桶的编号。
  • location:垃圾桶的GPS定位,考虑到成本,并不打算给垃圾桶安装GPS模块,每当移动位置时修改这个垃圾桶里的GPS参数即可。
  • recyleBitMap:一个四位的bitmap,每个二进制位标志着对应的垃圾桶满了,从高位到低位依次对应“可回收”,“有害”,“厨余”,“其他”。
  • usage:一个trash的每个subtrash的用量情况,从0到1共分11个分度,当大于等于0.9时对应的recyleBitMap置1,意味着此subtrash需要被回收。出现一长串数字的原因是jsoncpp库对小数的解析有点小问题,不能很好的表示非(1/2)^n的浮点数。
2-TCPServer
  • make.sh是一个编译TCPServer.cpp的一个shell脚本,懒得写makefile了,里面就一行编译命令。

  • TCPServer是编译出来的elf可执行文件。

  • TCPServer.cpp是TCP服务器源码,这个TCP服务器的作用是接受来自垃圾桶(树莓派)发过来的一段TCP报文,里面的信息是上面json文件中的那个对象,它包含了这个垃圾桶最新的状态。服务器收到这个报文后,按照其中的内容更新allTrashesInfo.json文件(读入-修改-写回)。服务器程序是多线程的,每当有一个树莓派建立连接都会pthread_create一个子线程去接收报文更新文件。由于各个线程共享打开的文件,文件属于临界资源,出于线程安全的考虑,应用的机制对线程访问文件时上锁,保证每个线程有序更改文件,原理示例如下:

    /*进入临界区,加锁*/
    pthread_mutex_lock(&mutex);
    
    /*临界区:操作文件*/
    
    pthread_mutex_unlock(&mutex);
    /*退出临界区,解锁*/
    
  • 剩下的两个wrap文件是把用于网络通信的系统调用函数进行了错误处理封装,提供给服务器程序使用,不赘述。

  • StressTest.sh是测试TCPServer程序用的,测试时在一两秒内迭代了请求512次,还挺快的。

3-WebServer

这是一个提供给管理者(环卫部门)或者用户的Web服务器,用于在地图上展示垃圾桶的使用情况,效果如下:

界面很丑不要在意,毕竟直男审美。

这部分比较简单:

  • img目录存放了一个favicon.ico图标和16张垃圾桶的图片。在地图上显示不同subtrash满的原理就是bitmap,上面提到过,4个binary位从高到低分别是“可回收(蓝色)”,“有害(红色)”,“厨余(绿色)”,“其他(黄色)”。比如说都没满就是0H(0D,0000B),表现在图上就是四个空条;都满了就是FH(16D,1111B),表现在图上就是“蓝红绿黄”四个条;厨余垃圾和有害满了就是6H(6D,0110B),表现在图上就是红条和绿条,以此类推。
  • index.html是主页代码,没啥东西。
  • map.js是index.html页面的脚本文件,主要负责发送ajax请求获取allTrashesInfo中的json对象,然后创建一张地图,最后根据allTrashInfo中的垃圾桶信息挑选合适的垃圾桶图标绑定上click事件放地图上去,具体见源码。
  • http.js是基于nodejs的一个后端程序,功能很简单,就是前端请求啥发给它啥,没啥大逻辑,几个if-else,具体见源码。

3-服务端源码

https://gitee.com/daniel187/InnovationLab/tree/cpp/Servers

五、开发环境

主机系统是Ubuntu18.04

树莓派跑的官方的是Debian系统;

一般用VSCode通过ssh连接到树莓派上编辑(VSCode天下第一);

六、项目源代码

https://gitee.com/daniel187/InnovationLab

你要用的话需要自己去百度AI开放平台和天行数据注册用户获取用户信息和密码等,写成一个json文件存入树莓派的进程工作目录,文件名叫personal_secret.json,程序会读取相关信息的,格式如下:

{
    "app_id_": "********",
    "api_key_": "********",
    "secret_key_": "********",
    "tian_key_": "********"
}

最后感谢zl同学的collaboration!

比赛需要故只开源了粗劣的第一个版本demo实现,第二版本改进使用yoloV3模型进行垃圾分类检测,机器臂分拣垃圾,垃圾分类数据集重新收集,并有微信小程序的用户查询垃圾分类及反馈机制 注意看ReadMe文件,注意看ReadMe文件,注意看ReadMe文件 B站视频介绍地址:https://www.bilibili.com/video/av80830870 交流群:1074171553 题主双非师范院校2021考研狗,如果你觉得这个小项目有帮助到你,请为项目点一个star,不管是考试型选手毕设项目被迫营业还是直接拿去二开参加比赛,这些都没问题,开源项目就是人人为我我为人人,但请尊重他人劳动成果,大家都是同龄人.心上无垢,林间有风. 材料清单 树莓派 1个 pca9685 16路舵机驱动板 1个 7寸可触摸显示屏一个 MG996R 舵机4个 垃圾桶4个 usb免驱动摄像头1个 树莓派GPIO扩展板转接线柱1个 硅胶航模导线若干 环境需求 1.开发环境 神经网络搭建—python 依赖 tensorflow,keras 训练图片来源华为云2019垃圾分类大赛提供 训练图片地址:https://developer.huaweicloud.com/hero/forum.php?mod=viewthread&tid=24106 下载图片文件后将文件解压覆盖为 garbage_classify 放入 垃圾分类-本地训练/根目录 神经网络开源模型--- resnet50 models 目录需要手动下载resnet50 的模型文件放入 resnet50模型文件名:resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5 百度就可以找到下载放入即可:https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5 2.运行开发环境 进入 "垃圾分类-本地训练"目录 环境初始化 python3 安装框架flaskpip3 install flask 安装tensorflow,keras等依赖 pip3 install tensorflow==1.13.1 pip3 install keras==2.3.1 运行 1.命令python3 train.py开启训练 2.命令python3 predict_local.py开启输入图片测试 3. 训练服务模型部署 进入 "垃圾分类-服务部署"目录 output_model 目录存放的是本地训练完成导出的h5模型文件 models 目录需要手动下载resnet50 的模型文件放入 resnet50模型文件名:resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5 百度就可以找到下载放入即可:https://github.com/fchollet/deep-learning-models/releases/download/v0.2/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5 环境初始化 安装框架flaskpip3 install flask 安装tensorflow,keras等依赖 pip3 install tensorflow==1.13.1 pip3 install keras==2.3.1 运行 1.命令python3 run.py开启窗口本地调试 2.命令python3 flask_sever.py开启服务部署 3.命令sh ./start.sh开启后台运行服务部署 4.树莓派界面搭建 基于nodejs electron-vue 强烈建议使用cnpm来安装nodejs库 进入 "树莓派端/garbage_desktop"目录 安装依赖 cnpm install 开发模式 cnpm run dev 打包发布 cnpm run build 5.树莓派端flask-api接口操作硬件 进入"进入 "树莓派端/garbage_app_sever"目录" 注意树莓派应该开启I2C,确保pca9685 I2C方式接入后可显示地址 命令:i2cdetect -y 1 查看 地址项 0x40是否已经接入树莓派 运行 python3 app_sever.py 或者 sh start.sh 启动 若提示缺少依赖: pip3 install adafruit-pca9685 pip3 install flask
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值