2. 传感器数据模拟:用Python生成模拟数据

好,咱们进入第二个环节。上一章我们把开发环境搭好了,现在该让数据“跑”起来了。

你可能会问:“我手头又没有真实的传感器,怎么测试?”

嗯,这个问题我当年也遇到过。记得我第一次做温室项目时,传感器还没到货,但代码得先写。怎么办?模拟数据就是干这个用的。

说白了,就是用Python生成一些看起来像真实传感器读数的数字。温度在20~30℃之间波动,湿度在60%~80%之间变化,光照在0~1000 lux之间跳动。这样,你的显示界面、存储逻辑、报警机制都能提前调试好。

2.1 模拟数据的核心思路

我个人习惯把模拟数据分成两类:

  • 随机数据:每次取值完全随机,适合测试界面刷新
  • 趋势数据:带有变化规律,比如白天光照强、晚上弱,更贴近真实场景

咱们先做简单的随机版本,后面再升级成带趋势的。

2.2 基础版:随机生成单次数据

先写一个函数,每次调用返回一组模拟数据。代码很简单,但有几个坑我得提醒你。

import random
import time
from datetime import datetime

def generate_sensor_data():
    """
    生成一组模拟的温室环境数据
    返回:字典,包含温度、湿度、光照和时间戳
    """
    temperature = round(random.uniform(20.0, 30.0), 1)
    humidity = round(random.uniform(60.0, 80.0), 1)
    light = round(random.uniform(0.0, 1000.0), 1)
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    data = {
        "temperature": temperature,
        "humidity": humidity,
        "light": light,
        "timestamp": timestamp
    }
    return data

# 测试一下
if __name__ == "__main__":
    sample = generate_sensor_data()
    print(sample)
💡 我的小技巧:round() 保留一位小数就够了。传感器精度没那么高,显示太多位反而显得假。我在项目里见过有人保留三位小数,结果用户问“这数据准不准?”——其实没必要。

2.3 进阶版:连续生成数据流

单次生成没意思,咱们要的是持续输出。就像真实的传感器每隔几秒上报一次数据。

这里我用了 while True 循环,配合 time.sleep() 控制频率。你想想看,如果每秒生成100次数据,你的数据库和前端都扛不住。

def continuous_data_stream(interval=2):
    """
    持续生成模拟数据
    :param interval: 生成间隔(秒),默认2秒一次
    """
    print("开始模拟数据流,按 Ctrl+C 停止...")
    try:
        while True:
            data = generate_sensor_data()
            print(f"[{data['timestamp']}] 温度: {data['temperature']}℃, "
                  f"湿度: {data['humidity']}%, 光照: {data['light']} lux")
            time.sleep(interval)
    except KeyboardInterrupt:
        print("\n数据流已停止。")

# 运行
continuous_data_stream(interval=3)
⚠️ 我曾经踩过的坑:一开始我忘了加 try...except,结果按 Ctrl+C 停止时直接报错退出,数据都没保存。后来我养成了习惯——所有长时间运行的循环都要捕获 KeyboardInterrupt,优雅退出。

2.4 真实感升级:加入趋势和波动

纯随机数据有个问题:太“跳”了。真实环境里,温度不会从20℃瞬间跳到30℃。它应该是缓慢变化的,偶尔有点小波动。

怎么做?我教你一个简单方法:基于上一次的值,加上一个随机偏移量

class SmartSensorSimulator:
    """带趋势的智能传感器模拟器"""
    
    def __init__(self):
        # 初始值设定在合理范围中间
        self.temperature = 25.0
        self.humidity = 70.0
        self.light = 500.0
        
    def update(self):
        # 温度:缓慢变化,偏移量 ±0.5℃
        self.temperature += random.uniform(-0.5, 0.5)
        self.temperature = max(20.0, min(30.0, self.temperature))
        
        # 湿度:偏移量 ±2%
        self.humidity += random.uniform(-2.0, 2.0)
        self.humidity = max(60.0, min(80.0, self.humidity))
        
        # 光照:白天高、晚上低(模拟时间影响)
        hour = datetime.now().hour
        if 6 <= hour <= 18:  # 白天
            base_light = 800
        else:  # 晚上
            base_light = 100
        self.light = base_light + random.uniform(-100, 100)
        self.light = max(0.0, min(1000.0, self.light))
        
        return {
            "temperature": round(self.temperature, 1),
            "humidity": round(self.humidity, 1),
            "light": round(self.light, 1),
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

# 测试
sim = SmartSensorSimulator()
for _ in range(5):
    print(sim.update())
    time.sleep(1)

🔑 关键点解析:

  • 限幅:用 max()min() 把数据限制在合理范围内,防止异常值
  • 时间关联:光照根据当前小时自动切换白天/夜晚模式
  • 平滑变化:每次只变一点点,更接近真实传感器

2.5 数据格式与输出方式

模拟数据生成后,怎么用?我一般提供三种输出方式:

输出方式 适用场景 代码示例
打印到控制台 快速调试 print(data)
返回字典 供其他模块调用 return {"temp": 25.3, ...}
写入CSV文件 后续分析或回放 csv.writer 逐行写入

我个人最常用的是返回字典,因为后面接数据库、接Web界面都方便。CSV文件适合做历史数据回放测试。

2.6 避坑指南:模拟数据的常见问题

我曾经犯过的三个错误:

  1. 数据范围太理想:只生成20~30℃的数据,结果真实传感器在夏天中午能到40℃。后来我加了“极端天气”模式,故意生成一些边界值,测试报警系统是否正常。
  2. 忘记时间戳:早期版本没加时间,结果数据入库后不知道哪条是哪条。现在我的每个数据点都带 timestamp,这是基本功。
  3. 生成频率过快:一开始设了0.1秒一次,结果数据库写入跟不上,CPU跑满。建议开发阶段用2~5秒间隔,够用就行。

2.7 本章小结

好,咱们来捋一捋今天干了什么:

  • 学会了用 random.uniform() 生成模拟温度、湿度、光照数据
  • while True + time.sleep() 实现了持续数据流
  • 用类封装的方式,让数据带有趋势和变化规律
  • 知道了三种输出方式,以及各自的适用场景

下一章,咱们要把这些数据存到数据库里。你想想看,光生成数据不存下来,关了程序就没了,那多可惜。到时候我会教你用 SQLite 做本地存储,轻量又方便。

今天就到这儿。代码都跑通了吗?有问题随时调试,模拟数据这块搞扎实了,后面开发会顺很多。