
智能宠物喂食器定时提醒系统 - 科学养宠好帮手一、实际应用场景描述现代都市年轻人工作繁忙经常加班或出差导致无法按时给宠物喂食。传统宠物喂食器需要手动操作或依赖简单的机械定时存在时间不准、无法灵活调整、缺乏提醒等问题。某智能宠物用品公司计划开发一款智能喂食器结合手机App和本地提示帮助宠物主科学安排喂食时间确保毛孩子按时吃饭同时记录喂食数据形成完整的宠物健康管理方案。本系统目标通过Python程序控制智能喂食器支持多时段定时投放每次触发时通过蜂鸣器、LED和手机推送提醒投放粮食并记录喂食历史让宠物主即使不在家也能掌握宠物的饮食情况实现省心养宠科学喂养。二、引入痛点痛点类型 具体表现 本方案解决方式时间不准 机械定时器误差大错过喂食时间 高精度软件定时器误差1秒遗忘喂食 工作忙碌忘记时间 多渠道提醒本地手机无法调整 出差/假期需要调整喂食计划 动态时段配置远程修改缺乏记录 不知道今天喂了几次 自动记录生成喂养报告紧急缺粮 粮仓空了没发现 余量检测低粮预警多宠差异 不同宠物食量和时间不同 多宠物独立配置支持三、核心逻辑讲解graph TDA[系统初始化] -- B[传感器校准与设备检测]B -- C[加载喂食计划配置]C -- D[启动定时任务调度器]D -- E[进入主循环监测]E -- F{检查是否到喂食时间?}F -- 是 -- G[触发喂食提醒]G -- H[播放提示音LED闪烁]H -- I[手机App推送通知]I -- J[记录喂食事件]J -- K[等待投喂完成确认]K -- 已投喂 -- L[更新余量状态]K -- 超时未投喂 -- M[发送紧急提醒]L -- EF -- 否 -- N{检查粮仓余量?}N -- 低粮量 -- O[发送补粮提醒]N -- 正常 -- P{检查设备状态?}P -- 异常 -- Q[发送故障警报]P -- 正常 -- EM -- EO -- EQ -- E关键逻辑点1. 多时段调度支持每天多个固定时间点可独立启停2. 双重确认机制定时触发提醒后等待投喂完成确认或超时处理3. 余量监控实时检测粮仓余量低粮时主动提醒4. 数据持久化所有喂食记录存储支持历史查询和统计分析5. 异常处理设备故障、网络中断等异常情况下的降级处理四、代码模块化实现项目结构smart_pet_feeder/├── main.py # 主程序入口├── config.py # 配置文件├── scheduler/ # 定时调度模块│ ├── __init__.py│ └── feeding_scheduler.py # 喂食计划调度器├── sensors/ # 传感器模块│ ├── __init__.py│ ├── food_level_sensor.py # 粮仓余量传感器│ └── door_sensor.py # 投食口门磁传感器├── actuators/ # 执行器模块│ ├── __init__.py│ ├── servo_motor.py # 伺服电机控制出粮│ └── led_buzzer.py # LED和蜂鸣器├── notifications/ # 通知模块│ ├── __init__.py│ ├── local_notifier.py # 本地声光提示│ └── app_notifier.py # 手机App推送├── storage/ # 数据存储模块│ ├── __init__.py│ └── feeding_logger.py # 喂食日志记录├── models/ # 数据模型│ ├── __init__.py│ └── pet_profile.py # 宠物档案模型└── utils/ # 工具函数├── __init__.py└── time_utils.py # 时间工具函数1. 配置文件 (config.py)智能宠物喂食器配置文件包含喂食计划、设备参数、通知设置等# 系统基本信息SYSTEM_NAME SmartPetFeederSYSTEM_VERSION 1.0.0DEVICE_ID PET_FEEDER_001DEVICE_NAME 小萌喂食器# 喂食计划配置FEEDING_SCHEDULE [{time: 07:00, portions: 2, enabled: True, pet: cat},{time: 12:00, portions: 2, enabled: True, pet: cat},{time: 18:00, portions: 2, enabled: True, pet: cat},{time: 20:00, portions: 1, enabled: True, pet: cat},]# 宠物档案配置PET_PROFILES {cat: {name: 咪咪,species: cat,weight: 4.5, # kgdaily_calories: 280, # kcalportion_weight: 30, # g per portiondietary_restrictions: [],feeding_notes: 喜欢干粮少食多餐},dog: {name: 旺财,species: dog,weight: 12.0,daily_calories: 520,portion_weight: 80,dietary_restrictions: [no_chocolate],feeding_notes: 早晚各一次饭后散步}}# 设备引脚配置 (BCM编号)SERVO_MOTOR_PIN 18 # 伺服电机控制引脚FOOD_LEVEL_TRIG_PIN 23 # 超声波传感器TrigFOOD_LEVEL_ECHO_PIN 24 # 超声波传感器EchoDOOR_SENSOR_PIN 25 # 门磁传感器LED_PIN 17 # 状态LEDBUZZER_PIN 27 # 蜂鸣器BUTTON_PIN 22 # 手动按钮# 喂食参数PORTIONS_PER_ROTATION 1 # 每次旋转对应的份数SERVO_OPEN_ANGLE 90 # 开门角度SERVO_CLOSE_ANGLE 0 # 关门角度SERVO_SPEED_DELAY 0.5 # 伺服转动延时FEEDING_TIMEOUT 300 # 投喂超时时间秒- 5分钟# 粮仓参数FOOD_TANK_HEIGHT 15.0 # 粮仓高度cmLOW_FOOD_THRESHOLD 3.0 # 低粮预警阈值cmEMPTY_THRESHOLD 1.0 # 空粮阈值cm# 通知配置ENABLE_LOCAL_NOTIFICATIONS True # 启用本地提示ENABLE_APP_NOTIFICATIONS True # 启用App推送LOCAL_ALERT_DURATION 10 # 本地提醒时长秒APP_PUSH_SERVER https://api.push.example.com/v1/sendUSER_DEVICE_TOKEN user_device_token_here# 数据存储配置FEEDING_LOG_FILE feeding_log.csvPET_DATA_FILE pet_profiles.jsonSETTINGS_FILE feeder_settings.jsonMAX_LOG_ENTRIES 5000# 节能配置DO_NOT_DISTURB_START_HOUR 23 # 免打扰开始23:00DO_NOT_DISTURB_END_HOUR 7 # 免打扰结束07:00LED_IDLE_BLINK_INTERVAL 10.0 # LED空闲闪烁间隔秒# 调试配置DEBUG_MODE FalseLOG_LEVEL INFOSIMULATION_MODE False # 模拟模式不实际控制硬件2. 喂食计划调度器模块 (scheduler/feeding_scheduler.py)喂食计划调度器模块管理每日喂食时间表精确到分钟的定时触发import threadingimport timefrom datetime import datetime, time as dt_timefrom typing import Callable, List, Dict, Optionalfrom config import FEEDING_SCHEDULE, FEEDING_TIMEOUTfrom utils.time_utils import parse_time_string, format_datetimeclass FeedingScheduler:智能喂食计划调度器支持多时段、多宠物、动态启停的定时任务管理def __init__(self, callback: Callable None):初始化喂食调度器callback: 到达喂食时间的回调函数 function(schedule_item)self.callback callbackself.schedules: List[Dict] []self.active_schedules: List[Dict] []self.is_running Falseself.scheduler_thread: Optional[threading.Thread] Noneself.lock threading.Lock()self.last_trigger_times: Dict[str, datetime] {}# 加载默认喂食计划self.load_default_schedule()print(f✅ 喂食计划调度器初始化成功)print(f 预设喂食时段: {len(self.schedules)}个)def load_default_schedule(self):加载默认喂食计划self.schedules []for item in FEEDING_SCHEDULE:schedule_entry {id: len(self.schedules),time: parse_time_string(item[time]),portions: item[portions],enabled: item[enabled],pet: item[pet],last_triggered: None,trigger_count: 0,missed_count: 0}self.schedules.append(schedule_entry)self._update_active_schedules()def _update_active_schedules(self):更新激活的喂食计划列表self.active_schedules [s for s in self.schedules if s[enabled]]# 按时间排序self.active_schedules.sort(keylambda x: x[time])def add_schedule(self, feed_time: str, portions: int, pet: str cat,enabled: bool True) - int:添加新的喂食计划feed_time: 喂食时间字符串 HH:MMportions: 喂食份数pet: 宠物标识enabled: 是否启用return: 计划IDwith self.lock:new_id len(self.schedules)schedule_entry {id: new_id,time: parse_time_string(feed_time),portions: portions,enabled: enabled,pet: pet,last_triggered: None,trigger_count: 0,missed_count: 0}self.schedules.append(schedule_entry)self._update_active_schedules()print(f➕ 添加喂食计划: {feed_time} - {pet} {portions}份)return new_iddef remove_schedule(self, schedule_id: int) - bool:删除喂食计划with self.lock:for i, schedule in enumerate(self.schedules):if schedule[id] schedule_id:removed self.schedules.pop(i)self._update_active_schedules()print(f➖ 删除喂食计划: {removed[time].strftime(%H:%M)})return Truereturn Falsedef enable_schedule(self, schedule_id: int, enabled: bool True):启用/禁用喂食计划with self.lock:for schedule in self.schedules:if schedule[id] schedule_id:schedule[enabled] enabledstatus 启用 if enabled else 禁用print(f{✅ if enabled else ⏸️} {status}喂食计划: f{schedule[time].strftime(%H:%M)})breakself._update_active_schedules()def modify_schedule(self, schedule_id: int, feed_time: str None,portions: int None, pet: str None):修改喂食计划with self.lock:for schedule in self.schedules:if schedule[id] schedule_id:if feed_time:schedule[time] parse_time_string(feed_time)if portions:schedule[portions] portionsif pet:schedule[pet] petchanges []if feed_time:changes.append(f时间:{feed_time})if portions:changes.append(f份数:{portions})if pet:changes.append(f宠物:{pet})print(f✏️ 修改喂食计划: { .join(changes)})breakself._update_active_schedules()def get_schedule(self, schedule_id: int) - Optional[Dict]:获取指定喂食计划详情for schedule in self.schedules:if schedule[id] schedule_id:return schedule.copy()return Nonedef get_all_schedules(self) - List[Dict]:获取所有喂食计划return [s.copy() for s in self.schedules]def get_active_schedules(self) - List[Dict]:获取激活的喂食计划return [s.copy() for s in self.active_schedules]def start(self):启动调度器if self.is_running:returnself.is_running Trueself.scheduler_thread threading.Thread(targetself._scheduler_loop, daemonTrue)self.scheduler_thread.start()print(f 喂食调度器已启动)print(f 下次喂食: {self._get_next_feeding_time()})def stop(self):停止调度器self.is_running Falseif self.scheduler_thread:self.scheduler_thread.join(timeout2.0)print(⏹️ 喂食调度器已停止)def _scheduler_loop(self):调度器主循环while self.is_running:try:current_time datetime.now().time()with self.lock:for schedule in self.active_schedules:schedule_time schedule[time]# 检查是否到达喂食时间精确到分钟if (current_time.hour schedule_time.hour andcurrent_time.minute schedule_time.minute andschedule[last_triggered] ! today_date()):# 触发喂食事件self._trigger_feeding(schedule)schedule[last_triggered] today_date()schedule[trigger_count] 1# 每分钟检查一次time.sleep(1.0)except Exception as e:print(f调度器错误: {e})time.sleep(5.0)def _trigger_feeding(self, schedule: Dict):触发喂食事件current_time datetime.now()time_str current_time.strftime(%H:%M)print(f⏰ 喂食时间到! [{time_str}])print(f 宠物: {schedule[pet]})print(f ️ 份数: {schedule[portions]}份)# 调用回调函数if self.callback:try:self.callback(schedule)except Exception as e:print(f回调函数执行失败: {e})# 记录触发时间self.last_trigger_times[schedule[id]] current_timedef _get_next_feeding_time(self) - str:获取下次喂食时间current_time datetime.now().time()today today_date()next_feeding Nonemin_diff float(inf)for schedule in self.active_schedules:schedule_time schedule[time]# 计算时间差diff (schedule_time.hour * 60 schedule_time.minute) - \(current_time.hour * 60 current_time.minute)# 如果已过今日时间计算明日时间if diff 0:diff 24 * 60if diff min_diff:min_diff diffnext_feeding schedule_timeif next_feeding:return next_feeding.strftime(%H:%M)return 无激活计划def get_status(self) - Dict:获取调度器状态return {is_running: self.is_running,total_schedules: len(self.schedules),active_schedules: len(self.active_schedules),next_feeding_time: self._get_next_feeding_time(),today_triggers: len([s for s in self.schedulesif s[last_triggered] today_date()]),schedules_detail: [{id: s[id],time: s[time].strftime(%H:%M),pet: s[pet],portions: s[portions],enabled: s[enabled],triggered_today: s[last_triggered] today_date()}for s in self.schedules]}def today_date() - str:获取今天的日期字符串return datetime.now().strftime(%Y-%m-%d)3. 粮仓余量传感器模块 (sensors/food_level_sensor.py)超声波粮仓余量传感器模块HC-SR04超声波传感器测量粮仓剩余粮食高度import RPi.GPIO as GPIOimport timefrom config import FOOD_LEVEL_TRIG_PIN, FOOD_LEVEL_ECHO_PIN, FOOD_TANK_HEIGHTfrom utils.time_utils import retry_on_exceptionclass FoodLevelSensor:超声波粮仓余量检测器通过测量声波反射时间计算粮食表面高度# 声音在空气中传播速度cm/sSOUND_SPEED 34300def __init__(self):初始化超声波传感器self.trig_pin FOOD_LEVEL_TRIG_PINself.echo_pin FOOD_LEVEL_ECHO_PINself.tank_height FOOD_TANK_HEIGHTself.last_reading 0self.last_read_time 0self.calibration_offset 0 # 校准偏移量self._setup_gpio()self._calibrate()print(f✅ 粮仓余量传感器初始化成功)print(f 引脚: Trig{FOOD_LEVEL_TRIG_PIN}, Echo{FOOD_LEVEL_ECHO_PIN})print(f 粮仓高度: {FOOD_TANK_HEIGHT}cm)def _setup_gpio(self):配置GPIO引脚GPIO.setmode(GPIO.BCM)GPIO.setup(self.trig_pin, GPIO.OUT)GPIO.setup(self.echo_pin, GPIO.IN)# 初始化Trig为低电平GPIO.output(self.trig_pin, GPIO.LOW)time.sleep(0.5)retry_on_exception(retries3, delay0.1)def measure_distance(self) - float:测量超声波往返距离return: 距离值cm# 发送10us的触发脉冲GPIO.output(self.trig_pin, GPIO.HIGH)time.sleep(0.00001) # 10微秒GPIO.output(self.trig_pin, GPIO.LOW)# 等待回响信号开始pulse_start time.time()timeout_start pulse_startwhile GPIO.input(self.echo_pin) GPIO.LOW:pulse_start time.time()if pulse_start - timeout_start 0.1: # 100ms超时raise TimeoutError(超声波测量超时)# 等待回响信号结束pulse_end time.time()timeout_end pulse_endwhile GPIO.input(self.echo_pin) GPIO.HIGH:pulse_end time.time()if pulse_end - timeout_end 0.1:raise TimeoutError(超声波测量超时)# 计算距离往返距离/2pulse_duration pulse_end - pulse_startdistance (pulse_duration * self.SOUND_SPEED) / 2# 应用校准偏移calibrated_distance distance self.calibration_offsetself.last_reading calibrated_distanceself.last_read_time time.time()return calibrated_distancedef get_food_level(self) - float:获取粮仓粮食高度return: 粮食高度cm0表示空仓try:# 测量传感器到粮面的距离distance_to_food self.measure_distance()# 计算粮食高度food_level self.tank_height - distance_to_food# 限制在合理范围内food_level max(0, min(self.tank_height, food_level))return round(food_level, 2)except TimeoutError as e:print(f⚠️ 超声波测量超时: {e})return self.last_reading if self.last_reading 0 else 0except Exception as e:print(f⚠️ 粮仓测量错误: {e})return self.last_reading if self.last_reading 0 else 0def get_food_percentage(self) - float:获取粮食剩余百分比food_level self.get_food_level()percentage (food_level / self.tank_height) * 100return round(max(0, min(100, percentage)), 1)def is_low_food(self) - bool:检查是否低粮from config import LOW_FOOD_THRESHOLDreturn self.get_food_level() LOW_FOOD_THRESHOLDdef is_empty(self) - bool:检查是否空粮from config import EMPTY_THRESHOLDreturn self.get_food_level() EMPTY_THRESHOLDdef get_food_status(self) - Dict:获取粮食状态详情level self.get_food_level()percentage self.get_food_percentage()if percentage 0:status emptymessage 粮仓已空请立即加粮elif percentage 10:status criticalmessage 粮食严重不足建议尽快加粮elif percentage 25:status lowmessage 粮食偏少记得及时补充else:status normalmessage 粮食充足return {level_cm: level,percentage: percentage,status: status,message: message,tank_height: self.tank_height,raw_distance: self.last_reading}def _calibrate(self):传感器校准print( 开始传感器校准...)# 多次测量取平均值readings []for _ in range(10):try:dist self.measure_distance()readings.append(dist)time.sleep(0.1)except:passif readings:avg_distance sum(readings) / len(readings)# 假设空仓时距离约为粮仓高度expected_empty self.tank_heightself.calibration_offset expected_empty - avg_distanceprint(f 校准偏移量: {self.calibration_offset:.2f}cm)print(f 平均测量值: {avg_distance:.2f}cm)else:print( 校准失败使用默认参数)def cleanup(self):清理传感器资源GPIO.cleanup([self.trig_pin, self.echo_pin])4. 伺服电机控制模块 (actuators/servo_motor.py)伺服电机控制模块控制SG90等标准伺服电机实现出粮门的开关import RPi.GPIO as GPIOimport timeimport threadingfrom config import SERVO_MOTOR_PIN, SERVO_OPEN_ANGLE, SERVO_CLOSE_ANGLE, SERVO_SPEED_DELAYfrom utils.time_utils import retry_on_exceptionclass ServoMotor:伺服电机控制器通过PWM信号控制角度实现精准的出粮门控制# SG90伺服电机参数PWM_FREQUENCY 50 # 50Hz PWM频率MIN_PULSE_WIDTH 0.5 # 最小脉宽ms- 对应0度MAX_PULSE_WIDTH 2.5 # 最大脉宽ms- 对应180度PULSE_RANGE MAX_PULSE_WIDTH - MIN_PULSE_WIDTHdef __init__(self):初始化伺服电机self.pin SERVO_MOTOR_PINself.current_angle 0self.target_angle 0self.is_moving Falseself.pwm Noneself.movement_lock threading.Lock()self._setup_pwm()print(f✅ 伺服电机初始化成功)print(f 引脚: GPIO{SERVO_MOTOR_PIN})print(f 开合角度: {SERVO_CLOSE_ANGLE}° → {SERVO_OPEN_ANGLE}°)def _setup_pwm(self):配置PWM输出GPIO.setmode(GPIO.BCM)GPIO.setup(self.pin,利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛