树莓派Pico与HX711构建高精度电子秤:从模拟信号到数字滤波全解析

树莓派Pico与HX711构建高精度电子秤:从模拟信号到数字滤波全解析

1. 项目概述:当树莓派Pico遇上HX711,打造你的高精度数字秤

如果你手头正好有一块树莓派Pico,又对嵌入式测量项目感兴趣,那么用Pico搭配HX711模组制作一个电子秤,绝对是一个能让你深入理解模拟信号采集、数字滤波和嵌入式系统开发的绝佳实践。这不仅仅是简单地把传感器数据读出来,更涉及到从微弱的模拟信号中提取稳定、可靠重量信息的完整链路。我最初接触这个组合是为了一个需要精确控制配料的小型自动化项目,市面上成品模块要么太贵,要么接口不灵活,于是决定自己动手。实测下来,这套方案成本极低(核心部件不到50元),精度却足以应对大多数电子制作、厨房烘焙甚至一些轻量级的科学实验场景,其核心原理与工业电子秤一脉相承。

这个项目适合所有对嵌入式开发、物联网数据采集感兴趣的开发者、创客乃至学生。你不需要深厚的电子工程背景,但需要一些基本的焊接能力和Micropython/C++的编程经验。通过这个项目,你将掌握如何驱动24位高精度ADC(模数转换器)、理解应变片电桥的工作原理、学习实用的数字滤波算法,并最终构建一个带校准功能的、可联网或显示的数字秤系统。整个过程就像在解构一个黑盒,把“放上东西显示重量”这个看似简单的动作,拆解成一个个你可以完全掌控的环节。

2. 核心硬件解析与选型考量

2.1 树莓派Pico:为何是性价比之选

在众多微控制器中,选择树莓派Pico作为本项目的主控,是基于多重考量后的平衡之选。其核心RP2040双核ARM Cortex-M0+处理器,运行频率高达133MHz,对于处理HX711的数据流和运行滤波算法绰绰有余。更关键的是,Pico拥有丰富的GPIO(通用输入输出引脚)和2个可编程I/O(PIO)子系统。

PIO在这里扮演了“硬件外挂”的角色。HX711通信协议是一种简单的同步串行接口,虽然可以用软件模拟时序,但在主循环中频繁进行位操作可能会被其他任务(如刷新显示、处理网络请求)打断,导致读取失败。而PIO允许我们编写微小的汇编程序,由硬件独立、精准地控制引脚时序,实现“免CPU干预”的通信,极大提高了数据读取的稳定性和系统可靠性。这是Pico相对于其他同价位MCU(如STM32系列某些型号)的独特优势,对于追求稳定数据采集的项目至关重要。

此外,Pico极低的价格(官方售价仅4美元)、完善的Micropython和C/C++ SDK支持、以及庞大的社区生态,使得从原型验证到最终产品化都异常顺畅。其内置的ADC虽然精度一般(12位),但本项目并不使用它,而是完全依赖外置的HX711,因此Pico的ADC性能不足并不构成短板。

2.2 HX711模块:24位ADC芯片的核心价值

HX711是一款专为电子秤设计的24位模数转换器芯片。它的价值在于将模拟信号放大和数字化这两个关键步骤集成到了一颗芯片里,极大简化了设计。

首先,它内部集成了一个可编程低噪声放大器(PGA),增益有128和64两档可选。应变片输出的信号极其微弱,通常是毫伏级别甚至更小,直接测量非常困难。HX711的放大器将这个微小信号放大到适合ADC转换的范围。选择128倍增益通常能获得更好的信噪比,适用于大多数基于应变片的秤重传感器。

其次,24位的ADC分辨率是其高精度的基石。我们来算一下:假设参考电压是5V,那么一个24位ADC的理论最小可分辨电压是 5V / (2^24) ≈ 0.298微伏。这意味着它能够捕捉到极其微小的电压变化,对应到重量上,可能就是几克甚至更小的变化。相比之下,Pico内置的12位ADC在同样5V参考下,最小分辨电压约为1.22毫伏,差距巨大。这就是为什么专业电子秤必须使用外置高精度ADC的原因。

市面上常见的HX711模块通常将芯片、外围电路(如稳压、滤波电容)和便于连接的引脚(VCC, GND, DT, SCK, E+, E-, A+, A-)集成在一块小板上。E+和E-为传感器提供激励电压(通常等于VCC),A+和A-则连接应变片电桥的输出端。DT(数据)和SCK(时钟)引脚用于与MCU通信。

注意:购买HX711模块时,务必确认其工作电压与你的Pico匹配。Pico的GPIO电压是3.3V,因此应选择支持3.3V逻辑电平的HX711模块(绝大多数模块的VCC接3.3V-5V均可,但IO口电平需与Pico一致)。若模块只支持5V逻辑,则需在DT、SCK线上加电平转换电路,否则可能损坏Pico。

2.3 称重传感器:应变片电桥的原理

项目的“感知器官”是称重传感器,最常见的是悬臂梁式。它的核心是贴在金属弹性体上的应变片——一种电阻值会随着材料形变(拉伸或压缩)而微小变化的元件。通常,四个应变片会连接成惠斯通电桥电路。

当没有负载时,电桥处于平衡状态,输出电压差(A+与A-之间)为零。当放置重物时,弹性体弯曲,导致两个应变片受拉电阻增加,另外两个受压电阻减小,从而破坏电桥平衡,产生一个与压力(重量)成正比的微小差分电压信号。这个信号就是HX711要放大和测量的对象。

传感器的关键参数是量程(如5kg)和灵敏度(如2.0mV/V)。假设我们使用一个5kg/2.0mV/V的传感器,供电(激励电压)为5V(即E+与E-之间的电压),那么满量程(5kg)时,输出差分电压最大为 5V * 2.0mV/V = 10mV。这个10mV的微弱信号,经过HX711放大128倍后,变为1.28V,然后被24位ADC量化。整个链条的线性度,决定了秤的准确性。

3. 系统搭建与硬件连接实操

3.1 材料清单与工具准备

在开始焊接和编程前,请准备好以下材料:

  • 树莓派Pico:一片,建议使用带焊接排针的版本。
  • HX711模块:一片。
  • 称重传感器:一个,常见量程有1kg, 5kg, 10kg等,根据你的需求选择。推荐从5kg/10kg开始,通用性较好。通常有红、黑、白、绿四根线。
  • 杜邦线:若干(公对公、公对母),用于连接。
  • 面包板:一块,用于原型搭建。
  • 微动开关或轻触开关:一个(可选),用于去皮/校准触发。
  • OLED显示屏(I2C接口):一块(可选),用于本地显示重量。
  • USB数据线:一根,用于给Pico供电和编程。
  • 已知重量的校准砝码:如100g, 500g, 1kg的标准砝码。如果没有,可以用未开封的矿泉水(500ml约500g)、标准袋装食盐(500g)等作为临时参考,但精度会受影响。

所需工具:电烙铁、焊锡、镊子、万用表(可选,用于调试)。

3.2 电路连接详解与避坑指南

连接是整个项目的基础,错误的连接可能导致读数不稳甚至硬件损坏。请严格按照以下步骤操作:

  1. 给Pico和HX711供电:将Pico的VBUS(引脚40)或VSYS(引脚39)连接到面包板正极,GND(引脚3, 8, 13, 18, 23, 28, 33, 38)中任意一个连接到面包板负极。这是系统的主电源(来自USB的5V)。然后,用杜邦线将HX711模块的VCC引脚连接到面包板正极(5V),GND连接到面包板负极。请注意:虽然HX711的VCC可以接5V以获得更大的传感器激励电压,但其数据引脚(DT, SCK)输出的是5V TTL电平。为了安全,我强烈建议将HX711的VCC也接到Pico的3.3V输出(引脚36)上,这样其IO电平就是3.3V,与Pico GPIO直接兼容,无需电平转换。

  2. 连接称重传感器与HX711:称重传感器通常有四根线:

    • :电源正极(E+)
    • :电源负极(E-)
    • :信号正极(A+)
    • 绿:信号负极(A-) 将传感器的红、黑线分别接到HX711模块的E+E-引脚。将白、绿线分别接到A+A-引脚。如果读数出现负值,可以尝试交换A+和A-的接线。
  3. 连接HX711与树莓派Pico:这是数据通信的关键。

    • HX711的DT(数据)引脚 → Pico的GP16(或其他任意数字GPIO)。
    • HX711的SCK(时钟)引脚 → Pico的GP17(或其他任意数字GPIO)。 我选择GP16和GP17是因为它们位置集中,便于布线。你可以选择任何一对数字GPIO,只需在代码中相应修改即可。
  4. (可选)连接显示屏:如果使用OLED,通常通过I2C接口连接。

    • OLED的VCC→ Pico的3.3V(引脚36)。
    • OLED的GND→ Pico的GND。
    • OLED的SCL→ Pico的GP5(I2C1时钟线)。
    • OLED的SDA→ Pico的GP4(I2C1数据线)。
  5. (可选)连接功能按钮:将一个轻触开关一端接Pico的某个GPIO(如GP15),另一端接地。通过软件配置内部上拉电阻,实现按键检测,用于去皮或进入校准模式。

实操心得:在通电前,务必用万用表通断档检查所有连接,确保没有短路(特别是VCC和GND之间)。首次上电时,建议先不接传感器,观察HX711模块指示灯是否正常。连接传感器后,用手轻轻按压传感器平台,观察后续读数的变化趋势是否合理,这是快速验证硬件连接是否正确的有效方法。

4. 固件编程与核心驱动实现

4.1 开发环境搭建与库选择

对于树莓派Pico,你有两种主要的编程语言选择:MicropythonC/C++

  • Micropython:上手极快,交互性强,适合快速原型验证。你可以使用Thonny IDE,它集成了Pico固件烧录和代码上传功能,对新手非常友好。我们需要安装hx711的Micropython库,通常通过Thonny的包管理工具或手动下载hx711.py文件放到Pico上即可。
  • C/C++:性能更高,对硬件控制更底层,适合最终产品和需要极致稳定性的场景。你需要搭建基于Pico SDK的开发环境(如使用VS Code + CMake)。驱动HX711需要自己编写或移植底层时序函数。

本项目将以Micropython为例进行讲解,因为它能更清晰地展示逻辑,且更容易修改和调试。确保你的Pico已经烧录了最新的Micropython固件。

4.2 HX711驱动程序编写与原理解读

即使使用现成的库,理解其背后的驱动原理也至关重要。HX711的通信协议是同步串行接口,读取一次24位数据(外加3位通道和增益选择位)的流程如下:

  1. 等待就绪:HX711的DT引脚在空闲时为高电平。当ADC转换完成、数据准备好后,DT会拉低。因此,主控(Pico)首先需要检测DT引脚是否为低电平,如果不是,则等待。
  2. 时钟脉冲读取数据:当DT变低后,主控通过SCK引脚发出25个时钟脉冲。在前24个脉冲的下降沿,HX711会依次从高位(MSB)到低位(LSB)输出数据位。Pico需要在SCK为低电平时读取DT的状态。
  3. 通道与增益选择:第25个时钟脉冲用于设置下一次转换的通道和增益。脉冲后,芯片会立即开始下一次转换。

下面是一个简化但核心的Micropython驱动函数片段,展示了如何读取一个原始值:

from machine import Pin import time class HX711: def __init__(self, dt_pin, sck_pin): self.dt = Pin(dt_pin, Pin.IN) self.sck = Pin(sck_pin, Pin.OUT) self.sck.value(0) # 初始时钟低电平 def read_raw(self): # 等待HX711准备就绪 while self.dt.value() == 1: time.sleep_us(10) # 短暂延迟,避免忙等耗尽CPU raw_value = 0 # 读取24位数据 for _ in range(24): self.sck.value(1) # 产生时钟上升沿 self.sck.value(0) # 下降沿,此时数据稳定 raw_value = (raw_value << 1) | self.dt.value() # 左移并读入位 # 发送第25个脉冲,设置下一次为通道A,增益128 for _ in range(1): self.sck.value(1) self.sck.value(0) # 处理符号位(24位数据是补码形式) if raw_value & 0x800000: # 检查最高位(符号位) raw_value |= ~0xffffff # 如果是负数,进行符号扩展 return raw_value

这段代码的关键在于精确的时序控制。time.sleep_us()用于产生微秒级延迟,确保满足HX711芯片的时序要求(如SCK高/低电平最小脉宽)。读取到的raw_value是一个有符号的24位整数,其绝对值会随着重量的增加而增大(或减小,取决于传感器安装方向)。

4.3 数据滤波算法:从噪声中提取真实信号

直接从HX711读取的原始数据是充满噪声的,即使空载时数值也会在小范围内跳动。这是因为存在电源纹波、热噪声、机械振动等干扰。因此,滤波是电子秤程序的核心。

移动平均滤波是最简单有效的方法。其原理是维护一个固定长度的数据队列,每次读取新值后,计算队列中所有值的平均值作为输出。这能平滑掉随机高频噪声。

class MovingAverageFilter: def __init__(self, window_size=10): self.window_size = window_size self.data = [] def add(self, value): self.data.append(value) if len(self.data) > self.window_size: self.data.pop(0) # 移除最旧的数据 def get_average(self): if not self.data: return 0 return sum(self.data) / len(self.data) # 使用示例 hx711 = HX711(dt_pin=16, sck_pin=17) filter = MovingAverageFilter(window_size=20) # 窗口大小20 while True: raw = hx711.read_raw() filter.add(raw) filtered_raw = filter.get_average() # 后续将filtered_raw转换为重量 time.sleep_ms(50) # 控制采样率,约20Hz

窗口大小的选择是一个权衡:窗口越大,曲线越平滑,但响应速度越慢(滞后越大)。对于电子秤,20-50的窗口大小通常能取得不错的效果。你还可以尝试更高级的滤波算法,如中值滤波(对脉冲噪声效果好)或一阶低通数字滤波new_value = α * old_value + (1-α) * measured_value),根据实际噪声特性进行调整。

实操心得:不要盲目追求复杂的滤波算法。对于大多数桌面电子秤应用,一个适当窗口大小的移动平均滤波已经完全足够。调试时,可以先关闭滤波,观察原始数据的跳动范围(峰峰值),然后逐步增加窗口大小,直到读数在静止时稳定在1-2个数字以内,同时确保放上重物后读数能在1秒内稳定下来。

5. 校准与标定:将AD值转换为重量

5.1 校准的数学模型:两点定标法

传感器输出的AD原始值(raw)与施加的重量(weight)之间存在线性关系,但需要两个参数来确定这条直线:零点偏移(offset)比例系数(scale)

公式为:weight = (raw - offset) / scale

  • 零点偏移(offset):传感器空载(秤盘上无任何东西)时,HX711读出的原始AD值。
  • 比例系数(scale):单位重量所对应的AD值变化量。需要通过已知重量的砝码来测算。

两点定标法是最常用的方法:

  1. 记录空载时的AD值raw_zero
  2. 放置一个已知准确重量known_weight(如500g砝码)的物体,记录稳定后的AD值raw_weight
  3. 计算比例系数:scale = (raw_weight - raw_zero) / known_weight

此后,对于任何未知物体,测量其AD值raw_x,则重量为:weight_x = (raw_x - raw_zero) / scale

5.2 实现交互式校准流程

我们需要在代码中实现一个校准模式。通常通过一个按钮来触发:长按3秒进入校准模式,然后根据提示依次进行“空载”和“加载标准重量”的操作。

def calibrate(hx711, filter, known_weight): print("校准开始...请确保秤盘空载,然后按任意键继续。") input() # 等待用户确认 # 采集多个空载样本求平均,作为零点 zero_samples = [] for i in range(50): zero_samples.append(hx711.read_raw()) time.sleep_ms(20) offset = sum(zero_samples) / len(zero_samples) print(f"零点偏移已记录: {offset}") print(f"请放置 {known_weight}g 的标准砝码,然后按任意键继续。") input() # 采集多个加载样本求平均 weight_samples = [] for i in range(50): weight_samples.append(hx711.read_raw()) time.sleep_ms(20) raw_weight = sum(weight_samples) / len(weight_samples) scale = (raw_weight - offset) / known_weight print(f"比例系数已计算: {scale}") # 将offset和scale保存到Pico的文件系统或Flash中,下次上电直接读取 save_calibration(offset, scale) return offset, scale

这个流程将校准参数(offset,scale)保存下来,避免了每次上电都需要重新校准的麻烦。保存到文件是最简单的方式,在Micropython中可以使用open().write()实现。

5.3 温度漂移与长期稳定性补偿

一个容易被忽视的问题是传感器的温度漂移。环境温度变化会导致应变片的电阻发生微小改变,从而影响零点偏移和灵敏度。对于精度要求不高的场合,可以忽略。但如果要求较高,可以考虑以下策略:

  1. 开机自动清零:每次启动时,自动进行一次零点校准(假设此时秤盘为空)。这可以消除大部分的零点温漂。
  2. 多点温度标定:如果应用环境温度变化范围已知且固定,可以在不同温度点下进行校准,建立温度-补偿参数查找表,在实际使用时根据温度传感器读数进行插值补偿。
  3. 使用带温度补偿的传感器:一些高端的称重传感器本身内置了温度补偿电路。

此外,传感器的蠕变(长时间负载下输出缓慢变化)和滞后(加载和卸载路径不完全重合)也会影响精度。在软件上,可以通过定期检查零点(在确认空载时)进行微调来部分补偿。

6. 系统集成与功能扩展

6.1 本地显示与用户交互

集成一个OLED显示屏(如SSD1306驱动的0.96寸屏)可以极大提升项目的完整度和实用性。使用ssd1306.py库可以轻松驱动。

核心任务是在主循环中,将计算得到的重量值格式化后显示在屏幕上。可以设计简单的界面,如大字体显示当前重量(单位:g/kg),小字体显示电池电量(如果使用电池供电)、单位标识等。结合之前提到的按钮,可以实现以下交互:

  • 短按:去皮(Tare),将当前重量设为零点。
  • 长按(3秒):进入校准模式。

去皮功能在软件上很容易实现,即更新当前的offset值:current_offset = raw_current。这样,后续计算重量时都以此刻为新的零点。

6.2 数据上传与物联网应用

树莓派Pico W型号内置了Wi-Fi功能,这为电子秤插上了物联网的翅膀。你可以将重量数据通过MQTT协议发布到云端服务器(如Home Assistant, AWS IoT),或者通过HTTP POST请求发送到自定义的API接口。

一个典型的应用场景是智能厨房秤:每次称重后,数据自动上传到家庭自动化系统,记录食谱配料用量,甚至可以根据累计重量触发下一步的烹饪提示。另一个场景是仓库物料管理:将秤放在货架下,定期上报剩余物品重量,实现低库存预警。

实现时需要注意:

  1. 功耗:如果使用电池,需要优化Wi-Fi连接策略(如定时唤醒、发送后休眠)。
  2. 数据格式:定义清晰的数据结构,如{"weight_g": 250.5, "timestamp": 1678886400, "sensor_id": "scale_1"}
  3. 错误处理:网络连接可能不稳定,代码中需要加入重试机制和离线缓存(如果Pico的Flash空间允许)。

6.3 外壳设计与机械安装

一个实用的电子秤离不开稳固的外壳和正确的机械安装。对于悬臂梁传感器,通常需要设计一个底座来固定传感器的根部(有固定孔的一端),而秤盘则安装在传感器的悬空端。确保传感器只承受垂直方向的力,避免侧向力或扭力,否则会严重影响精度甚至损坏传感器。

外壳可以用3D打印(PLA/ABS材料)、亚克力板激光切割甚至木材制作。关键是在传感器受力点设计一个承压头(通常是一个金属或硬塑料的小圆柱),将秤盘的压力集中传递到传感器受力区的中心点。同时,要为Pico、HX711模块和电池(如果有)预留空间和走线孔。

注意事项:传感器的量程选择要有余量。如果你最大需要称量2kg的物品,最好选择5kg的传感器,并确保其工作在满量程的20%-80%范围内,这样线性度和寿命都更好。绝对不要让传感器长时间处于超载状态(超过其额定量程的120%)。

7. 调试、优化与常见问题排查

7.1 上电无读数或读数异常

这是最常见的问题。请按照以下清单逐项排查:

现象可能原因排查步骤与解决方案
读数始终为0或固定值1. 电源未接通或电压不足。
2. HX711与Pico连线错误(DT/SCK接反)。
3. 传感器损坏或接线错误(线序错)。
1. 用万用表测量HX711的VCC和GND之间电压,确保在3.3V或5V。
2. 检查DT、SCK是否与代码中定义的GPIO号对应。
3. 断开传感器,用万用表测量其输入电阻(红黑线间)和输出电阻(白绿线间)。输入电阻通常约1kΩ,输出电阻约1kΩ且对称。若为无穷大或零,则可能损坏。
读数跳动剧烈(数百到数千)1. 电源噪声大。
2. 传感器或HX711模块未稳定放置,受振动影响。
3. 滤波算法未启用或窗口太小。
4. 通信受到干扰。
1. 尝试使用线性稳压电源或高质量的USB充电器供电,在HX711的电源引脚附近增加一个10uF和0.1uF的电容滤波。
2. 将整个系统放在稳固的桌面上,避免触碰。
3. 启用移动平均滤波,并逐步增大窗口大小(如从10到50)。
4. 确保数据线(DT, SCK)不要太长,且远离电源等干扰源。
读数随重量增加而减小(负增长)传感器信号线接反。交换连接在HX711 A+和A-引脚上的两根传感器信号线(通常是白线和绿线)。
读数有固定偏移,空载不为零这是正常现象,需要进行零点校准执行校准流程,记录空载时的offset值。
放上重物后读数线性度差1. 传感器安装不当,受力不纯(有侧向力)。
2. 传感器量程选择过小,已接近或超载。
3. 校准用的砝码不准。
1. 检查机械结构,确保重量垂直、均匀地施加在传感器受力点。
2. 更换更大量程的传感器。
3. 使用经过计量的标准砝码进行校准。

7.2 精度提升与长期稳定性技巧

  1. 预热:HX711芯片和传感器在通电后需要一段时间(几分钟)达到热稳定。对于高精度要求,建议上电预热后再进行校准或使用。
  2. 多次采样与剔除异常值:在read_raw()函数中,可以进行连续多次读取(如5次),然后去掉最大值和最小值,再取中间值的平均,这能有效抵抗单次读取的突发干扰。
  3. 软件去皮:在称重前,先放上容器,按下去皮键,这样后续读数就是净重。确保容器重量在传感器量程内。
  4. 定期自动零点跟踪:在程序中加入逻辑,当检测到重量在极小的阈值范围内(如±2g)保持一段时间(如10秒)时,自动微调零点偏移。这可以补偿缓慢的温度漂移。
  5. 供电优化:为模拟部分(HX711和传感器)单独供电,或使用低噪声的LDO(低压差线性稳压器)供电,与数字部分(Pico)隔离,能显著降低电源噪声带来的跳动。

7.3 从原型到产品的思考

当你完成一个稳定工作的原型后,如果希望将其产品化,还需要考虑更多:

  • PCB设计:将Pico、HX711、电源管理、连接器等集成到一块定制PCB上,可以大大减小体积、提高可靠性。
  • 低功耗设计:如果使用电池,需要让Pico和HX711在空闲时进入深度睡眠模式,仅由外部中断(如称重变化)或定时器唤醒。
  • 法规认证:如果作为商业电子秤销售,可能需要符合当地的计量法规,这通常涉及更严格的硬件校准、软件锁和法定计量单位控制。
  • 用户体验:设计友好的用户界面(UI)、清晰的指示灯、清脆的按键反馈,这些都能提升产品质感。

这个项目最吸引我的地方在于,它完美地连接了物理世界(重量)与数字世界(数据),并且每一个环节——从机械结构、模拟电路、数字接口到软件算法——都清晰可见、可控可调。它不像使用一个封装好的模块那样简单,但正是这种从底层构建的过程,让你对“测量”二字有了更深刻的理解。当你第一次放上一个已知重量的物体,屏幕上稳定地显示出几乎分毫不差的数字时,那种成就感是无可替代的。