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

使用Tkinter搭配Pi Camera Module 3导致Raspberry Pi 5长时间运行后卡顿

问题分析与解决方案

你的核心问题是Xwayland CPU占用持续攀升,根源在于Tkinter事件循环被阻塞、Canvas图像更新方式低效,以及摄像头配置不够优化,长时间运行后导致Xwayland渲染任务积压。以下是具体问题点和修复方案:

关键问题点

  1. 不必要的阻塞:Camera.py的update_frame中使用time.sleep(0.01),会阻塞Tkinter的事件循环,导致Xwayland无法及时处理渲染事件,累积后CPU占用飙升。
  2. 低效的Canvas更新:每次更新图像时删除旧的Canvas元素再创建新元素,频繁的创建/销毁操作会大幅增加Xwayland的渲染负担。
  3. 冗余代码:Camera.__init__中错误的线程初始化代码(直接调用update_frame()而非作为线程目标),不仅没用还会导致方法提前执行。
  4. 未正确释放摄像头资源:没有显式停止Picamera2的捕获,可能存在隐性资源泄漏。
  5. 摄像头配置不匹配:使用create_preview_configuration适合临时预览,长时间视频捕获应使用更高效的create_video_configuration。

修复后的代码

App.py

import tkinter
from Camera import Camera

class App:
    def __init__(self, window, window_title, video_name):
        self.window = window
        self.window.title(window_title)
        
        self.source = Camera(self.window, video_name, 1024, 768)
        self.source.grid(row=0, column=0, sticky=tkinter.N)

        self.window.protocol("WM_DELETE_WINDOW", self.delete)
        self.window.mainloop()

    def delete(self):
        print('[App] stopping camera and threads')
        self.source.stop()
        print('[App] exit')
        self.window.destroy()

if __name__ == '__main__':
    App(tkinter.Tk(), "DAC Gas Loader Cameras", 'PI Camera')

Camera.py

import tkinter as tk
import PIL.Image, PIL.ImageTk
from VideoCapture import *

class Camera(tk.Frame):
    def __init__(self, window, name="", width=400, height=300, fps=24):
        super().__init__(window)
        self.window = window
        
        self.vid = PicamVideoCapture(width, height)

        self.canvas = tk.Canvas(self, width=width, height=height)
        self.canvas.pack()

        self.fps = fps
        self.delay = int(1000/self.fps)
        print('[Camera] source:', 'fps:', self.fps, 'delay:', self.delay)
        
        self.photo = None 
        self.imageID = None
        
        self.running = True
        # 启动帧更新
        self.update_frame()

    def update_frame(self):
        if not self.running:
            return
            
        ret, frame = self.vid.get_frame()
        
        if ret:
            image = PIL.Image.fromarray(frame)
            self.photo = PIL.ImageTk.PhotoImage(image=image)
            if self.imageID is None:
                self.imageID = self.canvas.create_image(0, 0, image=self.photo, anchor='nw')
            else:
                # 直接更新已有图像元素,避免删除重建
                self.canvas.itemconfig(self.imageID, image=self.photo)
        
        # 调度下一次更新
        self.window.after(self.delay, self.update_frame)
    
    def stop(self):
        self.running = False
        self.vid.stop()

VideoCapture.py

import time
import os
from picamera2 import Picamera2

class PicamVideoCapture:
    def __init__(self, width=400, height=300):
        self.vid = Picamera2()
        # 改用视频捕获配置,比预览配置更高效稳定
        self.vid.configure(self.vid.create_video_configuration(main={"size": (width, height)}))
        self.vid.start()

    def get_frame(self):
        return True, self.vid.capture_array()

    def take_picture(self):
        os.makedirs(os.path.dirname("./images/"), exist_ok=True)
        self.vid.capture_file(time.strftime("images/picam-%d-%m-%Y-%H-%M-%S.jpg"))
        
    def stop(self):
        # 显式停止摄像头捕获并释放资源
        if self.vid.is_recording():
            self.vid.stop()
        self.vid.close()

额外优化建议

  • 降低分辨率或帧率:如果1024x768@24fps不是必需,适当降低可以进一步减轻Xwayland的渲染压力。
  • 启用Picamera2的硬件加速:确保系统已开启硬件加速(Raspberry Pi OS默认已开启),让摄像头捕获和图像转换更高效。
  • 定期清理:虽然修复后已解决主要问题,但可以考虑每小时重启一次帧更新循环(如果允许),避免极端情况下的隐性资源累积。

内容的提问来源于stack exchange,提问作者Alex StAubin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 00:07:08