news 2026/5/28 12:16:51

基于树莓派与RFID的智能药品分发器DIY全流程解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于树莓派与RFID的智能药品分发器DIY全流程解析

1. 项目概述与核心价值

作为一名长期混迹于创客社区和嵌入式开发领域的“老鸟”,我经手过不少智能家居和健康监测项目。最近,我完成了一个让我自己都觉得很实用的作品:一个基于树莓派的自动药品分发器,我给它起名叫“DailyDose”。这个项目的初衷很简单,就是为了解决我自己和家人经常忘记按时吃药,或者搞不清该吃哪种药的麻烦事。市面上虽然有一些智能药盒,但要么功能单一,要么价格昂贵,而且总觉得少了点“自己动手,丰衣足食”的乐趣和完全按需定制的灵活性。

这个“DailyDose”智能药盒,核心就是利用树莓派作为大脑,配合RFID进行身份认证,再加上传感器和舵机,实现一个安全、可控的自动发药流程。它不仅仅是“到点就响”的提醒器,而是真正做到了“认证-检测-分发-确认”的闭环管理。想象一下,你只需要刷一下专属的卡片或钥匙扣,药盒检测到杯子已就位,就会自动转动内部的药仓,将一次剂量的药片精准投递到你的杯子里,然后你需要按一下确认按钮,系统才会记录“已服药”。整个过程,既避免了误操作,也留下了可追溯的记录。

对于有慢性病需要长期服药的患者、记忆力减退的老年人,或者只是单纯想规范自己用药习惯的年轻人来说,这样一个DIY方案的价值是显而易见的。它提升了用药依从性,减少了因遗忘或错拿导致的风险,并且通过物联网技术(虽然本项目侧重于本地控制,但留有扩展接口),为未来的远程监护或数据统计提供了可能。接下来,我就把这套从硬件选型、结构设计、代码编写到调试上线的完整过程,毫无保留地拆解给你看。

2. 系统整体设计与思路拆解

在动手之前,清晰的顶层设计是避免后期反复折腾的关键。这个自动药品分发器虽然不算极度复杂,但涉及硬件联动、逻辑控制和用户体验,必须想清楚再动手。

2.1 核心需求与功能定义

首先,我们要明确这个盒子到底要干什么。我把它归纳为四个核心功能和两个安全边界:

  1. 身份认证:发药是一个需要权限的操作,不能谁都能按。我选择了RFID读卡器,为每位用户配置一张专属卡片。这比密码输入更便捷,也比指纹(在潮湿或特定环境下)更稳定、成本更低。
  2. 环境检测:发药前必须确保“药有去处”。我使用了一个超声波测距传感器,安装在出药口下方,用于检测杯子是否放置到位。没有杯子,坚决不发药,防止药片滚落丢失。
  3. 可控分发:这是机械部分的核心。我需要一个能将存储的药片一次一份、可靠地推送出来的机构。经过权衡,我选择了舵机驱动的旋转式药仓。每个仓格存放一次剂量,舵机旋转固定角度,将对应仓格的药片倒入滑道。
  4. 服药确认与记录:药片掉落后,系统需要用户一个明确的“我已取走并服用”的反馈。我设置了一个独立的“确认按钮”。只有按下它,本次发药流程才算正式完成,系统可以记录一次成功的服药事件。这个设计避免了药片已出但用户未及时取走造成的“已发药未服用”的状态错乱。

安全边界

  • 硬件急停:设置一个独立的物理电源开关,在任何软件逻辑失效时,可以立即切断系统电源,确保安全。
  • 软件互锁:在代码逻辑上,确保各个步骤严格顺序执行,并设置状态超时。例如,RFID认证成功后,如果在30秒内未检测到杯子,则流程重置,需要重新认证。

2.2 硬件方案选型与考量

为什么是这些零件?每个选择背后都有原因:

  • 主控:树莓派 3B+:没选用更便宜的微控制器(如Arduino),是因为我预见到后期可能需要添加网络功能(如用药记录上传、远程提醒)、连接小型显示屏或处理更复杂的调度逻辑。树莓派的Linux环境和丰富的GPIO、USB接口,为未来扩展留足了空间。对于这个项目,树莓派Zero 2W其实也够用,性价比更高。
  • 身份认证:RC522 RFID读卡器模块:这是最经典、资料最多的13.56MHz RFID读卡方案,价格低廉,与树莓派通过SPI接口通信,稳定可靠。卡片可定制,成本几乎可以忽略。
  • 检测单元:HC-SR04超声波传感器:用于非接触式距离测量。安装在出药口下方约5-8厘米处,当检测到距离小于一个设定阈值(例如3厘米)时,就认为杯子已放好。选择它是因为其原理简单、精度对本案足够,且价格便宜。
  • 执行机构:SG90舵机:这是一个9克微型舵机,扭矩适中(1.6kg/cm),足以驱动我设计的小型药仓旋转。通过PWM信号控制旋转角度,定位精确。我需要计算好药仓分格数量与舵机旋转角度的关系。
  • 人机交互:轻触开关与LED灯带:两个轻触开关分别作为“发药请求按钮”和“服药确认按钮”。一个WS2812B可寻址RGB LED灯带用于状态指示(例如:待机蓝色、认证成功绿色、错误红色、发药中黄色等),比简单的单色LED能传达更多信息。
  • 电源管理:整个系统由一块5V/2A的USB电源适配器供电。树莓派和舵机对电流需求较大,尤其是舵机动作瞬间。这里有个关键点:务必使用外部电源(如手机充电器)通过树莓派的USB-C口供电,绝对不要试图通过GPIO的5V引脚为树莓派供电,电流和稳定性都不够,极易导致树莓派重启或损坏。

2.3 机械结构与外壳设计思路

原作者的木制外壳方案很有质感,但制作门槛较高。我的设计思路更偏向于模块化和可重复制作,核心是药仓机构

我设计了一个圆柱形的药仓,用3D打印制作。圆柱侧面均匀开设了6个或8个扇形仓格(具体数量取决于每日服药次数)。药仓中心轴与舵机的输出轴固定。舵机旋转0度时,第一个仓格对准上方的入药口(用于装药)和下方的出药口滑道。当需要发药时,树莓派控制舵机旋转360 / 仓格数量度,让下一个满载的仓格对准出药口,同时空的仓格对准入药口,实现循环。

外壳的作用是包裹所有电子部件和机械部件,提供结构支撑、防尘和美观。我采用激光切割亚克力板的方式来制作,设计图可以用Fusion 360或Inkscape绘制。亚克力板易于加工,透明或半透明的材质还能让你看到内部工作状态,很有科技感。面板上需要预留RFID读卡区、按钮孔、超声波传感器孔、出药口和电源开关孔。

注意:药仓的设计是成败关键。仓格的大小需要根据你常服用的最大药片尺寸(包括胶囊)来定,并留有余量。滑道的倾角要足够大(建议>45度),确保药片能依靠重力顺利滑落,不会卡住。可以在滑道内壁粘贴特氟龙胶带或打磨得非常光滑以减少摩擦。

3. 硬件连接与电路搭建详解

有了方案,接下来就是“搬砖”环节。正确的电路连接是系统稳定的基础。

3.1 树莓派GPIO引脚分配规划

树莓派有40个GPIO针脚,但并非所有都能随意使用。我们需要合理分配,避免冲突(如复用同一组SPI引脚)。下面是我的引脚分配表:

组件连接树莓派引脚功能说明物理引脚 (BCM编码)
RC522 (SDA)GPIO 8 (CE0)SPI片选0物理引脚24
RC522 (SCK)GPIO 11 (SCLK)SPI时钟物理引脚23
RC522 (MOSI)GPIO 10 (MOSI)SPI主出从入物理引脚19
RC522 (MISO)GPIO 9 (MISO)SPI主入从出物理引脚21
RC522 (IRQ)未连接中断,本项目未用-
RC522 (GND)任意GND接地如物理引脚6, 9, 14, 20等
RC522 (RST)GPIO 25复位引脚物理引脚22
RC522 (3.3V)3.3V Power供电物理引脚1或17
HC-SR04 (Trig)GPIO 23触发测距信号物理引脚16
HC-SR04 (Echo)GPIO 24接收回波信号物理引脚18
HC-SR04 (Vcc)5V Power供电物理引脚2或4
HC-SR04 (GND)任意GND接地同上
SG90舵机 (信号)GPIO 18 (PWM0)PWM控制信号物理引脚12
SG90舵机 (Vcc)外部5V电源重要!勿接树莓派5V-
SG90舵机 (GND)外部电源GND & 树莓派GND共地-
发药按钮GPIO 17输入,内部上拉物理引脚11
确认按钮GPIO 27输入,内部上拉物理引脚13
WS2812B灯带 (Din)GPIO 21 (PWM)数据输入物理引脚40
WS2812B灯带 (5V)外部5V电源供电,电流需求大-
WS2812B灯带 (GND)外部电源GND & 树莓派GND共地-

接线实操要点与避坑指南:

  1. 电平匹配:RC522是3.3V器件,必须连接树莓派的3.3V引脚,其IO口也是3.3V电平,与树莓派GPIO直接兼容。
  2. 电源隔离与共地:这是最容易出问题的地方。舵机和LED灯带工作时电流可能瞬间很大(尤其是多个LED同时亮起或舵机堵转时),如果直接从树莓派的5V引脚取电,极易引起电压骤降,导致树莓派重启。正确做法:使用一个独立的5V/2A以上的电源适配器,其正极(5V)同时接给舵机和LED灯带的VCC,其负极(GND)必须与树莓派的GND(例如物理引脚39)连接在一起,即“共地”。树莓派自身仍由其专用的USB-C电源供电。
  3. 上拉电阻:两个按钮我配置为使用树莓派GPIO的内部上拉电阻。在代码中,将引脚模式设置为GPIO.IN,并启用内部上拉pull_up_down=GPIO.PUD_UP。这样按钮一端接GPIO,另一端接地。当按钮未按下时,GPIO读到高电平(1);按下时,GPIO被拉到地,读到低电平(0),避免了外部接电阻的麻烦。
  4. PWM引脚:控制舵机需要硬件PWM以获得稳定信号,树莓派GPIO 12、13、18、19支持硬件PWM。我选择了GPIO 18。
  5. 焊接与整理:建议使用杜邦线连接面包板进行原型测试。确认所有功能正常后,再使用排针、排母和导线进行焊接,制作一个整洁的转接板或直接焊接在洞洞板上。良好的线材整理和固定能极大提升系统可靠性,防止因拉扯导致接触不良。

3.2 原型测试与分步验证

不要一次性接好所有线再上电测试!分模块验证是高效排错的不二法门。

  1. 树莓派基础系统:先单独给树莓派上电,确保能正常启动,SSH或屏幕输出正常。
  2. 测试RFID:只连接RC522模块,编写一个简单的Python脚本,循环读取卡片ID并打印到终端。验证SPI接口是否已启用(可通过raspi-config配置),以及接线是否正确。
  3. 测试超声波传感器:单独连接HC-SR04,编写测距代码,在终端打印实时距离。用手在传感器前移动,观察数值变化是否灵敏、合理。
  4. 测试舵机:连接舵机(注意电源!),编写代码让舵机在0度和180度之间往复运动。观察转动是否平滑,有无异响。
  5. 测试按钮与LED:连接按钮和LED灯带,编写代码测试按钮按下触发和LED颜色变化。

每个模块都独立工作正常后,再将它们组合起来,进行集成逻辑测试。

4. 软件逻辑与核心代码实现

硬件是躯体,软件是灵魂。整个系统的智能都体现在Python代码的逻辑中。

4.1 开发环境与依赖库安装

我使用树莓派官方Raspbian系统,并通过SSH进行远程开发。首先安装必要的Python库:

sudo apt update sudo apt install python3-pip sudo pip3 install RPi.GPIO # GPIO控制 sudo pip3 install spidev # SPI通信(用于RFID) sudo pip3 install mfrc522 # RFID库 sudo pip3 install rpi_ws281x # WS2812B LED灯带驱动 sudo pip3 install gpiozero # 可选的更高层GPIO库,这里我们主要用RPi.GPIO

对于超声波传感器,我们使用RPi.GPIO库直接操作IO口进行时序控制。

4.2 主程序逻辑流程图与状态机

程序的核心是一个状态机,它定义了系统在不同条件下如何切换行为。我将其设计为以下几个主要状态:

  1. IDLE (待机):系统启动后的初始状态。LED显示蓝色呼吸灯效。等待RFID认证。
  2. AUTHENTICATED (已认证):成功刷入授权卡片。LED变为绿色常亮。此时“发药按钮”被激活。
  3. CUP_CHECKING (检测杯子):用户按下“发药按钮”。LED变为黄色闪烁。系统触发超声波传感器,检查下方是否有杯子。
  4. DISPENSING (分发中):检测到杯子。LED变为黄色常亮。控制舵机旋转预定角度,完成一次发药动作。完成后进入等待确认状态。
  5. CONFIRMATION_WAITING (等待确认):LED变为紫色闪烁。等待用户按下“确认按钮”。如果超时(如60秒),则系统复位到IDLE状态,并记录一次“未确认”事件(可闪烁红灯报警)。
  6. COMPLETED (完成):用户按下“确认按钮”。LED快速闪烁绿色三次,然后恢复IDLE状态。记录一次成功的服药事件。

4.3 关键代码模块解析

以下是核心代码片段的讲解,完整代码可在文末的GitHub链接中找到。

1. 初始化与引脚设置

import RPi.GPIO as GPIO import time from mfrc522 import SimpleMFRC522 from rpi_ws281x import PixelStrip, Color import threading # 引脚定义 (使用BCM编码) PIN_BUTTON_DISPENSE = 17 PIN_BUTTON_CONFIRM = 27 PIN_TRIG = 23 PIN_ECHO = 24 PIN_SERVO = 18 LED_COUNT = 8 # LED灯珠数量 LED_PIN = 21 LED_FREQ_HZ = 800000 LED_DMA = 10 LED_BRIGHTNESS = 100 LED_INVERT = False # 初始化GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(PIN_BUTTON_DISPENSE, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(PIN_BUTTON_CONFIRM, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(PIN_TRIG, GPIO.OUT) GPIO.setup(PIN_ECHO, GPIO.IN) # 初始化RFID读卡器 reader = SimpleMFRC522() # 初始化LED灯带 strip = PixelStrip(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS) strip.begin() # 初始化舵机PWM GPIO.setup(PIN_SERVO, GPIO.OUT) servo_pwm = GPIO.PWM(PIN_SERVO, 50) # 50Hz频率 servo_pwm.start(0) # 初始占空比0,舵机不会动 # 预定义授权卡片ID列表 (实际使用时,通过一个学习程序获取并存入) AUTHORIZED_CARDS = [123456789012, 987654321098] # 替换为你的实际卡片ID # 系统状态变量 current_state = "IDLE" current_user_card_id = None dispense_timer = None

2. 超声波测距函数

这个函数负责驱动HC-SR04并计算距离。

def get_distance(): """使用HC-SR04超声波传感器测量距离,返回厘米值""" # 确保触发引脚先输出低电平 GPIO.output(PIN_TRIG, False) time.sleep(0.000002) # 2微秒延迟,确保稳定 # 发送10微秒的高脉冲触发信号 GPIO.output(PIN_TRIG, True) time.sleep(0.00001) # 10微秒 GPIO.output(PIN_TRIG, False) # 等待回波引脚变为高电平,记录开始时间 start_time = time.time() timeout = start_time + 0.04 # 设置40ms超时(对应约6.8米) while GPIO.input(PIN_ECHO) == 0 and start_time < timeout: start_time = time.time() if start_time >= timeout: return -1 # 超时,返回错误 # 回波引脚高电平结束,记录结束时间 stop_time = time.time() timeout = stop_time + 0.04 while GPIO.input(PIN_ECHO) == 1 and stop_time < timeout: stop_time = time.time() if stop_time >= timeout: return -1 # 超时,返回错误 # 计算时间差,声速取34300 cm/s,除以2(往返距离) elapsed_time = stop_time - start_time distance = (elapsed_time * 34300) / 2 # 过滤掉过近或过远的无效值(根据实际安装高度调整) if distance < 2 or distance > 20: return -1 return round(distance, 1)

3. 舵机控制函数

控制SG90舵机旋转到指定角度。SG90的PWM控制周期为20ms(50Hz),脉宽0.5ms到2.5ms对应0到180度。

def set_servo_angle(angle): """控制舵机旋转到指定角度(0-180度)""" # 将角度转换为占空比 # 0.5ms脉冲对应0度,2.5ms脉冲对应180度,占空比 = 脉冲时间 / 周期(20ms) duty_cycle = (0.5 + (angle / 180.0) * 2) / 20.0 * 100 servo_pwm.ChangeDutyCycle(duty_cycle) time.sleep(0.5) # 给舵机足够时间转动到位 servo_pwm.ChangeDutyCycle(0) # 停止发送PWM信号,防止舵机抖动和过热

4. 主状态机循环

这是程序的核心,在一个while True循环中不断检查状态并执行相应操作。

def main_loop(): global current_state, current_user_card_id, dispense_timer try: while True: if current_state == "IDLE": led_breathing(Color(0, 0, 255)) # 蓝色呼吸 # 检测RFID卡片 card_id, _ = reader.read_no_block() # 非阻塞读取 if card_id in AUTHORIZED_CARDS: print(f"认证成功!卡片ID: {card_id}") current_user_card_id = card_id current_state = "AUTHENTICATED" led_set_color(Color(0, 255, 0)) # 绿色常亮 elif current_state == "AUTHENTICATED": # 检查发药按钮是否被按下(低电平触发) if GPIO.input(PIN_BUTTON_DISPENSE) == GPIO.LOW: time.sleep(0.05) # 简单消抖 if GPIO.input(PIN_BUTTON_DISPENSE) == GPIO.LOW: print("发药请求已接收,开始检测杯子...") current_state = "CUP_CHECKING" led_blink(Color(255, 255, 0), 0.3) # 黄色闪烁 elif current_state == "CUP_CHECKING": distance = get_distance() if distance != -1 and distance < CUP_DETECTION_THRESHOLD: # 例如阈值设为5cm print(f"杯子检测成功!距离: {distance}cm") current_state = "DISPENSING" led_set_color(Color(255, 255, 0)) # 黄色常亮 else: print("未检测到杯子,请放置杯子。") # 可以在此处添加超时逻辑,比如10秒后仍未检测到杯子则返回IDLE状态 elif current_state == "DISPENSING": print("正在分发药品...") # 控制舵机旋转到下一个药仓位置 # 这里需要根据你的药仓设计计算角度。假设8个仓格,每次转45度。 set_servo_angle(current_dispense_angle) current_dispense_angle = (current_dispense_angle + 45) % 360 # 更新下一次角度 time.sleep(1) # 等待药片滑落 print("分发完成,请取药并确认。") current_state = "CONFIRMATION_WAITING" led_blink(Color(255, 0, 255), 0.5) # 紫色闪烁 # 启动确认超时计时器(在另一个线程中) dispense_timer = threading.Timer(60.0, confirmation_timeout) # 60秒超时 dispense_timer.start() elif current_state == "CONFIRMATION_WAITING": # 检查确认按钮 if GPIO.input(PIN_BUTTON_CONFIRM) == GPIO.LOW: time.sleep(0.05) if GPIO.input(PIN_BUTTON_CONFIRM) == GPIO.LOW: print("服药已确认。") if dispense_timer: dispense_timer.cancel() current_state = "COMPLETED" elif current_state == "COMPLETED": # 成功反馈 led_success_animation() # 记录日志(这里可以写入文件或数据库) log_event(current_user_card_id, "MEDICATION_CONFIRMED") # 重置状态 current_user_card_id = None current_state = "IDLE" time.sleep(0.1) # 主循环延迟,降低CPU占用 except KeyboardInterrupt: print("程序被用户中断") finally: cleanup() def confirmation_timeout(): """确认超时回调函数""" global current_state, current_user_card_id print("确认超时!") current_state = "IDLE" current_user_card_id = None led_set_color(Color(255, 0, 0)) # 红色报警 time.sleep(3) # 记录超时事件 log_event(None, "CONFIRMATION_TIMEOUT")

5. 辅助函数(LED控制、日志等)

def led_set_color(color): """设置所有LED为同一颜色""" for i in range(strip.numPixels()): strip.setPixelColor(i, color) strip.show() def led_blink(color, interval): """LED闪烁效果(简易版,实际可用线程实现更复杂效果)""" led_set_color(color) time.sleep(interval) led_set_color(Color(0,0,0)) time.sleep(interval) def led_breathing(color): """LED呼吸灯效果(简易版)""" # 实现一个简单的亮度循环,此处省略详细代码 pass def led_success_animation(): """成功确认的LED动画""" for i in range(3): led_set_color(Color(0, 255, 0)) time.sleep(0.2) led_set_color(Color(0, 0, 0)) time.sleep(0.2) def log_event(card_id, event_type): """记录事件到日志文件""" timestamp = time.strftime("%Y-%m-%d %H:%M:%S") with open("/home/pi/medication_log.csv", "a") as f: f.write(f"{timestamp},{card_id},{event_type}\n") def cleanup(): """程序退出前的清理工作""" servo_pwm.stop() led_set_color(Color(0,0,0)) GPIO.cleanup() print("GPIO已清理,程序退出。")

实操心得:状态机是编写此类嵌入式交互程序的利器。它让复杂的逻辑变得清晰,每个状态只关心自己的进入条件、执行动作和退出条件。调试时,可以通过打印当前状态和关键变量值,快速定位问题出在哪个环节。

5. 机械组装、调试与优化

当代码在面包板上跑通后,就可以着手将它们和机械部分整合到一个完整的外壳中了。

5.1 药仓与传动机构安装

  1. 固定舵机:将舵机用螺丝或热熔胶牢固地固定在外壳内部的底座上。确保其输出轴朝向正确,并且有足够的空间进行旋转。
  2. 安装药仓:将3D打印的药仓中心孔与舵机输出轴对接。我使用了一个小的联轴器(也可以直接用螺丝顶紧)来连接。关键是确保药仓与舵机轴同轴,并且药仓在旋转时不会与周围结构发生摩擦。
  3. 校准零点:这是关键步骤!在代码中,你需要找到舵机旋转0度时,药仓的哪个仓格正好对准出药口。上电后,手动将药仓转到这个位置,然后运行一个校准程序,将此时舵机的角度定义为“零点”。后续所有发药动作都基于这个零点进行角度累加。
  4. 安装滑道:出药口下方安装一个倾斜的滑道,引导药片落入杯中。滑道内壁务必光滑。可以用亚克力板折弯制作,或者3D打印。在出药口与滑道连接处,可以贴一小块柔软的海绵或硅胶,起到缓冲和静音的作用。

5.2 传感器与面板集成

  1. 超声波传感器:将其固定在出药口滑道末端的正下方,探头朝下。测量从探头到放置杯子平面的距离,将这个距离减去杯子的高度,就是“有杯子”时的检测距离。通过多次测试,确定一个可靠的阈值(比如CUP_DETECTION_THRESHOLD = 5cm)。
  2. RFID读卡器:将读卡器模块的天线板(通常是那个方形线圈)粘贴在外壳面板上预先开好的孔洞后面。面板材料不能是金属,否则会屏蔽信号。亚克力或木材是理想选择。可以在天线板位置画一个图标,提示刷卡区域。
  3. 按钮与LED:将轻触开关和LED灯带安装在面板上。LED灯带可以用扩散条覆盖,使光线更柔和均匀。
  4. 走线与固定:将所有连接线用扎带或线槽规整好,避免它们干扰舵机或药仓的转动。电路板(树莓派、面包板/转接板)用尼龙柱或螺丝固定在外壳内部。

5.3 系统联调与问题排查

组装完成后,进行全流程测试:

  1. 上电自检:系统启动后,LED应执行预设的启动动画(如流水灯),舵机应归零。观察有无异常发热或异响。
  2. 功能逐项测试
    • 刷卡:用授权卡片靠近读卡区,LED应变绿,终端打印认证成功信息。
    • 无杯检测:不放置杯子,按下发药按钮。系统应进入“检测杯子”状态(黄闪),并持续报“未检测到杯子”或最终超时返回待机。
    • 有杯发药:放置杯子,按下发药按钮。系统应检测到杯子(打印距离),舵机旋转,听到药片(可以用几粒豆子模拟)落入杯子的声音,然后进入等待确认状态(紫闪)。
    • 确认与超时:按下确认按钮,LED应显示成功动画并返回待机。不按确认按钮,等待60秒,系统应超时报警(红灯)并复位。
  3. 压力与异常测试
    • 连续快速刷卡、按按钮,测试系统防抖和状态机稳定性。
    • 在发药过程中突然断电再上电,看系统能否正常复位(药仓位置可能错乱,需要重新校准)。
    • 尝试用未授权卡片刷卡,系统应无反应。

6. 常见问题、优化与扩展思路

在实际制作和测试中,你肯定会遇到一些我踩过的坑。这里列出来,希望能帮你节省时间。

6.1 硬件与机械问题

问题现象可能原因排查与解决思路
舵机不转或抖动1. 电源功率不足。
2. PWM信号线接触不良。
3. 舵机卡死(机械阻力过大)。
1.首要检查:确保舵机使用独立5V电源,且与树莓派共地。
2. 用万用表测量舵机VCC和GND之间电压,动作时是否跌落到4.5V以下。
3. 脱开舵机与药仓的连接,单独测试舵机是否能正常转动。
药片卡在仓格或滑道1. 仓格尺寸太小或形状不合适。
2. 滑道倾角不够。
3. 药片表面太涩或有静电。
1. 重新设计药仓,仓格尺寸至少比最大药片宽2-3mm。
2. 增大滑道角度,确保大于45度。
3. 在滑道内壁涂抹少量食用级滑石粉或使用特氟龙胶带。
超声波传感器读数不稳定或总是-11. 供电不稳(特别是与舵机共用电源时)。
2. 触发和回波引脚接反。
3. 传感器前方有障碍物或过于靠近边缘。
1. 给HC-SR04的VCC引脚并联一个10uF以上的电解电容,以稳定电压。
2. 仔细核对Trig和Echo引脚连接。
3. 确保传感器探测方向开阔,且安装牢固,避免振动干扰。
RFID读卡距离变短或不灵敏1. 天线板背面有金属或大面积导电材料。
2. 天线板损坏或线圈断开。
3. 树莓派SPI未启用或频率设置不当。
1. 确保读卡区面板为非金属材料。
2. 检查天线板焊接点。可以尝试用另一张卡片或模块测试。
3. 运行ls /dev/spi*检查SPI设备是否存在。在/boot/config.txt中调整spi相关参数(一般默认即可)。

6.2 软件与逻辑问题

问题现象可能原因排查与解决思路
按钮按下无反应或连触发1. 未启用内部上拉电阻,引脚悬空。
2. 机械按键抖动。
1. 确认代码中设置了pull_up_down=GPIO.PUD_UP
2. 在代码中添加软件消抖:检测到低电平后,延时20-50ms再次检测,如果仍是低电平才视为有效按下。更可靠的方法是使用gpiozero库的Button类,它内置了消抖功能。
状态机逻辑混乱,状态乱跳1. 多个按钮或传感器检测在循环中阻塞。
2. 没有处理好并发事件(如发药过程中又刷卡)。
1. 确保主循环time.sleep的延迟很短(如0.1秒),保证响应及时。
2. 在关键状态(如DISPENSING)下,忽略其他无关输入事件。或者使用多线程,将RFID读取、超声波检测等放在独立线程中,通过线程安全的队列与主状态机通信。
舵机角度不准,每次位置有偏差1. PWM占空比计算或控制不精确。
2. 舵机存在“死区”或回差。
3. 电源电压波动导致舵机基准变化。
1. 使用硬件PWM(如GPIO.PWM)而非软件模拟。
2. 每次发药动作后,让舵机回到一个固定的“归零”位置,而不是依赖相对角度累加。可以增加一个光电传感器或微动开关作为“零点”传感器进行物理校准。
程序运行一段时间后卡死或无响应1. 内存泄漏(在循环中不断创建对象)。
2. 文件读写或网络操作阻塞。
3. 异常未捕获导致线程崩溃。
1. 检查代码,确保在循环外初始化对象,循环内只调用方法。
2. 将日志写入等IO操作放在单独的线程,或使用异步方式。
3. 用try...except包裹可能出错的代码段(如传感器读数),并记录错误日志。

6.3 功能优化与扩展方向

这个基础版本已经可用,但还有很大的提升空间:

  1. 增加显示与交互:添加一块小OLED或LCD屏幕,可以显示当前时间、下次服药时间、药品名称、欢迎语等。配合旋转编码器或更多按钮,可以实现设置菜单。
  2. 联网与数据同步:让树莓派连接Wi-Fi。可以:
    • 将用药记录实时上传到云端数据库(如InfluxDB)或物联网平台(如Home Assistant, Blynk)。
    • 实现远程提醒:如果用户超时未取药,通过手机APP、短信或语音电话(集成Twilio等API)提醒。
    • 支持远程查看药盒状态(剩余药量、电池电量等)。
  3. 多用户与药品管理:为每个RFID卡关联一个用户配置文件。在代码中维护一个字典或小型数据库(如SQLite),记录每个用户对应的药品名称、每次剂量、服药时间表。系统可以根据时间自动提醒对应用户服药。
  4. 药量监测:在药仓底部增加一个重量传感器(如HX711模块+称重传感器),实时监测每个仓格的剩余药量,并在快用完时提醒补充。
  5. 电源管理:加入锂电池和充电管理模块,实现断电续航。配合树莓派的休眠/唤醒功能,可以极大延长待机时间。
  6. 外观与人性化设计:优化外壳设计,使其更美观、更坚固。增加语音提示功能(使用USB小音箱和TTS引擎),对每一步操作进行语音引导,对视力不佳的用户更友好。

这个基于树莓派的智能药盒项目,从构思到实现,是一个典型的硬件、软件、机械结合的嵌入式系统实践。它没有用到特别高深的技术,但完整地走通了从需求分析、方案设计、部件选型、编程调试到组装测试的全流程。过程中遇到的每一个问题,都是宝贵的经验。希望这份超详细的拆解,能给你带来启发,也欢迎你基于这个框架,创造出功能更强大、更适合自己需求的智能药盒。

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

基于Next.js 14与NeuroLink构建高性能AI应用:全栈开发实践

1. 项目概述&#xff1a;为什么是Next.js NeuroLink&#xff1f; 最近在折腾一个AI应用的原型&#xff0c;目标是让用户通过自然语言对话&#xff0c;就能实时生成、编辑和操作文档。试了一圈框架和工具&#xff0c;最终敲定了Next.js 14作为前端框架&#xff0c;搭配NeuroLin…

作者头像 李华
网站建设 2026/5/28 12:15:54

从仿真到上板:FPGA频率测量实战避坑指南(含低频信号处理技巧)

从仿真到上板&#xff1a;FPGA频率测量实战避坑指南&#xff08;含低频信号处理技巧&#xff09;在FPGA开发中&#xff0c;频率测量是一个看似简单却暗藏玄机的任务。许多工程师在仿真阶段获得完美结果后&#xff0c;信心满满地将代码烧录到开发板&#xff0c;却在实测低频信号…

作者头像 李华
网站建设 2026/5/28 12:12:24

C8051F调试中访问断点问题的分析与解决方案

1. 问题背景与现象分析最近在调试Silicon Labs&#xff08;原Cygnal&#xff09;C8051F系列MCU时&#xff0c;遇到了一个典型的调试器限制问题。当尝试通过Vision调试器设置内存访问断点&#xff08;Access Breakpoint&#xff09;时&#xff0c;调试器报错"Error 73: uns…

作者头像 李华
网站建设 2026/5/28 12:11:19

A-Pot:基于ARM硬件与容器化的Android恶意软件高隐蔽动态分析平台

1. 项目概述与核心挑战在移动安全领域&#xff0c;Android恶意软件分析一直是一场攻防双方不断升级的“猫鼠游戏”。作为一名长期奋战在一线的安全研究员&#xff0c;我深刻体会到&#xff0c;传统的动态分析手段正变得越来越力不从心。你精心搭建的模拟器环境&#xff0c;可能…

作者头像 李华
网站建设 2026/5/28 12:11:09

用Arduino PWM驱动旧电压表,打造蒸汽朋克桌面时钟

1. 项目概述&#xff1a;用旧仪表盘打造一台桌面蒸汽朋克时钟几年前&#xff0c;我在一个旧货市场淘到了几块老式指针电压表。它们的外壳斑驳&#xff0c;刻度盘泛黄&#xff0c;但指针的摆动依然顺滑。当时就在想&#xff0c;除了测量电压&#xff0c;这些充满机械美感的“老家…

作者头像 李华