☰
基于STM32的智能坐垫毕业设计:从传感器到状态机的嵌入式实践
2026/9/29 21:13:08 网站建设 项目流程

简介:本资源是一套面向高校电子类、自动化及嵌入式方向本科生的毕业设计实战案例,聚焦智能健康硬件开发场景,解决传统坐垫缺乏人因交互与健康干预能力的问题。项目基于STM32F103系列单片机实现温控调节、多点压力坐姿识别、久坐振动提醒及基础人机反馈功能,覆盖从硬件驱动(HAL库)、传感器数据融合、状态机逻辑到低功耗策略的完整嵌入式开发链路。压缩包共84个文件,含33个头文件(h)定义外设接口与模块协议、32个C源文件(c)实现核心控制逻辑与算法,另有KEIL工程文件(uvprojx/uvoptx)、实物清单(xlsx)、演示视频(mp4)、README说明文档(md)及调试配置(dbgconf)等,总大小49.44MB,结构清晰,便于分模块学习与移植。已有55人下载学习,配套坐垫实物演示视频与完整可编译工程,助读者快速理解压力传感阵列布局、PID温控实现、坐姿判据建模等关键技术细节,并为课程设计、竞赛原型开发提供可靠参考。

1. 项目概述与核心价值

最近在整理资料时,翻到了几年前带学生做的一个毕业设计项目,一个基于STM32的智能坐垫控制系统。当时这个项目还挺有意思的,它瞄准了一个我们每天都要面对,却又常常忽略的问题——久坐。无论是程序员、设计师,还是学生,一天在椅子上坐七八个小时是常态,随之而来的腰酸背痛、颈椎不适,甚至更严重的健康隐患,都成了“办公室病”的代名词。这个项目的初衷,就是想用最朴素的单片机技术,做一个能提醒你、甚至能“干预”你久坐行为的智能小装置。

这个智能坐垫,核心功能听起来简单:监测你的坐姿和久坐时长,并在异常时通过震动、灯光等方式提醒你调整。但麻雀虽小,五脏俱全。它涉及了传感器数据采集(压力、姿态)、微控制器核心处理、执行机构驱动(电机、LED)、以及可能的上位机数据交互,是一个典型的嵌入式系统综合应用案例。对于电子信息、自动化、物联网相关专业的同学来说,用它来做毕业设计,既能巩固STM32、C语言、电路设计等核心课程知识,又能接触到完整的“感知-决策-控制”系统闭环,实践性非常强。更重要的是,它的硬件成本可控,软件逻辑清晰,从零开始实现的过程,能让你对嵌入式开发有一个从模糊到清晰的全景认知。

2. 系统整体设计与方案选型

2.1 核心需求与功能定义

在动手画原理图之前,我们必须先明确这个坐垫到底要干什么。经过分析,我们定义了三个核心需求层级:

  1. 基础监测层:这是系统的“眼睛”。需要实时、准确地感知用户是否坐在垫子上、坐了多久,以及大致的坐姿(如前倾、后仰、左倾、右倾)。这直接决定了后续所有逻辑的触发条件。
  2. 智能提醒层:这是系统的“大脑”和“嘴巴”。基于监测数据,系统需要做出判断。例如,连续静坐超过30分钟,判定为“久坐”;压力分布严重不均(如一侧压力远大于另一侧),判定为“坐姿不良”。一旦判定成立,就要通过温和但有效的方式提醒用户。
  3. 交互与扩展层:为了提升体验和可维护性,系统最好能提供一些交互接口。比如,通过按键设置久坐提醒的阈值时间;通过LED灯显示当前状态(正常、警告、严重警告);甚至通过蓝牙模块将坐姿数据和提醒记录发送到手机APP,方便用户长期查看和分析。

2.2 硬件平台选型:为什么是STM32?

提到单片机,很多人会想到51、Arduino、ESP32。我们最终选择STM32F103C8T6(俗称“蓝桥杯”或“最小系统板”的那款)作为主控,是基于以下几点考量:

  • 性能与资源平衡:STM32F103基于ARM Cortex-M3内核,主频72MHz,远超传统的51单片机。这意味着它有足够的算力来处理多路传感器数据滤波、算法判断和定时任务调度。其内置的ADC(模数转换器)、多个定时器、USART串口等外设资源丰富,正好匹配我们的需求。
  • 开发生态成熟:STM32拥有Keil MDK、STM32CubeMX、标准库/HAL库等成熟的开发工具链。特别是STM32CubeMX,可以通过图形化配置快速生成引脚、时钟、外设的初始化代码,极大降低了底层驱动的开发难度,让学生能把精力集中在应用逻辑上。
  • 成本与可获得性:STM32F103系列芯片及其开发板在国内市场保有量巨大,价格低廉,相关资料和开源项目极多。这对于预算有限的毕业设计来说,是至关重要的优势。
  • 学习价值:相比Arduino的封装性,使用STM32进行开发,需要更深入地接触寄存器操作、中断管理、时钟树等底层知识,这对巩固专业基础非常有帮助。

2.3 传感器与执行器选型解析

传感器的选择直接决定了系统的感知精度和成本。

  1. 压力/坐姿检测方案:

    • 方案A:薄膜压力传感器阵列。这是最直观的方案。在坐垫的四个角或特定区域布置多个薄膜压力传感器(如FSR402),通过ADC读取每个传感器的电压值,映射为压力大小。通过比较四个点的压力值,可以粗略判断重心偏移,识别坐姿。优点是原理简单,数据直观。缺点是传感器本身有一定厚度,可能影响坐感;多点布线稍显复杂;长期受压可能存在线性度变化。
    • 方案B:MPU6050姿态传感器。将MPU6050(三轴加速度计+三轴陀螺仪)嵌入坐垫内部。通过解算其姿态角(俯仰角、横滚角),可以直接判断坐垫的倾斜状态,从而反推人的坐姿。优点是集成度高,数据更直接反映“姿态”,且不依赖压力分布。缺点是坐垫本身的形变和人体微动可能带来噪声,需要更复杂的滤波算法(如互补滤波、卡尔曼滤波)来获取稳定角度。
    • 我们的选择:为了兼顾可靠性和学习深度,我们采用了主从结合的方案。使用一个水银开关或低成本角度传感器作为“在位检测”的粗判据(判断是否有人坐下),然后使用两个薄膜压力传感器分别布置在坐垫左右侧,进行坐姿平衡的细粒度判断。这样既降低了系统复杂度,又实现了核心功能。
  2. 提醒执行机构选型:

    • 震动马达:这是最有效的触觉提醒方式。我们选用普通的扁平硬币震动马达(型号310)即可。STM32通过一个三极管(如S8050)或MOS管驱动电路来控制其开关,通过PWM可以调节震动强度。
    • RGB LED灯:用于视觉状态指示。例如,绿色常亮表示正常,黄色闪烁表示轻度提醒(坐姿歪了),红色闪烁+震动表示严重提醒(久坐超时)。WS2812B这种单总线控制的智能RGB灯珠,一个IO口就能驱动一串,非常方便。
    • 蜂鸣器:用于声音提醒。但考虑到办公室或图书馆等安静环境,声音提醒可能造成干扰,因此我们将其作为可选或可配置的提醒方式。

2.4 系统架构框图

整个系统的信号流可以概括为:

[薄膜压力传感器*2 + 水银开关] --> [信号调理电路] --> [STM32的ADC/GPIO引脚] | V [STM32F103C8T6 主控] | |<--[按键输入] |<--[蓝牙模块(可选)] | V [震动马达驱动电路] [RGB LED] [蜂鸣器(可选)]

这个框图清晰地展示了数据从采集、处理到输出的完整路径。

3. 硬件电路设计与核心细节

3.1 主控最小系统与电源设计

任何单片机系统,稳定可靠的电源是基石。我们采用常见的USB供电(5V),通过一颗AMS1117-3.3V线性稳压芯片,为整个系统提供稳定的3.3V工作电压。STM32、传感器、LED等均工作在此电压下。

注意:AMS1117虽然常用,但其压差(Dropout Voltage)相对较大。当USB供电电压因线损降到4.5V以下时,3.3V输出可能不稳。如果对稳定性要求高,可以选用压差更低的LDO,如SPX3819。同时,在电源入口和芯片的VCC附近,务必放置一个100uF的电解电容和一个0.1uF的瓷片电容进行退耦,以滤除高频和低频噪声。

STM32最小系统包括复位电路(10k上拉电阻+0.1uF电容到地)、boot启动模式选择电路(BOOT0/BOOT1引脚通过跳线帽选择)、以及外部8MHz晶振和两个负载电容(通常22pF)组成的时钟电路。使用STM32CubeMX生成代码时,它会根据你选择的晶振频率自动配置时钟树,这部分非常省心。

3.2 传感器信号调理电路

薄膜压力传感器(FSR)的电阻值随压力增大而减小。我们需要将其电阻变化转换为STM32 ADC可以读取的电压变化。最常用的方法是构建一个分压电路。

VCC (3.3V) --- FSR --- ADC_IN --- 固定电阻(如10kΩ) --- GND

固定电阻(R_fixed)与FSR串联。ADC_IN引脚测量固定电阻两端的电压。当压力增大(FSR电阻减小)时,固定电阻分得的电压升高,ADC读取值变大。这个固定电阻的阻值选择很重要,它决定了测量范围的灵敏度和线性区间。通常需要根据FSR的阻值范围(例如无压力时>1MΩ,最大压力时约几百欧姆)来选取一个折中的值,比如10kΩ,使得在典型压力下,电压变化范围能覆盖ADC量程的大部分。

实操心得:FSR的输出非线性且存在迟滞。对于坐姿判断这种不需要绝对压力值的应用,我们更关心的是相对变化。因此,可以在软件中采用“标定”的方法:让用户以标准坐姿坐下,单片机记录下此时左右两个传感器的ADC值作为基准。后续通过比较实时值与基准值的偏差百分比来判断坐姿是否失衡。这比试图用复杂公式拟合电阻-压力曲线要简单有效得多。

水银开关或滚珠开关直接接一个上拉电阻(如10kΩ)到3.3V,中间点接STM32的GPIO引脚并设置为上拉输入模式。当开关水平放置(无人就坐)时,电路断开,GPIO读到高电平;当坐垫受压倾斜(有人就坐)时,开关导通,GPIO读到低电平。这是一个简单可靠的数字信号检测。

3.3 执行机构驱动电路

STM32的GPIO引脚驱动能力有限(通常单个引脚输出电流在20mA左右),无法直接驱动震动马达(工作电流可能超过100mA)或多个LED。因此需要驱动电路。

  1. 震动马达驱动:最经济简单的方案是使用NPN三极管(如S8050)作为开关。

    STM32_GPIO --- 1kΩ电阻 --- 三极管基极(B) | 发射极(E) --- GND | 集电极(C) --- 震动马达负极 震动马达正极 --- VCC (3.3V或5V)

    当GPIO输出高电平时,三极管导通,马达两端形成压差,开始震动。1kΩ的基极电阻用于限制基极电流,保护GPIO口。如果马达工作电压是5V,而STM32是3.3V系统,需要确保三极管能承受5V的Vce电压。也可以使用逻辑电平的MOS管(如2N7002),其驱动更简单(几乎不需要驱动电流),效率更高。

  2. RGB LED驱动:如果使用普通LED,每个颜色通道串联一个限流电阻(如220Ω-1kΩ,根据亮度调整)后直接接GPIO即可。如果使用WS2812B,则只需要一个GPIO口接其数据输入(DIN),按照严格的单总线时序发送数据包,即可控制其颜色和亮度。STM32的普通IO口模拟时序需要关闭中断以保证精度,或者使用SPI的MOSI口配合DMA来模拟,后者更高效稳定。

3.4 PCB设计注意事项

如果项目要求制作PCB,以下几点需要特别关注:

  • 布局:电源模块、单片机、驱动电路应分区布局。模拟部分(传感器分压电路)和数字部分(单片机、LED)尽量分开,地线在一点连接(单点接地),以减少数字噪声对模拟信号的干扰。
  • 走线:电源线和地线要尽量粗。晶振电路要紧靠单片机相关引脚,走线短且粗,下方避免走其他信号线。
  • 接口:为传感器、马达、电源等预留接插件(如XH2.54排针),方便调试和组装。务必给所有外部接口加上ESD保护器件或至少预留TVS管的位置。

4. 软件系统设计与代码实现

4.1 开发环境搭建与工程创建

我们使用Keil MDK作为IDE,并利用STM32CubeMX进行初始化配置。步骤如下:

  1. 打开CubeMX,选择MCU型号为STM32F103C8Tx。
  2. 引脚配置:在图形界面上,将需要用到的引脚功能一一指定。
    • ADC1_IN0, ADC1_IN1:分配给两个薄膜压力传感器。
    • 一个GPIO输入(上拉):分配给水银开关。
    • 几个GPIO输出:分配给震动马达控制、LED控制、蜂鸣器控制。
    • 一个USART:如果使用蓝牙模块(如HC-05),则配置一个串口,波特率通常为9600或115200。
    • SYS->Debug: 选择Serial Wire,这是ST-Link调试接口。
  3. 外设配置:
    • ADC:启用ADC1,将通道0和1设置为规则组,配置为连续转换模式(Continuous Conversion Mode),使能DMA(直接存储器访问)。这样ADC可以自动、不间断地将两个通道的数据搬运到指定的内存数组中,无需CPU频繁干预,大大提高了效率。
    • TIM:启用一个基本定时器(如TIM2),用于产生系统时基,例如1ms的中断,用于实现精准的延时和计时功能(如久坐30分钟倒计时)。
  4. 生成代码,选择MDK-ARM V5作为工具链。

4.2 主程序逻辑与状态机设计

软件的核心是一个状态机,它使程序逻辑清晰,易于维护和扩展。我们定义几个主要状态:

  • STATE_IDLE:空闲状态。无人就坐,系统低功耗运行(可以关闭部分外设),LED显示待机颜色(如蓝色呼吸)。
  • STATE_NORMAL:正常就坐状态。检测到有人坐下,且坐姿平衡。启动久坐计时器,LED显示绿色。
  • STATE_WARNING_POSTURE:坐姿警告状态。检测到左右压力偏差超过阈值(如20%),但未达到久坐时间。触发轻度提醒(如单侧LED黄色闪烁)。
  • STATE_WARNING_DURATION:久坐警告状态。连续就坐时间超过设定阈值(如25分钟)。触发中度提醒(如所有LED橙色慢闪)。
  • STATE_ALERT:严重警告状态。连续就坐时间超过最大阈值(如30分钟)或坐姿严重失衡。触发强烈提醒(震动马达启动,LED红色快闪)。

主循环中,根据传感器输入和定时器标志,进行状态转移判断。例如:

// 伪代码示例 switch(current_state) { case STATE_IDLE: if (seat_occupied == TRUE) { calibrate_sensor(); // 就坐瞬间进行传感器标定,获取基准值 start_duration_timer(); current_state = STATE_NORMAL; } break; case STATE_NORMAL: if (posture_imbalance_detected()) { current_state = STATE_WARNING_POSTURE; } else if (duration_warning_threshold_reached()) { current_state = STATE_WARNING_DURATION; } else if (seat_occupied == FALSE) { stop_duration_timer(); reset_duration_counter(); current_state = STATE_IDLE; } break; // ... 其他状态转移 }

在每个状态中,执行相应的输出控制(LED显示模式、震动开关)。

4.3 传感器数据处理与滤波算法

直接从ADC读取的原始数据噪声很大,必须进行滤波。

  1. ADC数据读取:由于配置了DMA,我们只需在程序中定义一个数组adc_buffer[2],DMA会自动将通道0和1的结果循环填入。在主循环或定时中断中,直接读取这个数组即可获得最新的转换值。

  2. 软件滤波:最简单的也是效果不错的办法是滑动平均滤波。

    #define FILTER_LEN 10 uint32_t adc_raw_history[2][FILTER_LEN] = {0}; uint8_t history_index = 0; // 在定时中断(如10ms一次)中执行 void Timer_IRQ_Handler() { adc_raw_history[0][history_index] = adc_buffer[0]; adc_raw_history[1][history_index] = adc_buffer[1]; history_index = (history_index + 1) % FILTER_LEN; // 计算平均值 uint32_t sum_left = 0, sum_right = 0; for(int i=0; i<FILTER_LEN; i++) { sum_left += adc_raw_history[0][i]; sum_right += adc_raw_history[1][i]; } filtered_adc_left = sum_left / FILTER_LEN; filtered_adc_right = sum_right / FILTER_LEN; }

    滑动平均能有效平滑随机噪声。滤波窗口大小FILTER_LEN需要权衡:越大越平滑,但响应越慢。对于坐姿检测,10~20的窗口通常合适。

  3. 坐姿判断逻辑:使用标定后的基准值base_left,base_right。

    // 计算当前压力与基准值的偏差百分比 int32_t diff_left = abs(filtered_adc_left - base_left); int32_t diff_right = abs(filtered_adc_right - base_right); float imbalance_ratio = (float)abs(diff_left - diff_right) / (base_left + base_right); if (imbalance_ratio > THRESHOLD_SERIOUS) { // 例如 THRESHOLD_SERIOUS = 0.3 posture_status = POSTURE_BAD_SERIOUS; } else if (imbalance_ratio > THRESHOLD_LIGHT) { // 例如 THRESHOLD_LIGHT = 0.15 posture_status = POSTURE_BAD_LIGHT; } else { posture_status = POSTURE_GOOD; }

4.4 定时器应用与久坐计时

我们需要一个精准的计时器来统计就坐时间。利用CubeMX配置的1ms定时器中断(TIM2)非常合适。

volatile uint32_t sit_duration_ms = 0; // 就坐持续时间(毫秒) volatile uint8_t timer_1s_flag = 0; // 1秒标志位 void TIM2_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET) { __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); if (current_state != STATE_IDLE) { sit_duration_ms++; if (sit_duration_ms % 1000 == 0) { // 每1000ms=1秒 timer_1s_flag = 1; } } } }

在主循环中检查timer_1s_flag,将其置位后,可以更新一个“秒计数器”,并判断是否达到设定的久坐阈值(如30分钟 = 1800秒)。

4.5 蓝牙通信与上位机交互(可选扩展)

如果添加了HC-05蓝牙模块,可以将数据发送到手机,实现更丰富的功能。

  1. 硬件连接:HC-05的TXD接STM32的RX引脚,RXD接TX引脚,VCC接3.3V或5V,GND接地。
  2. 软件协议:定义简单的文本协议,便于调试。例如,每秒发送一次数据:
    if (timer_1s_flag && uart_tx_idle) { sprintf(uart_tx_buffer, "L:%d,R:%d,T:%d,S:%d\r\n", filtered_adc_left, filtered_adc_right, (int)(sit_duration_ms/1000), current_state); HAL_UART_Transmit(&huart1, (uint8_t*)uart_tx_buffer, strlen(uart_tx_buffer), 100); timer_1s_flag = 0; }
  3. 手机端:可以使用串口调试助手APP(如“蓝牙串口”)直接接收查看,或者使用App Inventor、Android Studio等开发一个简单的APP,解析数据并绘制坐姿压力曲线图、久坐时间统计图等。

5. 系统调试、问题排查与优化

5.1 硬件调试常见问题

  1. 电源问题:系统不工作或工作不稳定。首先用万用表测量3.3V和5V电压是否准确、稳定。检查AMS1117是否发烫,发烫可能是后级短路或负载过重。
  2. 传感器无读数:
    • 检查接线:确认FSR、分压电阻、ADC引脚的连接正确无误。
    • 检查ADC配置:在CubeMX和代码中,确认ADC通道、采样时间、DMA配置正确。可以用杜邦线将ADC引脚接到已知电压(如通过电阻分压得到的1.65V),看读取值是否约为4096/2=2048(12位ADC,3.3V参考电压)。
    • 检查滤波算法:原始ADC值是否在合理范围内变化?滤波后的值是否稳定?
  3. 执行机构不动作:
    • 驱动电路:用万用表测量驱动三极管或MOS管的控制极(基极/栅极)电压,当GPIO输出高电平时,是否达到导通电压?测量执行机构两端电压是否正常。
    • GPIO配置:确认CubeMX中将控制引脚配置为推挽输出(Push-Pull Output),而不是开漏输出。

5.2 软件调试与逻辑验证

  1. 利用串口打印:这是嵌入式调试最强大的工具。在关键逻辑分支、状态切换处、传感器数据读取后,通过串口(可以是USB转TTL,也可以是蓝牙)将变量值打印出来。这能让你清晰地看到程序的实际运行流程和数据变化。

    printf("State: %d, LeftADC: %d, RightADC: %d\r\n", current_state, filtered_adc_left, filtered_adc_right);

    注意:在STM32上使用printf需要重定向fputc函数到串口。同时,避免在高速循环或中断中频繁调用printf,可能导致程序阻塞或数据丢失。可以设置一个标志位,在主循环中条件打印。

  2. 逻辑分析仪或示波器:对于调试WS2812B的时序、PWM输出波形、中断响应时间等,逻辑分析仪是神器。它可以直观地显示信号的电平和时间关系,快速定位时序错误。

  3. 状态机逻辑错误:这是最容易出bug的地方。务必画出详细的状态转移图,并在每个状态入口和出口打印日志,确保转移条件符合预期。特别注意那些可能同时满足多个转移条件的情况,要明确优先级。

5.3 性能优化与体验提升

  1. 低功耗优化:在STATE_IDLE下,可以关闭ADC、部分定时器、降低主频,甚至让STM32进入睡眠模式(Stop Mode),通过水银开关的外部中断来唤醒。这能显著延长电池供电时的使用时间。
  2. 提醒策略优化:简单的定时震动可能让人烦躁。可以设计更人性化的策略:久坐25分钟时,LED温和闪烁;30分钟时,轻微震动一次;35分钟时,震动强度和时间增加。坐姿提醒也可以分级,轻度失衡时仅亮灯,严重失衡时才震动。
  3. 参数自适应:不同体重、不同坐姿习惯的人,压力基准值差异很大。可以增加一个“学习模式”,长按按键进入,让用户以最舒适的姿势静坐10秒,系统自动记录此时的压力值作为个人化基准。
  4. 增加反馈机制:在用户调整坐姿后,如果系统检测到压力恢复平衡,可以给出一个积极的反馈,比如LED快速闪烁两下绿色,让用户有成就感。

6. 项目总结与进阶思考

回顾整个智能坐垫控制系统的实现,它完美地串联了模拟电路(传感器分压)、数字电路(单片机最小系统、驱动)、嵌入式软件(外设驱动、状态机、滤波算法)等多个知识点。从需求分析、方案选型、电路设计、PCB绘制、焊接调试,到软件编程、算法实现、系统联调,完成这样一个项目,无异于经历了一次微型的产品研发全流程。

对于毕业设计而言,这个项目提供了充足的发挥空间和深度挖掘的可能性。基础版本完成后,你可以从以下几个方向进行深化,提升论文和答辩的亮点:

  • 算法升级:引入更高级的姿态解算算法(如Mahony滤波、卡尔曼滤波)来处理MPU6050数据,实现更精准的3D姿态判断。
  • 无线充电:为坐垫增加无线充电接收线圈(Qi标准),实现无接触充电,提升产品感。
  • 机器学习边缘计算:如果使用更高级的MCU(如STM32H7系列或ESP32),可以尝试集成TinyML框架,在设备端运行简单的神经网络模型,识别“瘫坐”、“翘二郎腿”、“前倾打字”等更复杂的坐姿模式。
  • 云平台对接:通过ESP32的Wi-Fi模块,将久坐数据上传到云平台(如阿里云IoT、ThingsBoard),实现多设备数据汇总、长期健康趋势分析、甚至团队久坐排行榜等社交功能。

这个项目最让我有成就感的地方,不在于它用了多高深的技术,而在于它用切实可行的工程方法,解决了一个真实的小问题。当看到第一个原型机成功地在久坐后“嗡嗡”震动起来时,那种从无到有、让想法落地的快乐,正是工程学的魅力所在。希望这个详细的拆解,能为你点亮一盏从课堂理论走向实践项目的灯。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询