lot_manager/README.md

21 KiB
Raw Permalink Blame History

1. 项目简介

📃 Lot_manager:基于MQTT(客户端-服务器的消息发布/订阅传输协议)、FFmpeg(多媒体视频处理工具)、SupervisorLinux进程控制系统物联网数据上传视频推流视频录像应用。

⛓️ 本项目大致实现原理如图所示,包含两个模块:视频推流模块和物联网数据管理模块。

  1. 视频推流模块使用MQTT的客户端连接MQ服务器EMQXMQ服务端通过发布消息如推流命令、停止推流命令、录像上传命令等MQTT客户端接收指令后通过supervisor启动相应的进程从而启动服务将视频推流至ZLMediaKit服务器。
  2. 物联网数据管理模块数据上传通过MQTT客户端进行连接服务器如果连接成功则通过supervisor启动数据上传服务并发布数据到服务端。如果连接失败或者断开了连接则通过supervisor停止数据上传服务则将数据保存到本地(sqlite)。 image

技术路线图

  • 视频推流
    • FFmpeg
  • 物联网数据管理
  • MQTT程序控制
  • MQTT程序控制的返回结果设置统一的返回状态码

2. 开发环境部署

软件要求

已经测试过的系统

  • Debian GNU/Linux 12(bookworm) Linux version 5.16.17+

环境安装

# 首先,确信你的机器安装了python 3.11.2
$ python --version
Python 3.11.2
# 同步本地软件包列表与远程软件源中的最新软件包
$ sudo apt update

# 安装supervisor进程管理
$ sudo apt install supervisor
$ supervisord -v    
4.2.5

# 安装FFmpeg推流管理
$ sudo apt install ffmpeg -y
$ ffmpeg -version
ffmpeg version 5.1.4-0+deb12u1 Copyright (c) 2000-2023 the FFmpeg developers
built with gcc 12 (Debian 12.2.0-14)

# 安装git
$ sudo apt install git

# 设置时区为上海
$ sudo timedatectl set-timezone Asia/Shanghai
$ sudo hwclock --systohc
$ sudo hwclock --hctosys
$ date 
Thu Dec 14 15:13:45 CST 2023
# 更新py库
$ pip3 install --upgrade pip

配置wifi

# 使用arp协议查看以太网接口的摄像机地址需要将摄像机连接以太网
$ sudo arp -a
# 系统设置静态IP
# 使用nmcli radio wifi查看wifi开启状态
$ nmcli radio wifi
enabled
# 连接wifi
$ sudo nmcli dev wifi connect <SSID> password <PASSWORD>

配置系统静态IP

使用nmtui网络界面管理

$ sudo nmtui

  • 设置的静态IP地址网段需要和摄像机在同一个网段
  • 无需设置网关即为空。此项解决wifi上网和以太网上网冲突的问题
  • DNS设置为8.8.8.8
  • 勾选始终不使用此网络于默认路由

image image

配置git和pip

# 设置git的username
$ git config --global user.name xxx
# 设置git的email
$ git config --global user.email xxx
# 设置git拉取代码时用户名和密码的存储
$ git config --global credential.helper store
$ git clone https://gitea.lihaink.cn/xyj/lot_manager.git
$ cd lot_manager
# 安装项目依赖
$ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

创建设备名称(系统唯一标识名称)

该部分创建核桃派系统的设备名称,用于标识。设备名称创建在主目录下。

# lihai_lot_walnutpi_dev_1 代表1号设备可以自行设置
$ cd ~
$ echo 'lihai_lot_walnutpi_dev_1' | tee device_name
lihai_lot_walnutpi_dev_1

创建视频录像保存目录

录像保存在主目录下的mp4目录下。

$ mkdir /home/pi/mp4

配置定时任务

配置一些定时任务如录像只保存2天。物联网数据只保存3天。对于32G的内存卡只要超过20G就进行删除录像直至录像小于20G。

$ sudo crontab -e
# 公共定时任务
# 每小时0分执行
0 * * * * /usr/bin/bash /home/pi/lot_manager/bash/cron_delete_mp4.sh
# 每天00分执行
0 0 * * * /usr/bin/python /home/pi/lot_manager/delete_lot_data_3_days.py

# 针对32G内存卡的定时额外加这个
# 每隔15分钟进行
*/15 * * * * /usr/bin/python /home/pi/lot_manager/delete_than20G.py

初始化本地数据库

本项目采用sqlite进行数据的保存数据库文件存放在主目录下。

$ python /home/pi/lot_manager/create_db.py

配置supervisor文件

  1. 安装完supervisor后会在/etc/supervisor目录下存在supervisord.conf文件和conf.d目录。
$ cd /etc/supervisor/
$ ls
conf.d  supervisord.conf  supervisord.conf.bak
  1. 对supervisord.conf文件进行配置 首先备份默认配置,然后进行配置。
$ sudo cp supervisord.conf  supervisord.conf.bak
$ sudo vim supervisord.conf

配置信息:

; supervisor config file

[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0777                       ; sockef file mode (default 0700)

[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
childlogdir=/var/log/supervisor            ; ('AUTO' child log dir, default $TEMP)
user=root
; the below section must remain in the config file for RPC
; (supervisorctl/web interface) to work, additional interfaces may be
; added by defining them in separate rpcinterface: sections
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL  for a unix socket
user=pi
; The [include] section can just contain the "files" setting.  This
; setting can list multiple files (separated by whitespace or
; newlines).  It can also contain wildcards.  The filenames are
; interpreted as relative to this file.  Included files *cannot*
; include files themselves.
[inet_http_server]
host = 127.0.0.1
port = 9001
[program:__mqtt__]
directory=/home/pi/lot_manager
command=/usr/bin/python MQTT.py
user=pi
autostart=true
autorestart=true
redirect_stderr=true
stopsignal=TERM
stopasgroup=True
priority=1
[include]
files = /home/pi/lot_manager/conf/common/*.conf

上面是已经修改好的配置文件,修改了其中几项:

  • [unix_http_server] chmod改为0777使得普通用户得以执行。
  • [include] files = /etc/supervisor/conf.d/*.conffiles是用户定义的配置文件。
  • [supervisorctl] 添加user=pi使得普通用户得以执行。
  • [program:__mqtt__] 添加mqtt配置信息
  • 修改/etc/supervisor/conf.d的权限sudo chmod 777 /etc/supervisor/conf.d
  1. 设置开启启动supervisor
$ sudo systemctl enable supervisor
  1. 启动supervisor
$ sudo systemctl start supervisor

3. 项目结构

  • lot_manager
    • bash
      • cron_delete_mp4.sh 定时删除录像脚本
      • modify_device_name.sh 修改设备唯一名称脚本
      • record.sh 录像脚本
      • start_data_upload.sh 开启数据上传脚本
      • start_push_stream.sh 启动推流脚本
      • stop_data_upload.sh 停止数据上传脚本
      • stop_push_stream.sh 停止推流脚本
      • stream.sh 推流命令脚本
    • conf
      • common
        • data_upload.conf 数据上传配置
        • mqtt.conf mqtt配置
        • push_stream.conf 推流配置
        • record.conf 录像配置
        • sensor_to_local.conf 数据保存在本地
        • sensor_to_server.conf 数据发布到mq服务器
      • device
        • device.conf 不同设备的配置
      • example
        • cron_for_32G.conf 对于32G卡的定时任务配置
        • cron_for_64G.conf 对于64G卡的定时任务配置
        • supervisord.conf 配置supervisor文件
      • main
        • common.conf 公共配置
      • tmp
        • device_name 需要修改的设备名称
      • zhanguan
        • topic.conf 展馆的特殊设备
    • db/ 本地数据库管理
    • MQTT.py
    • data_upload.py 数据上传程序
    • api.py 本地数据库api
    • config.py 读取各种配置文件
    • create_db.py 创建数据库程序
    • delete_lot_3_days.py 删除数据库中早于3天的数据
    • delete_than20G.py 当录像超过20G直接删除直到小于20G
    • sensor_to_local.py 数据保存在本地
    • sensor_to_server.py 数据发布到服务器
    • tool.py 工具
    • git_push.sh 推送git脚本
    • git_update.sh 更新git脚本
    • install.sh 安装依赖脚本

MQTT程序控制 -MQTT.py

image

物联网数据上传 -data_upload.py

image

sensor_to_server.py

image

项目的配置文件 -config.py

通过configparser读取.conf文件。 项目的配置文件均在conf目录下

  • conf
    • common 表示supervisor管理的进程配置文件
    • device 设置不同device的配置包括订阅主题、发布主题、用户名和密码等
    • main 公共配置,包括域名设置、录像上传地址等
import configparser

import subprocess

p = subprocess.Popen(['cat /home/pi/device_name'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
# 设备名称,必须要有
device_name = out.decode('utf-8').strip()

# 读取配置
config = configparser.ConfigParser()
# 读取公共配置
config.read('conf/main/common.conf')
# 域名
broker = config.get("broker", "host")
# 端口这里必须是int类型
port = config.getint("broker", "port")
# 录像地址
post_record_list_url = config.get("record", "post_record_list_url")
post_record_url = config.get("record", "post_record_url")
# 发布消息的认证pwd
publish_pwd = config.get("publish_pwd", "value")


# 读取设备配置
config.read('conf/device/device.conf')
# 订阅的主题
subscribe_topic = config.get(device_name, "subscribe_topic")
# 发布的主题
publish_topic = config.get(device_name, "publish_topic")
# 用户
username = config.get(device_name, "username")
# 密码
password = config.get(device_name, "password")

# 特殊配置
config.read('conf/zhanguan/topic.conf')
zhanguan_device_name = config.get("device", "name")

# tool配置
mp4_path = '/home/pi/mp4'

4. Supervisor服务参数配置

配置文件都在conf文件夹下。主要是common、main和device文件夹。

  • conf
    • common 表示supervisor管理的进程配置文件
    • device 设置不同device的配置包括订阅主题、发布主题、用户名和密码等
    • main 公共配置,包括域名设置、录像上传地址等

supervisor服务配置

  • directory表示执行命令时首先进入这个文件夹
  • command表示要执行的命令
  • user必须是pi
  • autostart表示随开机或supervisor重启时跟着启动
  • autorestart表示启动失败自动重启3次
[program:__mqtt__]
directory=/home/pi/lot_manager
command=/usr/bin/python MQTT.py
user=pi
autostart=true
autorestart=true
redirect_stderr=true
stopsignal=TERM
stopasgroup=True
priority=1

数据上传的supervisor配置文件

[program:data_upload]
directory=/home/pi/lot_manager
command=/usr/bin/python data_upload.py
user=pi
;是否随开机自启 或者reload自启动
autostart=true
;失败重启
autorestart=true
;重启次数
restart_times=3
redirect_stderr=true
stopsignal=TERM
stopasgroup=True

推流的supervisor配置文件

注意不要跟随开机或supervisor重启启动

[program:push_stream]
directory=/home/pi/lot_manager/bash
command=/usr/bin/bash stream.sh
user=pi
autostart=false
autorestart=true
redirect_stderr=true
stopsignal=TERM
stopasgroup=True

录像的supervisor配置文件

[program:record]
directory=/home/pi/lot_manager/bash
command=/usr/bin/bash record.sh
user=pi
;是否随开机自启 或者reload自启动
autostart=true
;失败重启
autorestart=true
;重启次数
restart_times=3
redirect_stderr=true
stopsignal=TERM
stopasgroup=True

数据保存在本地的supervisor配置文件

[program:sensor_to_local]
directory=/home/pi/lot_manager
command=/usr/bin/python sensor_to_local.py
user=pi
;是否随开机自启 或者reload自启动
autostart=false
;失败重启
autorestart=true
;重启次数
restart_times=3
redirect_stderr=true
stopsignal=TERM
stopasgroup=True

数据保存在mq服务器的supervisor配置文件

[program:sensor_to_server]
directory=/home/pi/lot_manager
command=/usr/bin/python sensor_to_server.py
user=pi
;是否随开机自启 或者reload自启动
autostart=false
;失败重启
autorestart=true
;重启次数
restart_times=3
redirect_stderr=true
stopsignal=TERM
stopasgroup=True

不同设备的配置

  1. 订阅的主题表示MQTT控制时发布的主题比如对设备lihai_lot_walnutpi_dev_1进行推流。
  2. 发布消息的主题,表示物联网数据上传时发布消息的主题。
  3. username和password是MQTT连接服务器的用户名和密码。
# 设备1-32G
# 编号
[lihai_lot_walnutpi_dev_1]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_1
# 发布消息的主题
publish_topic=camera_1
username=lihai_lot_land_1
password=123456
####################################
# 设备2-32G
[lihai_lot_walnutpi_dev_2]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_2
# 发布消息的主题
publish_topic=camera_2
username=lihai_lot_land_1
password=123456
####################################
# 设备3-64G
[lihai_lot_walnutpi_dev_3]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_3
# 发布消息的主题
publish_topic=camera_3
username=lihai_lot_land_1
password=123456
####################################
# 设备4-64G
[lihai_lot_walnutpi_dev_4]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_4
# 发布消息的主题
publish_topic=camera_4
username=lihai_lot_land_1
password=123456
####################################
# 设备5-64G
[lihai_lot_walnutpi_dev_5]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_5
# 发布消息的主题
publish_topic=camera_5
username=lihai_lot_land_1
password=123456
####################################
# 设备6-64G
[lihai_lot_walnutpi_dev_6]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_6
# 发布消息的主题
publish_topic=camera_6
username=lihai_lot_land_1
password=123456

公共配置

  1. host和port表示MQTT的服务器地址。
  2. post_record_list_url是上传录像的目录的地址。
  3. post_record_url是上传录像的地址。
# 域名地址
[broker]
host=mqtt.lihaink.cn
port=1883
[record]
post_record_list_url=https://ceshi-iot.lihaink.cn/api/index/file_list
post_record_url=https://ceshi-iot.lihaink.cn/api/index/upload
[publish_pwd]
value=123456

5. ffmpeg推流设置

推流的脚本为stream.sh内容如下

#!/bin/bash
device_name=`cat /home/pi/device_name`
/usr/bin/ffmpeg -rtsp_transport tcp -re -i rtsp://admin:123456@192.168.0.123:554/mpeg4 -c copy -preset fast -r 20 -flvflags no_duration_filesize -f rtsp -rtsp_transport tcp rtsp://47.108.186.87:554/live/$device_name

对于ffmpeg的参数设置如下

  • -rtsp_transport_tcp tcp 表示以tcp进行获取流保证数据完整性
  • -re 表示推流设置
  • -i 表示输入流,这里是摄像机地址
  • -c 表示直接复制源流对象,不进行编码
  • -preset fast表示快速传输
  • -r 20表示以帧率20传输
  • flvflags no_duration_filesize表示其他一些错误排除
  • -f rtsp -rtsp_transport tcp 表示输出的文件格式输出为rtsp流也是tcp传输
  • rtsp://47.108.186.87:554/live/$device_name表示推流地址

6.录像保存设置

录像的脚本为record.sh内容如下

#!/bin/bash

RTSP_URL="rtsp://admin:123456@192.168.0.123:554/mpeg4"  # RTSP流的URL
OUTPUT_DIR="/home/pi/mp4"  # 录像文件保存的目录
RECORD_DURATION=900  # 录像时长(秒)
# 获取当前时间
CURRENT_TIME=$(date +"%Y-%m-%d_%H-%M-%S")
# 构建录像文件名
OUTPUT_FILE="${OUTPUT_DIR}/${CURRENT_TIME}.mp4"
# 开始录制RTSP流
/usr/bin/ffmpeg -rtsp_transport tcp -i "${RTSP_URL}" -c copy -c:a aac -s 1920x1080 -r 15 -crf 35 -t "${RECORD_DURATION}" -f mp4 "${OUTPUT_FILE}"

参数设置如下:

  • -rtsp_transport_tcp tcp 表示以tcp进行获取流保证数据完整性
  • -i 表示输入流,这里是摄像机地址
  • -c 表示直接复制源流对象,不进行编码
  • -c:a aac表示音频编码格式为aac
  • -s 1920x1080是分辨率裁剪
  • -r 15是帧率改为15
  • -crf 35是改变恒定传输速率值越大压缩越好但质量越差默认值是28
  • -t 表示900秒一次保存一次
  • -f mp4是保存为mp4文件

7. MQTT程序控制配置发布消息和订阅消息

设备的编号配置信息

设备的配置为:

[lihai_lot_walnutpi_dev_1]
# 订阅的控制主题,必须和系统设置的相同
subscribe_topic=lihai_lot_walnutpi_dev_1
# 发布消息的主题
publish_topic=camera_1
username=lihai_lot_land_1
password=123456

其中,

  • subscribe_topic 表示订阅的主题,只有订阅后才能进行控制
  • publish_topic 表示数据发布的主题
  • username 表示客户端进行连接的用户名
  • password 表示客户端进行连接的密码

控制设备(推流、数据上传、录像等)

  1. 发布的主题
lihai_lot_walnutpi_dev_1
  1. 发送的消息体payload example:
{
  "msg":"exec",
  "data":"sudo ifconfig",
  "pwd":"123456"
}

msg类型

msg表示消息类型包括

  • push_stream 设备启动推流
发布主题lihai_lot_walnutpi_dev_1
{
  "msg":"exec",
  "pwd":"123456"
}
  • close_stream 设备关闭推流
发布主题lihai_lot_walnutpi_dev_1
{
  "msg":"close_stream",
  "pwd":"123456"
}
  • record_list 录像文件列表
发布主题lihai_lot_walnutpi_dev_1
{
  "msg":"record_list",
  "pwd":"123456"
}
  • record 上传选定文件
发布主题lihai_lot_walnutpi_dev_1
{
  "msg":"record_list",
  "data":"2023-12-13_11-11-11.mp4"
  "pwd":"123456"
}
  • status 查看supervisor运行状态
发布主题lihai_lot_walnutpi_dev_1
订阅success
{
  "msg":"status",
  "pwd":"123456"
}
  • reload 重启supervisor
发布主题lihai_lot_walnutpi_dev_1
订阅success,error
{
  "msg":"reload",
  "pwd":"123456"
}
  • update 更新git
发布主题lihai_lot_walnutpi_dev_1
订阅success,error
{
  "msg":"update",
  "pwd":"123456"
}
  • exec 执行linux命令
发布主题lihai_lot_walnutpi_dev_1
订阅success,error
{
  "msg":"exec",
  "data":"supervisorctl tail record"
  "pwd":"123456"
}

data

  • data在执行linux命令和获取录像时必须设置。
  • 在执行命令时data则是需要执行的命令获取录像时需要指定需要的录像名称
  • 订阅success和error主题可以查看错误消息和命令执行结果

8. 本地数据库字段设置

class LOT_DATA(BaseModel):
    create_time: int = Field(None, description='创建时间(时间戳) ')
    wind_speed: float = Field(None, description='风速:(0到30)m/s ')
    wind_direction: float = Field(None, description='风向:0360°')
    ambient_temperature: float = Field(None, description='环境温度:℃')
    ambient_humidity: float = Field(None, description='环境湿度:%RH')
    carbon_dioxide: float = Field(None, description='二氧化碳0~5000ppm')
    ambient_air_pressure: float = Field(None, description='环境气压0~120KPa')
    rainfall: float = Field(None, description='雨量mm')
    ambient_lighting: float = Field(None, description='环境光照0-65535Lux0-20万Lux')
    soil_temperature: float = Field(None, description='土壤温度:-40~80℃')
    soil_moisture: float = Field(None, description='土壤湿度0-100%RH')
    soil_conductivity: float = Field(None, description='土壤电导率0-20000μS/cm')
    soil_PH: float = Field(None, description='土壤PH:3~9PH')
    soil_potassium_phosphate_nitrogen: float = Field(None, description='土壤磷酸钾氮的标准值在140-225mg/kg')
    soil_potassium_phosphate_phosphorus: float = Field(None, description='土壤磷酸钾磷的标准值在57-100mg/kg,')
    soil_potassium_phosphate_potassium: float = Field(None, description='土壤磷酸钾钾的标准值在106-150mg/kg')

本地数据库存放位置

# 存放的路径
KB_ROOT_PATH = "/home/pi/"
# 数据库名称
DB_ROOT_PATH = os.path.join(KB_ROOT_PATH, "lot_data.db")
# 连接URI
SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_ROOT_PATH}"

数据库操作api

本项目采用sqlalchemy对sqlite进行数据的增删改查。api服务为项目的api.py文件。 api包括的功能

  • add 添加数据到数据库
  • delete 删除3天之前的数据