基于树莓派的智能称重系统:从传感器到Web全栈物联网实践 1. 项目概述与核心价值作为一名长期混迹于硬件开发和物联网领域的工程师我经手过不少用树莓派“折腾”出来的项目但这次这个“智能称重系统”有点不一样。它不是为了炫技而是为了解决一个非常具体且有点“冷门”的痛点——热气球飞行集会上的丙烷气瓶管理。想象一下一群热气球飞行员在活动结束后围着一堆气瓶拿着纸笔记录前后重量、计算用量和费用效率低下还容易出错。原作者Bart-Roels作为一名飞行学员亲身经历了这个麻烦于是决定用技术手段把它“摆平”。这个项目的核心就是利用物联网IoT技术将传统的称重动作自动化、数据化、网络化。它不再是一个孤立的秤而是一个集成了身份识别RFID、实时称重HX711称重传感器、本地计算Raspberry Pi、数据持久化MariaDB数据库和远程可视化Apache2 Web前端的完整系统。用户只需刷一下气瓶上的RFID标签系统就能自动记录重量并将数据同步到后台和网页上彻底告别纸笔。对于硬件爱好者、物联网初学者甚至是有类似资产管理需求的小型企业主来说这个项目都是一个绝佳的练手和学习案例。它涵盖了从传感器选型、电路搭建、嵌入式编程、服务器部署到全栈Web开发的完整链路技术栈扎实且非常“接地气”。接下来我就带你深入拆解这个系统的每一个环节分享我在复现和优化过程中积累的实操细节与避坑经验。2. 系统整体架构与设计思路2.1 核心需求与方案选型这个智能称重系统的设计目标非常明确无人化、自动化、可追溯。围绕这三点我们拆解出核心功能模块身份识别模块需要自动识别不同的气瓶避免手动输入编号。RFIDRC522模块是成本低廉、可靠性高的成熟方案每个气瓶贴上一个无源标签即可。重量采集模块需要高精度、稳定的重量测量。工业级的悬臂梁式称重传感器Load Cell配合专用的24位高精度ADC芯片HX711是电子秤领域的标准配置精度和抗干扰能力远超市面上常见的电阻应变式传感器。核心处理与通信模块需要实时处理传感器数据、驱动外围设备如LCD屏、LED灯带、运行数据库和Web服务。树莓派4B以其强大的通用计算能力、丰富的GPIO接口和原生网络支持成为不二之选。它既能当单片机用又能当服务器用。数据存储与服务模块需要安全存储每一次称重记录并提供查询接口。轻量级的MariaDB数据库足以应对此类低频数据写入。而Apache2作为久经考验的Web服务器部署简单稳定性高非常适合承载一个轻量级的后台管理页面。人机交互与状态指示模块现场需要直观显示重量和状态。一个20x4的字符LCD屏可以显示丰富信息WS2812B可编程LED灯带则能通过颜色变化如绿色代表就绪、红色代表错误、蓝色代表识别成功提供更直观的状态反馈。整个系统的数据流是这样的RFID读取标签ID → 树莓派查询数据库获取气瓶信息 → HX711读取当前重量 → 树莓派将“标签ID重量时间戳”写入数据库 → 同时通过Socket.io实时推送到网页前端更新显示 → LCD屏本地显示结果LED灯带变换颜色。设计心得在物联网项目选型时一定要遵循“够用就好兼顾扩展”的原则。比如树莓派4B 2GB版本对于这个项目绰绰有余没必要上4GB或8GB。传感器也选择了性价比最高的型号。但接口上留了余地例如使用WS2812B这种可编程灯带未来想增加更复杂的灯光提示如电量不足闪烁会非常方便。2.2 硬件清单与采购避坑指南原作者的物料清单BOM已经比较详细但我结合自己的采购和调试经验做一些关键补充和提醒树莓派Raspberry Pi 4B 2GB目前树莓派5已上市但4B的生态更成熟功耗和发热控制得更好。务必购买官方或靠谱渠道的电源5V/3A电源不稳是树莓派各种灵异问题的罪魁祸首。SD卡强烈推荐选择A2等级的UHS-I microSD卡例如三星EVO Plus或闪迪Extreme系列。A2等级针对随机读写做了优化能显著提升系统尤其是数据库的响应速度。32GB容量是甜点16GB在安装完系统和服务后剩余空间会非常紧张。称重传感器与HX711这是精度关键。SEN-13329TAL220是一款10kg量程的优质传感器。购买时注意区分“传感器”和“传感器模块”。单独传感器需要自己焊线并搭配HX711模块使用。避坑重点市面上很多廉价的HX711模块供电不稳或基准电压不准会导致读数跳动大。多花几块钱选择口碑好的模块通常带有稳压芯片和滤波电容能省去大量调试时间。RFID-RC522模块这个模块很成熟价格透明。注意它需要SPI接口与树莓派通信在配置系统时需要开启SPI。LCD1602/2004液晶屏I2C接口务必购买带I2C转接板的版本。直接驱动1602需要占用树莓派6-7个GPIO而I2C版本只需要2根线SDA, SCL大大简化了布线。购买时确认转接板上的电位器是可调的用于调节对比度。WS2812B LED灯带注意工作电压是5V。树莓派GPIO输出是3.3V理论上驱动5V的WS2812B存在电平不匹配问题。稳妥的做法是使用一个逻辑电平转换模块如74HCT125将3.3V信号转换为5V或者选择声称“3.3V兼容”的灯带型号。AC/DC电源适配器5V 2.25A这个电源是给整个系统供电的树莓派本身峰值电流可能接近2A再加上传感器、屏、灯带总电流需求可能超过2.5A。因此选择一个5V/3A或4A的优质适配器更为稳妥确保在大电流点亮灯带时系统不重启。3. 硬件电路搭建与传感器集成详解3.1 电路原理图分析与安全规范原项目提供了Fritzing图这对于理解连接关系很有帮助。但我们不能完全照搬必须理解每一根线背后的意义尤其是电压和电流。核心连接梳理与安全警告树莓派供电通过Micro USB或Type-C口由5V/3A适配器独立供电。严禁尝试通过GPIO的5V引脚反向为树莓派供电极易损坏主板。HX711模块连接VCC- 树莓派5V引脚引脚2或4。GND- 树莓派 **GND 引脚如引脚6。DT(数据) - 树莓派 GPIOGPIO5(引脚29)。SCK(时钟) - 树莓派 GPIOGPIO6(引脚31)。称重传感器的四根线通常为红、黑、白、绿接到HX711模块对应的E E- A- A端子。如果读数出现负值交换A和A-的线即可。RFID-RC522模块连接SPIVCC- 树莓派3.3V引脚引脚1。特别注意RC522必须接3.3V接5V会烧毁GND- 树莓派 GND。RST- 树莓派 GPIOGPIO25(引脚22)。SDA- 树莓派 SPICE0(GPIO8 引脚24)。SCK- 树莓派 SPISCLK(GPIO11 引脚23)。MOSI- 树莓派 SPIMOSI(GPIO10 引脚19)。MISO- 树莓派 SPIMISO(GPIO9 引脚21)。LCD1602 (I2C接口) 连接VCC- 树莓派5V。GND- 树莓派 GND。SDA- 树莓派 I2CSDA(GPIO2 引脚3)。SCL- 树莓派 I2CSCL(GPIO3 引脚5)。WS2812B LED灯带连接5V- 外部5V电源正极。强烈建议使用独立电源供电或从树莓派5V引脚引出但需确保电源总功率足够。GND- 外部电源与树莓派GND共地。这是必须的否则信号无法识别。DIN(数据输入) - 树莓派 GPIOGPIO18(PWM0 引脚12)。需要通过一个330Ω的电阻串联以保护GPIO引脚。如前所述最好经过电平转换模块。实操技巧在面包板上搭建原型电路时建议先用万用表测量所有电源引脚树莓派5V 3.3V的对地电压是否正常。连接任何模块前先断电。连接顺序遵循“先地线后电源线最后信号线”的原则。对于树莓派GPIO的编号务必使用GPIO编号BCM模式而不是物理引脚编号在代码中需要保持一致。3.2 称重传感器的标定与软件滤波这是整个系统精度的基础也是最容易出问题的环节。1. 物理安装 称重传感器悬臂梁式的安装必须牢固且只承受垂直方向的力。原项目用两块木板和螺栓固定传感器两端中间悬空放置称重托盘。确保传感器底部平整螺栓不要拧得过紧导致壳体变形。称重托盘的中心应尽量对准传感器的受力点。2. HX711驱动程序与标定 首先安装Python库pip3 install hx711。但更推荐使用git clone一些社区维护更积极的版本它们通常有更好的树莓派兼容性。标定过程是关键需要已知重量的砝码如500g 1kg。# 标定示例代码片段 import HX711 # 初始化 hx HX711(5 6) # DT引脚GPIO5 SCK引脚GPIO6 hx.set_reading_format(MSB MSB) # 1. 读取空载时的原始值tare hx.reset() empty_raw_values [] for _ in range(20): empty_raw_values.append(hx.read_long()) empty_raw sum(empty_raw_values) / len(empty_raw_values) # 2. 放置已知重量砝码如1000克 input(请放置1000g砝码然后按回车...) weight_raw_values [] for _ in range(20): weight_raw_values.append(hx.read_long()) weight_raw sum(weight_raw_values) / len(weight_raw_values) # 3. 计算比例因子scale # 重量差 1000g # 读数差 weight_raw - empty_raw scale (weight_raw - empty_raw) / 1000.0 print(f比例因子(scale)为: {scale}) # 4. 后续测量重量 (当前原始值 - empty_raw) / scale hx.set_offset(empty_raw) hx.set_scale(scale) current_weight hx.get_weight(5) # 读取5次取平均 print(f当前重量: {current_weight:.2f} g)3. 软件滤波降噪 HX711的原始读数会有微小跳动。简单的软件滤波能极大提升显示稳定性。移动平均滤波如上例中的get_weight(5)读取多次取平均。中值滤波读取奇数次如7次排序后取中间值能有效消除偶发性尖峰干扰。一阶低通滤波适用于重量缓慢变化的场景。filtered_value alpha * raw_value (1 - alpha) * last_filtered_value其中alpha为滤波系数0~1值越小越平滑但延迟越大。我的经验是移动平均中值滤波组合使用。先取7个样本用中值滤波剔除异常点再对剩下的数据求平均。在代码中将这个滤波过程封装成一个函数get_stable_weight()供主循环调用。4. 树莓派系统配置与服务部署4.1 无头模式Headless初始化与网络配置对于物联网设备通常不需要连接显示器。无头设置是必备技能。烧录系统与基础配置使用Raspberry Pi Imager工具烧录Raspberry Pi OS Lite无桌面或Full版本。在烧录前工具高级设置齿轮图标中可以直接预配置开启SSH设置密码。配置Wi-Fi国家CN、SSID和密码。设置主机名如smart-scale。设置静态IP如192.168.1.200。这一步比原作者修改cmdline.txt更规范。烧录完成后SD卡根目录会自动生成ssh和wpa_supplicant.conf等文件无需手动创建。首次启动与远程连接将SD卡插入树莓派并上电。等待1-2分钟。在你的电脑上使用SSH客户端如PuTTY 或终端ssh命令连接树莓派的IP地址或主机名ssh pismart-scale.local。用户pi密码是你刚才设置的。安全建议首次登录后立即使用sudo raspi-config修改默认密码并考虑创建新用户禁用pi账户。系统更新与必要服务开启sudo apt update sudo apt upgrade -y sudo raspi-config在Interface Options中启用I2C用于LCD屏幕。SPI用于RFID模块。Serial Port如果未来需要可启用本次不需要。 完成后重启。4.2 数据库MariaDB与Web服务器Apache2搭建安装MariaDB并创建数据库sudo apt install mariadb-server mariadb-client -y sudo mysql_secure_installation安全安装向导中设置root密码并移除匿名用户、禁止远程root登录等。# 登录数据库 sudo mysql -u root -p # 创建项目专用数据库和用户强烈建议不用root CREATE DATABASE smart_scale_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER scale_userlocalhost IDENTIFIED BY YourStrongPassword123!; GRANT ALL PRIVILEGES ON smart_scale_db.* TO scale_userlocalhost; FLUSH PRIVILEGES; EXIT;安全提醒密码不要用简单的123456或password。utf8mb4字符集能更好地支持各种语言。根据原项目SQL文件创建表结构 将原作者提供的SQL文件或在backend代码仓库中找到的database_schema.sql上传到树莓派然后导入mysql -u scale_user -p smart_scale_db /path/to/database_schema.sql安装并配置Apache2与PHP 原项目前端似乎是纯静态HTML/JS但考虑到扩展性安装PHP有益无害。sudo apt install apache2 php libapache2-mod-php -y将克隆的前端代码例如在/home/pi/smart-scale/frontend链接到Apache的web根目录# 备份默认首页 sudo mv /var/www/html/index.html /var/www/html/index.html.bak # 创建软链接或直接复制文件 sudo ln -s /home/pi/smart-scale/frontend /var/www/html/smart-scale修改Apache配置确保能正确解析sudo nano /etc/apache2/sites-available/000-default.conf将DocumentRoot修改为你的前端目录绝对路径例如/var/www/html/smart-scale。 修改目录权限允许Apache访问sudo chown -R www-data:www-data /home/pi/smart-scale/frontend sudo chmod -R 755 /home/pi/smart-scale/frontend重启Apache服务sudo systemctl restart apache2现在在浏览器访问http://树莓派IP/smart-scale应该能看到前端页面了。4.3 后台Python服务与开机自启Python环境与依赖 进入后端代码目录/home/pi/smart-scale/backend。# 创建虚拟环境可选但推荐 python3 -m venv venv source venv/bin/activate # 安装依赖通常有一个requirements.txt文件 pip3 install -r requirements.txt # 如果没有手动安装核心包 pip3 install flask flask-socketio pymysql RPi.GPIO spidev mfrc522 rpi_ws281xrpi_ws281x库需要以sudo权限运行这是原作者提到的。我们可以通过配置系统服务来解决。配置数据库连接 编辑config.py或类似配置文件填入之前创建的数据库信息DB_CONFIG { host: localhost, user: scale_user, password: YourStrongPassword123!, database: smart_scale_db, charset: utf8mb4 }创建Systemd服务实现开机自启 这是比原作者rc.local更现代和可靠的方式。sudo nano /etc/systemd/system/smart-scale.service写入以下内容根据你的路径调整[Unit] DescriptionSmart Scale Backend Service Afternetwork.target mariadb.service [Service] Typesimple Userpi WorkingDirectory/home/pi/smart-scale/backend # 注意如果使用虚拟环境需要指定虚拟环境中的python ExecStart/home/pi/smart-scale/backend/venv/bin/python3 app.py # 如果不用虚拟环境直接用系统python # ExecStart/usr/bin/python3 /home/pi/smart-scale/backend/app.py Restarton-failure RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target保存后启用并启动服务sudo systemctl daemon-reload sudo systemctl enable smart-scale.service sudo systemctl start smart-scale.service # 检查状态和日志 sudo systemctl status smart-scale.service sudo journalctl -u smart-scale.service -f这样后端服务就会在树莓派启动时自动运行并且崩溃后会自动重启。5. 软件逻辑与前后端交互剖析5.1 后端Python Flask Socket.io核心逻辑后端是整个系统的大脑负责协调所有硬件、处理业务逻辑、提供API接口和实时数据推送。多线程与硬件监听 主程序需要同时做几件事监听RFID读卡、持续读取重量、处理HTTP请求、通过WebSocket推送数据。这天然适合多线程。import threading from flask import Flask, jsonify from flask_socketio import SocketIO app Flask(__name__) socketio SocketIO(app, async_modethreading) # 全局变量用于线程间通信 current_weight 0.0 current_tag_id None def weight_monitoring_thread(): 独立的重量监测线程 global current_weight while True: stable_weight get_stable_weight() # 调用之前封装的滤波函数 if abs(stable_weight - current_weight) 0.5: # 重量变化超过0.5克才更新 current_weight stable_weight # 通过Socket.io实时推送 socketio.emit(weight_update, {weight: current_weight}) time.sleep(0.1) # 100ms读取一次 def rfid_monitoring_thread(): 独立的RFID监听线程 global current_tag_id while True: tag_id read_rfid() # 读取RFID无卡时返回None if tag_id and tag_id ! current_tag_id: current_tag_id tag_id # 查询数据库获取气瓶信息 bottle_info db_query_bottle(tag_id) # 推送识别结果到前端和LCD socketio.emit(tag_scanned, bottle_info) update_lcd_display(bottle_info) time.sleep(0.5) # 500ms检查一次 app.route(/api/record, methods[POST]) def record_weight(): API端点手动或自动触发记录当前重量 data request.json tag_id data.get(tag_id, current_tag_id) if not tag_id: return jsonify({error: No tag scanned}), 400 # 将 tag_id, current_weight, timestamp 写入数据库 weighing_history 表 db_insert_record(tag_id, current_weight) socketio.emit(record_saved, {tag_id: tag_id, weight: current_weight}) return jsonify({status: success}) if __name__ __main__: # 启动硬件监听线程 threading.Thread(targetweight_monitoring_thread, daemonTrue).start() threading.Thread(targetrfid_monitoring_thread, daemonTrue).start() # 启动Web服务监听所有网络接口 socketio.run(app, host0.0.0.0, port5000, debugFalse)数据库操作封装 使用PyMySQL库进行数据库操作。将所有数据库操作封装成函数如db_query_bottle()db_insert_record()并在其中处理异常确保程序不会因为SQL错误而崩溃。5.2 前端HTML/CSS/JS与实时数据展示前端页面负责展示数据、提供操作界面并通过Socket.io与后端保持实时连接。建立Socket.io连接script srchttps://cdn.socket.io/4.5.0/socket.io.min.js/script script const socket io(http://树莓派IP:5000); // 连接到后端Socket.io服务 socket.on(connect, () { console.log(Connected to server); document.getElementById(status).innerText 系统已连接; }); socket.on(weight_update, (data) { // 实时更新重量显示 document.getElementById(current-weight).innerText data.weight.toFixed(2) g; // 可以添加图表库如Chart.js动态绘制重量曲线 }); socket.on(tag_scanned, (data) { // 显示扫描到的气瓶信息 document.getElementById(bottle-id).innerText data.id; document.getElementById(pilot-name).innerText data.pilot_name; document.getElementById(last-weight).innerText data.last_weight; }); socket.on(record_saved, (data) { // 显示记录保存成功的通知 showNotification(气瓶 ${data.tag_id} 的重量 ${data.weight}g 已保存); }); /script调用后端API// 手动保存记录 function manualRecord() { fetch(http://树莓派IP:5000/api/record, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({ action: manual_record }) }) .then(response response.json()) .then(data { if(data.status success) { alert(记录成功); } }); }数据表格与历史查询 可以增加一个页面或模块通过调用后端另一个API端点如/api/history获取历史称重记录并用DataTables等JS插件渲染成可排序、可分页的表格。6. 外壳设计与制作经验分享原作者的木制外壳是一个很好的起点。对于DIY项目外壳的考量优先级应该是功能性 坚固性 易加工性 美观性。设计要点分层结构这是最实用的设计。底层放置树莓派、电源模块、面包板等中间层固定HX711模块、RFID读卡器天线朝上顶层是称重托盘和LCD屏。层与层之间用铜柱或木条隔开便于散热和理线。传感器安装称重传感器必须刚性固定底座且受力方向绝对垂直。使用金属角码或专门加工的固定块比直接用螺丝拧在木头上更可靠。在传感器和固定面之间可以垫一层薄橡胶减少应力集中。走线与维护在侧板或背板开孔使用航空插头或标准的DC电源插座、网线插座让外部线缆连接更整洁。内部线缆用扎带或线槽固定避免缠绕。确保外壳有一面可以轻松打开如用合页和磁吸扣方便日后维护升级。材料与工具选择亚克力板如果追求美观和现代感激光切割亚克力板是极佳选择。设计好图纸在淘宝或本地加工店即可制作精度高外观漂亮。PVC板或铝型材易于切割和打孔强度也不错适合快速原型制作。3D打印对于复杂的内部结构件如传感器固定座、模块支架3D打印是最灵活的。使用Fusion 360或Tinkercad进行设计。工具除了作者提到的一把好的台钳、一套手电钻和阶梯钻头用于开各种尺寸的孔、锉刀和砂纸用于打磨毛边会极大提升制作体验和成品质量。避坑实录我第一次做外壳时把RFID读卡器天线直接贴在了木壳内侧发现读卡距离变得极短1cm。后来才知道金属和过厚的非导电材料会严重屏蔽RFID信号。解决方案是在读卡器天线对应的外壳位置开一个薄壁区域比如把木壳掏薄或者直接镶嵌一小块塑料或亚克力板。LCD屏幕的视角也要考虑最好设计成微微向上倾斜方便观看。7. 系统调试与常见问题排查即使按照步骤操作也难免会遇到问题。这里汇总一些典型问题及解决方法。问题现象可能原因排查步骤与解决方案树莓派无法启动红灯常亮/闪烁电源不足或SD卡问题。1. 检查电源适配器是否为5V/3A并尝试更换电源线和USB口。2. 重新烧录SD卡确保使用Raspberry Pi Imager格式化并烧录。SSH无法连接网络配置错误或SSH未开启。1. 确认树莓派和电脑在同一局域网。2. 在路由器后台查看树莓派是否获取到IP。3. 重新烧录SD卡在Imager中明确开启SSH并设置密码。HX711读数始终为0或跳动巨大接线错误、供电不稳或传感器未正确加载。1. 用万用表测量HX711模块的VCC和GND之间电压是否为稳定的5V。2. 检查传感器四根线是否接牢尝试交换A和A-。3. 确保称重托盘有重量施加在传感器上检查传感器安装是否悬空。4. 在代码中增加hx.set_gain(128)对应通道A尝试。RFID模块读不到卡SPI未启用、接线错误、电压不对或天线问题。1. 运行lsmodLCD屏幕无显示或乱码I2C地址错误、对比度不对或接线问题。1. 运行sudo i2cdetect -y 1扫描I2C设备确认LCD的地址通常是0x27或0x3F。2. 调节LCD模块上的蓝色电位器改变对比度。3. 检查I2C线是否接好SDA-GPIO2 SCL-GPIO3。WS2812B灯带不亮或颜色错乱电源功率不足、信号电平不匹配、数据线接错。1. 确保灯带5V和GND有独立、充足的电源供电如5V/5A电源。2. 数据线DIN必须接在树莓派GPIO18并串联一个330Ω电阻。3. 代码中初始化时指定正确的LED数量和控制引脚。Python后台服务启动失败依赖未安装、数据库连接失败、端口被占用。1. 检查sudo systemctl status smart-scale.service和journalctl -xe查看具体错误日志。2. 手动在虚拟环境中运行python app.py看报错信息。3. 检查数据库配置config.py中的用户名、密码、数据库名是否正确。4. 检查5000端口是否被占用sudo netstat -tlnp网页前端能打开但无实时数据Socket.io连接失败、后端服务未运行或防火墙阻止。1. 浏览器F12打开开发者工具查看“网络(Network)”和“控制台(Console)”有无错误。2. 确认后端Socket.io服务正在运行sudo systemctl status。3. 检查前端JS代码中连接的IP和端口是否正确。4. 树莓派防火墙可能阻止了5000端口sudo ufw allow 5000/tcp。调试心法硬件项目调试务必遵循“分模块测试”的原则。不要一次性接好所有线路。先单独测试树莓派系统、网络。然后单独接上HX711和传感器用简单的Python脚本测试读数。再单独测试RFID、LCD。最后再整合到主程序中。这样一旦出现问题能快速定位是哪个模块的问题。善用print()语句打印关键变量和状态这是最直接的调试手段。