作者:Ajeet Singh Raina 2023年3月29日
想象一下,你是一家智能农业公司,使用物联网传感器帮助农民优化作物产量。你们出售各种传感器,可以监测农田中的土壤湿度、温度、湿度和光照水平。
为了帮助农民充分利用他们的资源,你需要建立一个集中的系统。该系统收集来自所有田地的传感器数据,并提供对作物健康和生长的实时监测。有了这些数据,农民可以在知情的情况下决定何时灌溉、施肥和收割作物。
使用Neo4j,一种图形数据库技术,可以成为从收集的数据中释放有价值监测的关键。它还使智能农业系统能够充分发挥其潜力。
在这篇博客文章中,我们探讨了如何使用Neo4j来构建一个强大的智能农业系统。我们首先讨论什么是Neo4j,以及为什么它是存储和查询复杂、互连数据的理想选择。然后,我们深入研究Neo4j在智能农业中的具体用例,例如识别作物模式、预测作物产量和优化资源利用。最后,我们将逐步介绍如何构建基于Neo4j的智能农业系统,帮助农民做出更好的决策,提高作物产量。
架构
硬件组件
软件组件
Jetson Nano开发套件SD卡固件:
https://developer.nvidia.com/embedded/l4t/r32_release_v7.1/jp_4.6.1_b110_sd_card/jeston_nano/jetson-nano-jp461-sd-card-image.zip
etcher:https://www.balena.io/etcher/
desktop Docker:
https://www.docker.com/products/docker-desktop/
Neo4j Aura数据库实例:
https://neo4j.com/cloud/platform/aura-graph-database
步骤1:为操作系统安装准备Jetson Nano SD卡映像
要将Jetson Nano SD卡映像闪存到SD卡,您可以按照以下步骤操作:
. 从NVIDIA(https://developer.nvidia.com/embedded/downloads)下载Jetson Nano SD卡映像。
. 将SD卡插入计算机的SD卡读卡器。
. 从下载并安装Etcher软件https://www.balena.io/etcher/.
. 打开Etcher并选择要闪存的Jetson Nano SD卡映像文件。
. 解压缩从下载的SD卡映像https://developer.nvidia.com/embedded/downloads
. 选择SD卡驱动器作为下载目标。
. 点击“烧录!”开始烧录进程。
步骤2:连接传感器
Seeed Studio的BME680传感器是一款紧凑型环境传感器,专为移动应用和可穿戴设备设计。它可以高精度测量温度、压力、湿度和室内空气质量,并且设计为功耗非常低。该传感器与流行的微控制器平台兼容,如树莓派和Arduino,使其易于集成到项目中。
Grove Base Hat是一款40针Grove附加板,旨在与树莓派板兼容。然而,通过将其连接到适当的GPIO引脚,它也可以与NVIDIA Jetson Nano一起使用。它有15个Grove连接器,包括6个数字、4个模拟、3个I2C、1个UART和1个PWM。
该硬件模块提供了一种将Grove传感器和执行器连接到Jetson Nano的简单方便的方法。它支持各种Grove模块,如温度、湿度、光线和声音传感器,以及电机和显示器等执行器。
要将Grove Base Hat连接到Jetson Nano,您必须连接以下引脚:
. Jetson Nano上的VCC至3.3V引脚
. Jetson Nano上的GND到GND引脚
. SDA至Jetson Nano的引脚3(SDA1)
. SCL至Jetson Nano的引脚5(SCL1)
一旦您将Grove Base Hat连接到Jetson Nano,您就可以开始在Jetson Nano项目中使用Grove传感器和执行器。将传感器连接到Grove后,建议使用i2cdetect命令运行I2C检测,以验证您是否看到设备:在我们的情况下,它显示76。请注意,传感器使用I2C或SPI通信协议与微控制器进行通信。
$ i2cdetect -r -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- 76 --
步骤3:编写Python代码以获取传感器值
以下是从BME680传感器获取传感器值的Python脚本:
from bme680 import BME680
# Initialize BME680 sensor object
sensor = BME680()
# Check sensor is connected
if not sensor.begin():
print("Failed to initialize BME680 sensor.")
exit()
# Read and print sensor values
while True:
if sensor.get_sensor_data():
temperature = round(sensor.data.temperature, 2)
humidity = round(sensor.data.humidity, 2)
pressure = round(sensor.data.pressure, 2)
gas_resistance = round(sensor.data.gas_resistance, 2)
print("Temperature: {} C".format(temperature))
print("Humidity: {} %".format(humidity))
print("Pressure: {} hPa".format(pressure))
print("Gas Resistance: {} Ohms".format(gas_resistance))
else:
print("Error reading BME680 sensor data.")
time.sleep(1)
from bme680 import bme680语句导入bme680传感器库,该库提供了从传感器读取环境数据的接口。传感器变量初始化BME680传感器对象。if not sensor.begin():语句检查传感器是否正确连接和初始化。如果初始化失败,代码将退出并打印一条错误消息。
代码的主循环重复读取BME680传感器的传感器数据,并将值打印到控制台。time.sleep(1)语句在循环的每次迭代之间暂停代码执行1秒。
此脚本使用BME680库从连接到运行脚本的系统的BME680传感器读取温度、湿度、压力和气体电阻值。结果以循环方式打印到控制台,每次读取之间有1秒的延迟。
假设BME680传感器已连接并正常工作,则输出如下所示:
温度:26.68摄氏度
湿度:41.35%
压力:1008.6 hPa
气体电阻:3110.63欧姆
温度、湿度和压力值分别以摄氏度、百分比和百帕斯卡为单位,使用round()函数四舍五入到小数点后两位。气体电阻值以欧姆为单位,也可以四舍五入到小数点后两位。
如果读取BME680传感器数据时出错,脚本将在控制台上打印消息“读取BME680sensor data时出错”
步骤4:编写Python程序,将BME680传感器值发送到Neo4j图形数据库
现在是时候编写Python代码,使用Neo4j驱动程序和BME680环境传感器建立与Neo4j Graph数据库的连接了。
from neo4j import GraphDatabase
from bme680 import BME680
import time
# Set up the Neo4j driver
uri = "neo4j+s://41275b2a.databases.neo4j.io"
driver = GraphDatabase.driver(uri, auth=("neo4j", "3DXXXXXXXXXXXXXXXaM"))
# Set up the BME680 sensor
sensor = BME680()
# Define a function to create a sensor reading node in Neo4j
def create_sensor_reading(tx, temperature, humidity, pressure, gas):
tx.run("CREATE (:SensorReading {temperature: $temperature, humidity: $humidity, pressure: $pressure, gas: $gas, timestamp: $timestamp})",
temperature=temperature, humidity=humidity, pressure=pressure, gas=gas, timestamp=int(time.time()))
# Generate and insert sensor readings into Neo4j every 5 seconds
while True:
if sensor.get_sensor_data():
temperature = round(sensor.data.temperature, 2)
humidity = round(sensor.data.humidity, 2)
pressure = round(sensor.data.pressure, 2)
gas = round(sensor.data.gas_resistance, 2)
with driver.session() as session:
session.write_transaction(create_sensor_reading, temperature, humidity, pressure, gas)
print(f"Inserted sensor reading - temperature: {temperature}, humidity: {humidity}, pressure: {pressure}, gas: {gas}")
else:
print("Error reading BME680 sensor data.")
time.sleep(5)
此Python代码使用Neo4j驱动程序和BME680环境传感器建立与Neo4j图形数据库的连接。然后,它定义了一个函数,在图形数据库中创建一个传感器读取节点,其中包含当前的温度、湿度、压力、气体阻力和时间戳。代码的主循环每5秒重复生成一次传感器读数,并使用定义的函数将其插入图形数据库。
from neo4j import GraphDatabase语句导入neo4j驱动程序,该驱动程序允许Python代码与neo4j数据库交互。from bme680 import bme680语句导入bme680传感器库,该库提供了从传感器读取环境数据的接口。
uri变量指定Neo4j数据库的位置和访问该数据库的凭据。驱动程序变量使用指定的uri和凭据初始化Neo4j驱动程序。
传感器变量初始化BME680传感器对象。create_sensor_reading函数接收Neo4j事务对象(tx)和当前传感器读数(温度、湿度、压力、气体和时间戳)。然后使用给定的设备在图形数据库中创建一个新节点。
代码的主循环重复读取BME680传感器的传感器数据,并使用Neo4j事务将读数插入图形数据库。time.sleep(5)语句在循环的每次迭代之间暂停代码的执行5秒
步骤5:导入Neo4j Python模块
git clone https://github.com/collabnix/bme680-jetson-neo4j
cd bme680-jetson-neo4j
导入Neo4j Python模块
您可以使用pip为Python安装Neo4j驱动程序:
pip install neo4j
python3 sensorloader.py
结果
Inserted sensor reading - temperature: 26.68, humidity: 41.35, pressure: 1008.6, gas: 3110.63
Inserted sensor reading - temperature: 12.42, humidity: 49.71, pressure: 1149.34, gas: 4815.11
Inserted sensor reading - temperature: 27.73, humidity: 77.2, pressure: 1081.24, gas: 4737.95
Inserted sensor reading - temperature: 19.22, humidity: 50.17, pressure: 958.73, gas: 516.57
步骤6:设置Neo4j Docker扩展
Neo4j Aura是Neo4j提供的一种完全托管的云数据库服务。它是一个数据库即服务(DBaaS)产品,允许您在云中创建、部署和管理自己的图形数据库。这就不必担心底层基础设施和维护任务。假设您已经安装并配置了Neo4j Aura DB连接UI,下一步将安装Neo4j Docker Desktop。
打开Docker Dashboard>Extensions>安装Neo4j Docker Extension。
要允许Neo4 Docker Extension连接到远程Neo4j Aura DB,您必须提供连接URL、身份验证类型和凭据。一旦成功,您应该能够开始运行密码查询。
步骤7:测量气体浓度
BME680传感器测量几种不同气体的浓度,包括挥发性有机化合物(VOC)、一氧化碳(CO)和二氧化氮(NO2)。这是除了测量温度、湿度和压力之外的。要从传感器中获取这些气体的浓度,可以使用BME680类的get_sensor_data()方法。这将返回一个包含最新传感器读数的BME680Data对象。然后,您可以使用以下属性访问BME680Data对象中的气体浓度值:
. gas_reresistance:气体传感器的电阻,单位为欧姆,与空气中挥发性有机物的浓度有关。
. gas(List):不同气体的浓度,单位为百万分之一(ppm),包括CO和NO2。
from neo4j import GraphDatabase
import time
import bme680
# Set up the Neo4j driver
uri = "neo4j+s://your-neo4j-instance-url-here"
driver = GraphDatabase.driver(uri, auth=("neo4j", "your-neo4j-instance-password-here"))
# Set up the BME680 sensor
sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
# Define a function to create a CO2 reading node in Neo4j
def create_co2_reading(tx, co2_concentration):
tx.run("CREATE (:CO2Reading {concentration: $concentration, timestamp: $timestamp})",
concentration=co2_concentration, timestamp=int(time.time()))
# Wait for the sensor to warm up
print("Warming up sensor...")
sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
time.sleep(300)
# Start retrieving CO2 concentration data and inserting it into Neo4j
print("Starting CO2 data collection...")
while True:
if sensor.get_sensor_data():
co2_concentration = round(sensor.data.gas_resistance / 10, 2)
with driver.session() as session:
session.write_transaction(create_co2_reading, co2_concentration)
print(f"Inserted CO2 reading - concentration: {co2_concentration}")
else:
print("Error retrieving sensor data")
time.sleep(60)
执行脚本
python3 sensorloader_co2.py
结果
Warming up sensor... (takes 4-5 minutes)
Starting CO2 data collection...
Inserted CO2 reading - concentration: 1294686.
与典型的室内二氧化碳水平相比,1294686.06ppm(百万分之一)的二氧化碳浓度相当高。在通风良好的室内环境中,二氧化碳浓度应在400-1000ppm左右。二氧化碳含量超过1000ppm会导致嗜睡、头痛和其他症状,而超过5000ppm会对健康造成严重影响,在极端情况下甚至死亡。
然而,对二氧化碳水平的解释取决于测量的背景和环境。例如,在一些工业环境中,如啤酒厂或温室,出于特定目的,CO2水平可能会故意升高。同样重要的是要考虑可能影响室内空气质量的其他因素,如湿度、通风和其他污染物的存在。
步骤8:对BME680传感器建模
以下是如何在Neo4j中对BME680传感器及其读数进行建模的示例。首先,您将创建一个“传感器”节点来表示您的BME680传感器。此节点可能具有“名称”和“制造商”等属性,以及您要存储的有关传感器的任何其他信息。
CREATE (:Sensor {name: 'BME680', manufacturer: 'Bosch'})
接下来,您将创建一个“时间戳”节点来表示读取的特定时间点。此节点可能有一个“时间戳”属性,用于存储读取的日期和时间。
CREATE (:Timestamp {timestamp: datetime()})
然后,您将在Sensor节点和Timestamp节点之间创建一个“READS”关系。“温度”、“压力”、“湿度”等财产表示当时从传感器读取的值。例如,要创建温度为25摄氏度、压力为1000hPa、湿度为50%的读数,可以使用以下查询:
MATCH (s:Sensor {name: 'BME680'}), (t:Timestamp)
CREATE (s)-[:READS {temperature: 37.0, pressure: 1168.83, humidity: 37.23}]->(t)
此查询在传感器节点和时间戳节点之间创建“READS”关系。温度、压力和湿度的财产分别设置为25、1000和50。然后,您可以使用Cypher查询来:
. 从数据库中检索读数;
. 根据时间范围或传感器类型等标准对其进行筛选,以及
. 使用Neo4j Bloom等工具或其他可视化工具以各种方式可视化数据
步骤9:将其绘制到Grafana仪表板
Neo4j为Grafana提供了一个数据源插件,允许您直接在Grafana仪表板内可视化和分析存储在Neo4j图形数据库中的数据。要使用Neo4j数据源插件,您必须从Grafana插件库安装该插件。安装插件后,您可以通过指定数据库URL、用户名和密码将其配置为连接到Neo4j数据库。一旦配置了数据源,就可以使用Neo4j查询语言Cypher创建可视化。Cypher是一种功能强大的图形查询语言,允许您遍历和操作存储在Neo4j数据库中的图形数据。
您可以在几秒钟内在Docker Desktop上打开Grafana来使用Grafana Docker扩展。访问Grafana仪表板并使用默认的admin/admin作为用户名/密码登录。
登录后,单击“设置”>“数据源”>“插件”,然后搜索Neo4j数据源
安装Grafana数据源。
提供Neo4j Aura实例的连接URL、用户名和密码,然后连接。
Grafana使用Neo4j数据源插件的好处包括:
. 能够在用户友好的界面中可视化和分析复杂的图形数据。
. 与Grafana中提供的各种其他数据源和插件集成。
. 具有动态、交互式可视化功能的可定制仪表板。
. 支持基于Neo4j数据库事件的实时数据流和警报。
一旦连接到远程Neo4j Aura数据库,就应该能够对传感器数据运行密码查询。
MATCH (sr:SensorReading)
WHERE sr.timestamp >= $timeFrom AND sr.timestamp <= $timeTo
RETURN sr.timestamp as time, sr.temperature as temp, sr.humidity as hum, sr.pressure as press, sr.gas as gas_res
ORDER BY sr.timestamp ASC
查询使用MATCH子句指定节点标签,并指定一个变量sr来表示具有该标签的节点。WHERE子句使用变量$timeFrom和$timeTo指定一系列时间戳。RETURN子句指定每个传感器读数要返回的财产。ORDER BY子句按时间戳升序对结果进行排序。
最后,您应该能够创建所有四个仪表板——温度、压力、湿度和气体阻力,如下所示:
结论
图形数据库能够高效查询复杂且相互关联的数据,使其成为数据往往相互关联的环境监测应用程序的理想选择。这与农业尤其相关,因为农业需要这些数据来提高作物产量。
在这个博客中,我们讨论了如何使用Python将BME680环境传感器与Neo4j图形数据库接口。BME680传感器能够测量温度、湿度、压力和气体阻力。这使其成为环境监测应用的理想传感器,可以改善农业行业的决策。
Ajeet S Raina是Docker的开发者倡导者,也是Arm开发者计划的大使。