news 2026/8/27 20:58:06

从传感器到算法:基于单片机的电子秤设计全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从传感器到算法:基于单片机的电子秤设计全流程解析

1. 项目概述:从零打造一台自己的电子秤

做硬件开发的朋友,估计都绕不开“电子秤”这个经典项目。它看起来简单,不就是放个东西,屏幕上显示个重量嘛。但真上手做,你会发现里面门道不少:怎么把微小的形变转换成电信号?信号那么弱,怎么放大到单片机可以识别的范围?怎么让显示的数字又准又稳?我前前后后做过不下五六个版本的电子秤,从最简陋的51单片机配电阻应变片,到用上24位高精度ADC的STM32方案,踩过的坑、烧过的芯片,都能写本小册子了。今天,我就以“基于单片机的电子秤设计”这个项目为例,把整个从传感器原理到软件滤波的全流程,掰开揉碎了讲清楚。无论你是正在做课程设计的学生,还是想深入了解测力系统的工程师,这篇文章都能给你一套可以直接“抄作业”的完整方案。咱们不搞虚的,就从最核心的称重传感器和ADC选型开始聊。

2. 核心硬件选型与电路设计解析

电子秤的硬件核心就三样:称重传感器、信号调理电路、单片机。选型决定了你这款秤的性能天花板,是精度1克还是0.1克,是晃两下就稳定还是飘忽不定,硬件设计阶段就基本定调了。

2.1 称重传感器:电子秤的“感知神经”

市面上最常见的称重传感器是电阻应变式。它的核心是一根弹性梁(通常是合金钢),上面贴着像蜘蛛网一样的电阻应变片。当物体放在秤盘上,弹性梁发生弯曲形变,贴在它表面的应变片也随之被拉伸或压缩。应变片的奥秘在于,它的电阻值会随着自身的形变(应变)而线性变化,这就是“应变效应”。

通常,我们会把四个完全相同的应变片组成一个惠斯通电桥。两个应变片受拉,电阻增加;另外两个受压,电阻减小。这样,微小的形变就被转换成了电桥输出的差分电压信号。这个信号非常微弱,满量程输出通常在1-2mV/V左右。也就是说,如果你给电桥提供5V激励电压(V_excitation),那么满载时的输出电压变化只有5-10mV。要把这个信号准确测量出来,后续的放大和采集至关重要。

注意:选购传感器时,别只看量程。关键参数还有灵敏度(mV/V)、非线性度、蠕变和温度系数。对于DIY项目,一款常见的5Kg量程、2mV/V灵敏度、综合精度0.05%的悬臂梁式传感器就非常合适,价格也亲民。

2.2 信号调理电路:放大与稳定的艺术

传感器输出的毫伏级信号,直接送给单片机的ADC(通常是0-3.3V量程)无异于“蚊子与牛对话”,根本分辨不出来。所以,我们必须用仪表放大器将这个信号放大数百倍。

为什么一定是仪表放大器,而不是普通的运放?因为传感器输出的是差分信号,且源阻抗较高。仪表放大器具有极高的输入阻抗(几乎不从前级汲取电流)、极高的共模抑制比(CMRR)和精准的差分放大能力。它能有效抑制由长导线引入的共模干扰(比如工频噪声),只放大我们需要的差分信号。我常用的是ADI的AD620或TI的INA125。这里以INA125为例,它的放大倍数由一颗外接电阻R_g决定:Gain = 4 + (60kΩ / R_g)。假设我们需要500倍的增益,可以反算出R_g ≈ 120Ω。

除了放大,电路设计上还有几个关键点:

  1. 激励电压源必须稳定:传感器的输出与激励电压成正比。如果激励电压波动1%,输出信号就会波动1%。因此,必须使用一颗基准电压源芯片(如REF5050)或LDO(如AMS1117-5.0)来为电桥供电,绝不能直接用单片机的VCC。
  2. 滤波电路必不可少:在仪表放大器的输出端,需要加入一个低通滤波电路,滤除高频噪声。一个简单的RC无源滤波器(如1kΩ电阻和0.1uF电容组成截止频率约1.6kHz的滤波器)就很有用。如果想效果更好,可以用运放构成有源滤波器。
  3. 布局与走线要考究:模拟信号部分要远离数字电路(特别是单片机的晶振和数字IO线)。采用“星型接地”或单点接地,避免地线噪声干扰。传感器信号线最好使用双绞线或屏蔽线。

2.3 单片机与ADC选型:精度与速度的权衡

放大后的信号是模拟电压,需要ADC转换成数字量。很多单片机自带12位ADC,但对于电子秤够用吗?我们来算笔账。

假设系统设计:5Kg量程,目标分辨率1克。

  • 传感器满量程输出:5V激励 * 2mV/V = 10mV。
  • 放大500倍后,满量程电压为10mV * 500 = 5V。
  • 1克对应的电压变化为 (5V / 5000g) = 1mV。
  • 如果使用3.3V供电、12位ADC(分辨率 3.3V / 4096 ≈ 0.8mV),那么1g的变化刚刚超过1个LSB,勉强可以分辨,但毫无余量,噪声很容易导致显示值跳动。

因此,追求一定精度的电子秤,外置或内置高分辨率Σ-Δ型ADC是更佳选择。比如24位的ADS1232或HX711。以HX711为例,它专为电子秤设计,内部集成了可编程增益放大器(PGA)和24位ADC,分辨率高达2^24,动态范围极大。即使存在噪声,通过软件滤波也能得到非常稳定的读数。STM32等高端单片机的部分型号也内置了16位甚至更高精度的ADC,但通常需要精心设计模拟前端和滤波算法才能发挥最佳性能。

实操心得:对于初学者或快速原型,HX711模块是首选。它把最难搞的模拟前端和ADC集成好了,你只需要用单片机的两个IO口(时钟和数据)以串行方式读取数据即可,极大降低了硬件设计和调试门槛。我第一个稳定的电子秤就是用STM32F103C8T6+HX711做的。

3. 软件架构与核心算法实现

硬件是骨架,软件是灵魂。电子秤的软件核心就两件事:稳定地读取重量数据友好地与人交互。下面我们分步拆解。

3.1 驱动程序编写:与ADC的“对话”

首先,你得让单片机从ADC芯片里把数据读出来。我们以最常用的HX711为例,它的通信时序是特定的。

HX711有两个通道,增益可选128或64。驱动流程如下:

  1. 初始化:将数据线(DOUT)和时钟线(PD_SCK)配置为GPIO,DOUT设为输入,PD_SCK设为输出并置低。
  2. 等待就绪:HX711在数据未准备好时,DOUT为高电平;准备好后,DOUT拉低。因此,主程序需要不断查询DOUT引脚状态,一旦变低,即可读取。
  3. 读取数据:连续产生25个时钟脉冲。前24个脉冲的上升沿,从DOUT引脚依次移出24位数据(高位在先)。第25个脉冲用于设置下一个转换的通道和增益。
  4. 数据转换:读出的24位数据是二进制补码形式,需要转换成有符号整数。

下面是一段典型的HX711读取函数(C语言示例):

// 假设 PD_SCK 接 PB0, DOUT 接 PB1 #define HX711_SCK_GPIO_Port GPIOB #define HX711_SCK_Pin GPIO_PIN_0 #define HX711_DOUT_GPIO_Port GPIOB #define HX711_DOUT_Pin GPIO_PIN_1 int32_t HX711_Read(void) { uint32_t count = 0; uint8_t i; // 等待HX711准备好 while (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) == GPIO_PIN_SET); // 读取24位数据 for (i = 0; i < 24; i++) { HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); // 短暂延时,满足时序要求 count = count << 1; // 左移,为下一位腾出空间 HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); if (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) == GPIO_PIN_SET) { count++; } } // 第25个脉冲,设置下一次为通道A,增益128 HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); // 将24位二进制补码转换为有符号32位整数 if (count & 0x800000) { // 检查最高位(第24位)是否为1,即是否为负数 count |= 0xFF000000; // 进行符号扩展 } return (int32_t)count; }

3.2 标定算法:将AD值转换为重量

读出来的原始AD值(RawData)只是一个数字,我们需要通过标定,建立它与实际重量的线性关系。标定需要两个已知重量的砝码,通常用零点(空载)和满量程附近的一个点(如5Kg砝码)。

标定过程:

  1. 清空秤盘,读取并记录此时的AD值RawData_Zero
  2. 放置已知重量的标准砝码(如Weight_Cal = 5000.0g),读取并记录稳定后的AD值RawData_Cal
  3. 计算比例系数(系数K)K = Weight_Cal / (RawData_Cal - RawData_Zero)这个K值就是每个AD值增量所代表的重量(单位:g/AD)。
  4. 实时重量计算Current_Weight = (RawData_Current - RawData_Zero) * K

在程序中,我们需要保存RawData_ZeroK这两个参数。为了防止断电丢失,务必将其存入单片机的EEPROM或Flash中。每次上电初始化时,从存储器中读取这些参数。

3.3 数字滤波算法:让显示值“稳如泰山”

直接从ADC读取的数据是包含噪声的,会导致显示值不停跳动。数字滤波的目的就是平滑这些噪声。电子秤常用的滤波算法有:

  1. 滑动平均滤波:最简单有效。维护一个固定长度的数组,每次存入新数据,并计算数组内所有数据的平均值作为输出。

    #define FILTER_LEN 10 int32_t filter_buf[FILTER_LEN]; uint8_t filter_index = 0; int32_t Moving_Average_Filter(int32_t new_val) { filter_buf[filter_index] = new_val; filter_index = (filter_index + 1) % FILTER_LEN; int64_t sum = 0; for(int i=0; i<FILTER_LEN; i++) { sum += filter_buf[i]; } return (int32_t)(sum / FILTER_LEN); }

    优点:简单,对周期性干扰有良好抑制。缺点:会产生滞后,响应速度随窗口长度增加而变慢。

  2. 一阶滞后滤波(低通滤波)Y(n) = α * X(n) + (1-α) * Y(n-1)。其中α是滤波系数(0<α<1),X(n)是新采样值,Y(n-1)是上次滤波输出。α越接近1,响应越快但滤波效果差;α越接近0,越平滑但滞后严重。这个算法计算量小,非常适合单片机。

  3. 中位值平均滤波:结合了中值滤波和平均滤波。连续采样N个数据,去掉一个最大值和一个最小值,然后计算剩余N-2个数据的平均值。这种方法能有效抑制脉冲性干扰。

我的经验:在实际项目中,我常采用“滑动平均 + 一阶滞后” 两级滤波。先用一个长度为5的滑动平均快速平滑毛刺,再将结果送入一阶滞后滤波器(α=0.2~0.5),这样既能保证一定的实时性,又能让最终显示值非常稳定,不会出现尾数频繁跳变的情况。

4. 人机交互与系统功能实现

重量数据稳定了,接下来就是如何展示给用户,以及增加一些实用功能。

4.1 显示模块驱动

最常用的是LCD1602液晶屏或OLED屏。LCD1602价格低廉,驱动简单;OLED显示效果更佳,但价格稍高。驱动本质都是按照时序向屏幕发送命令和数据。

以LCD1602为例,你需要实现:

  • 初始化序列:设置显示模式、光标、清屏等。
  • 写命令/写数据函数:根据数据手册的时序图,操作RS、RW、E和D0-D7引脚。
  • 显示字符串/数字函数:将计算得到的重量(浮点数)转换为字符数组,并定位到屏幕特定位置显示。

现在有很多现成的驱动库(如Arduino的LiquidCrystal库),但在实际产品中,为了优化代码体积和可控性,我更喜欢根据数据手册自己编写驱动,这样能更深入了解时序和屏幕的“脾气”。

4.2 按键功能与菜单设计

一个基本的电子秤至少需要以下几个按键:

  • “去皮/归零”键:将当前重量设为零点,用于扣除容器重量。
  • “单位切换”键:在克(g)、千克(kg)、盎司(oz)等单位间切换。
  • “标定”键:进入标定模式(通常需要组合键或长按进入,防止误触发)。

按键处理推荐使用状态机模型,并加入消抖处理。下面是一个简单的按键状态机示例:

typedef enum { KEY_IDLE, KEY_DEBOUNCE, KEY_PRESSED, KEY_REPEAT, KEY_RELEASED } KeyState; KeyState key_scan(GPIO_PinState pin_state) { static KeyState state = KEY_IDLE; static uint32_t press_tick = 0; switch(state) { case KEY_IDLE: if(pin_state == GPIO_PIN_RESET) { // 按键按下(假设低电平有效) state = KEY_DEBOUNCE; press_tick = HAL_GetTick(); } break; case KEY_DEBOUNCE: if(HAL_GetTick() - press_tick > 20) { // 消抖20ms if(pin_state == GPIO_PIN_RESET) { state = KEY_PRESSED; // 确认为有效按下 } else { state = KEY_IDLE; } } break; case KEY_PRESSED: // 执行一次按键动作 // ... state = KEY_REPEAT; // 进入长按/连按判断 break; // ... 其他状态处理 } return state; }

菜单设计上,为了不占用过多RAM,可以使用“菜单项表”的方式,用结构体数组定义所有菜单项和对应的处理函数,通过上下键切换焦点,确认键执行。

4.3 低功耗与稳定性考量

对于电池供电的便携式电子秤,低功耗设计是必须的。

  1. 单片机休眠:在无操作一段时间后,让单片机进入STOP或SLEEP模式。HX711本身功耗很低,也可以控制其掉电引脚(PDWN)使其进入休眠。
  2. 间歇采样:不需要实时快速更新时,可以降低ADC的采样频率,比如从10Hz降到1Hz。
  3. 外设管理:关闭不用的外设时钟,如显示屏背光、蜂鸣器等。

稳定性方面,除了硬件上的滤波,软件上还要做“零位跟踪”“动态检测”

  • 零位跟踪:长时间运行,传感器的零点可能会缓慢漂移。可以设定一个很小的阈值(比如±2g),当重量持续一段时间处于此阈值内时,自动缓慢地修正零点值,避免长期使用后空秤不归零。
  • 动态检测:通过判断连续多次采样的差值是否超过某个阈值,来判断秤盘上的物体是否已稳定放置。只有稳定后,才更新最终显示重量,避免在放东西的过程中数字乱跳。

5. 系统集成调试与性能优化

当硬件焊接好,软件也写得差不多了,就到了最考验耐心和经验的环节——调试与优化。

5.1 调试流程与工具

  1. 电源检查:万用表测量传感器激励电压、运放供电电压、基准电压是否准确稳定。这是所有问题的根源。
  2. 信号通路探测:用示波器是最直观的。
    • 第一步:测量传感器电桥输出端(差分信号),空载和加载时是否有毫伏级的电压变化?这能验证传感器和机械结构是否正常。
    • 第二步:测量仪表放大器输出端。空载时,理论上应为0V,但实际可能有几毫伏到几十毫伏的偏移(由运放失调电压引起)。加载后,电压是否按预期比例(增益*输入)变化?
    • 第三步:测量单片机ADC输入引脚或HX711模拟输入端的波形。观察信号是否干净,有无明显的毛刺或周期性噪声(如50Hz工频干扰)。
  3. 软件数据观察:通过串口将单片机读取的原始AD值、滤波后的值、计算出的重量实时打印出来。这是分析问题的关键。

5.2 常见问题与解决方案实录

下面是我在调试中遇到的一些典型问题及解决方法,整理成了表格:

问题现象可能原因排查步骤与解决方案
显示值始终为0或接近01. 传感器未正确受力或损坏。
2. 电桥激励电压为0。
3. 仪表放大器无输出或增益设置错误。
4. ADC未正确读取数据。
1. 万用表测量传感器输入(激励)电压是否正常(如5.0V)。
2. 测量电桥输出差分电压,用手轻压传感器看是否有变化。
3. 测量仪表放大器输出,检查增益电阻是否焊接正确。
4. 检查单片机与ADC的通信时序,用逻辑分析仪抓取波形。
显示值跳动剧烈(跳变几十上百克)1. 电源噪声大。
2. 模拟地线设计不合理,引入数字噪声。
3. 机械结构不稳定或传感器安装不当。
4. 软件滤波参数过弱或未启用。
1. 示波器查看电源纹波,在电源入口和芯片电源脚加退耦电容(如10uF钽电容+0.1uF陶瓷电容)。
2. 检查PCB布局,模拟部分和数字部分地线单点连接。
3. 确保传感器受力均匀,安装牢固无晃动。
4. 加大滑动平均窗口或减小一阶滞后滤波的α值。
空秤时显示值缓慢漂移(蠕变)1. 传感器质量差,自身蠕变大。
2. 温度变化引起电桥和运放参数漂移。
3. 激励电压源温漂大。
1. 选用更高品质的传感器。
2. 在软件中加入自动零位跟踪功能。
3. 使用低温漂的基准电压源为电桥供电。
标定后线性度差(小重量准,大重量偏)1. 传感器非线性度过大。
2. 仪表放大器输出接近电源轨,进入非线性区。
3. 机械结构在重载下形变不理想。
1. 检查放大后的满量程电压是否在ADC量程的70%-90%内,为运放留出余量。
2. 采用多点标定(如0g, 2Kg, 5Kg),在软件中采用分段线性补偿或查表法。
按键响应不灵或误触发1. 按键硬件消抖不足。
2. 软件消抖逻辑或状态机有bug。
3. IO口配置错误(如上拉电阻未启用)。
1. 确保硬件上有上拉电阻和滤波电容。
2. 用逻辑分析仪查看按键按下时的波形,调整软件消抖时间(通常10-50ms)。
3. 检查单片机IO口配置模式是否正确。

5.3 精度提升的进阶技巧

如果基础功能实现后,还想进一步提升精度和稳定性,可以尝试以下方法:

  1. 软件校准与补偿

    • 多点标定:不止校准零点和满点,在中间量程再取1-2个点进行标定,用最小二乘法拟合出更精确的曲线,补偿传感器的非线性。
    • 温度补偿:如果应用环境温度变化大,可以增加一个温度传感器(如DS18B20)。通过实验,测量出传感器零点、灵敏度随温度变化的系数,在软件中进行实时补偿。
  2. 高级滤波算法

    • 卡尔曼滤波:这是一种最优估计算法,特别适合存在随机噪声的系统。它可以根据系统的模型(比如重量的变化通常比较缓慢)和观测值(ADC读数),预测出最可能的重重量值。虽然计算比滑动平均复杂,但在STM32这类MCU上完全可以运行,能极大提升动态称重(比如放上物品瞬间)的稳定性和响应速度。
  3. 抗干扰设计

    • 屏蔽与隔离:将传感器信号线用屏蔽线包裹,屏蔽层单点接地。在信号进入ADC前,可以使用一个由运放构成的“驱动屏蔽层”电路,有效消除电缆电容的影响。
    • 软件陷波器:如果发现示波器上有明显的50Hz/60Hz工频干扰,可以在数字滤波算法中增加一个陷波器,专门滤除这个特定频率的噪声。

做完所有这些,一台由你亲手打造的、显示稳定、反应灵敏、精度可靠的电子秤就真正诞生了。从理解应变片的微小形变,到编写滤波算法平滑数据,每一个环节的打通,都是对“从物理世界到数字世界”这一过程更深一层的理解。这个项目麻雀虽小,五脏俱全,涵盖了模拟电路、数字电路、单片机编程、传感器应用和算法,是一个绝佳的嵌入式系统综合实践。我建议你在基本功能实现后,不妨挑战一下自己,试试增加蓝牙模块连接手机APP记录重量曲线,或者加入电池管理系统做成便携式,乐趣和收获会更多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 20:58:02

SDR多通道同步射频收发套件:从时钟到相位的完整设计实践

前阵子做多通道测向原型验证&#xff0c;被板卡之间采样不同步的问题折腾得够呛&#xff0c;后来干脆自己攒了一套同步射频收发快速原型套件。这套东西的核心思路很简单&#xff1a;把SDR&#xff08;软件无线电&#xff09;里那些分散的射频收发通道&#xff0c;用一套统一的时…

作者头像 李华
网站建设 2026/8/27 20:57:07

具身智能入门:从3D视觉到抓取注意力热图的真实机器人实践

1. 先想清楚“真实机器人”课程里真正要验证的命题是什么 上个月有位师弟来找我聊具身智能的起步设备&#xff0c;问了一个特别常见的问题&#xff1a;预算两三万&#xff0c;是先买机械臂&#xff0c;还是先买一台带深度相机的移动机器人&#xff1f;他的原话是&#xff0c;看…

作者头像 李华
网站建设 2026/8/27 20:55:20

中国海洋大学 2026年夏季《移动软件开发》 实验1:第一个微信小程序

微信小程序进阶之路&#xff1a;我的第一个小程序实战记录 &#x1f4d6; 博客简介 本博客旨在系统性地记录我完成首个微信小程序的学习与实战过程&#xff0c;内容涵盖实验步骤、代码实践、问题排查以及心得体会。 作者&#xff1a;Olivia 所属课程&#xff1a;中国海洋大学…

作者头像 李华
网站建设 2026/8/27 20:52:01

进程与线程:从概念到实践

1. 进程概念程序&#xff1a;存放在外存中的一段代码的集合。进程&#xff1a;是程序动态执行的过程&#xff0c;包括创建、调度、消亡。2. 进程命令top&#xff1a;根据 CPU 占用率查看进程信息。PID&#xff1a;唯一区分进程的 ID 号。PPID&#xff1a;父进程的 ID 号。ps -e…

作者头像 李华
网站建设 2026/8/27 20:50:52

生产代码库中LLM漂移的工程化防治方案

在生产代码库里跑 LLM&#xff0c;最大的麻烦往往不是模型“看不懂代码”&#xff0c;而是“跑着跑着就偏了”。同一个需求&#xff0c;早上的回答还严格遵循代码库现有风格&#xff0c;晚上再问一次&#xff0c;输出的实现方式就已经换了套路&#xff1b;让它基于某个模块做改…

作者头像 李华