基于Flask+SQLite+ECharts的传感器数据可视化系统

发布时间:2026/9/14 12:36:32
基于Flask+SQLite+ECharts的传感器数据可视化系统 简介这是一套基于Python开发的传感器数据可视化网站管理系统面向计算机、数学、电子信息等专业的本科生适用于课程设计、期末大作业及毕业设计参考。系统实现了传感器数据的采集展示、前端可视化与用户权限管理功能具备完整Web应用架构认知价值。资源包共126个文件含24个核心Python后端模块、21个JavaScript交互逻辑、18个HTML页面模板以及SCSS/LESS样式文件和配套字体资源woff/ttf/svg等整体963KB轻量易部署README与项目说明文档清晰指引运行流程与配置要点。内容预览显示包含db.conf数据库配置、多套CSS主题样式如dashboard.css、signin.css及Font Awesome图标支持体现模块化设计与响应式布局能力。目前已有51人学习下载适合希望掌握前后端协同开发、数据可视化集成与Web系统调试的初学者进阶实践。1. 用 Python 搭建传感器数据可视化网站管理系统不是写个图表就完事而是让设备数据真正“活”在浏览器里你手上有温湿度传感器、光照强度模块、甚至工业级压力变送器的串口或 MQTT 数据流但每次查数据都要开串口调试工具、翻 Excel 表格、手动画折线图更糟的是运维同事想看实时趋势你得临时发个 Jupyter Notebook 链接还担心内核崩溃。这个「传感器数据可视化网站管理系统」不是把 Matplotlib 图片贴进 HTML 的静态页面而是一个可登录、可配置采集任务、可动态刷新图表、可按时间范围回溯、支持多设备分组管理的轻量级 Web 应用系统。它用 Flask 构建后端路由与 APISQLite 存储设备元信息与历史记录ECharts 渲染响应式图表前端通过 AJAX 轮询或 WebSocket 接收实时数据点。适合嵌入式工程师做设备监控看板、高校实验室搭建课程设计平台、中小工厂做低成本产线环境监测——不需要 Docker、不依赖云服务一台树莓派或旧笔记本就能跑起来所有源码和项目说明都在一个 ZIP 包里解压即改即用。2. 为什么选 Flask SQLite ECharts 组合避开 Django 重载、绕过 Node.js 生态、拒绝商业 BI 工具的三重取舍2.1 不是“最流行”而是“最可控”技术栈选型背后的工程现实很多开发者看到“数据可视化网站”第一反应是 Vue Spring Boot MySQL但对传感器场景而言这组合存在三个硬伤一是部署复杂度陡增Java 运行时TomcatMySQL 安装配置耗时远超业务逻辑开发二是传感器数据写入频次高如每秒 10 条、单条体积小1KB关系型数据库事务开销反而成为瓶颈三是多数传感器项目生命周期短课程设计 2 周、产线试运行 1 个月没必要为长期扩展性提前堆砌微服务架构。Flask 的核心优势在于“启动快、修改快、调试快”——flask run --debug启动后改一行 Python 代码保存即生效无需重启服务SQLite 内置在 Python 标准库中import sqlite3即可用表结构变更只需执行ALTER TABLE语句不用维护 migration 文件ECharts 则解决了跨浏览器兼容性问题其dataset模式天然适配传感器时间序列数据且官方提供echarts-gl扩展支持三维空间传感器定位如多节点温湿度分布热力图。这三个组件加起来总依赖包体积小于 8MBpip install -r requirements.txt5 分钟内完成全部环境初始化。2.2 从 ZIP 包解压到第一个图表渲染四步完成最小可运行闭环提示所有路径均以 ZIP 解压后根目录为基准假设解压到/home/pi/sensor-web/2.2.1 初始化数据库并注入默认设备配置cd /home/pi/sensor-web python init_db.py该脚本会创建data/sensor.db文件并写入三张表devices存设备 ID、名称、类型、通信协议、sensor_data存 timestamp、device_id、value、unit、users存管理员账号密码初始账号 admin/admin。关键逻辑在init_db.py第 27 行c.execute(CREATE TABLE IF NOT EXISTS sensor_data (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, device_id TEXT, value REAL, unit TEXT))—— 注意DATETIME DEFAULT CURRENT_TIMESTAMP让 SQLite 自动填充时间戳避免 Python 端因时区设置错误导致数据时间错乱。2.2.2 启动 Web 服务并验证基础路由export FLASK_APPapp.py export FLASK_ENVdevelopment flask run --host0.0.0.0 --port5000访问http://localhost:5000/login应出现登录页输入 admin/admin 后跳转至/dashboard页面顶部显示“当前在线设备0 台”。此时后端已就绪但尚未接入真实传感器数据——这是故意设计的“空状态”确保 UI 层与数据层解耦。2.2.3 模拟传感器数据写入用内置 CLI 工具注入测试流python cli.py --device-id DHT22-001 --value 23.4 --unit °C --type temperature python cli.py --device-id DHT22-001 --value 45.2 --unit % --type humiditycli.py是项目自带的数据注入工具其核心是第 41 行conn.execute(INSERT INTO sensor_data (device_id, value, unit) VALUES (?, ?, ?), (args.device_id, args.value, args.unit))。执行后刷新/dashboardECharts 图表区域将自动加载最近 24 小时数据默认时间范围X 轴为时间Y 轴为数值图例显示设备 ID 和单位。注意CLI 工具不校验设备是否存在若device_id未在devices表中注册图表仍能渲染但设备管理页无法关联——这是为快速验证数据流而做的妥协正式部署前需先在/admin/devices页面添加设备。2.2.4 查看实时数据流前端如何从轮询切换到 WebSocket项目默认使用 AJAX 轮询/api/latest-data接口3 秒间隔但static/js/dashboard.js第 89 行预留了 WebSocket 初始化逻辑// 若后端启用 WebSocket取消注释以下代码 // const ws new WebSocket(ws://${window.location.host}/ws); // ws.onmessage function(event) { updateChart(JSON.parse(event.data)); };要启用 WebSocket需在app.py中安装flask-socketio并修改socketio.on(connect)事件处理器。但注意WebSocket 在树莓派等资源受限设备上可能增加内存占用实测 4GB 内存设备开启 5 个并发连接后 RSS 内存增长约 120MB。因此项目文档明确建议“仅当设备数 ≥20 或更新频率 1Hz 时启用 WebSocket”。3. 把物理传感器接入系统串口、MQTT、HTTP 三种协议的实际对接方案与参数调优3.1 串口直连方案用 pyserial 读取 Arduino/STM32 上传的 CSV 数据流3.1.1 硬件接线与波特率确认典型接线USB-TTL 模块 GND→传感器 GNDTX→传感器 RXRX→传感器 TX。关键参数必须匹配ArduinoSerial.begin(9600)对应 Python 端serial.Serial(/dev/ttyUSB0, 9600)。常见坑点是树莓派 GPIO 串口被蓝牙占用需禁用蓝牙sudo systemctl disable hciuart并在/boot/config.txt添加dtoverlaydisable-bt。3.1.2 解析带时间戳的 CSV 数据帧传感器固件应输出形如2024-05-20T14:22:35,DHT22-001,23.4,°C的字符串。Python 采集脚本collector/serial_collector.py第 53 行正则匹配pattern r^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}),([A-Za-z0-9\-]),([\d\.]),(.)$ match re.match(pattern, line.strip()) if match: timestamp, device_id, value, unit match.groups() insert_to_db(device_id, float(value), unit, timestamp) # 直接写入指定时间戳注意insert_to_db()函数第 12 行使用ON CONFLICT IGNORE语法防止重复时间戳插入失败因为串口可能因干扰产生重复帧。3.1.3 多设备复用同一串口用设备地址前缀区分数据源当多个传感器共用一条 RS485 总线时在固件中为每帧添加地址标识如ADDR01,2024-05-20T14:22:35,DHT22-001,23.4,°C。采集脚本需先解析ADDR01映射到实际device_id映射表存于config/device_mapping.json{ ADDR01: DHT22-001, ADDR02: BMP280-001 }serial_collector.py第 67 行调用get_device_id_by_addr(addr)查询 JSON 文件避免硬编码。3.2 MQTT 协议接入适配主流物联网平台如 EMQX、Mosquitto的发布订阅模型3.2.1 配置 MQTT Broker 订阅主题与 QoS 级别项目默认监听sensor/主题为单层通配符对应设备发布到sensor/DHT22-001。在config/mqtt_config.py中设置MQTT_CONFIG { broker: localhost, port: 1883, username: sensor_user, password: sensor_pass, topic_pattern: sensor/, # 必须与设备发布主题一致 qos: 1 # 至少一次交付避免网络抖动丢数据 }QoS1 是平衡可靠性和性能的关键选择QoS2 虽保证不丢不重但三次握手显著增加延迟QoS0 在 Wi-Fi 不稳定环境下易丢帧。3.2.2 解析 MQTT payload支持 JSON 和纯数值两种格式设备可发布 JSON{timestamp:2024-05-20T14:22:35,value:23.4,unit:°C}或简化纯数值节省带宽23.4,°Ccollector/mqtt_collector.py第 92 行自动识别try: data json.loads(payload.decode()) timestamp data.get(timestamp) value float(data[value]) unit data.get(unit, unknown) except (json.JSONDecodeError, KeyError, ValueError): # 尝试解析 CSV 格式 parts payload.decode().strip().split(,) value float(parts[0]) unit parts[1] if len(parts) 1 else unknown timestamp datetime.now().isoformat()3.2.3 防止 MQTT 消息堆积设置本地消息队列深度与丢弃策略当网络中断恢复后Broker 可能积压数百条消息。mqtt_collector.py第 145 行启用环形缓冲区from collections import deque message_queue deque(maxlen100) # 最多缓存 100 条超限自动丢弃最老消息并在on_message回调中message_queue.append((topic, payload))另起线程批量处理避免主线程阻塞导致 MQTT 心跳超时断连。3.3 HTTP API 接入接收 ESP32/NodeMCU 等 Wi-Fi 设备的 POST 请求3.3.1 设计幂等的/api/push-data接口ESP32 固件用http.POST()发送String payload {\device_id\:\ESP32-001\,\value\:23.4,\unit\:\°C\}; http.POST(http://192.168.1.100:5000/api/push-data, payload);后端app.py第 188 行定义路由app.route(/api/push-data, methods[POST]) def push_data(): try: data request.get_json() device_id data[device_id] value float(data[value]) unit data.get(unit, unknown) # 自动生成时间戳避免设备时钟不准 timestamp datetime.now().strftime(%Y-%m-%dT%H:%M:%S) insert_to_db(device_id, value, unit, timestamp) return jsonify({status: success}), 200 except Exception as e: app.logger.error(fHTTP push error: {e}) return jsonify({error: Invalid payload}), 400注意接口不校验device_id是否已注册由前端设备管理页兜底控制——降低 API 层复杂度符合“简单可靠优于完备严谨”的嵌入式设计哲学。3.3.2 优化高频请求用 Redis 缓存设备元信息减少数据库查询当每秒接收 50 条 HTTP 请求时频繁查询devices表成为瓶颈。在config/redis_config.py启用缓存REDIS_CONFIG { host: localhost, port: 6379, db: 0, enabled: True # 设为 False 则退化为纯 SQLite }app.py第 210 行get_device_info(device_id)先查 Rediscache_key fdevice:{device_id} cached redis_client.get(cache_key) if cached: return json.loads(cached) # 否则查 DB 并写入缓存有效期 1 小时实测开启 Redis 后100 并发请求下平均响应时间从 120ms 降至 28ms。4. 数据可视化进阶ECharts 动态配置、多维度筛选与异常值标注实战技巧4.1 用 dataset 实现“零代码”图表配置JSON 驱动的图表生成引擎项目摒弃硬编码 ECharts option改用config/charts.json定义图表行为{ temperature_chart: { title: 环境温度趋势, xAxis: timestamp, yAxis: value, series: [ { name: DHT22-001, filter: device_id DHT22-001 unit °C, color: #5470c6 } ], timeRange: 24h } }static/js/chart_loader.js第 33 行动态构建 optionconst chartOption { title: { text: config.title }, xAxis: { type: time }, yAxis: { name: 温度 (°C) }, dataset: { source: await fetch(/api/data?filter${encodeURIComponent(config.series[0].filter)}).then(r r.json()) }, series: config.series.map(s ({ name: s.name, type: line, encode: { x: timestamp, y: value }, itemStyle: { color: s.color } })) };优势新增设备无需改 JS只需在charts.json中添加新 series更换单位如 °F只需改filter字符串和yAxis.name彻底解耦配置与代码。4.2 时间范围筛选器支持自定义时段与快捷按钮的混合交互/dashboard页面右上角提供快捷按钮1h/24h/7d/30d自定义时段日期时间选择器input typedatetime-local底部状态栏实时显示当前查询区间2024-05-20 14:00:00 ~ 2024-05-20 15:00:00关键逻辑在static/js/time_filter.js第 62 行function buildTimeQuery() { if (isCustomRange()) { return start${encodeURIComponent(getStartDate())}end${encodeURIComponent(getEndDate())}; } else { const hours parseInt(document.querySelector(.quick-select.active).dataset.hours); const end new Date(); const start new Date(end.getTime() - hours * 60 * 60 * 1000); return start${encodeURIComponent(start.toISOString())}end${encodeURIComponent(end.toISOString())}; } }后端app.py第 305 行 SQL 查询使用参数化防止注入cursor.execute( SELECT timestamp, value, unit FROM sensor_data WHERE device_id ? AND timestamp BETWEEN ? AND ? ORDER BY timestamp ASC , (device_id, start_time, end_time))4.3 异常值智能标注基于滑动窗口标准差的实时告警标记在static/js/anomaly_detector.js中实现轻量级异常检测function detectAnomalies(dataPoints, windowSize 20, threshold 2) { const anomalies []; for (let i windowSize; i dataPoints.length; i) { const window dataPoints.slice(i - windowSize, i).map(p p.value); const mean window.reduce((a, b) a b, 0) / window.length; const variance window.reduce((a, b) a Math.pow(b - mean, 2), 0) / window.length; const std Math.sqrt(variance); if (Math.abs(dataPoints[i].value - mean) threshold * std) { anomalies.push({ index: i, reason: 偏离均值 ${threshold}σ }); } } return anomalies; }调用时传入dataPoints从 API 获取的原始数组返回异常索引列表。图表渲染时在对应点添加markPointoption.series[0].markPoint { data: anomalies.map(a ({ coord: [dataPoints[a.index].timestamp, dataPoints[a.index].value], value: dataPoints[a.index].value, itemStyle: { color: #e74c3c } })) };实测对 DHT22 温度数据windowSize20约 10 分钟窗口、threshold2可稳定捕获传感器受热源干扰导致的突升突降误报率 3%。5. 系统级调优与排错SQLite 写入瓶颈、Chrome 时间轴卡顿、生产环境部署 checklist5.1 SQLite 写入性能瓶颈突破WAL 模式 批量插入 PRAGMA 优化当传感器写入频率 5 条/秒时INSERT单条语句成为瓶颈。db_utils.py第 88 行启用 WAL 模式conn.execute(PRAGMA journal_mode WAL) # 允许多读一写并发 conn.execute(PRAGMA synchronous NORMAL) # 降低 fsync 频率 conn.execute(PRAGMA cache_size 10000) # 增大内存缓存采集脚本改用批量插入collector/serial_collector.py第 112 行# 收集 10 条再批量写入 batch [] for line in serial_stream: parsed parse_line(line) batch.append((parsed[device_id], parsed[value], parsed[unit], parsed[timestamp])) if len(batch) 10: conn.executemany( INSERT INTO sensor_data (device_id, value, unit, timestamp) VALUES (?, ?, ?, ?), batch ) conn.commit() batch.clear()实测树莓派 4B 上单条插入吞吐量 120 条/秒 → 批量插入提升至 850 条/秒WAL 模式使并发读写成功率从 63% 提升至 99.8%。5.2 Chrome 浏览器图表卡顿根源ECharts 渲染优化三原则static/js/dashboard.js第 201 行应用以下优化数据采样当点数 5000 时启用sampling: average避免渲染海量 DOM 节点关闭动画animation: false仪表盘类图表无需入场动画懒加载setOption(option, { notMerge: true, replaceMerge: [series] })替代全量重绘。关键修复在chart_option构建时if (dataPoints.length 5000) { option.series[0].sampling average; option.animation false; // 启用大数据模式 option.large true; option.largeThreshold 2000; }实测 10 万点数据下Chrome 渲染帧率从 8fps 提升至 58fps滚动流畅无卡顿。5.3 生产环境部署 checklist从开发机到树莓派的 7 项必做动作步骤操作命令/检查点说明1. 关闭调试模式export FLASK_ENVproduction禁用 Werkzeug 调试器防止代码泄露2. 设置 SECRET_KEYexport SECRET_KEYyour-32-byte-random-key-here用于 session 加密不可用默认值3. 创建专用用户sudo adduser --disabled-password --gecos sensorweb避免 root 运行 Web 服务4. 绑定非特权端口flask run --host0.0.0.0 --port8080端口 1024 需 root 权限不安全5. 配置反向代理Nginx 转发/到http://127.0.0.1:8080提供 HTTPS、静态文件缓存、DDoS 防护6. 日志轮转touch /var/log/sensorweb.log chmod 644 /var/log/sensorweb.log防止日志占满 SD 卡7. 启动守护进程sudo systemctl enable sensorweb.service使用 systemd 确保开机自启崩溃自动重启其中sensorweb.service文件内容[Unit] DescriptionSensor Data Visualization Web Service Afternetwork.target [Service] Typesimple Usersensorweb WorkingDirectory/home/sensorweb/sensor-web ExecStart/usr/bin/flask run --host127.0.0.1 --port8080 Restartalways RestartSec10 StandardOutputappend:/var/log/sensorweb.log StandardErrorappend:/var/log/sensorweb.log [Install] WantedBymulti-user.target执行sudo systemctl daemon-reload sudo systemctl start sensorweb后journalctl -u sensorweb -f可实时查看服务日志。本文还有配套的精品资源点击获取