IoT-For-Beginners 农场实战:用 Jupyter Notebook 可视化温度数据并计算 GDD 生长积温
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
本文基于 IoT-For-Beginners 课程「2-farm / 1-predict-plant-growth」的课后作业文档(丹麦语版)编写,完整覆盖其全部操作步骤与评分标准。核心主题是:在前一课已经用 IoT 温度传感器 + MQTT 服务端把温度数据落盘为temperature.csv的基础上,如何用 Jupyter Notebook 逐单元格运行代码,可视化连续多天的温度曲线,并按天聚合出最高/最低温,最终计算作物的生长积温(GDD,Growing Degree Days)。读完本文,你可以独立完成从数据收集、环境搭建到运行 notebook 产出每日 GDD 的完整链路,并能看懂 notebook 中每一段 pandas/matplotlib 代码的作用。
背景:GDD 数据从哪里来
这篇作业文档的开场即点明前提:
在本课中,你已经使用 IoT 传感器采集了 GDD 数据。要得到可靠的 GDD 数据,需要连续多天的数据积累。为了帮助可视化温度数据并计算 GDD,可以使用 Jupyter Notebooks 这类工具来分析数据。
结合本课 README(2-farm/lessons/1-predict-plant-growth/README.md)的数据链路来看,整条链路是:
- IoT 设备(Wio Terminal / Raspberry Pi / 虚拟设备)读取环境温度,通过 MQTT 把遥测消息发布到
<ID>/telemetry主题; - 服务端 Python 代码订阅该主题,把每条遥测消息追加写入 CSV 文件
temperature.csv; - 本作业:把
temperature.csv与仓库自带的 gdd.ipynb 放在一起,用 Jupyter 逐单元格分析。
GDD(生长积温)的简化公式(见本课 README)为:
- Tmax:当日最高温度(℃)
- Tmin:当日最低温度(℃)
- Tbase:该作物的基础温度(base temperature,℃)
例如草莓 Tbase为 10℃,玉米(maize)多为 10℃ 且需要约 800–2700 GDD 成熟。notebook 的核心价值就是自动完成「从 CSV 找每天最高/最低温 → 套公式算 GDD」这一步,替代人工查表。
前提:先持续收集几天数据
作业文档明确要求:先开始收集几天的数据。要拿到「好」的 GDD 数据,温度采集必须跨多个完整白天。文档给出了两条保证服务端整夜不中断的关键手段:
- 调整电脑/主板的电源管理设置,阻止系统休眠;
- 或者运行一个「保持系统活跃」类的 Python 脚本(原文档引用了外部 keep-system-active 项目,本文不附外部链接,思路一致:一个空转循环 + 阻止睡眠调用的脚本即可)。
这里有一个容易踩的坑:temperature.csv由 MQTT 服务端产生,服务端代码必须在 IoT 设备运行的整个时间段内持续运行。如果电脑半夜进入睡眠,MQTT 客户端断线,服务端就会漏掉夜间温度——而夜间最低温 Tmin恰恰是 GDD 公式里的关键输入,缺了它整天的数据都不可用。这也正是作业文档把「数据采集」单列为评分项的原因。
数据收集完成后,temperature.csv应该长这样(格式由服务端代码决定,见下文源码分析):
date,temperature 2021-04-19T17:21:36-07:00,25 2021-04-19T17:31:36-07:00,24 2021-04-19T17:41:36-07:00,25操作步骤:完整继承作业文档的 7 步流程
以下 7 步完整对应原文档(丹麦语版translations/da/2-farm/lessons/1-predict-plant-growth/assignment.md)中的编号步骤,仅将其中相对链接转换为仓库根路径。
第 1 步:创建gdd-calculation文件夹
mkdir gdd-calculation第 2 步:下载并复制gdd.ipynb
从仓库中复制 gdd.ipynb 到gdd-calculation文件夹。原文档写的是“Download the gdd.ipynb file and copy it”,在本仓库上下文中即指这份 notebook 源文件。
第 3 步:复制temperature.csv
把 MQTT 服务端生成(temperature-sensor-server目录下)的temperature.csv复制到gdd-calculation文件夹,必须与gdd.ipynb位于同一目录——因为 notebook 里用的是pd.read_csv('temperature.csv')这种相对路径读取。
第 4 步:创建 Python 虚拟环境
在gdd-calculation文件夹内创建一个新的 Python 虚拟环境,例如:
python3 -m venv venv source venv/bin/activate # Linux / macOS venv\Scripts\activate # Windows第 5 步:安装 Jupyter 与数据处理库
原文档给出的 pip 安装命令(完整保留):
pip install --upgrade pip pip install pandas pip install matplotlib pip install jupyter其中pandas负责读取 CSV 与按日期分组聚合,matplotlib负责绘制温度曲线,jupyter提供 notebook 运行环境。
第 6 步:在 Jupyter 中运行 notebook
jupyter notebook gdd.ipynbJupyter 启动后会在浏览器中打开 notebook。按原文档的指引:依次阅读说明单元格、逐格运行代码单元格。这个 notebook 的说明与代码混合在称为cells(单元格)的块中,通常是 Python 代码,你可以逐块阅读、逐块执行,也可以直接编辑代码。
Notebook 源码逐格解析:每一步在做什么
打开 gdd.ipynb 可以看到它由若干 Markdown 说明单元格和代码单元格组成。逐格拆解如下:
单元格 1:设置基础温度base_temperature = 10
base_temperature = 10这是全文档强调的可编辑点——「在这个 notebook 中,你可以编辑用于计算你的植物的 GDD 的基础温度」。10 是通用默认值(对应草莓、玉米等 Tbase= 10℃ 的作物),换成你实际种植的作物对应的基础温度即可,后续所有计算都会引用这个变量。
单元格 2:用 pandas 读取 CSV
import pandas as pd import matplotlib.pyplot as plt # Read the temperature CSV file df = pd.read_csv('temperature.csv')注意这里读的是当前工作目录下的temperature.csv,与第 3 步「文件必须和 notebook 同目录」严格对应。
单元格 3:绘制温度曲线
plt.figure(figsize=(20, 10)) plt.plot(df['date'], df['temperature']) plt.xticks(rotation='vertical');用 20×10 英寸的大画布把date列对temperature列画成折线图,并竖排旋转横轴刻度,方便查看连续多天的原始采样点。这一步的作用首先是数据质检:如果某天夜里没有数据点(服务端休眠导致),图上会直接出现缺口,提醒你重新采集。
单元格 4:按日期聚合出每天的最高/最低温
# Convert datetimes to pure dates so we can group by the date df['date'] = pd.to_datetime(df['date']).dt.date # Group the data by date so it can be analyzed by date data_by_date = df.groupby('date') # Get the minimum and maximum temperatures for each date min_by_date = data_by_date.min() max_by_date = data_by_date.max() # Join the min and max temperatures into one dataframe and flatten it min_max_by_date = min_by_date.join(max_by_date, on='date', lsuffix='_min', rsuffix='_max') min_max_by_date = min_max_by_date.reset_index()关键点:服务端写入的date列是带时分秒的 ISO 8601 时间戳(如2021-04-19T17:21:36-07:00),而 GDD 是「按天」计算的,所以先用pd.to_datetime(...).dt.date把时间戳截断为纯日期,再groupby('date'),对每组分别求min()和max(),最后用join把两个结果拼成一张含temperature_min、temperature_max两列的表。这一步自动完成了本课 README 中描述的手算步骤「从 temperature.csv 找出当天的最高与最低温度」。
单元格 5:套用 GDD 公式并输出结果
def calculate_gdd(row): return ((row['temperature_max'] + row['temperature_min']) / 2) - base_temperature # Calculate the GDD for each row min_max_by_date['gdd'] = min_max_by_date.apply (lambda row: calculate_gdd(row), axis=1) # Print the results print(min_max_by_date[['date', 'gdd']].to_string(index=False))calculate_gdd就是公式 GDD = (Tmax+ Tmin)/2 − Tbase的逐行实现,输出为每天一行的「日期 + GDD」表。对照本课 README 中的草莓手算示例(当日最高 25℃、最低 12℃、Tbase10℃):25 + 12 = 37 → 37 / 2 = 18.5 → 18.5 − 10 =8.5 GDD;notebook 会对采集到的每一天自动产出同样的结果。累计这些日 GDD 即可对照作物所需总 GDD(如草莓约 250 GDD 结果)判断成熟进度。
数据源头:temperature.csv是如何被服务端写出来的
作业文档第 3 步提到temperature.csv是「由 MQTT 服务端创建的文件」。仓库中对应的实现是 code-server/temperature-sensor-server/app.py,它解释了 CSV 的确切格式,也解释了为什么 notebook 用相对路径就能直接读取:
mqtt_client = mqtt.Client(client_name) mqtt_client.connect('test.mosquitto.org') ... temperature_file_name = 'temperature.csv' fieldnames = ['date', 'temperature'] if not path.exists(temperature_file_name): with open(temperature_file_name, mode='w') as csv_file: writer = csv.DictWriter(csv_file, fieldnames=fieldnames) writer.writeheader() def handle_telemetry(client, userdata, message): payload = json.loads(message.payload.decode()) ... with open(temperature_file_name, mode='a') as temperature_file: temperature_writer = csv.DictWriter(temperature_file, fieldnames=fieldnames) temperature_writer.writerow({'date' : datetime.now().astimezone().replace(microsecond=0).isoformat(), 'temperature' : payload['temperature']})从源码可以确认三点,直接影响你使用 notebook 的正确性:
- 两列结构固定:
fieldnames = ['date', 'temperature'],首行为表头。notebook 中df['date']、df['temperature']的列名依赖于此。 date列是服务端收到消息的时间戳,格式为带时区、去掉微秒的 ISO 8601(datetime.now().astimezone()...isoformat())。设备本身不保证时钟准确,时间以服务端为准——这正是 notebook 用pd.to_datetime解析该列的原因。- 文件在服务端脚本所在目录生成(
'temperature.csv'是相对当前工作目录的路径),因此第 3 步要求你把这个文件复制出来,而不是在别处找它。
服务端订阅的主题是<ID>/telemetry(client_telemetry_topic = id + '/telemetry'),这里的<ID>需要替换为你自己的设备标识,这与 IoT 设备侧发布遥测所用的 ID 保持一致。
评分标准(Rubric)
作业文档末尾的评分表完整保留如下(已从丹麦语/英语译为中文,分级语义与原文一致):
| 评价维度 | 优秀 | 合格 | 待改进 |
|---|---|---|---|
| 数据采集 | 采集到至少2 个完整日的数据 | 采集到至少1 个完整日的数据 | 仅采集到部分数据 |
| 计算 GDD | 成功运行 notebook 并计算出 GDD | 成功运行 notebook | 无法运行 notebook |
从评分表可以读出作业设计的验收逻辑:数据侧强调「完整日」——因为 GDD 按天聚合,缺半天的数据(尤其夜间)会让当天 Tmin失真;计算侧则要求 notebook 不仅能跑通,还要真正产出 GDD 数值,即单元格 5 的print(min_max_by_date[['date', 'gdd']]...)输出非空。
小结与排错提示
- 文件三件套必须齐全且同目录:
gdd-calculation/下的gdd.ipynb、temperature.csv,再加上虚拟环境与四个 pip 包; read_csv报文件不存在,优先检查temperature.csv是否与 notebook 同级(服务端写出的 CSV 默认在temperature-sensor-server目录);- 图上出现整段空白,通常是电脑休眠导致服务端断线漏采夜间数据,回到「前提」一节检查电源设置或保活脚本;
- 换了作物只需改第一个代码单元格的
base_temperature,其余单元格无需改动; - 想进一步扩展(如累计 GDD、绘制 GDD 随时间的累积曲线)可以直接在该 notebook 末尾追加单元格,原仓库 notebook 末尾本身留有一个空代码单元格供练习。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考