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

如何用Raspberry Pi与SPI显示屏制作Vector机器人流畅眼睛动画?

树莓派SPI LCD流畅动画优化方案(适配Vector机器人眼睛效果)

问题背景

手头有Raspberry Pi 4(4GB)和2寸Waveshare SPI LCD显示屏,想实现类似Vector机器人的动态眼睛表情,但用Waveshare Python库结合PIL开发时帧率极低——测试代码中移动矩形50次耗时近7秒,无法满足流畅动画需求。

原测试代码:

import os
import sys
import time
import logging
import spidev as SPI
from lib import LCD_2inch
from PIL import Image, ImageDraw, ImageFont

class CoreDisplay:
    # Raspberry pi pin configuration:
    RST = 27
    DC = 25
    BL = 18
    bus = 0
    device = 0

    def __init__(self):
        # Display with hardware SPI
        # DON'T CRERATE MULTIPLE DISPLAYER OBJECTS!!!
        self.disp = LCD_2inch.LCD_2inch()
        # initialize library
        self.disp.Init()
        # Clear display
        self.disp.clear() 

    def testmotion(self):
        # test to move a rectangle on screen
        x, y = 20, 20
        old_shape = [(x, y), (x + 40, y + 40)]
        for i in range(50): 
            # Cover the old shape in black
            image = Image.new("RGB", (self.disp.height, self.disp.width ), "BLACK")
            draw = ImageDraw.Draw(image)
            draw.rectangle(old_shape, fill ="Black")

            image = image.rotate(180)
            self.disp.ShowImage(image)

            # draw new shape
            shape = [(x, y), (x + 40, y + 40)]
            image = Image.new("RGB", (self.disp.height, self.disp.width ), "BLACK")
            draw = ImageDraw.Draw(image)
            draw.rectangle(shape, fill ="Cyan")

            image = image.rotate(180)
            self.disp.ShowImage(image)
            x += 1
            old_shape = shape

popDisplay = CoreDisplay()
popDisplay.testmotion()
time.sleep(5)

帧率低下的核心原因

  1. 重复创建画布对象:每次循环都新建Image和ImageDraw实例,带来大量内存分配与销毁开销。
  2. 冗余的屏幕刷新:单次循环内调用两次ShowImage,相当于刷新两次屏幕,直接翻倍耗时。
  3. 全量画布重建:每次都重新创建全黑画布,而非在原有画布上仅更新变化区域。
  4. 重复旋转操作:每帧都执行rotate(180),可提前在初始化时完成一次旋转,避免重复计算。

优化方案与代码实现

关键优化点

  • 复用画布与绘图对象:在初始化时创建一次画布,后续仅在其上修改内容。
  • 单帧单次刷新:每帧仅执行一次ShowImage,避免冗余刷新。
  • 局部更新画布:仅覆盖上一帧的图形,而非重建整个画布。
  • 提前旋转画布:初始化时就将画布旋转180度,后续无需重复操作。

优化后的代码

import os
import sys
import time
import spidev as SPI
from lib import LCD_2inch
from PIL import Image, ImageDraw, ImageFont

class CoreDisplay:
    # Raspberry pi pin configuration:
    RST = 27
    DC = 25
    BL = 18
    bus = 0
    device = 0

    def __init__(self):
        self.disp = LCD_2inch.LCD_2inch()
        self.disp.Init()
        self.disp.clear() 
        
        # 初始化画布,提前旋转180度,避免每帧重复旋转
        self.canvas = Image.new("RGB", (self.disp.height, self.disp.width ), "BLACK")
        self.canvas = self.canvas.rotate(180)
        self.draw = ImageDraw.Draw(self.canvas)

    def testmotion(self):
        x, y = 20, 20
        rect_width, rect_height = 40, 40
        # 初始绘制第一个矩形
        self.draw.rectangle([(x, y), (x + rect_width, y + rect_height)], fill="Cyan")
        self.disp.ShowImage(self.canvas)
        
        for i in range(50): 
            # 用黑色覆盖上一帧的矩形
            self.draw.rectangle([(x, y), (x + rect_width, y + rect_height)], fill="BLACK")
            x += 1
            # 绘制新位置的矩形
            self.draw.rectangle([(x, y), (x + rect_width, y + rect_height)], fill="Cyan")
            # 仅刷新一次屏幕
            self.disp.ShowImage(self.canvas)
            # 可选:控制帧率,避免刷新过快(根据需求调整)
            # time.sleep(0.01)

popDisplay = CoreDisplay()
popDisplay.testmotion()
time.sleep(5)

进阶优化:局部屏幕刷新

如果你的Waveshare LCD库支持局部刷新(查看LCD_2inch.py中是否有ShowPartialImage或类似方法),可以进一步提升帧率——仅刷新矩形移动的区域,而非全屏幕:

# 假设库中有局部刷新方法,参数为图像、起始坐标、区域宽高
# 在绘制新矩形后,仅刷新变化的区域
self.disp.ShowPartialImage(self.canvas, x-1, y, rect_width+2, rect_height+2)

注:局部区域需要包含矩形的移动范围,避免出现残留像素。

额外性能提升建议

  • 使用硬件加速的图形库:PIL的软件渲染性能有限,可尝试使用pygame或OpenCV替代,它们对树莓派的GPU有更好的支持,能显著提升动画帧率。
  • 预渲染静态元素:如果眼睛表情有固定的静态部分,提前渲染好,动画时仅更新动态部分。
  • 调整SPI总线速度:在初始化SPI时尝试提高总线速度(需确保显示屏支持),比如将SPI时钟频率从默认值调高至16MHz或更高,可减少数据传输时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 11:36:14