0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AMD Ryzen AI MAX+ 395 の NPU で YOLO11m×VLM ライブパイプラインを動かす — Ubuntu 26.04 アップグレード後の amdxdna / venv 復旧記

0
Last updated at Posted at 2026-07-24

はじめに

AMD Ryzen AI MAX+ 395(Strix Halo / XDNA2 NPU)搭載マシンで、USB カメラ映像に対して

  • YOLO11m の物体検出を NPU(VitisAI EP)でオフロード
  • VLM(Nemotron Nano Omni)の日本語キャプションを GPU(ROCm)で生成

しながら Chrome に MJPEG 配信する「LLaVA-NPU」パイプラインを、Ubuntu を 26.04 へ・ROCm を 7.14 へアップグレードした後に動かし直した記録です。

記事は 2 部構成です。

  • 前半: このパイプラインをどう動かすか(構成・バックエンド切替・一発起動・検証)
  • 後半: アップグレード直後にどこでトラブルが起きて、どう回避したか(NPU が消える/serve が起動しない/NPU サイドカーの Python が壊れる)

検証環境: NucBox EVO X2 / Ryzen AI MAX+ 395 w/ Radeon 8060S (gfx1151) / Ubuntu 26.04 LTS / kernel 7.0.0-28-generic / ROCm 7.14 / XRT 2.21.75 / NPU Firmware 1.1.2.65


最新のGitHub


第 1 部: どのように動かすか

1-1. 全体構成

パイプラインは 4 プロセス。tmux セッション llava の 4 ウィンドウとして起動します。

ウィンドウ 役割 実行環境
capture USB カメラ → 共有メモリ(SHM) に最新フレーム書込(1280×720 BGR, 30fps) uv venv
serve FastAPI + MJPEG 配信 + YOLO bbox / VLM caption を WebSocket 配信 uv venv
vlm llama-server(Nemotron Nano Omni, マルチモーダル) ROCm
npu-yolo NPU サイドカー(VitisAI EP で YOLO11m 推論、ローカル HTTP で bbox 配信) Ryzen AI venv

ポイントは NPU 推論を独立プロセス(サイドカー)に分離していること。NPU 実行には onnxruntime-vitisai + XRT の LD_LIBRARY_PATH が必要で、これは ~/ryzenai/ryzenai_venv にしか無いため、本体(torch-ROCm / ultralytics / fastapi)の venv とは混ぜません。VLM の llama-server と同じ「別プロセス + ローカル HTTP」パターンです。

[capture]            [npu-yolo sidecar]                 [serve]
 USB cam              RAI venv + VitisAI EP              FastAPI + MJPEG + WS
   |                    |  SHM(webcam_latest) を attach     |
   |--- SHM ----------->|  最新フレーム取得                  |
                        |  letterbox640 / RGB / ÷255        |
                        |  onnx(a16w8) 推論 @ NPU           |
                        |  decode + class-aware NMS         |
                        |  逆letterbox → 入力フレーム座標    |
                        |  bbox JSON --GET /latest--------->|  poll → /ws/bbox へ push

SHM を書く FrameSHM は純 Python(numpy + multiprocessing.shared_memory のみ、torch 非依存)なので、RAI venv からも import してフレームを読めるのが成立の鍵です。

1-2. バックエンド切替(GPU ⇄ NPU は 1 行)

config.yamlyolo.backend を切り替えるだけ。NPU が不調なら gpu に戻せば従来の Ultralytics/ROCm 経路が serve プロセス内で動きます。

yolo:
  backend: npu   # npu = XDNA2 NPU(VitisAI, 別プロセス) / gpu = Ultralytics(ROCm, serve内)

なぜ A16W8 か: YOLO11m を NPU で動かすには量子化が必須ですが、XINT8(活性化 8bit)だと分類ヘッドが潰れて検出ゼロになります。A16W8(活性化 INT16 / 重み INT8)で FP32 相当の精度に回復します。

1-3. 一発起動と検証

cd ~/LLaVA-NPU
./start_all.sh          # backend:npu なら npu-yolo ウィンドウも自動起動
# 停止:
./stop_all.sh
# ログ:
tmux attach -t llava    # Ctrl-b 0/1/2/3 = capture / serve / vlm / npu-yolo

起動順は不問(serve 側はサイドカーが立つまで HTTP リトライ)。初回のみ VitisAI が量子化モデルをコンパイルするため、最初の 1 推論に約 20 秒かかります。サイドカーはウォームアップ完了後に /latest を出す設計なので、serve は準備できるまで自然に待ちます。

NPU で実際に走っているかは xrt-smi で確認します。

/opt/xilinx/xrt/bin/xrt-smi examine -d 0000:c6:00.1 -r all
#  → HW Context=Active・Columns[0-7]・Submissions 増加 なら NPU 実行中

サイドカーの HTTP を直接叩けば bbox が見えます。

curl -s http://127.0.0.1:8082/latest
# {"frame_seq":1171,"frame_w":1280,"frame_h":720,"connected":true,
#  "boxes":[{"label":"person","conf":0.894,"x1":485.8,"y1":411.7,"x2":828.7,"y2":711.3},
#           {"label":"bed","conf":0.406,...}]}

あとはブラウザで http://localhost:8080/ を開き、人・物が bbox に正しく収まるか目視確認するだけです。判定基準は「xrt-smi が Active」+「ブラウザで正しい位置に bbox」です。

正常時の各ウィンドウの様子:

capture   : 30.0 fps  1280x720 BGR → SHM
serve     : http://localhost:8080/  HTTP 200 / VLM キャプション ~1.2s
vlm       : llama-server 8081  image processed in 325 ms
npu-yolo  : VitisAIExecutionProvider セッション確立 / warmup 40ms / 26.8 inf/s

第 2 部: どこでトラブルが起きて、どう回避したか

ここからが本題です。Ubuntu 26.04 / ROCm 7.14 へアップグレードした直後、上記パイプラインは動きませんでした。原因は独立した 4 つの問題で、順に切り分けました。

2-1. NPU が丸ごと消える(xrt-smi → 0 devices found)

再起動後、まず NPU デバイス自体が認識されません。

  • xrt-smi examine0 devices found
  • /dev/accel/ が存在しない
  • lsmod | grep xdna → 空(amdxdna 未ロード)
  • ただし PCI では見えている: c6:00.1 ... Strix Halo Neural Processing Unit

症状 A: modprobe amdxdnaExec format error

手動ロードを試すと、こうなります。

modprobe: ERROR: could not insert 'amdxdna': Exec format error
# dmesg: amdxdna: disagrees about version of symbol module_layout

原因は DKMS モジュールが古いヘッダ状態でビルドされていたこと。稼働カーネルは gcc-15 ビルドで module_layout の CRC が 0xe9196a28 なのに、DKMS 生成物は 0xd954c786 を要求していました。26.04 アップグレードの過程で「稼働カーネル本体」と「DKMS がビルドに使うヘッダ状態」がズレたのが正体です。

回避策は現行カーネルに対して DKMS を作り直すだけ。

sudo dkms remove  xrt-amdxdna/2.21.260102.53.release --all
sudo dkms install xrt-amdxdna/2.21.260102.53.release
sudo depmod -a

# 事前確認: 要求 CRC が稼働カーネルと一致するか(.ko.zst であることに注意)
modprobe --dump-modversions /lib/modules/$(uname -r)/updates/dkms/amdxdna.ko.zst | grep module_layout
#  → 0xe9196a28  module_layout  (= カーネル本体と一致すれば成功見込み)

sudo modprobe amdxdna
ls -l /dev/accel/          # → accel0 が生成される

💡 カーネル同梱の in-tree amdxdna/lib/modules/$(uname -r)/kernel/drivers/accel/amdxdna/amdxdna.ko.zst)も存在し、その CRC は必ずカーネルと一致します。DKMS がどうしても直らなければ in-tree 版に切り替える手もありますが、XRT 2.21 ユーザ空間との ABI 互換は xrt-smi examine の成否で確認してください。今回は DKMS 版で成功しました。

症状 B: mmap ... failed (EAGAIN) — memlock 不足

ドライバはロードできても、今度は xrt-smi がこう落ちます。

xrt-smi examine: mmap(len=64MB, offset=4GB) failed (err=-11 EAGAIN)

原因は memlock リミットが **8MB(8192KB)**と低く、XRT/NPU が要求する 64MB のピン留めメモリを確保できなかったこと(root では memlock が緩く成功したことで確定しました)。

回避策は全ユーザに memlock unlimited を適用。

echo '* - memlock unlimited' | sudo tee /etc/security/limits.d/99-xrt-memlock.conf

⚠️ この設定は PAM ログイン経由のセッションにのみ効きます(反映は再ログイン後)。将来 start_all.shsystemd サービス化する場合は PAM を通らないため、unit に LimitMEMLOCK=infinity が別途必要です。

復旧確認

再起動後、一般ユーザで以下の 3 点が sudo なしで通れば OK。

ulimit -l                                              # → unlimited                     ✅
ls -l /dev/accel/accel0                                # crw-rw-rw-+ root render 261,0    ✅
source /opt/xilinx/xrt/setup.sh && xrt-smi examine     # [0000:c6:00.1] NPU Strix Halo    ✅
XRT
  Version              : 2.21.75
  NPU Firmware Version : 1.1.2.65
Device(s) Present
  [0000:c6:00.1]  NPU Strix Halo   ✅

2-2. start_all.sh を叩くと serve と npu-yolo だけ落ちる

NPU デバイスが復旧したので ./start_all.sh を実行すると、4 ウィンドウのうち capture / vlm は正常serve と npu-yolo が別々の理由で起動失敗しました。どちらもアップグレードの副作用です。

症状 C: serve が ModuleNotFoundError: No module named 'fastapi'

File ".../src/server/app.py", line 22, in <module>
    from fastapi import FastAPI, WebSocket, WebSocketDisconnect
ModuleNotFoundError: No module named 'fastapi'

原因は、アップグレードで .venvbase 依存のみで再作成されていたこと。fastapi / aiortc / uvicorn / requests は pyproject.toml の optional group webrtc にあり、uv run serve(extra 指定なし)だと暗黙同期で env を base に揃えてしまい fastapi が消えるのです。

回避策は起動時に extra を明示すること。start_all.sh の serve 行を次のように修正しました。

-tmux send-keys -t "$SESSION:serve" "${ENV_PREFIX}uv run serve" C-m
+tmux send-keys -t "$SESSION:serve" "${ENV_PREFIX}uv run --extra webrtc serve" C-m

症状 D: npu-yolo サイドカーの Python が起動すらしない

これが一番厄介でした。サイドカーのウィンドウはこう出ます。

sys.executable = '/home/user/ryzenai/ryzenai_venv/venv/bin/python'
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
ModuleNotFoundError: No module named 'encodings'

インタプリタ自体が起動不能です。調べると根本原因はこうでした。

  • ryzenai venv は/usr/bin/python3.12(3.12.3)から --copies で作成されていた。
  • Ubuntu 26.04 で system Python が 3.14 になり、/usr/bin/python3.12/usr/lib/python3.12(stdlib)が消失
  • コピーされた python バイナリは残っているが、参照すべき stdlib が無くなり起動できない。

ここで安易に「新しい 3.14 で venv を作り直す」のはです。venv の site-packages に入っている RAI スタック(onnxruntime_vitisai 1.23.3 / voe 1.7.1 など 330 個)はすべて cp312(Python 3.12 ABI)のコンパイル済み wheel なので、3.14 では import できません。公式にも Ryzen AI は Python 3.12.x 専用Linux InstallationInstall Python 3.12.x と明記、3.13/3.14 は非対応)でした。

回避策: 壊れているのはインタプリタと stdlib だけなので、site-packages を温存したままインタプリタだけを 3.12 に差し替えます。apt に python3.12 が無い 26.04 では、uv 管理の standalone cpython-3.12.13(3.12 系なので cp312 wheel と ABI 互換)が使えます。

STD=$HOME/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/bin/python3.12
VENV=$HOME/ryzenai/ryzenai_venv/venv

# 旧 --copies バイナリを消してから venv を「in-place upgrade」する
# (--clear しないので site-packages 330 個は消えない)
rm -f "$VENV"/bin/python "$VENV"/bin/python3 "$VENV"/bin/python3.12
"$STD" -m venv --without-pip "$VENV"     # bin/ を standalone への symlink で再生成

# 検証: VitisAIExecutionProvider が出れば成功
source $HOME/ryzenai/ryzenai_venv/setup_ryzenai_env.sh
python -c "import onnxruntime as ort, voe; print(ort.__version__, ort.get_available_providers())"
#  → 1.23.3.dev...  ['VitisAIExecutionProvider', 'CPUExecutionProvider']

python -m venv <既存dir>--clear なし)は bin/ と pyvenv.cfg を作り直すだけで site-packages は保持します。旧 --copies バイナリが残っていると symlink 生成がスキップされるので、先に rm するのがコツです。

2-3. 復旧完了

以上 4 点を解消して ./start_all.sh を回すと、全 4 ウィンドウが一発で立ち上がりました。

capture   : 30fps  1280x720 BGR → SHM
serve     : http://localhost:8080/  HTTP 200
vlm       : llama-server 8081  Nemotron キャプション ~1.2s
npu-yolo  : VitisAIExecutionProvider セッション確立 / warmup 40ms
            /latest → {"boxes":[{"label":"person","conf":0.73,...}]}  ✅ NPU 推論

まとめ: 再発しやすいポイント

OS メジャーアップグレード後に NPU パイプラインが動かなくなったら、次の順で疑うと早いです。

  1. NPU が消えたら、まず DKMS 再ビルド。 版数が同じでも稼働カーネルとヘッダがズレると module_layout CRC 不一致(Exec format error)が起きる。sudo dkms install xrt-amdxdna/<ver> で解消。
  2. memlock unlimited は XRT/NPU の必須要件。 低いと mmap ... EAGAIN でデバイス認識に失敗する。
  3. system Python 依存の venv は壊れる。 --copies で作った venv は base の Python が消えると起動不能に。RAI は 3.12.x 専用なので、uv の standalone 3.12.13python -m venv --without-pip(旧 bin を rm 後)し、site-packages 温存でインタプリタだけ差し替えるのが最短。cp312 wheel はそのまま動く。
  4. uv run は extra を勝手に外す。 optional group に依存があるスクリプトは uv run --extra <group> ... で起動する。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?