XGO-mini2SWWiki 首页
Wiki 首页/XGO-mini2SW/Luwu-OS 用户手册 / 开发篇
XGO-mini2SW 知识库Luwu-OS 用户手册 / 开发篇

02 系统架构

适用版本新版 Luwu-OS
版本说明
系列共用文档切换机型,继续阅读本篇
共用内容按本机型的执行器、外设和版本使用。

在机器狗上写代码前,先搞清楚 Luwu-OS 到底是怎么跑起来的。本章讲清它的整体分层、硬件组成、启动顺序、进程间通信与目录结构——理解了这些,你改代码时才知道该改哪里、改了会影响到谁。

本章包含:

小节

内容

难度

2.1 整体架构

三层架构、9 项外设与“单点独占”硬件设计(含 2.1.1 硬件规格表、2.1.2 官方四层分层)

★★☆

2.2 启动流程

从开机到主界面就绪经历了哪些 systemd 服务

★★☆

2.3 进程间通信(IPC)

FIFO、D-Bus、MQTT,以及外部启动应用的调试入口

★★☆

2.4 按键系统

A/B/C/D 四键如何从内核映射到应用

★★☆

2.5 目录结构

/opt/luwu-os/ 下每个目录放什么

★☆☆

2.6 常见问题排查

黑屏 / 应用起不来 / 按键无响应等

★☆☆

🧭 阅读路径:只想快速改代码的,可跳过 2.2/2.4 先看 2.5 目录结构;要自己新增功能或应用的,建议 2.1 → 2.3 → 2.5。

前置条件:

  • 完成第一章开发环境搭建,已能用 SSH 登录机器狗;

  • 会基本的 Linux 命令(ls、cat、cd 等)。

2.1 整体架构

┌──────────────────────────────────────────────────────┐
│  用户层                                               │
│  ┌────────────────────────────────────────────────┐  │
│  │     Qt 5.15 Launcher (C++)                      │  │
│  │  GalleryView │ StatusBar │ KeyFilter            │  │
│  └──────────────────┬─────────────────────────────┘  │
│                     │ FIFO pipes                       │
│  ┌──────────────────┴─────────────────────────────┐  │
│  │     PySide6 Applications (Python 3)             │  │
│  │  AI │ Coding │ Demos │ Settings │ ... 23 apps  │  │
│  └────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────┤
│  系统层 (Linux Kernel)                                │
│  FBTFT │ gpio-keys │ ALSA dmix │ pwm-fan            │
│  Picamera2 │ BlueZ │ UART │ GPIO                     │
└──────────────────────────────────────────────────────┘

核心设计要点: - Qt C++ 启动器管理主界面和应用生命周期 - Python PySide6 应用以独立进程运行,通过 FIFO 管道通信 - 无 X11/Wayland,直接渲染到 Linux 帧缓冲(240×320 SPI LCD,通过 udev 规则映射为 /dev/fb-spi) - 单个应用崩溃不影响其他应用

2.1.1 硬件规格

Luwu-OS 的硬件由若干外设组成,每个外设均有唯一主人(内核驱动或系统服务),应用只能通过标准接口访问:

#

组件

接口

驱动

1

SPI LCD(ST7789V,240×320)

SPI0 + GPIO 27/25/0

fbtft 内核驱动 → /dev/fb-spi

2

摄像头(OV5647)

CSI

Picamera2(各应用自行管理生命周期)

3

4 颗实体按键(A/B/C/D)

GPIO 17/22/23/24

gpio-keys → /dev/input/eventX

4

蓝牙(Cypress)

mini-UART / PL011

bluetoothd 通过 D-Bus

5

音频 + 麦克风(WM8960)

I2S + I2C

ALSA dmix + dsnoop

6

机器狗串口

ttyAMA0 / UART5

xgolib(自动识别)

7

舵机 / IMU / 电池

UART → MCU

xgolib 转发

8

散热风扇(4 线 PWM)

GPIO PWM + TACH

pwm-fan 内核驱动(hwmon)

9

电源 + 电池

VC 电压监控 + UART

vcgencmd + xgolib 联合监控

设计原则:每个硬件外设有且只有一个主人(内核驱动或系统服务);应用通过标准接口访问硬件,绝不互相争抢裸设备。

2.1.2 架构分层(官方定义)

层级

技术栈

职责

启动器

Qt 5.15 C++(EGLFS / LinuxFB)

进程生命周期管理、按键路由、状态栏

应用层

PySide6 Python(LinuxFB)

功能应用,渲染到帧缓冲

硬件抽象

内核驱动 + systemd 服务

单点独占硬件访问,经标准 Linux 接口暴露

进程通信

FIFO(/tmp/luwu_keys.fifo)、D-Bus、MQTT

跨进程数据交换

2.2 启动流程

电源接通 → Kernel 加载(gpio-keys/FBTFT/ALSA/PWM)
    → systemd 启动
    → luwu-hw-autoconf(硬件检测,切换新/老硬件 config.txt)
    → luwu-splash(启动画面)
    → luwu-launcher(主界面就绪)
    → luwu-portal-api(启动 Portal API Flask 服务)

提示:硬件型号由 configs/detect_device.py 串口实时探测(优先级:串口探测 > configs/device.ini 手动覆盖 > unknown),launcher 据此过滤不适用的示例程序。

这个过程由几个 systemd 服务串联,每个服务负责一件明确的事,可用 systemctl 查看与管理:

systemd 服务

作用

常用命令

luwu-splash.service

开机启动画面

sudo systemctl status luwu-splash

luwu-launcher.service

主界面(Qt 启动器),最核心

sudo systemctl restart luwu-launcher

luwu-hw-autoconf.service

硬件自动识别(CM4 / 老硬件 config.txt 切换)

sudo systemctl status luwu-hw-autoconf

luwu-portal-api.service

Portal API 服务(apps/portal/server.py,Flask)

sudo systemctl status luwu-portal-api

💡 改完代码后一般只需重启对应的应用(见 2.3 的 run.fifo),不必重启整机;只有动了内核配置 / 系统服务才需要 sudo reboot。

2.3 进程间通信(IPC)

FIFO 路径

方向

用途

/tmp/luwu_keys.fifo

Launcher → App

转发按键事件(子应用运行期间)

/tmp/luwu_preload.fifo

Launcher → Preload

通知预加载进程(apps/demo_page/preload_app.py)

/tmp/luwu_run.fifo

外部进程 → Launcher

外部写入脚本路径,由 launcher 走 launchApp 流程启动应用

Launcher 启动时会拉起 apps/demo_page/preload_app.py 预加载进程(提前加载 PySide6 等重依赖,加速示例页启动),并通过 /tmp/luwu_preload.fifo 通知其预加载;子应用退出后该进程也会被重启,用于兜底重绘防黑屏。

/tmp/luwu_run.fifo 是外部触发应用的调试入口,无需经过主界面菜单:

# 相对 LUWU_ROOT 路径
echo "apps/foo/main.py" > /tmp/luwu_run.fifo
# 绝对路径
echo "/opt/luwu-os/apps/foo/main.py" > /tmp/luwu_run.fifo

其他通信方式:MQTT(多机群组通信)、UDP 广播(无外网时的群组通信)。

如何用 run.fifo 调试单个应用:这是开发时最常用的一条调试路径——让 launcher 走完整的启动流程(自动套用主题、注册按键路由、接入状态栏),而不是裸 python3 main.py 只跑窗口。启动后 launcher 会把应用全屏显示到 /dev/fb-spi,你就能在实体屏上看到真实的界面效果:

# ① 让 launcher 启动你的应用(相对 LUWU_ROOT 路径即可)
echo "apps/my_new_app/main.py" > /tmp/luwu_run.fifo

# ② 查看应用是否被正确拉起(看最近的日志)
sudo journalctl -u luwu-launcher -n 50

💡 裸跑 python3 main.py 与走 fifo 的差别:裸跑不会应用 launcher 的主题 / 按键路由,部分应用在无 launcher 上下文时可能报错或显示异常。开发时建议先裸跑看代码逻辑,再用 fifo 验证完整效果。

2.4 按键系统

gpio-keys 内核驱动管理物理按键:

物理按键

GPIO

Linux Key

Qt::Key

A (左上)

GPIO17

KEY_LEFT (105)

Qt::Key_Left

B (右上)

GPIO22

KEY_RIGHT (106)

Qt::Key_Right

C (左下)

GPIO23

KEY_BACK (158)

Qt::Key_Back

D (右下)

GPIO24

KEY_ENTER (28)

Qt::Key_Enter

Launcher 的 KeyFilter 拦截按键 → 写入 /tmp/luwu_keys.fifo → 子应用通过 QSocketNotifier 读取。

在应用内处理按键,直接用 keyPressEvent(PySide6 会把按键事件派发给当前聚焦的控件):

from PySide6.QtCore import Qt
from PySide6.QtGui import QKeyEvent

class MyPage(AppFrame):
    def keyPressEvent(self, ev: QKeyEvent):
        if ev.key() == Qt.Key.Key_Back:      # 对应右下角 C 键(val:149)
            self.close()
        elif ev.key() == Qt.Key.Key_Enter:   # 对应 D 键(val:28)
            self.doSomething()
        else:
            super().keyPressEvent(ev)

⚠️ 用 Qt.Key_Back / Qt.Key_Enter 而不是硬编码按键值,是为兼容不同机型的按键映射(见 2.4 的物理键→QT 键对照)。

2.5 目录结构

/opt/luwu-os/
├── apps/          # 23 个应用 (ai/ai_chat_pro/ball_catch/ball_track/
│                  #   bluetooth_gamepad/coding/demo_page/face_follow/gamepad/
│                  #   gesture/group_perform/hotspot/joystick/network/perform/
│                  #   person_follow/portal/radar/rc_mode/rl_demo/settings/
│                  #   sound_locate/voice_chat)
├── launcher/      # Qt5 C++ 启动器 (main.cpp/galleryview/demogridview/
│                  #   statusbar/keyfilter/devicetable/i18n)
├── configs/       # 系统配置 (install.sh/luwu-keys.dts/detect_device.py/
│                  #   hardware_autoconf.py/language.ini/systemd services/
│                  #   asound.conf/udev rules/luwu-undervolt-monitor.py)
├── libs/          # 共享库 (xgolib/xgoedu-luwuos/theme/ui/gamepad_config/
│                  #   ydlidar_sdk/i18n.py/ball_catch_core.py)
├── model/         # AI 模型 ONNX (人脸/手掌/手部/人体/姿态/情绪/性别年龄/
│                  #   YOLO/语音唤醒词(hi_luka, xiaolu_classmate 等)/中文字体 msyh.ttc)
├── assets/        # 资源 (expressions/images/music)
├── scripts/       # 工具脚本 (check_update.py/edu_test.py/snap.py)
└── docs/          # 文档

目录结构说明:

  • launcher/ 下 galleryview.cpp 的 CARDS 数组定义主界面 5 张卡片(无线网络 / 图形化编程 / AI交互 / 示例程序 / 系统设置);demogridview.cpp 定义「示例程序」页内的 9 个演示程序;devicetable.h 是机型注册表(新增机型只需加一行)。

  • configs/language.ini 是全局语言配置(内容仅 cn 或 en 一行)。

  • model/ 中的 mp_*.py 是 MediaPipe ONNX 模型对应的 Python 包装类(MPPalmDet/MPHandPose/MPPersonDet/MPPose)。

2.6 常见问题排查

现象

可能原因

处理

开机后主界面黑屏 / 无画面

SPI LCD 未正确绑定 /dev/fb-spi

确认 ls -l /dev/fb-spi;查 configs/install.sh 第 4~6 步的 config.txt 与 udev 规则

主界面能看到,但分辨率不对 / 撕裂

帧缓冲尺寸与 LCD 不匹配

查 /boot/firmware/config.txt 中的 SPI 屏参数

某个应用起不来,run.fifo 写了没反应

launcher 未运行,或路径不对

确认 systemctl status luwu-launcher 正常;用相对 LUWU_ROOT 路径;看应用日志

按键无响应

按键设备树 / udev 未加载

ls /dev/input/event*;确认 luwu-keys.dtbo 已编译部署(install.sh 第 5 步)

改完代码不生效

没重启对应应用

用 2.3 的 run.fifo 重启该应用;改系统服务才需 reboot

机器狗对运动指令无反应

串口被占用 / 未识别机型

见第四章 xgolib 的 4.1.9 常见报错

资料来源XGO 官方 Wiki
返回 XGO-mini2SW 知识库目录