[AI] OpenPilot C3X/C4 UI 模擬器操作教學:Windows 11+WSL2 桌面預覽

openpilot C3X/C4 UI 模擬器操作教學:Windows 11+WSL2 桌面預覽

本文介紹如何在 Windows 11 的 WSL2 環境中執行 openpilot 原生 UI, 並分別模擬 C3X/tizi 與 C4 的畫面配置。

這套方法不需要連接 comma 裝置、Panda 或車輛 CAN, 而是利用獨立的 Params 與 Cereal Fake Message Publisher, 提供 UI 所需的空白訊息,讓 openpilot UI 可以在 WSLg 視窗中啟動。

重要提醒:
這不是完整的車輛模擬器,也不會輸出 CAN 訊號。它只能用來檢查 UI、字型、翻譯、按鈕、設定頁面、警示文字及基本排版,不能取代 C3X/C4 實機與實車驗證。

一、模擬器能測什麼?

可以先在電腦上檢查

  • openpilot UI 是否能正常啟動。
  • 中文字型是否正確顯示。
  • Git LFS 圖片與字型資源是否完整。
  • 設定頁面與初始設定頁面。
  • 警示文字、按鈕及 HUD 排版。
  • C3X/tizi 大畫面配置。
  • C4 預設固定畫布的桌面呈現。
  • 不同 Git branch 或 commit 之間的 UI 差異。
  • 同時開啟兩個版本進行畫面比對。

不能取代的測試

  • Camera、modeld 與影像辨識。
  • Panda、CAN 與車型辨識。
  • 方向盤、ACC、煞車及 safety 驗證。
  • C3X/C4 GPU 實際效能。
  • 觸控、亮度、自動調光及溫度表現。
  • 實際安裝於車內時的可讀性。

二、C3X 與 C4 模式差異

模擬目標 UI 類型 必要環境變數 注意事項
C3X tizi/tici 大畫面 BIG=1 不要使用 BIG_UI=1
C4 預設 UI 不設定 BIG 維持 UI 預設畫布,不設定 SCALE
C3X 的關鍵參數是:
BIG=1
不是:
BIG_UI=1

此 Fork 的 C3X 對應 UI 類型為 tizi。 啟用 BIG=1 後,會使用較大的 tizi/tici 畫面配置, 原生畫布通常為 2160 × 1080。

C4 模擬則沿用 UI 預設尺寸。為了讓桌面畫面盡可能接近實機排版, 不要額外設定 SCALE=0.75SCALE=1.5 或其他縮放值。

三、模擬器架構

openpilot UI
    │
    ├── Raylib/WSLg 視窗
    ├── 獨立 Params 資料夾
    └── Cereal Fake msgq 假資料源

每一個模擬器視窗都必須有自己的:

  • Repository 或 worktree。
  • PARAMS_ROOT
  • CEREAL_FAKE_PREFIX
  • OPENPILOT_PREFIX
  • /dev/shm/msgq_<prefix>
  • Fake Message Publisher。

如果兩個 UI 共用 Params 或 Fake msgq 前綴,可能會出現資料互相污染、 卡在教學頁面或 MultiplePublishersError

四、Windows 與 WSL2 前置環境

建議環境:

  • Windows 11。
  • WSL2。
  • Ubuntu 22.04。
  • WSLg。
  • 可正常啟動的 openpilot Python virtual environment。
  • 已準備好的 opendbc_repomsgq_repo

1. 安裝 Ubuntu 22.04

以系統管理員身分開啟 PowerShell:

wsl --install -d Ubuntu-22.04

完成 Ubuntu 使用者建立後,確認 WSL 狀態:

wsl --list --verbose
wsl --status
wsl --version
wsl --update

Ubuntu 應顯示為 WSL 2。Windows 11 的 WSLg 可以直接顯示 Linux GUI, 一般不需要另外安裝 X Server。

2. 確認 WSLg

wsl -d Ubuntu-22.04 -- bash -lc 'echo $WAYLAND_DISPLAY; echo $DISPLAY'

也可以檢查 OpenGL:

wsl.exe -d Ubuntu-22.04 -- glxinfo -B

3. 安裝基本套件

進入 Ubuntu 後執行:

sudo apt update

sudo apt install -y \
  build-essential \
  git \
  git-lfs \
  libgl1-mesa-dev \
  libegl1-mesa-dev \
  libglfw3 \
  libglfw3-dev \
  libzmq3-dev \
  fonts-noto-cjk

初始化 Git LFS:

git lfs install

若該 branch 的建置工具明確顯示缺少 imgui,再針對 openpilot 的 virtual environment 安裝:

~/.local/bin/uv pip install \
  --python ~/openpilot/.venv/bin/python \
  imgui

不建議尚未看到錯誤就預先安裝大量 Python 套件。 不同 Fork 與 commit 的相依套件可能不同,應優先依照該 Repository 的 README 或 setup script 建立環境。

五、準備 openpilot Repository

建議不同版本使用獨立 Repository 或 Git worktree, 不要在同一個 working tree 中反覆切換正在開發的 branch。

範例:Clone 模擬器專用 Repository

Windows PowerShell:

cd C:\Users\<USER>\Documents\Codex

git clone https://github.com/KingChang168/openpilot.git openpilot-ui-sim

cd openpilot-ui-sim

git fetch origin --tags --prune
git switch --detach origin/main

$env:GIT_LFS_SKIP_SMUDGE='1'

git submodule update --init --recursive opendbc_repo msgq_repo

git lfs pull --include="openpilot/selfdrive/assets/**,openpilot/sunnypilot/selfdrive/assets/**"

切換其他 branch

git status --short --branch

git fetch origin <branch>:refs/remotes/origin/<branch>

git switch --detach origin/<branch>

git submodule update --init --recursive opendbc_repo msgq_repo

git lfs pull --include="openpilot/selfdrive/assets/**,openpilot/sunnypilot/selfdrive/assets/**"
Submodule 必須同步。
若主 Repository 已更新,但 opendbc_repomsgq_repo 仍停留在舊 SHA,可能造成 import、Cereal schema 或原生模組不相容。

模擬工作目錄不建議直接修改正式 branch,也不要 force push。 測試產生的 Params、log、route、build output 與 screenshot 也不要提交進 Repository。

六、設定模擬器環境變數

以下假設:

  • Repository:/home/kingchang/openpilot-main
  • Python:/home/kingchang/openpilot/.venv/bin/python
  • 模擬器名稱:main

先設定環境變數:

export REPO=/home/kingchang/openpilot-main
export PYTHON_BIN=/home/kingchang/openpilot/.venv/bin/python
export SIM_PREFIX=main
export PARAMS_ROOT=/tmp/openpilot-ui-main-params

export PYTHONPATH="$REPO:$REPO/opendbc_repo:$REPO/msgq_repo"

mkdir -p "$PARAMS_ROOT"
mkdir -p "/dev/shm/msgq_${SIM_PREFIX}"

若 Repository 位於 Windows 磁碟,例如:

/mnt/c/Users/kingchang.ECS/Documents/Codex/openpilot

一樣可以使用,但 SCons 掃描 Windows 檔案系統時通常比較慢。 若需要頻繁建置,建議將 checkout 放在 WSL ext4,例如:

~/src/openpilot-ui-sim

七、建立獨立 Params

不要使用真實 C3X/C4 裝置上的 Params, 也不要讓兩個模擬器共用同一個 Params 目錄。

執行以下指令寫入模擬器 Params:

env \
  OPENPILOT_PREFIX="$SIM_PREFIX" \
  PARAMS_ROOT="$PARAMS_ROOT" \
  PYTHONPATH="$PYTHONPATH" \
  "$PYTHON_BIN" - <<'PY'
from openpilot.common.params import Params

p = Params()

values = {
  "HasAcceptedTerms": "2",
  "HasAcceptedTermsSP": "1.0",
  "CompletedTrainingVersion": "0.2.0",
  "CompletedSunnylinkConsentVersion": "-1",
}

for key, value in values.items():
  try:
    p.put(key, value)
  except Exception as e:
    print(f"skip {key}: {e}")

try:
  p.put_bool("IsDriverViewEnabled", False)
except Exception as e:
  print(f"skip IsDriverViewEnabled: {e}")
PY

這些 Params 主要用來略過:

  • Terms 頁面。
  • SunnyLink consent。
  • Training 教學。
  • Driver Monitoring 教學或 Driver View。

不同 Fork 或 commit 的 Param key 可能不同。 如果出現 UnknownKeyName,應檢查該版本的:

openpilot/common/params_keys.h

不要因為某個 key 寫入失敗,就直接假設它已經生效。

重新測試 Onboarding

只刪除該模擬器自己的 Params:

rm -rf "$PARAMS_ROOT"

刪除後若要再次略過教學頁面,必須重新建立 Params 並重新執行前面的寫入指令。

八、建立 Fake Message Publisher

openpilot UI 會訂閱許多 Cereal services。 在沒有實機 daemon 的情況下,需要建立一個 Fake Publisher, 週期性送出空白 message。

建立檔案:

nano ~/start_ui_publisher.py

貼入以下內容:

from openpilot.cereal import messaging, services
import time

publishers = []

for name in services.SERVICE_LIST:
  if name == "uiDebug":
    continue

  try:
    publishers.append((name, messaging.PubMaster([name])))
  except Exception:
    pass

while True:
  for name, publisher in publishers:
    try:
      publisher.send(name, messaging.new_message(name))
    except Exception:
      pass

  time.sleep(0.1)
不要發布 uiDebug。
uiDebug 由 UI 本身擁有。如果 Fake Publisher 同時發布, 可能造成 multiple publishers 衝突。

啟動 Publisher

nohup env \
  CEREAL_FAKE=1 \
  CEREAL_FAKE_PREFIX="$SIM_PREFIX" \
  OPENPILOT_PREFIX="$SIM_PREFIX" \
  PARAMS_ROOT="$PARAMS_ROOT" \
  PYTHONPATH="$PYTHONPATH" \
  "$PYTHON_BIN" ~/start_ui_publisher.py \
  >"/tmp/openpilot-ui-${SIM_PREFIX}-publisher.log" \
  2>&1 \
  </dev/null &

確認 Message Socket

test -e "/dev/shm/msgq_${SIM_PREFIX}/deviceState" && echo READY

也可以直接查看:

ls -l "/dev/shm/msgq_${SIM_PREFIX}/deviceState"

看到 deviceState 後,才代表 Fake Publisher 已經建立訊息通道。

九、啟動 C4 UI 模擬器

C4 模式沿用 UI 預設畫面,不設定 BIG, 也不要額外設定 SCALE

cd "$REPO"

env \
  CEREAL_FAKE=1 \
  CEREAL_FAKE_PREFIX="$SIM_PREFIX" \
  OPENPILOT_PREFIX="$SIM_PREFIX" \
  PARAMS_ROOT="$PARAMS_ROOT" \
  PYTHONPATH="$PYTHONPATH" \
  DISPLAY=:0 \
  "$PYTHON_BIN" \
  "$REPO/openpilot/selfdrive/ui/ui.py"

UI 視窗出現後,即可檢查 C4 預設版面的字型、設定頁面、 警示文字及按鈕配置。

十、啟動 C3X/tizi UI 模擬器

C3X 模式與 C4 的主要差異,是啟動時加入:

BIG=1

完整指令:

cd "$REPO"

env \
  CEREAL_FAKE=1 \
  CEREAL_FAKE_PREFIX="$SIM_PREFIX" \
  OPENPILOT_PREFIX="$SIM_PREFIX" \
  PARAMS_ROOT="$PARAMS_ROOT" \
  PYTHONPATH="$PYTHONPATH" \
  BIG=1 \
  DISPLAY=:0 \
  "$PYTHON_BIN" \
  "$REPO/openpilot/selfdrive/ui/ui.py"

如果啟動後看起來仍然是一般 PC/C4 畫面,先確認是否真的傳入:

BIG=1

而不是:

BIG_UI=1

環境變數可能被背景啟動器遺失

如果 UI 是透過 Windows 背景工具或其他 launcher 啟動, launcher 可能沒有保留 BIGCEREAL_FAKE 或其他環境變數。

此時可在 Python 載入 UI 之前,直接透過 os.environ.update() 設定環境。

範例:

cd "$REPO"

"$PYTHON_BIN" -c "
import os
import runpy
import sys

repo = os.environ['REPO']

sys.path[:0] = [
  repo,
  repo + '/opendbc_repo',
  repo + '/msgq_repo',
]

os.environ.update({
  'CEREAL_FAKE': '1',
  'CEREAL_FAKE_PREFIX': os.environ['SIM_PREFIX'],
  'OPENPILOT_PREFIX': os.environ['SIM_PREFIX'],
  'PARAMS_ROOT': os.environ['PARAMS_ROOT'],
  'BIG': '1',
  'DISPLAY': ':0',
})

runpy.run_path(
  repo + '/openpilot/selfdrive/ui/ui.py',
  run_name='__main__',
)
"

Windows 掛載的工作樹特別容易遇到 Python 已經啟動, 但 Repository 路徑尚未加入 sys.path 的情況。 因此必須在 import openpilot 之前加入:

sys.path[:0] = [
  repo,
  repo + "/opendbc_repo",
  repo + "/msgq_repo",
]

十一、同時比較兩個版本

若要同時比較 maintest/sp-11.2, 兩個版本必須使用不同路徑、Params 與 Fake prefix。

版本 A:main

REPO=/home/kingchang/openpilot-main
SIM_PREFIX=main
PARAMS_ROOT=/tmp/openpilot-ui-main-params
MSGQ=/dev/shm/msgq_main

版本 B:test/sp-11.2

REPO=/mnt/c/Users/kingchang.ECS/Documents/Codex/2026-07-20/test-sp-11-2-codex-text/work/openpilot
SIM_PREFIX=local_a24
PARAMS_ROOT=/tmp/openpilot-ui-local-a24
MSGQ=/dev/shm/msgq_local_a24

確認第二個 Repository 的版本:

cd /mnt/c/Users/kingchang.ECS/Documents/Codex/2026-07-20/test-sp-11-2-codex-text/work/openpilot

git branch --show-current
git rev-parse HEAD
git status --short --branch

git submodule status opendbc_repo msgq_repo

每個版本都要分別執行:

  1. 設定自己的環境變數。
  2. 建立自己的 Params。
  3. 建立自己的 /dev/shm/msgq_<prefix>
  4. 啟動自己的 Fake Publisher。
  5. 啟動自己的 UI。
不要只啟動兩個 UI,卻讓它們共用同一個 Publisher。
這可能造成兩個視窗收到錯誤資料、同時卡在教學頁面, 或產生 MultiplePublishersError

十二、正確停止與重啟單一模擬器

不要使用:

pkill -f python
pkill -f ui.py

這些指令可能一次關掉其他正在運作的模擬器、Codex 工作或 Python 程序。

1. 找出指定 UI PID

ps -eo pid,args | grep -E 'ui.py|start_ui_publisher.py'

2. 只停止指定程序

kill <UI_PID>
kill <PUBLISHER_PID>

Publisher 應依照自己的 CEREAL_FAKE_PREFIX 與命令列內容辨識, 不要廣泛清除所有 Python process。

3. 重新啟動

重新執行:

  1. 建立或確認 msgq 目錄。
  2. 啟動 Fake Publisher。
  3. 確認 deviceState 存在。
  4. 啟動 C3X 或 C4 UI。

十三、常見問題與處理方式

問題 1:UI process 存在,但沒有視窗

先在 PowerShell 執行:

wsl --shutdown

再重新開啟 Ubuntu 並啟動 Publisher 與 UI。 如果仍無畫面,再完整重新啟動 Windows。

實際上可能發生 WSLg/GPU GUI 狀態失效: Python 程序仍在執行,OpenGL 檢查也可能正常, 但 Windows compositor 不再顯示 Linux 視窗。 這不一定是 openpilot source code 的問題。

問題 2:deviceState 不存在

錯誤可能類似:

deviceState: No such file or directory

依序確認:

  1. Publisher 是否仍在執行。
  2. /dev/shm/msgq_<prefix> 是否存在。
  3. UI 與 Publisher 的 prefix 是否完全一致。
  4. Publisher log 是否有 Python traceback。
mkdir -p "/dev/shm/msgq_${SIM_PREFIX}"

ls -l "/dev/shm/msgq_${SIM_PREFIX}/deviceState"

cat "/tmp/openpilot-ui-${SIM_PREFIX}-publisher.log"

問題 3:卡在 Driver Monitoring 教學

確認以下項目:

  • UI 與 Params 寫入指令使用完全相同的 PARAMS_ROOT
  • CompletedTrainingVersion 已成功寫入。
  • Terms 與 SunnyLink consent 已成功寫入。
  • IsDriverViewEnabled 已設為 false。
  • 沒有誤用另一個模擬器的 Params 目錄。

如果刪除 Params 重新建立,必須再次執行略過教學的 Params 寫入程序。

問題 4:No module named opendbc

確認:

export PYTHONPATH="$REPO:$REPO/opendbc_repo:$REPO/msgq_repo"

若使用 Python -c 或背景啟動器, 必須在 import openpilot 前加入:

sys.path[:0] = [
  repo,
  repo + "/opendbc_repo",
  repo + "/msgq_repo",
]

問題 5:UnknownKeyName

這通常代表 Python 正在載入另一個 branch 或 commit 所編譯的 params_pyx.so

當新 branch 增加了 Param key,卻沿用舊版原生模組時, 即使 Python source 已更新,原生模組仍可能拒絕該 key。

處理方向:

  1. 確認目前 branch 與完整 commit SHA。
  2. 確認 submodule SHA。
  3. 確認 Python 載入的是目前 Repository。
  4. 依照目前 commit 重新建置相關原生模組。

不建議直接以手動寫檔方式繞過 Params key 檢查, 因為 UI 讀取端仍可能檢查 key 是否存在。

問題 6:MultiplePublishersError

可能原因:

  • 舊 Publisher 尚未關閉。
  • 兩個模擬器使用相同 prefix。
  • Fake Publisher 發布了 uiDebug
  • 同時執行 replay 與同名 Message Publisher。

每個 UI 使用唯一的 CEREAL_FAKE_PREFIX, 並確認同一組 prefix 只有一個 Publisher。

問題 7:C3X 畫面仍像 C4/PC UI

確認真正使用:

BIG=1

不要使用:

BIG_UI=1

若透過背景 launcher 啟動,改用 Python os.environ.update(),並確保在載入 UI 前就設定完成。

問題 8:中文字型顯示問號

先確認 Git LFS 資源:

git lfs pull --include="openpilot/selfdrive/assets/**,openpilot/sunnypilot/selfdrive/assets/**"

接著檢查:

  • Fork 實際載入的 .ttf.otf.fnt
  • 字型檔是否只是 Git LFS pointer 文字檔。
  • Bitmap font 的 .fnt 與 atlas .png 是否成對。
  • PC 模擬器與 C3X/C4 實機是否載入同一套字型。

如果電腦與裝置使用不同字型,桌面模擬器的換行、 字寬與版面不能代表實機結果。

問題 9:圖片載入失敗

例如:

horizontal_scroll_indicator.png
always_offroad.png

通常表示 Git LFS 尚未下載:

git lfs pull --include="openpilot/selfdrive/assets/**,openpilot/sunnypilot/selfdrive/assets/**"

問題 10:Wi-Fi Adapter 錯誤

可能看到:

Object path ('T') must start with /

WSL 模擬環境通常沒有裝置端的 NetworkManager 或完整 Wi-Fi adapter, 此錯誤通常不影響 UI 畫面,可以先忽略。

問題 11:SCons 長時間停在 Reading SConscript files

Repository 若位於 /mnt/c, Windows 檔案系統掃描通常會比 WSL ext4 慢很多。

此外,tinygrad 的 GPU 探測也可能拖慢建置。 不建議直接永久修改 tinygrad source 規避問題。

較適合的長期做法:

  • 將 build checkout 放在 WSL ext4。
  • 只把截圖、結果或需要分享的檔案放回 Windows。
  • 保留各 branch 對應的原生模組與 submodule 狀態。

十四、安全原則

  • 模擬環境不可傳送 CAN。
  • 不要連接 Panda。
  • 不要啟動 controls output。
  • 不要修改或直接 push 正式 main branch。
  • 不同 UI 不共用 Params。
  • 不同 UI 不共用 Fake msgq prefix。
  • Fake Publisher 不發布 uiDebug
  • 不要使用全域 pkill 關閉其他模擬器。
  • 不要提交 Params、log、route、build output 或 screenshot。
  • UI 能開只代表桌面渲染成功,不代表車輛功能驗證通過。

十五、每次測試建議記錄的資訊

每次啟動模擬器,建議記錄:

  1. Repository 絕對路徑。
  2. Branch 名稱。
  3. 完整 commit SHA。
  4. opendbc_repo SHA。
  5. msgq_repo SHA。
  6. PARAMS_ROOT
  7. CEREAL_FAKE_PREFIX
  8. OPENPILOT_PREFIX
  9. 使用 C3X/tizi 或 C4 模式。
  10. 是否設定 BIG=1
  11. 是否保持未設定 SCALE
  12. 字型與 Git LFS assets 是否成功載入。
  13. UI 是否持續執行,而不是啟動後立即 crash。
  14. 與 C3X/C4 實機仍有哪些差異。

快速取得版本資訊

cd "$REPO"

git status --short --branch
git rev-parse HEAD
git submodule status opendbc_repo msgq_repo

十六、啟動流程快速摘要

  1. 安裝 Windows 11、WSL2、Ubuntu 22.04 與 WSLg。
  2. 準備 openpilot virtual environment。
  3. Clone 或建立獨立 Git worktree。
  4. 同步 opendbc_repomsgq_repo
  5. 下載 Git LFS 字型與圖片資源。
  6. 設定獨立的 Repository、prefix 與 Params 路徑。
  7. 建立模擬器專用 Params。
  8. 建立 /dev/shm/msgq_<prefix>
  9. 啟動 Fake Message Publisher。
  10. 確認 deviceState 已建立。
  11. C4 使用預設 UI,不設定 BIGSCALE
  12. C3X/tizi 加入 BIG=1,不要使用 BIG_UI=1
  13. 最後仍須回到 C3X/C4 bench 與實車驗證。
結論
openpilot UI 桌面模擬器非常適合在修改字型、翻譯、按鈕或設定頁面後, 先快速確認 C3X/C4 的基本排版。

但它沒有 Camera、modeld、Panda、CAN、觸控與實機 GPU, 因此模擬器畫面正常,只能代表 UI 在桌面環境成功渲染, 不能視為車輛控制或裝置相容性已經驗證完成。

文件整理依據:C4_UI_SIMULATOR_WSL2.md、openpilot-ui-simulator-guide.md

留言

這個網誌中的熱門文章

[旅遊]日本大阪部品(上) - 南海部品

[汽車] Caddy V (SB) 更換引擎空氣濾網教學