树莓派Python脚本故障:PIR触发mot.mp4仅生效一次,需修复重复触发
解决树莓派PIR传感器重复触发VLC视频播放问题
问题描述
我在树莓派上运行Python脚本,用VLC和PIR运动传感器实现以下需求:
- 无运动时循环播放
lop.mp4 - 检测到运动时播放
mot.mp4,播放完毕后回到lop.mp4的循环状态
目前脚本仅能在首次检测到运动时播放mot.mp4,后续无法再次触发,需要修复重复触发功能。
原代码:
import gpiozero import vlc import time import threading # create a button that uses GPIO4 pir_sensor = gpiozero.MotionSensor(4) # create vlc instance player = vlc.Instance() # create media player media_player = player.media_player_new() def play_video(path): media = player.media_new(path) media_player.set_media(media) media_player.play() # wait a bit for the player to start, then set fullscreen time.sleep(0.1) media_player.set_fullscreen(False) def play_default_video_loop(): play_video('/home/pi/elf/lop.mp4') while True: if media_player.get_state() == vlc.State.Ended: play_video('/home/pi/elf/lop.mp4') time.sleep(0.1) def on_motion(): print("Motion detected!") play_video('/home/pi/elf/mot.mp4') while media_player.get_state() != vlc.State.Ended: time.sleep(0.1) # wait for video to finish play_default_video_loop() # restart default video loop pir_sensor.when_motion = on_motion # start playing default video in a new thread threading.Thread(target=play_default_video_loop).start() try: while True: time.sleep(1) except KeyboardInterrupt: print("Exiting") media_player.stop()
问题根源
on_motion函数播放完mot.mp4后调用play_default_video_loop(),会在PIR回调线程中启动新的无限循环,而最初的默认视频循环线程仍在运行,两个线程同时控制播放器,导致状态冲突,后续运动检测无法正常触发。- 默认循环线程没有判断当前是否在播放触发视频,会在
mot.mp4播放过程中尝试重新播放lop.mp4,干扰触发视频的播放。
解决方案
使用线程安全的标志变量标记当前播放状态,让默认循环线程根据标志决定是否继续循环;移除on_motion中重新启动默认循环的代码,让原默认线程自动恢复循环。
修改后的代码:
import gpiozero import vlc import time import threading # 创建PIR传感器实例,使用GPIO4 pir_sensor = gpiozero.MotionSensor(4) # 初始化VLC实例和播放器 player = vlc.Instance() media_player = player.media_player_new() # 线程安全标志:标记当前是否在播放触发视频mot.mp4 is_playing_trigger = False # 给标志加锁,避免多线程冲突 flag_lock = threading.Lock() def play_video(path): media = player.media_new(path) media_player.set_media(media) media_player.play() time.sleep(0.1) media_player.set_fullscreen(False) def play_default_video_loop(): play_video('/home/pi/elf/lop.mp4') while True: with flag_lock: # 如果当前在播放触发视频,跳过本次循环 if is_playing_trigger: time.sleep(0.1) continue # 检测默认视频是否播放完毕,完毕则重新播放 if media_player.get_state() == vlc.State.Ended: play_video('/home/pi/elf/lop.mp4') time.sleep(0.1) def on_motion(): global is_playing_trigger # 加锁修改标志,避免多线程冲突 with flag_lock: # 如果已经在播放触发视频,直接返回,避免重复触发 if is_playing_trigger: return is_playing_trigger = True print("Motion detected!") play_video('/home/pi/elf/mot.mp4') # 等待触发视频播放完毕 while media_player.get_state() != vlc.State.Ended: time.sleep(0.1) # 播放完毕后重置标志,让默认循环恢复 with flag_lock: is_playing_trigger = False pir_sensor.when_motion = on_motion # 启动默认视频循环线程 threading.Thread(target=play_default_video_loop).start() try: while True: time.sleep(1) except KeyboardInterrupt: print("Exiting") media_player.stop()
代码修改说明
- 添加
is_playing_trigger全局标志和flag_lock线程锁,确保多线程下标志修改的安全性 - 修改
play_default_video_loop函数:每次循环先检查标志,若处于触发视频播放状态则跳过,避免干扰 - 修改
on_motion函数:- 触发时先检查标志,避免重复触发同一视频
- 播放触发视频前设置标志为
True,告知默认循环暂停 - 播放完毕后重置标志为
False,让默认循环自动恢复 - 移除原有的
play_default_video_loop()调用,不再启动新的循环线程
内容的提问来源于stack exchange,提问作者Mark Chaskelson
相关产品推荐
相关产品推荐

