You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

树莓派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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 10:57:32