RDK X5 + Waveshare 3.5" SPI LCD 多算法 AI 交互演示
一、项目概述
本项目在 RDK X5 开发板上,使用 Waveshare 3.5inch RPi LCD (F)(ST7796S 控制器,480×320 分辨率,SPI 接口)实现了一个完整的多算法 AI 交互演示系统。
核心特性:
- 左侧按钮栏(OpenCV 绘制)+ 右侧 Firefox 全屏显示(TogetheROS Web Display)
- 4 种 BPU AI 算法一键切换:手部关键点、手势识别、人脸年龄、行人重识别
- 自动隐藏 XFCE 桌面组件,退出时自动恢复
- 复用官方 ROS2 pipeline,无需自行编写推理代码
二、整体架构
┌─────────────────────────────────────────────┐
│ Hand Lmk │ │
│ │ TogetheROS Web Display │
│ Hand Gest │ (Firefox Kiosk 模式) │
│ │ 400×320 像素 │
│ Face Age │ │
│ │ 相机画面 + AI 渲染结果 │
│ ReID │ │
│ │ │
│ Exit │ │
└─────────────────────────────────────────────┘
80×320 400×320
OpenCV 按钮栏 Firefox 渲染区
数据流:
USB Camera → ROS2 Pipeline (BPU 推理) → WebSocket (:8000) → Firefox (TogetheROS Web Display)
↑
ROS2Manager 启动/切换算法
↑
OpenCV 按钮栏 (触摸切换)
技术栈:
| 组件 | 技术 | 作用 |
|---|---|---|
| AI 推理 | ROS2 + BPU (BPU 加速) | 4 种 AI 算法推理 |
| Web 渲染 | TogetheROS WebSocket | 将推理结果渲染到 Web 页面 |
| 显示 | Firefox Kiosk | 全屏显示 TogetheROS Web Display |
| 按钮栏 | OpenCV + X11 | 左侧 80px 按钮栏,支持触摸 |
| 窗口管理 | xdotool + xprop | 隐藏 XFCE 面板,无边框窗口 |
三、硬件连接
3.1 SPI LCD 引脚映射
| 功能 | RDK X5 40PIN | 设备树 GPIO |
|---|---|---|
| SPI SCK | Pin 23 | SPI1 总线 |
| SPI MOSI | Pin 19 | |
| SPI MISO | Pin 21 | |
| SPI CS | Pin 26 (CS1) | reg = <1> |
| DC | Pin 15 (BCM 22) | &ls_gpio0_porta 9 |
| Reset | Pin 13 (BCM 27) | &ls_gpio0_porta 0 |
| Backlight | Pin 12 (BCM 18) | &dsp_gpio_porta 10 (sysfs 421) |
注意: SPI CS 必须使用 CS1,避免与板载 IMU (BMI088) 冲突。
四、SPI LCD 驱动配置
4.1 编译内核模块
RDK X5 使用 panel-mipi-dbi 驱动,需要编译 4 个内核模块:
# 安装内核头文件
apt install hobot-kernel-headers
ln -sf /usr/src/linux-headers-6 /lib/modules/$(uname -r)/build
# 编译构建脚本
cd /usr/src/linux-headers-6 && make scripts
# 下载源码
BASE_URL="https://raw.githubusercontent.com/D-Robotics/x5-kernel/main"
curl -fsSL -o panel-mipi-dbi.c "$BASE_URL/drivers/gpu/drm/tiny/panel-mipi-dbi.c"
curl -fsSL -o drm_mipi_dbi.c "$BASE_URL/drivers/gpu/drm/drm_mipi_dbi.c"
curl -fsSL -o drm_gem_dma_helper.c "$BASE_URL/drivers/gpu/drm/drm_gem_dma_helper.c"
curl -fsSL -o gpio_backlight.c "$BASE_URL/drivers/video/backlight/gpio_backlight.c"
# 关键补丁:ST7796S 复位时序 (20us → 10ms)
sed -i 's/usleep_range(20, 1000)/usleep_range(10000, 15000)/' drm_mipi_dbi.c
# 编译
cat > Makefile << 'EOF'
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += drm_mipi_dbi.o drm_gem_dma_helper.o panel-mipi-dbi.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
EOF
make -j$(nproc)
# 编译 gpio_backlight
cat > Makefile << 'EOF'
KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
obj-m += gpio_backlight.o
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
EOF
make -j$(nproc)
# 安装
KDIR=/lib/modules/$(uname -r)/kernel/drivers/gpu/drm
mkdir -p $KDIR/tiny
cp drm_mipi_dbi.ko drm_gem_dma_helper.ko $KDIR/
cp panel-mipi-dbi.ko $KDIR/tiny/
cp gpio_backlight.ko /lib/modules/$(uname -r)/kernel/drivers/video/backlight/
depmod -a
4.2 设备树 Overlay
LCD Overlay (overlay-st7796s.dts):
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target-path = "/";
__overlay__ {
backlight: backlight {
compatible = "gpio-backlight";
gpios = <&dsp_gpio_porta 10 0>;
default-on;
};
};
};
fragment@1 {
target-path = "/soc/a55_apb0/spi@34010000";
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
spidev@1 { status = "disabled"; };
bmi08a@1 { status = "disabled"; };
panel-mipi-dbi@1 {
compatible = "panel-mipi-dbi-spi";
reg = <1>;
spi-max-frequency = <40000000>;
dc-gpios = <&ls_gpio0_porta 9 0>;
reset-gpios = <&ls_gpio0_porta 0 0>;
backlight = <&backlight>;
write-only;
width-mm = <85>;
height-mm = <53>;
panel-timing {
clock-frequency = <0>;
hactive = <480>;
vactive = <320>;
hfront-porch = <0>;
hsync-len = <0>;
hback-porch = <0>;
vfront-porch = <0>;
vsync-len = <0>;
vback-porch = <0>;
};
};
};
};
};
4.3 生成 ST7796S 初始化固件
#!/usr/bin/env python3
import struct
MAGIC = b"MIPI DBI\x00\x00\x00\x00\x00\x00\x00"
VERSION = 1
def build_commands():
cmds = bytearray()
def add_cmd(cmd, *params):
cmds.append(cmd); cmds.append(len(params)); cmds.extend(params)
def add_delay(ms):
cmds.append(0x00); cmds.append(0x01); cmds.append(ms & 0xFF)
add_cmd(0x01); add_delay(120) # Software Reset
add_cmd(0x11); add_delay(120) # Sleep Out
add_cmd(0x3A, 0x55) # 16bit/pixel (RGB565)
add_cmd(0xB6, 0x80, 0x02, 0x3B) # Display Function Control
add_cmd(0xB7, 0xC6) # Entry Mode Set
add_cmd(0xC5, 0xA0) # Display Output Ctrl Adjust
add_cmd(0xD0, 0xA7, 0x41, 0x1D) # Power Control 1
add_cmd(0xE0, 0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F,
0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B) # Positive Gamma
add_cmd(0xE1, 0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B,
0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B) # Negative Gamma
add_cmd(0x21) # Display Inversion On
add_delay(120)
add_cmd(0x36, 0x28) # MADCTL: landscape (480x320)
add_cmd(0x29) # Display ON
add_delay(120)
return bytes(cmds)
header = MAGIC + struct.pack("B", VERSION)
commands = build_commands()
with open("panel-mipi-dbi-spi.bin", "wb") as f:
f.write(header + commands)
print(f"Generated: {len(header) + len(commands)} bytes")
4.4 部署配置
# 编译 overlay
dtc -@ -I dts -O dtb -o overlay-st7796s.dtbo overlay-st7796s.dts
# 安装
cp overlay-st7796s.dtbo /boot/overlays/
cp panel-mipi-dbi-spi.bin /lib/firmware/
# 配置加载
cat > /boot/config.txt << 'EOF'
dtoverlay=overlay-st7796s
EOF
# 注意末尾必须有空行!
# 模块自动加载
cat > /etc/modules-load.d/mipi-dbi-lcd.conf << 'EOF'
drm_gem_dma_helper
drm_mipi_dbi
gpio_backlight
panel-mipi-dbi
EOF
# 重启
reboot
五、AI Demo 应用
5.1 支持的 4 种算法
| 按钮 | 算法 | ROS2 Launch | 输出话题 |
|---|---|---|---|
| Hand Lmk | 手部关键点检测 | hand_lmk_detection.launch.py |
/hobot_hand_lmk_detection |
| Hand Gest | 手势识别 | hand_gesture_fusion.launch.py |
/hobot_hand_dynamic_gesture_detection |
| Face Age | 人脸年龄检测 | body_det_face_age_det.launch.py |
/hobot_face_age_detection |
| ReID | 行人重识别 | reid.launch.py |
/perception/detection/reid |
5.2 安装
# 下载附件 rdk_x5_ai_demo.zip 并解压
unzip rdk_x5_ai_demo.zip -d rdk_x5_ai_demo
cd rdk_x5_ai_demo
# 一键安装
sudo bash install.sh
或手动安装:
mkdir -p /app/ai_demo_web
cp ai_demo_browser.py /app/ai_demo_web/
cp start_ros2.sh /app/ai_demo_web/
chmod +x /app/ai_demo_web/start_ros2.sh
cp ai-demo.desktop /home/sunrise/Desktop/
chmod +x /home/sunrise/Desktop/ai-demo.desktop
5.3 使用方法
# 方式1:双击桌面 "AI Demo (ROS2)" 图标
# 方式2:命令行启动
su - sunrise -c 'export DISPLAY=:0 && bash /app/ai_demo_web/start_ros2.sh'
操作说明:
- 触摸左侧按钮切换算法
- 右侧 Firefox 自动显示 TogetheROS Web Display(相机画面 + AI 渲染结果)
- 点击 Exit 按钮退出,自动恢复 XFCE 桌面
六、核心代码说明
6.1 主程序 (ai_demo_browser.py)
核心组件:
-
ROS2Manager - 管理 ROS2 pipeline 生命周期
switch(algo_id)- 切换算法(先 stop 再启动新的 ros2 launch)_wait_for_web()- 等待 TogetheROS Web 服务就绪_launch_browser()- 启动 Firefox Kiosk 并用 xdotool 调整窗口
-
AIDemo - OpenCV 窗口管理
- 左侧 80×320 按钮栏(
cv2.WINDOW_GUI_NORMAL无边框) - 使用
xprop设置为 DOCK 窗口类型(无边框但仍可接收鼠标事件) - 触摸切换算法后自动置顶按钮栏
- 左侧 80×320 按钮栏(
-
XFCE 桌面管理
- 启动时用
xdotool将 xfce4-panel 和 xfdesktop 移到屏幕外 - 退出时自动恢复
- 启动时用
6.2 窗口管理关键技巧
# 1. 设置窗口为 DOCK 类型(无边框但可接收鼠标)
subprocess.run(["xprop", "-id", wid, "-f", "_NET_WM_WINDOW_TYPE", "32a",
"-set", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DOCK"])
# 2. 移除 MOTIF 装饰
subprocess.run(["xprop", "-id", wid, "-f", "_MOTIF_WM_HINTS", "32i",
"-set", "_MOTIF_WM_HINTS", "2, 0, 0, 0, 0"])
# 3. 置顶 + 跳过任务栏
subprocess.run(["xprop", "-id", wid, "-f", "_NET_WM_STATE", "32a",
"-set", "_NET_WM_STATE",
"_NET_WM_STATE_ABOVE,_NET_WM_STATE_SKIP_TASKBAR,_NET_WM_STATE_SKIP_PAGER"])
七、常见问题
Q1: SPI LCD 不显示
- 检查内核模块是否加载:
lsmod | grep panel - 检查 DRM 设备:
ls /dev/dri/ - 检查 overlay 是否生效:
cat /sys/class/drm/card0-SPI-1/status
Q2: mono2d_body_detection 崩溃 (segfault)
- 原因:模型版本与 BPU SDK 不匹配
- 解决:使用默认
model_type=0,不要指定kps_model_type:=1
Q3: 按钮无法点击
- 原因:窗口类型为 SPLASH 时不接收鼠标事件
- 解决:使用 DOCK 窗口类型
Q4: Firefox 窗口位置不对
- 使用
xdotool强制调整(代码中已实现两次调整确保稳定)
Q5: XFCE 面板遮挡
- 启动脚本会自动隐藏 xfce4-panel 和 xfdesktop
- 退出时自动恢复
八、参考资料
- Waveshare 3.5inch RPi LCD (F) Wiki
- RDK X5 GPIO 配置
- RDK X5 SPI 配置
- RDK X5 驱动开发
- 手部关键点检测
- 手势识别
- 人脸年龄检测
- 行人重识别
- RDK X5 内核源码
九、附件下载
rdk_x5_ai_demo.zip - 包含所有代码文件 + 一键安装脚本
包内文件:
rdk_x5_ai_demo/
├── ai_demo_browser.py # 主程序 (531 行)
├── start_ros2.sh # 启动脚本
├── ai-demo.desktop # 桌面快捷方式
└── install.sh # 一键安装脚本