1.背景需求
目前家里燃气度数的读数上报,每个月在社区微信群里面将手机拍摄的燃气表读数截图(加住址信息水印),发到群里给抄表员。
2.总体设计
设计目标
功能一:手机上随时可以远程采集读数图片(自动加住址信息水印),在微信上发送给社区抄表员。
功能二:每小时采集一次,统计燃气每日使用状况,对使用情况进行分析和预警(由于OCR还需要进一步提升识别能力,暂不实现)。
功能三:燃气漏气检测(因测试有一定危险,暂不实现)。
3.采集系统
使用树莓派,部署Docker环境。
3.1 功能描述
接收管理服务器实时采集请求,实现燃气表拍照的采集上报,摄像头采集之前要自动打开小夜灯照明,厨柜内较黑拍不清楚。
3.2 容器环境配置
3.2.1 Dockerfile文件
FROM ubuntu:latest
ENV LANG C.UTF-8
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update
RUN apt install -y openssh-client openssh-server
RUN apt-get install -y wget curl git telnet vim make gcc
RUN apt-get install -y python3 python3-pip libpcre3 libpcre3-dev python3-opencv
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN echo "Port 8422" >> /etc/ssh/sshd_config
RUN echo 'root:cw' | chpasswd
RUN mkdir /var/run/sshd
EXPOSE 8422
RUN apt clean \
&& rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp* \
RUN apt autoremove
WORKDIR /tmp
RUN apt-get update
RUN apt-get upgrade -y
RUN apt install -y libusb-1.0-0-dev
RUN git clone https://github.com/mvp/uhubctl
WORKDIR /tmp/uhubctl
RUN make && make install
RUN pip3 install paho-mqtt pytest-shutil requests -i https://pypi.mirrors.ustc.edu.cn/simple/
WORKDIR /home
CMD ["/usr/sbin/sshd","-D"]
3.2.2 docker-compose.yaml文件
version: "3.0"
services:
#燃气采集设备端
#docker exec -it gas_meter_reading_t /bin/bash
gas_meter_reading_t:
build: .
image: gas_meter_reading:v1
container_name: gas_meter_reading_t
working_dir: /home
# devices:
# - /dev/video0:/dev/video0
ports:
- 8422:8422
volumes:
- ./home:/home
restart: always
tty: true
privileged: true
3.3 代码样例
3.3.1 控制USB小夜灯代码样例
#uhubctl 控制 usb 的供电情况,然后控制灯的状态
#控制命令:sudo uhubctl -l location -p port -a off
#location是USB集线起的位置,port是USB编号,off关闭,on打开;
uhubctl --help
uhubctl -l 2 -p 1 -a off
uhubctl -l 2 -p 1 -a on
3.3.2 摄像头拍摄代码样例
import cv2 as cv
cap = cv.VideoCapture(0)
if cap.isOpened():
_, frame = cap.read()
farm = cv.resize(frame, dsize = (1080,1080) )
cv.imwrite(r"/20231020/220301.jpg",farm)
4.管理系统
系统环境要求:LINUX系统,支持Docker环境。
4.1 容器环境配置
4.1.1 Dockerfile文件
FROM ubuntu
RUN apt update
RUN apt upgrade -y
RUN apt install -y wget libgl1 libglib2.0-dev python3 pip curl inetutils-ping graphicsmagick
# Install nvm
RUN apt install -y git
RUN git clone http://github.com/creationix/nvm.git /root/.nvm;
RUN chmod -R 777 /root/.nvm/;
RUN bash /root/.nvm/install.sh;
RUN export NVM_DIR="$HOME/.nvm";
RUN echo "[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh" >> $HOME/.bashrc;
RUN bash -i -c 'nvm install 16'
RUN bash -i -c 'npm install -g pnpm'
CMD ["/bin/bash"]
4.1.2 Dockerfile_OCR
FROM ubuntu
RUN apt update
RUN apt upgrade -y
RUN apt install -y wget libgl1 libglib2.0-dev python3 pip curl inetutils-ping graphicsmagick
# Install nvm
RUN apt install -y git
RUN git clone http://github.com/creationix/nvm.git /root/.nvm;
RUN chmod -R 777 /root/.nvm/;
RUN bash /root/.nvm/install.sh;
RUN export NVM_DIR="$HOME/.nvm";
RUN echo "[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh" >> $HOME/.bashrc;
RUN bash -i -c 'nvm install 16'
RUN bash -i -c 'npm install -g pnpm'
CMD ["/bin/bash"]
4.1.3 docker-compose.yaml
version: '3.3'
services:
#Gas meter reading platform:燃气采集平台
#docker exec -it gmrplat_t /bin/bash
gmrplat_t:
build: .
image: gmrplat:v1
volumes:
- ./home:/home
container_name: gmrplat_t
ports:
- 8477:8477
- 8488:8488
restart: always
command: /bin/bash
tty: true
networks:
back_net:
ipv4_address: 172.77.1.5
#百度AI/图像识别
#docker exec -it paddle_ocr_t /bin/bash
paddle_ocr_t:
container_name: paddle_ocr_t
build:
context: ./
dockerfile: Dockerfile-OCR
image: gmr_paddle_ocr
volumes:
- ./PaddleOCR:/PaddleOCR
tty: true
privileged: true
restart: always
command: /bin/bash
networks:
back_net:
ipv4_address: 172.77.1.6
#开源物联网MQTT服务器(https://www.emqx.com/)
#初始用户名密码:admin/public
#docker exec -it emq_t /bin/bash
emq_t:
container_name: emq_t
image: emqx/emqx:5.3.0
ports:
- 1883:1883
- 8083:8083
- 8084:8084
- 8883:8883
- 18083:18083
restart: always
tty: true
networks:
back_net:
ipv4_address: 172.77.1.7
#开源数据库,兼容MYSQL
mariadb_t:
container_name: mariadb_t
image: mariadb
environment:
- MYSQL_ROOT_PASSWORD=cjy
- MYSQL_DATABASE=gmrplat
- MYSQL_USER=cjy
- MYSQL_PASSWORD=cjy
restart: always
networks:
back_net:
ipv4_address: 172.77.1.8
#数据库WEB后台管理
phpmyadmin_t:
container_name: phpmyadmin_t
image: phpmyadmin:5.2-apache
ports:
- 4090:80
environment:
- PMA_ARBITRARY=1
- PMA_HOST=172.77.1.8
- PMA_PORT=3306
- PMA_USER=root
- PMA_PASSWORD=cjy
restart: always
networks:
back_net:
ipv4_address: 172.77.1.9
#自定义容器网络
networks:
back_net:
ipam:
driver: default
config:
- subnet: 172.77.1.0/24
4.2 图像识别系统
百度开源AI:https://github.com/PaddlePaddle/PaddleOCR
照片OCR识别样例代码
from paddleocr import PaddleOCR, draw_ocr
ocr = PaddleOCR(use_angle_cls=True, lang="en")
img_path = './ppocr_img/imgs/11.jpg'
result = ocr.ocr(img_path, cls=True)
for idx in range(len(result)):
res = result[idx]
for line in res:
print(line)
4.3 远程抄表前台系统
主要代码,不是全部。
<script setup>
import { ref } from 'vue'
import 'weui';
import axios from 'axios';
async function get_gmr_img() {
console.log('get_gmr_img')
try{
let res = await axios.get('/api/gmr_img_now');
console.log(res);
let result = res.data;
console.log(result);
res = await axios.get('/api/gmr_get_last_img');
console.log(res);
result = res.data[0];
console.log(result.img_name);
const imgUrl = 'http://58.212.21.66:8488/images/mask_' + result.img_name;
document.getElementById('gmr_img').src = imgUrl
}catch{
console.log('请求失败');
}
}
</script>
<template>
<img id="gmr_img" src="/favicon.ico" class=gmr_img>
<button class="weui-btn weui-btn_primary" @click="get_gmr_img">立即更新</button>
<div style="text-align:center;" id="actionsheet1" class="weui_actionsheet_cell">长按燃气表图片2秒转发给抄表员</div>
</template>
<style scoped>
.gmr_img {
width: 100vw;
height: 80vh;
}
</style>
4.4 远程抄表后台系统
CREATE TABLE `gmrinfo` (
`id` bigint(12) NOT NULL,
`gather_time` datetime NOT NULL DEFAULT current_timestamp(),
`img_name` varchar(30) NOT NULL,
`img_ocr` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
const {promises:fs} = require("fs");
const moment = require('moment');
const Koa = require('koa');
const Router = require('koa-router');
const koa_static = require('koa-static');
const bodyParser = require('koa-bodyparser');
const mqtt = require('mqtt');
const axios = require('axios');
const knex = require('knex'); //https://knexjs.org/
const gm = require('gm');
const OCR_SERVER_IP = '172.77.1.6'
const OCR_SERVER_PORT = 8466
const MQTT_SERVER_IP = '172.77.1.7'
const MQTT_SERVER_PORT = 1883
const DB_SERVER_IP = '172.77.1.8'
const DB_USER = 'cjy'
const DB_PWD = 'cjy'
const DB_NAME = 'gmrplat'
const GMRPLAT_SERVER_PORT = 8488
const db = knex({
client: "mysql2",
connection: {
host: DB_SERVER_IP,
port: 3306,
user: DB_USER,
password: DB_PWD,
database: DB_NAME,
},
});
const WWWROOT = __dirname + '/public'
const IMGDIR = WWWROOT + '/images/'
//图像识别
const GMRToOCR = async (imgfile) => {
const url = "http://" + OCR_SERVER_IP + ":" + OCR_SERVER_PORT + "/GMROCR"
const bitmap = await fs.readFile(imgfile);
const adata = {
request_id: '001',
img_base64: ''
}
adata['img_base64'] = Buffer.from(bitmap).toString('base64');
const res = await axios.post(url, adata);
console.log(res.data)
console.log(res.data.ocr_res)
return res.data
}
const client = mqtt.connect("mqtt://" + MQTT_SERVER_IP + ":" + MQTT_SERVER_PORT);
client.subscribe("gmrdev/fixedtime")
client.on('message', async (topic, payload) => {
console.log('Received Message:', topic);
const base64EncodedfileData = payload.toString();
const fileDataDecoded = Buffer.from(base64EncodedfileData,'base64');
const img_name = moment(Date.now()).format('YYYYMMDDHHmmss');
fs.writeFile(IMGDIR + img_name + ".jpg", fileDataDecoded, err => {})
await gmr_add_img(img_name + ".jpg")
gm(IMGDIR + img_name + ".jpg")
.stroke("blue") //字体外围颜色
.fill("blue") //字体内围颜色(不设置默认为黑色)
.font("./AlibabaPuHuiTi-3-65-Medium.ttf", 60) //字库所在文件夹和字体大小
.drawText(50,50, img_name + "\nXX街道X小区X幢X室")
.write(IMGDIR + "mask_" + img_name + ".jpg", function (err) {
if (!err) console.log('gm_ok');
else console.log(err);
});
//const ocr_res = await GMRToOCR(IMGDIR + img_name + ".jpg");
});
const app_http = new Koa()
app_http.use(bodyParser());
app_http.use(async (ctx, next) =>{
await next();
ctx.set("Access-Control-Allow-Origin", "*");
ctx.set("Access-Control-Allow-Headers", "Content-Type");
ctx.set("Access-Control-Allow-Methods", "*");
});
app_http.use(koa_static(WWWROOT, {
index: true,
hidden: false,
defer: true
}))
const gmr_add_img = async (img_name) => {
console.log('gmr_add_img:' + img_name)
await db('gmrinfo').insert({img_name: img_name})
}
const gmr_img_now = async (ctx) => {
console.log('gmr_img_now')
client.publish('gmrplat/nowtime', 'now')
ctx.body = 'OK'
}
const gmr_get_last_img = async (ctx) => {
let result = await db("gmrinfo").select(["id", "gather_time","img_name","img_ocr"]).orderBy('id', 'desc').limit(1)
console.log(result)
ctx.body = result
}
const router = new Router();
router.get('/api/gmr_img_now', gmr_img_now);
router.get('/api/gmr_get_last_img', gmr_get_last_img);
app_http.use(router.routes());
app_http.listen(GMRPLAT_SERVER_PORT, '0.0.0.0', () => {
console.log(`服务启动:http://58.212.21.66:${GMRPLAT_SERVER_PORT}`);
})
5.系统演示
配套代码