Advertise with Googlier.com CGBeginner たてはま https://cgbeginner.net 個人CGクリエイターの雑記帳 Sun, 20 Sep 2026 11:26:14 +0000 ja hourly 1 https://wordpress.org/?v=7.1.1 https://cgbeginner.net/wp-content/uploads/2016/09/cropped-favicon_512-32x32.png CGBeginner たてはま https://cgbeginner.net 32 32 152190934 RTX 3090を2枚挿したらおかしくなった問題解決メモ https://cgbeginner.net/dual-rtx3090-code43-chipset-pcie/ https://cgbeginner.net/dual-rtx3090-code43-chipset-pcie/#respond Sun, 20 Sep 2026 11:26:12 +0000 https://cgbeginner.net/?p=2622 RTX3090を二枚刺ししたら両方Code 43になった——原因はチップセット側のPCIeスロットだった

この記事は、筆者のWindowsマルチGPU環境で実際に起きた問題を、AIとの調査ログをもとに整理したものです。 具体的なマシン名、ユーザー名、IPアドレス、ローカルパスなどの環境固有情報は省略・一般化しています。 すべての環境で同じ結果になるとは限りませんが、Windows 11、NVIDIA GPU、マルチGPU構成、ローカルLLMを使っている人の切り分け材料になれば幸いです。

起きたこと

ローカルLLM用のサブ機に、中古で買い足したRTX 3090を追加しました。もともと1枚RTX 3090が入っていたので、これで24GB×2の48GB構成になるはずでした。

ところが換装した瞬間から、2枚とも動かなくなりました。デバイスマネージャーで両方ともCode 43です。

構成はこうです。

  • Windows 11 Pro
  • MSI MEG X570 UNIFY / Ryzen 9 5950X / 128GB
  • GPU 1枚目: RTX 3090(CPU直結のPCIe x8スロット)
  • GPU 2枚目: RTX 3090(チップセット経由のPCIe x4スロット)
  • 2枚目は表示出力なしのヘッドレス
  • ローカルLLM実行環境: LM Studio

ここで大事な前提があります。この構成は、2枚目がRTX 3080だったときには、同じスロット配置で数年間まったく問題なく動いていました。 変えたのは2枚目のカードだけです。スロットもケーブルも電源も、そのままです。

また、買い足した中古のRTX 3090は、別のマシンに単体で挿して事前に検品してありました。VRAM全域テスト、OCCTの3D負荷1時間、Blenderでの数時間のレンダリング、全出力端子の確認まで通っています。単体では完全に健全なカードです。

症状の詳細

症状には、いくつか奇妙な特徴がありました。

ブートすると必ず2枚とも落ちます。 コールドブートでもウォームリブートでも同じで、しかもWindowsのイベントログには何も残りません。エラーひとつ出ないまま、デバイスマネージャーだけが両方Code 43になっています。

起動後に1枚ずつ、時間を空けて初期化し直すと、確率的に成功します。 デバイスを削除してハードウェアの再スキャンをかける操作を、1枚ずつ60秒以上の間隔を空けて実行すると、体感で3〜5割くらいの確率で両方とも「正常」になりました。同時にやると必ず失敗します。

そして「正常」になっても、計算はできませんでした。 デバイスマネージャーが正常(Code 0)で画面も出ているのに、nvidia-smi は次のように失敗します。

Failed to initialize NVML: Unknown Error

CUDAの初期化も通りません。cuInit() は CUDA_ERROR_NO_DEVICE(エラーコード100)を返してきます。つまり表示系だけ生きていて、計算系は完全に死んでいる状態です。

おもしろいことに、両カードのHDMIオーディオ機能だけは、2枚同時でも常に正常に動いていました。 PCIeのバスとしては2枚とも見えていて、動かないのはGPU本体の初期化だけ、ということです。

唯一の物証はdxgkrnlのライブダンプだった

イベントログが無言なので、証拠がありません。唯一見つかったのが、Windowsが密かに保存していたライブダンプでした。

置き場所はここです。

C:\Windows\LiveKernelReports\
C:\ProgramData\Microsoft\Windows\WER\ReportQueue\

これをWinDbgで開くと、次の内容でした。

VIDEO_MINIPORT_FAILED_LIVEDUMP (0x1B0)
Arg1: 0x00000002       Start device failed
Arg2: 0xC000009A       STATUS_INSUFFICIENT_RESOURCES

stack:
dxgkrnl!DpiFdoStartAdapterThread
dxgkrnl!DpiLdaStartAdapterInChain
dxgkrnl!DpiFdoStartAdapter

読み解くと「グラフィックスドライバがアダプタの起動中にリソース確保に失敗し、自分から降りた」という意味になります。電源が落ちたわけでも、PCIeのリンクエラーが出たわけでもありません。実際、この期間中のWHEA(ハードウェアエラー)の記録はゼロでした。

STATUS_INSUFFICIENT_RESOURCES という名前から、つい「メモリが足りない」「BARが足りない」と考えたくなりますが、これはNTSTATUSとしてはかなり汎用的なコードです。ドライバ内部のどこかで必要な資源を確保できなかった、という以上のことは分かりません。

効かなかったものの一覧

ここからは消去法でした。丸一日かけて、思いつく限りのソフトウェア側の変数を潰しました。結果は次のとおりです。

試したこと内容結果
ドライバ世代552.44 / 566.36 / 610.47 / 610.88 をそれぞれクリーンインストール変わらず
Windows更新正常な別マシンと同じビルドまで更新変わらず
BIOS更新AGESA 1.2.0.E → 1.2.0.F変わらず
BIOS設定全初期化、SR-IOV無効、Data Link Feature Exchange無効、XMP無効、PBO無効変わらず
メモリ整合性(HVCI)完全に停止して再起動変わらず
ブートの種類コールドブート / ウォームリブート変わらず
モニターの接続先どちらのカードに挿しても変わらず
2枚目の無効化デバイスマネージャーで無効にしても、物理的に挿さっているだけで1枚目のブート初期化が失敗変わらず
割り込みとリソースMSI割り込み正常、BARと親ブリッジの窓を全段実測して正常を確認割当は正しいのに失敗

ドライバの世代で唯一違ったのは、壊れ方の行儀だけでした。

  • 552.44(R550系):
    片方の失敗がもう片方を巻き込まない。時間差で初期化すれば2枚とも「正常」になり得る(ただし計算は死んだまま)
  • 566.36 / 610.x系:
    2枚目の初期化失敗が、正常に動いている1枚目まで道連れにして落とす
  • 576.80: この失敗でブートごとハングする(いちばん行儀が悪い)

新しいドライバほど「中途半端な状態を許さない」設計になっているように見えました。どれも問題そのものは直しません。

解決したのは物理配置の変更だった

結論から言うと、2枚とも CPU直結のスロットに並べ替えたら、それだけで全部直りました。

このマザーボードには、CPU直結のx16スロットが2本(2枚挿すとx8/x8に分かれる)と、チップセット経由のスロットがあります。もともと2枚目はチップセット側に挿さっていました。これを、CPU直結の2本に並べ替えたわけです。

問題は、CPU直結の2本目には10GbEのネットワークカードが挿さっていたことでした。そこで、そのNICを撤去してオンボードの2.5GbEに降りる、という妥協をしています。

結果は劇的でした。

  • ブート直後から2枚ともCode 0
  • nvidia-smi が2枚を正常に列挙
  • cuInit() が成功し、CUDAデバイスが2個見える
  • 37GB級のLLMが2枚に分割してロードできた

同じドライバ、同じBIOS、同じOS、同じカード、同じ電源です。GPUについて変えたのは、挿す場所だけでした。

なぜチップセット側のスロットだと動かなかったのか

ここは推測を含みます。確実に言えるのは「この構成では動かず、CPU直結にしたら動いた」という事実だけで、原因そのものを実測で確定したわけではありません。

そのうえで、筆者の理解はこうです。

チップセット配下のPCIeスロットは、電気的にPCIeの信号が来ているだけで、CPU直結のスロットとは性質が違います。CPUとの間にチップセットという中継が1段入り、その先の帯域はNVMe SSDやUSB、SATAなど他のデバイスと共有されます。レイテンシもトポロジも、ファームウェアから見た扱いも別物です。「挿さって認識するから同じ」ではありません。

そして今回の決定的な違いは、2枚が同じ型番(同じPCIデバイスID)のGPUになったことでした。異なるカード(3090と3080)の組み合わせでは、ドライバは2枚を独立した別物として初期化していたのだと思います。ところが同一デバイスIDのGPUが2枚見えると、ドライバはマルチGPUとしてまとめて扱う経路に入ります。そこで、CPU直結とチップセット経由という非対称なトポロジが問題になったのではないか、という見立てです。

傍証として、世の中で「RTX 3090を2枚で動かしている」構成は、ほぼ例外なくCPU直結のx8/x8です。コミュニティを調べても、チップセット側に2枚目を挿した構成では、Code 43になる報告や、動いたとしても長いコンテキストで出力が壊れるという報告(llama.cppのissue)が見つかりました。

一方で、Linuxでは同じようなチップセット配下の構成が動いている報告もあります。つまりこれはWindowsのドライバ固有の相性という面もありそうです。ただし「OSを選べば動く」を前提にした構成は、あまり安全な賭けではないと思います。

物理的な落とし穴:スロットの間隔

CPU直結の2本に並べる、と言葉にするのは簡単ですが、ここにも罠がありました。

このマザーボードのCPU直結スロットは、**3スロット間隔(60.96mm)**で配置されています。2019年の設計で、当時の2スロット厚のカードを2枚並べる前提です。

ところが載せようとしたカードの厚みは、片方が61mm(3.002スロット)、もう片方が57mmでした。厚い方を上に置くと、計算上の隙間はマイナスになります。つまり物理的に当たります。

薄い57mmのカードを上にすると、下のカードのバックプレートまで数mmの隙間ができます。今回はこの順序で、ファンが干渉しないことを確認したうえで組みました。実際に測ってみるまでは「どちらの順でも入るだろう」と思っていたので、これは事前に計算しておくべきでした。

CPU直結スロットの間隔とカード厚の関係
3スロット間隔60.96mmに対して、61mmのカードは下段スロットに届いてしまう

スロットの間隔は「ブラケット1枚が20.32mm」で計算できます。手持ちのカードの公称寸法(メーカーのスペック表に厚みがmmで載っています)と突き合わせれば、挿す前に判断できます。

当然、この配置では上のカードの吸気がかなり苦しくなります。そこで電力制限を掛けました。全負荷で40分ほど回したところ、上のカードはファン98%で84〜85℃の平衡に落ち着きました。余裕があるとは言えませんが、実用範囲には収まっています。

同じ症状のときの確認手順

同じように「GPUを増やしたら動かなくなった」という場合、次の順で見ると切り分けが早いと思います。

1. デバイスマネージャーの「正常」を信じない

Code 0は「表示ドライバが起動した」以上の意味を持ちません。計算まで生きているかは、別に確かめる必要があります。

nvidia-smi

これで全部のカードが列挙されるか。さらに確実なのは、CUDAの初期化そのものを叩いてみることです。CUDA Toolkitを入れなくても、ドライバに含まれる nvcuda.dll を直接呼べば確認できます。

$src = @'
using System;
using System.Runtime.InteropServices;
public static class Cuda {
    [DllImport("nvcuda.dll")] public static extern int cuInit(uint flags);
    [DllImport("nvcuda.dll")] public static extern int cuDeviceGetCount(out int count);
}
'@
Add-Type -TypeDefinition $src
$rc = [Cuda]::cuInit(0)
$n = 0
[Cuda]::cuDeviceGetCount([ref]$n) | Out-Null
"cuInit=$rc devices=$n"

cuInit=0 で、デバイス数が実際の枚数と一致していれば本物です。

2. BARとブリッジの窓の割当を実測する

「Resizable BARを有効にしたから大丈夫」ではなく、割り当てられた結果を見ます。

pnputil /enum-devices /class Display /resources

これで各カードのメモリ窓が見えます。24GBクラスのカードなら、16MB程度の小さい窓と、256MB(ReBAR有効なら数GB〜数十GB)の大きな窓が並ぶはずです。大きい窓が割り当てられていないカードは、そこで既に詰んでいます。

実は今回、最初にぶつかったのはこれとは別の問題でした。Resizable BARを有効にしていると、このマザーボードのファームウェアがチップセット側のブリッジに17〜24MBしかアドレス窓を用意せず(必要なのは300MB以上)、2枚目の大きなBARが割り当てられないままになっていたのです。Resizable BARを無効にしてAbove 4G Decodingだけ有効にしたら、両方に正しく割り当たりました。

3. ライブダンプを探す

イベントログが無言でも、グラフィックスカーネルがダンプを残していることがあります。

C:\Windows\LiveKernelReports\
C:\ProgramData\Microsoft\Windows\WER\ReportQueue\

0x1B0 の Arg1=2 は「Start device failed」、Arg2 がドライバの返したNTSTATUSです。ここまで分かると、電源やケーブルを疑う段階は終わります。

4. 1枚ずつ時間差で初期化してみる

pnputil /remove-device "<インスタンスID>"
pnputil /scan-devices

1枚だけ再初期化して、60秒ほど待ってからもう1枚。これで通るなら、同時初期化の競合という線が濃くなります。ただし今回のように、通っても計算が死んでいる場合があるので、必ず手順1まで確認してください。

5. CPU直結スロットに移せるか検討する

ここまでで直らなければ、トポロジを疑う番です。マザーボードのマニュアルで、どのスロットがCPU直結で、どのスロットがチップセット経由かを確認します。仕様表には「x16/x8+x8(CPU)」「x4(チップセット)」のように書かれています。

移す前に、前の章で書いたスロット間隔とカード厚の計算をしておくことをおすすめします。

まとめ

今回の結論はこうです。

カードも電源もドライバも悪くありませんでした。同じ型番のGPUを2枚使うとき、片方をチップセット経由のスロットに挿していたことが問題でした。

ソフトウェア側で丸一日かけて潰した変数——ドライバ4世代、OS更新、BIOS更新、BIOS設定の全初期化、メモリ整合性——は、どれひとつとして症状を変えませんでした。結果として、それらの作業は「ソフトウェアでは直らない」ことを確かめるための消去法になりました。

マルチGPUを組むときは、挿す場所をカードの選定と同じくらい真剣に考える価値があります。特に同じ型番を並べる場合は、CPU直結のレーンに対称に置ける構成を最初から選んだほうが安全です。

最後に、ひとつ注意を書いておきます。この記事で解決したのは、あくまでGPUの初期化が失敗する問題です。このマシンには組み立て当初から続く別の不安定さ(重い処理中の突然の再起動など)があり、そちらはこの変更とは無関係に残りました。ひとつの症状が直ったことを、マシン全体が健康になった証拠と混同しないようにしています。

]]>
https://cgbeginner.net/dual-rtx3090-code43-chipset-pcie/feed/ 0 2622
Xiaomi Smart Band 7をGadgetbridgeに移行してInfluxDB/Grafanaで可視化 https://cgbeginner.net/xiaomi-smart-band-7-gadgetbridge-influxdb-grafana/ https://cgbeginner.net/xiaomi-smart-band-7-gadgetbridge-influxdb-grafana/#respond Sat, 13 Jun 2026 11:39:45 +0000 https://cgbeginner.net/?p=2616 これは、Xiaomi Smart Band 7 のデータを Gadgetbridge 経由で取り出し、InfluxDB に保存して Grafana で可視化するまでの個人的な作業メモです。

作業自体は、デバッグモードにしたスマホをUSBでつなぎ、Codex GPT-5.5 xhighに自由にadbコマンドをたたいてもらって95%くらい自律的にやってもらいました。

もし役立てばということで記録します。

やりたかったこと

Xiaomi Smart Band 7 は小さくて軽く、電池持ちもよく、日常的に着けっぱなしにしやすいスマートバンドです。

ただ、歩数、心拍、睡眠、SpO2 などのデータは基本的に Zepp Life や Mi Fitness のアプリ内で見る前提になっています。自分の用途では、これを外へ出して、InfluxDB に蓄積し、Grafana で長期的に見たいという目的がありました。

今回の方針はこうです。

  1. Xiaomi Smart Band 7 を Zepp Life から Gadgetbridge 運用へ移す
  2. Gadgetbridge でアクティビティデータを同期する
  3. Gadgetbridge の SQLite DB を Android スマホ内に自動エクスポートする
  4. Syncthing-Fork でその DB をサーバーへ同期する
  5. サーバー側で SQLite を読んで InfluxDB に投入する
  6. Grafana でダッシュボード化する

全体像はこうなりました。

flowchart LR
    band["Xiaomi Smart Band 7"] -->|Bluetooth| phone["Galaxy S24 Ultra<br/>Gadgetbridge"]
    phone -->|Auto fetch activity data| gbdb["Gadgetbridge internal DB"]
    gbdb -->|Auto export database| exportdb["/sdcard/Documents/Gadgetbridge/Gadgetbridge.db"]
    exportdb -->|Syncthing-Fork| server["Orion<br/>Syncthing receive-only folder"]
    server --> importer["gb2influx<br/>Docker container"]
    importer --> influx["InfluxDB"]
    influx --> grafana["Grafana dashboard"]

スマホは Bluetooth 同期と DB エクスポートだけを担当し、InfluxDB token を持たせない構成にしました。Python で DB を読んで InfluxDB に書き込む処理は、Android ではなく Linux サーバー側で動かします。

環境

今回確認した環境です。

Phone: Galaxy S24 Ultra
Android: 16
Model: SM-S928Q
Band: Xiaomi Smart Band 7
Gadgetbridge: nodomain.freeyourgadget.gadgetbridge 0.91.1
Zepp Life: com.xiaomi.hm.health 6.16.0
ADB: Windows platform-tools from WSL

ADB は WSL から Windows 側の platform-tools を呼び出しました。

/mnt/c/Software/platform-tools/adb.exe

重要な注意点

Smart Band 7 を Gadgetbridge で使うには、Huami/Xiaomi の auth key が必要です。

この auth key を取得する前に Zepp Life アプリ側でバンドを解除すると、キーが無効になり、やり直しになります。Android の Bluetooth 設定からペアリング情報を消すことと、Zepp Life アプリ内でデバイス解除することは別物です。

今回やったこと、やらなかったことはこうです。

OK:
  - Zepp Life を強制停止する
  - Zepp Life を disable-user で無効化する
  - Gadgetbridge 接続成功後に Zepp Life を止める
  - Android OS 側の Bluetooth ペアリング情報を必要に応じて整理する

NG:
  - auth key 取得前に Zepp Life アプリ内でバンドを解除する
  - バンド本体を初期化する

Auth key 自体はこの記事には載せません。

Zepp Life を止める

auth key を取得し、Gadgetbridge 側へ移行する準備ができたので、Zepp Life を強制停止して無効化しました。

/mnt/c/Software/platform-tools/adb.exe shell am force-stop com.xiaomi.hm.health
/mnt/c/Software/platform-tools/adb.exe shell pm disable-user --user 0 com.xiaomi.hm.health

無効化されていることを確認します。

/mnt/c/Software/platform-tools/adb.exe shell cmd package list packages -d

実行結果です。

package:com.xiaomi.hm.health

戻す場合は以下です。

/mnt/c/Software/platform-tools/adb.exe shell pm enable com.xiaomi.hm.health

Gadgetbridge にペアリングする

Gadgetbridge 側では、次の流れで Smart Band 7 を追加しました。

  1. Gadgetbridge を開く
  2. Device discovery を開く
  3. Start discovery を押す
  4. Smart Band 7 が見つかる
  5. デバイス行を長押しする
  6. Authentication settings を開く
  7. auth key を入力する
  8. Submit する
  9. デバイス行を選択する
  10. Android の Bluetooth ペアリング確認を許可する
  11. Companion device permission を許可する
  12. バンド側に確認が出たら承認する

接続後、Gadgetbridge のメイン画面に Smart Band 7 が表示され、歩数、距離、睡眠などの値が出るようになりました。

この時点で、Zepp Life は「アプリ内で解除した」のではなく、Android 側で止めただけです。

Gadgetbridge の自動同期設定

Gadgetbridge では、まずアクティビティデータの自動取得を有効化しました。

Settings -> Automations -> Auto fetch activity data

これは、バンドからスマホ上の Gadgetbridge へデータを取り込む設定です。サーバーへの送信ではありません。

次に DB の自動エクスポートを有効化しました。

Settings -> Automations -> Auto export database

エクスポート先は以下にしました。

/sdcard/Documents/Gadgetbridge/Gadgetbridge.db

Android のパスとしては、こちらと同じ場所です。

/storage/emulated/0/Documents/Gadgetbridge/Gadgetbridge.db

ここで重要なのは、Auto export database はあくまで「スマホ内の通常ストレージへ SQLite DB を書き出す」だけということです。サーバーへ自動で送るには、Syncthing などの別アプリが必要です。

エクスポート間隔は最終的に 3 時間にしました。健康データの可視化であればリアルタイム性はそこまで必要ないので、まずはこのくらいで十分と判断しました。反映遅延は、ざっくり「Gadgetbridge のエクスポート間隔 + Syncthing 同期 + importer の実行間隔」です。

スマホ内の DB を確認する

ADB で、スマホ内に DB が出ているか確認しました。

/mnt/c/Software/platform-tools/adb.exe shell '
for d in /sdcard/Documents/Gadgetbridge /storage/emulated/0/Documents/Gadgetbridge; do
  echo DIR:$d
  ls -la "$d" 2>&1
done
' | tr -d '\r'

実行結果です。

DIR:/sdcard/Documents/Gadgetbridge
total 2740
-rw-rw---- 1 u0_a293 media_rw 2805760 2026-06-13 18:40 Gadgetbridge.db

DIR:/storage/emulated/0/Documents/Gadgetbridge
total 2740
-rw-rw---- 1 u0_a293 media_rw 2805760 2026-06-13 18:40 Gadgetbridge.db

DB ファイルを探す場合は、こうしました。

/mnt/c/Software/platform-tools/adb.exe shell '
find /sdcard/Documents -maxdepth 4 \( -iname "*gadget*" -o -iname "*.zip" -o -iname "*.db" \) -print 2>/dev/null
' | tr -d '\r'

実行結果です。

/sdcard/Documents/Gadgetbridge
/sdcard/Documents/Gadgetbridge/Gadgetbridge.db

SQLite DB を WSL にコピーして中身を見る

初回確認では、Syncthing ではなく ADB で直接 DB を取り出しました。

adb pull で WSL 側のパスにうまく書けないことがあったので、今回は exec-out cat を使いました。

rm -rf /tmp/gadgetbridge-check
mkdir -p /tmp/gadgetbridge-check

/mnt/c/Software/platform-tools/adb.exe exec-out \
  cat /sdcard/Documents/Gadgetbridge/Gadgetbridge.db \
  > /tmp/gadgetbridge-check/Gadgetbridge.db

ls -lh /tmp/gadgetbridge-check/Gadgetbridge.db
sqlite3 /tmp/gadgetbridge-check/Gadgetbridge.db 'PRAGMA integrity_check;'
sqlite3 /tmp/gadgetbridge-check/Gadgetbridge.db '.tables'

実行結果です。

-rw-r--r-- 1 ctrluser ctrluser 2.7M Jun 13 18:41 /tmp/gadgetbridge-check/Gadgetbridge.db
ok

実際に入っていたテーブル

Smart Band 7 のデータは、今回の DB では XIAOMI_* 系ではなく、主に HUAMI_* 系のテーブルに入っていました。

件数はこうでした。

65172 HUAMI_EXTENDED_ACTIVITY_SAMPLE
 1599 HUAMI_SPO2_SAMPLE
  993 HUAMI_STRESS_SAMPLE
   40 HUAMI_PAI_SAMPLE
   26 BATTERY_LEVEL
    6 HUAMI_HEART_RATE_RESTING_SAMPLE

一方で、以下のテーブルは空でした。

XIAOMI_ACTIVITY_SAMPLE
XIAOMI_SLEEP_STAGE_SAMPLE
XIAOMI_SLEEP_TIME_SAMPLE
XIAOMI_DAILY_SUMMARY_SAMPLE
XIAOMI_MANUAL_SAMPLE
MI_BAND_ACTIVITY_SAMPLE

ここは Gadgetbridge のバージョン、デバイス、移行経路によって変わる可能性があるので、最初に .tables と PRAGMA table_info(...) を見るのが安全です。

歩数・心拍のテーブル

メインの活動データは HUAMI_EXTENDED_ACTIVITY_SAMPLE に入っていました。

sqlite3 /tmp/gadgetbridge-check/Gadgetbridge.db \
  'PRAGMA table_info(HUAMI_EXTENDED_ACTIVITY_SAMPLE);'

カラムは以下です。

TIMESTAMP
DEVICE_ID
USER_ID
RAW_INTENSITY
STEPS
RAW_KIND
HEART_RATE
UNKNOWN1
SLEEP
DEEP_SLEEP
REM_SLEEP

データ範囲と件数を確認しました。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  datetime(MIN(TIMESTAMP),'unixepoch','localtime') AS first_time,
  datetime(MAX(TIMESTAMP),'unixepoch','localtime') AS last_time,
  COUNT(*) AS rows,
  SUM(STEPS) AS total_steps,
  SUM(CASE WHEN HEART_RATE > 0 AND HEART_RATE != 255 THEN 1 ELSE 0 END) AS valid_hr_rows
FROM HUAMI_EXTENDED_ACTIVITY_SAMPLE;
"

実行結果です。

first_time           last_time            rows   total_steps  valid_hr_rows
2026-04-29 11:58:00  2026-06-13 18:10:00  65172  98252        14867

日別に見るとこうです。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  date(TIMESTAMP,'unixepoch','localtime') AS day,
  SUM(STEPS) AS steps,
  COUNT(*) AS rows,
  SUM(CASE WHEN HEART_RATE > 0 AND HEART_RATE != 255 THEN 1 ELSE 0 END) AS valid_hr_rows,
  MIN(NULLIF(HEART_RATE,255)) AS min_hr,
  MAX(CASE WHEN HEART_RATE != 255 THEN HEART_RATE END) AS max_hr
FROM HUAMI_EXTENDED_ACTIVITY_SAMPLE
GROUP BY day
ORDER BY day DESC
LIMIT 10;
"

実行結果の一部です。

day         steps  rows  valid_hr_rows  min_hr  max_hr
2026-06-13  2283   1091  609            55      141
2026-06-12  9      1440  348            56      111
2026-06-11  9      1440  324            56      113
2026-06-10  30     1440  341            54      102
2026-06-09  25     1440  388            53      106
2026-06-08  3459   1440  607            51      134

直近の有効な心拍行も見ました。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  datetime(TIMESTAMP,'unixepoch','localtime') AS local_time,
  STEPS,
  HEART_RATE,
  RAW_KIND,
  RAW_INTENSITY
FROM HUAMI_EXTENDED_ACTIVITY_SAMPLE
WHERE HEART_RATE > 0 AND HEART_RATE != 255
ORDER BY TIMESTAMP DESC
LIMIT 20;
"

実行結果の一部です。

local_time           STEPS  HEART_RATE  RAW_KIND  RAW_INTENSITY
2026-06-13 17:44:00  0      141         67        52
2026-06-13 17:43:00  31     140         64        63
2026-06-13 17:42:00  102    125         64        94

HEART_RATE = 255 は心拍なしとして扱い、InfluxDB へは入れないようにしました。

SpO2・ストレス・安静時心拍・バッテリー

SpO2 は HUAMI_SPO2_SAMPLE に入っていました。

sqlite3 /tmp/gadgetbridge-check/Gadgetbridge.db \
  'PRAGMA table_info(HUAMI_SPO2_SAMPLE);'
TIMESTAMP
DEVICE_ID
USER_ID
TYPE_NUM
SPO2

ここで注意が必要なのは、TIMESTAMP が13桁のミリ秒タイムスタンプだったことです。Unix 秒にするには /1000 します。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  datetime(TIMESTAMP/1000,'unixepoch','localtime') AS local_time,
  TYPE_NUM,
  SPO2
FROM HUAMI_SPO2_SAMPLE
ORDER BY TIMESTAMP DESC
LIMIT 10;
"

実行結果の一部です。

local_time           TYPE_NUM  SPO2
2026-06-13 12:55:30  0         97
2026-06-13 12:50:30  0         98
2026-06-13 12:45:30  0         97
2026-06-13 12:40:30  0         94

ストレスは HUAMI_STRESS_SAMPLE です。これもミリ秒タイムスタンプでした。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  datetime(TIMESTAMP/1000,'unixepoch','localtime') AS local_time,
  TYPE_NUM,
  STRESS
FROM HUAMI_STRESS_SAMPLE
ORDER BY TIMESTAMP DESC
LIMIT 10;
"
local_time           TYPE_NUM  STRESS
2026-06-13 13:06:00  1         10
2026-06-13 12:56:00  1         29
2026-06-13 12:51:00  1         20

安静時心拍は HUAMI_HEART_RATE_RESTING_SAMPLE です。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  datetime(TIMESTAMP/1000,'unixepoch','localtime') AS local_time,
  UTC_OFFSET,
  HEART_RATE
FROM HUAMI_HEART_RATE_RESTING_SAMPLE
ORDER BY TIMESTAMP DESC
LIMIT 10;
"
local_time           UTC_OFFSET  HEART_RATE
2026-06-13 12:56:00  32400000    61
2026-06-12 23:59:00  32400000    61
2026-06-11 23:59:00  32400000    60

バッテリーは BATTERY_LEVEL に入っていました。こちらは Unix 秒でした。

sqlite3 -header -column /tmp/gadgetbridge-check/Gadgetbridge.db "
SELECT
  datetime(TIMESTAMP,'unixepoch','localtime') AS local_time,
  LEVEL,
  BATTERY_INDEX
FROM BATTERY_LEVEL
ORDER BY TIMESTAMP DESC
LIMIT 10;
"
local_time           LEVEL  BATTERY_INDEX
2026-06-13 18:38:42  84     0
2026-06-13 18:37:42  83     0
2026-06-13 18:36:42  81     0

Syncthing-Fork でスマホからサーバーへ送る

Gadgetbridge の DB エクスポートはスマホ内で完結します。そこで、Android 側に Syncthing-Fork を入れ、サーバー側の Syncthing と同期させました。

Android 側は以下です。

package: com.github.catfriend1.syncthingfork
version: 2.1.1.0

このときは F-Droid の APK を ADB で入れました。

wget -O /tmp/syncthingfork.apk \
  https://f-droid.org/repo/com.github.catfriend1.syncthingfork_2010100.apk

/mnt/c/Software/platform-tools/adb.exe install "$(wslpath -w /tmp/syncthingfork.apk)"

通知権限やバックグラウンド動作まわりも ADB から設定しました。

/mnt/c/Software/platform-tools/adb.exe shell pm grant \
  com.github.catfriend1.syncthingfork android.permission.POST_NOTIFICATIONS
/mnt/c/Software/platform-tools/adb.exe shell pm grant \
  com.github.catfriend1.syncthingfork android.permission.ACCESS_FINE_LOCATION
/mnt/c/Software/platform-tools/adb.exe shell appops set \
  com.github.catfriend1.syncthingfork MANAGE_EXTERNAL_STORAGE allow
/mnt/c/Software/platform-tools/adb.exe shell dumpsys deviceidle whitelist \
  +com.github.catfriend1.syncthingfork

Android 側の Syncthing-Fork では、次のフォルダを共有しました。

label: Gadgetbridge
id: gadgetbridge
directory: /storage/emulated/0/Documents/Gadgetbridge

サーバー側は、Syncthing の gadgetbridge フォルダを receive-only にしました。

id: gadgetbridge
label: Gadgetbridge
path inside container: /var/syncthing/Gadgetbridge
host path: /home/ctrluser/docker/gb2influx/data
type: receiveonly

receive-only にした理由は、Android/Gadgetbridge 側を正とし、サーバー側の古い DB をスマホへ押し戻さないためです。

同期後、サーバー側には以下のように DB が届きました。

ssh orion '
ls -la ~/docker/gb2influx/data
find ~/docker/gb2influx/data -maxdepth 1 -type f \
  -printf "%f %s bytes %TY-%Tm-%Td %TH:%TM:%TS\n"
'

実行結果です。

Gadgetbridge.db 2805760 bytes 2026-06-13 09:40:15...

Linux 側の find では UTC 表示なので、これは Android 側の 2026-06-13 18:40 JST のエクスポートに対応します。

Syncthing のフォルダ状態も正常でした。

state: idle
globalFiles: 1
globalBytes: 2805760
localFiles: 1
localBytes: 2805760
needFiles: 0
needBytes: 0

InfluxDB に入れるデータ構造

InfluxDB 側は、最初から細かくやりすぎず、測定値ごとに measurement を分けました。

wearable_activity
  tags:
    source=gadgetbridge
    device=xiaomi_smart_band_7
  fields:
    steps
    heart_rate_bpm
    raw_intensity
    raw_kind
    sleep
    deep_sleep
    rem_sleep

wearable_spo2
  tags:
    source=gadgetbridge
    device=xiaomi_smart_band_7
    type_num=<TYPE_NUM>
  fields:
    spo2_percent

wearable_stress
  tags:
    source=gadgetbridge
    device=xiaomi_smart_band_7
    type_num=<TYPE_NUM>
  fields:
    stress

wearable_resting_heart_rate
  tags:
    source=gadgetbridge
    device=xiaomi_smart_band_7
  fields:
    heart_rate_bpm

wearable_battery
  tags:
    source=gadgetbridge
    device=xiaomi_smart_band_7
    battery_index=<BATTERY_INDEX>
  fields:
    level_percent

タイムスタンプの扱いは以下です。

HUAMI_EXTENDED_ACTIVITY_SAMPLE.TIMESTAMP: Unix seconds
BATTERY_LEVEL.TIMESTAMP: Unix seconds
HUAMI_SPO2_SAMPLE.TIMESTAMP: Unix milliseconds
HUAMI_STRESS_SAMPLE.TIMESTAMP: Unix milliseconds
HUAMI_HEART_RATE_RESTING_SAMPLE.TIMESTAMP: Unix milliseconds

Importer では、毎回直近 72 時間を再処理するようにしました。Gadgetbridge 側の同期遅延や睡眠データの後補正を拾うためです。

importer の配置

importer はスマホではなく、サーバー側で Docker コンテナとして動かしました。

配置はこうです。

Galaxy S24 Ultra
  /sdcard/Documents/Gadgetbridge/Gadgetbridge.db
  -> Syncthing-Fork

Orion
  /home/ctrluser/docker/gb2influx/data/Gadgetbridge.db
  /home/ctrluser/docker/gb2influx/app/gb2influx.py
  /home/ctrluser/docker/gb2influx/state/state.json
  -> writes to InfluxDB

InfluxDB / Grafana
  wearable_* measurements

手動実行は以下です。

ssh orion 'cd ~/docker/gb2influx && docker compose run --rm -e RUN_ONCE=1 gb2influx'

常駐実行は以下です。

ssh orion 'cd ~/docker/gb2influx && docker compose up -d'

初回の historical import では、以下の結果になりました。

written_points=67796 db=/data/Gadgetbridge.db

Syncthing で DB が届いたあとの再実行では、以下です。

written_points=4685 db=/data/Gadgetbridge.db

2回目の件数が少ないのは、state file を持っていて、初回の全量投入後は直近ウィンドウだけを再処理しているためです。

InfluxDB 側では、以下の measurement が確認できました。

wearable_activity
wearable_battery
wearable_resting_heart_rate
wearable_spo2
wearable_stress

件数確認では、以下のようになりました。

wearable_activity             405899 field values
wearable_battery                  26 field values
wearable_resting_heart_rate        6 field values
wearable_spo2                   1599 field values
wearable_stress                  993 field values

wearable_activity が SQLite の行数より多く見えるのは、InfluxDB 側では field value 単位で数えているためです。1行の SQLite レコードから複数 field が入るので、元の行数とは一致しません。

Grafana ダッシュボード

Grafana では、最初のダッシュボードとして以下を作りました。

uid: wearable-health-smart-band-7
title: Wearable Health - Smart Band 7
folder: Studio-managed
panels: 11

最初に見るパネルは、次のあたりで十分だと思います。

  1. 日次歩数
  2. 心拍の時系列
  3. SpO2 の時系列
  4. ストレスの時系列
  5. バッテリー残量
  6. 各 measurement の最新 timestamp

睡眠については、HUAMI_EXTENDED_ACTIVITY_SAMPLE に SLEEP, DEEP_SLEEP, REM_SLEEP のカラムがあります。ただ、これらの値の意味は Gadgetbridge の UI 表示と照合してから使った方がよさそうです。少なくとも最初のダッシュボードでは、睡眠ステージをきれいに解釈するより、就寝中の心拍、心拍の低下、起床前の上昇、データ欠損を見る方が安全だと思います。

現時点の状態

2026-06-13 時点で、状態はこうです。

Gadgetbridge pairing: OK
Zepp Life: disabled, not unpaired
Auto fetch activity data: enabled
Auto export database: enabled
Exported DB: /sdcard/Documents/Gadgetbridge/Gadgetbridge.db
DB integrity_check: ok
Syncthing-Fork: Android -> Orion sync OK
Orion Syncthing folder: idle / in-sync
Importer: Docker container running
InfluxDB measurements: wearable_activity, wearable_battery, wearable_resting_heart_rate, wearable_spo2, wearable_stress
Grafana dashboard: deployed

残っている作業は、数日から1週間ほど運用して、同期抜け、エクスポート間隔、バッテリー消費、睡眠データの解釈を確認することです。

感想

今回の構成で分かったのは、Xiaomi Smart Band 7 は Gadgetbridge との相性がかなりよく、買い替え前に試す価値が高いということです。

一方で、Gadgetbridge へつながっただけで自動的に InfluxDB まで流れるわけではありません。実際のワークフローは、バンド、Android、Gadgetbridge、DB export、Syncthing、importer、InfluxDB、Grafana の複数段に分かれます。

ただ、一度流れを作ってしまえば、各段階はかなり素直です。

特に重要なのは、最初に以下を切り分けて考えることでした。

Band -> Gadgetbridge:
  Bluetooth pairing, auth key, activity sync

Gadgetbridge -> Android storage:
  Auto export database

Android storage -> Server:
  Syncthing-Fork

Server -> InfluxDB:
  SQLite importer

InfluxDB -> Grafana:
  Dashboard

ここを混ぜて考えると、「Gadgetbridge に入れたのに Grafana に出ない」のように見えますが、実際には途中にいくつも確認ポイントがあります。

Smart Band 7 をすでに持っていて、Zepp Life のアプリ内だけでデータが閉じるのが不満な場合は、まずこの Gadgetbridge ルートを試すのがよさそうです。

]]>
https://cgbeginner.net/xiaomi-smart-band-7-gadgetbridge-influxdb-grafana/feed/ 0 2616
Windowsのネットワークドライブ「ローカル デバイス名は既に使用されています」の直し方 https://cgbeginner.net/windows-local-device-name-already-in-use-net-use/ https://cgbeginner.net/windows-local-device-name-already-in-use-net-use/#respond Sat, 30 May 2026 18:48:18 +0000 https://cgbeginner.net/?p=2415 WindowsでNASやSamba共有をネットワークドライブに割り当てていると、「Microsoft Windows Network: ローカル デバイス名は既に使用されています」と出て、ドライブに入れなくなることがあります。

原因は、古いSMB接続、ドライブ文字の競合、保存済み資格情報の不整合であることが多いです。PC再起動の前に、まずは問題のドライブ文字だけを切り直します。

まずこれを試す:対象ドライブだけ切って再接続

コピペ用です。実行前に、先頭3行の DRIVE、SHARE、USER だけ自分の環境に合わせて書き換えてください。パスワードは画面に出る入力プロンプトで入れます。

CMD版です。コマンドプロンプトを開いて実行します。失敗する場合は管理者として開き直してください。

set DRIVE=Z:
set SHARE=\\NAS\share
set USER=NASユーザー名

net use %DRIVE% /delete /y
net use %DRIVE% "%SHARE%" /user:%USER% * /persistent:yes

PowerShell版です。資格情報ダイアログにNASのパスワードを入力します。

$drive = "Z:"
$share = "\\NAS\share"
$user = "NASユーザー名"
$cred = Get-Credential -UserName $user -Message "NASの資格情報"
Remove-SmbMapping -LocalPath $drive -UpdateProfile -Force -ErrorAction SilentlyContinue
New-SmbMapping -LocalPath $drive -RemotePath $share -Credential $cred -Persistent $true

最初から net use * /delete を打たないのがポイントです。* /delete は全ネットワーク接続を切断します。別NASや開いているファイルも巻き込むので、まずは問題のドライブだけを狙います。

エラーの正体

Microsoftの資料では、このエラーはシステムエラー85、つまり「ローカルデバイス名がすでに使われている」状態です。ここでいうローカルデバイス名は、たいてい Z: や X: などのドライブ文字です。

見た目どおりに「別のドライブが見えている」とは限りません。エクスプローラー上では切断済みに見えても、SMBクライアント側には古い接続が残ることがあります。逆に、USBメモリ、カードリーダー、仮想ドライブなどが同じ文字を使っていることもあります。

「NASが壊れた」というより、Windowsがそのドライブ文字を再利用できない状態、と考えると切り分けしやすくなります。

まだ直らないときの順番

現在の接続を確認します。

net use

PowerShellなら次のように確認できます。

Get-SmbMapping

それでも「使用中」と言われる場合は、エクスプローラーやアプリがそのドライブを開いていないか確認してください。Microsoftの net use 資料でも、現在のドライブとして使っていたり、プロセスが使用中だったりする共有は切断できないと説明されています。Excel、バックアップソフト、同期ツール、ターミナルのカレントディレクトリが原因になることがあります。

Workstationサービスを再起動する場面

対象ドライブを削除しても残り続ける、net use では見えないのに同じ文字で再接続できない、ログオフや再起動なら直る、という場合は、Workstation サービス、つまり LanmanWorkstation を再起動します。

net stop workstation /y
net start workstation

注意点があります。この操作はSMBクライアント機能を一時停止するので、接続中のネットワークドライブや共有フォルダは切れます。共有上のファイルを閉じ、ほかの共有ドライブを使っていないタイミングで実行してください。

資格情報をリセットする場面

同じNASに入れるPCと入れないPCがある、パスワード変更後からおかしい、サーバー名とIPで挙動が違う、という場合は、古い資格情報を疑います。

GUIなら「資格情報マネージャー」から「Windows資格情報」を開き、対象のサーバー名やIPアドレスの資格情報を削除します。その後、正しいユーザー名とパスワードで再接続します。

コマンドで確認するなら、次のように保存済み資格情報を一覧できます。

cmdkey /list

削除は対象名を確認してから行います。別サービスの認証情報を消さないように注意してください。

cmdkey /delete:NAS名またはIPアドレス

ドライブ文字の競合を確認する

net use や Get-SmbMapping に出てこないのに Z: が使えない場合は、diskmgmt.msc で「ディスクの管理」を開き、USBドライブ、カードリーダー、仮想ディスクなどに同じ文字がないか確認します。

競合しているローカルディスクがある場合は、そのローカルデバイス側のドライブ文字を変更するのが基本です。ネットワークドライブには、普段ローカル機器に使われにくい X:、Y:、Z: などを使うと衝突しにくくなります。

Samba/NAS側も見るべきケース

Windows側を直しても再接続できないなら、NASやSamba側を確認します。特に見るべきなのは次の点です。

  • 共有パス \\NAS\share が正しいか
  • NASがスリープしていないか
  • Sambaサービスが起動しているか
  • サーバーのディスク空き容量が不足していないか
  • そのユーザーに共有とフォルダの権限があるか
  • Windowsから名前解決できているか

名前解決が怪しいときは、サーバー名ではなくIPアドレスで \\192.168.1.10\share のように試すと切り分けできます。ただし、同じサーバーへ別名・別ユーザーで同時接続すると別の認証エラーを招くことがあるので、先に既存接続を削除しておきます。

最終手段に近い操作

net use * /delete /y は、全ネットワーク接続をまとめて切る強い操作です。どうしても対象ドライブだけでは解消しない場合に限り、開いている共有ファイルを閉じてから実行します。

net use * /delete /y

レジストリ変更はさらに慎重に扱うべきです。Microsoftのシステムエラー85の記事には ProtectionMode というレジストリ値に触れた回避策がありますが、古いWindows/Terminal Server文脈を含む限定的な説明です。Windows 10/11の一般的な復旧手順として最初に触る場所ではありません。

レジストリを変更する場合は、必ずバックアップを取り、変更対象と理由を記録してから行ってください。原因が単なる残留接続や資格情報なら、レジストリ編集まで進む必要はありません。

サービスやタスクから使うならドライブ文字を避ける

バックアップソフトやWindowsサービスからNASにアクセスする場合は、Z:\backup のような割り当て済みドライブではなく、\\NAS\share\backup のUNCパスを使うのが基本です。

Microsoftは、サービスが実行時に net use でドライブ文字を割り当てる方法を推奨していません。ドライブマッピングはログオンセッションごとに管理されるため、ユーザーが見ている Z: とサービスが見ている Z: が一致しないことがあります。

まとめ

このエラーは、まず「問題のドライブ文字だけを消して、同じパスへ作り直す」のが最短です。それで直らない場合は、Workstationサービス、資格情報、ディスク管理のドライブ文字、NAS/Samba側の順に切り分けます。

再起動は確かに効くことがありますが、原因を残したままだとまた起きます。net use * /delete やレジストリ編集のような広い操作は最後に回し、対象を絞って直すのが安全です。

参考資料

]]>
https://cgbeginner.net/windows-local-device-name-already-in-use-net-use/feed/ 0 2415
Windows 11でCUDA使用中なのにタスクマネージャーのGPU使用率が0%になる問題をHAGS無効化で直した https://cgbeginner.net/windows11-hags-task-manager-gpu-utilization/ https://cgbeginner.net/windows11-hags-task-manager-gpu-utilization/#respond Sat, 30 May 2026 17:50:26 +0000 https://cgbeginner.net/?p=2604 この記事は、筆者のWindowsマルチGPU環境で実際に起きた問題を、AIとの調査ログをもとに整理したものです

起きたこと

Windows 11のマルチGPU環境で、ローカルLLMを実行しているときに奇妙な症状に遭遇しました。

nvidia-smiでは2枚のGPUにしっかり負荷がかかっているのに、Windowsのタスクマネージャーでは片方のGPU使用率だけが0%のままになっていました。

今回の構成は、ざっくり言うと次のようなものです。

  • Windows 11 Pro
  • NVIDIA GeForce RTX 3090 + RTX 3080
  • NVIDIA Driver 576.80
  • ローカルLLM実行環境: LM Studio
  • 片方のGPUは表示出力あり
  • もう片方のGPUはモニターなしのヘッドレス状態
  • マシンはサブ機で、普段はリモート操作

最終的には、HAGS(ハードウェア アクセラレータによる GPU スケジューリング)を無効化して再起動したところ、タスクマネージャー側でもCUDA負荷が見えるようになりました。

最初に疑ったこと: phantom device

このPCは過去にGPU構成を何度か入れ替えていました。

そのため、まず疑ったのはWindows上に残っている古いGPUの履歴です。Device Managerで「非表示のデバイスを表示」したときに出てくる、いわゆる phantom device / non-present device です。

PowerShellでは次のように確認できます。

Get-PnpDevice -Class Display

実際、過去に使っていたGPUや、同じGPUを別スロットに挿していたときのデバイスインスタンスが CM_PROB_PHANTOM として残っていました。

不要なphantom deviceは、管理者権限のPowerShellなどから pnputil で削除できます。

pnputil /remove-device "PCI\VEN_...."

この作業で、Device Manager相当の表示は現在刺さっている2枚のGPUだけになりました。BlenderのCycles設定にも古いGPUが残っていたので、userpref.blend のバックアップを取った上でデバイス一覧を再生成しました。

その結果、BlenderやDevice Manager上のGPU一覧は正常になりました。

しかし、タスクマネージャーで片方のGPU使用率が見えない問題はまだ残りました。

nvidia-smiではGPUはちゃんと動いている

ローカルLLMで負荷をかけると、nvidia-smiでは両方のGPUが使われていました。

実測では、おおよそ次のような状態でした。

RTX 3080: GPU使用率 約40%, VRAM 約9.7GB, 消費電力 約150W
RTX 3090: GPU使用率 約60%, VRAM 約19GB, 消費電力 約340W

つまり、GPUが使われていないわけではありません。

一方で、タスクマネージャーやWindowsのGPU Engine counterでは、片方のGPUだけ使用率が0%に見えていました。

Windows側のカウンタは次のように確認できます。

Get-Counter '\GPU Engine(*)\Utilization Percentage'

この時点では、GPUメモリ使用量は見えているのに、GPU Engine utilizationだけが0%という状態でした。

タスクマネージャーとnvidia-smiは同じものを見ていない

ここで重要なのは、Windowsのタスクマネージャーと nvidia-smi は同じ計測経路を見ているわけではない、ということです。

Microsoftの説明では、タスクマネージャーのGPU表示はWDDMのGPU schedulerやvideo memory manager由来の情報を使っています。

参考:

GPUs in the task manager – DirectX Developer Blog
The below posting is from Steve Pronovost, our lead engineer responsible for the GPU scheduler and memory manager. GPUs …

一方、nvidia-smiはNVIDIAのNVML側からGPU使用率を見ています。

参考:

https://docs.nvidia.com/deploy/nvidia-smi/index.html

そのため、CUDAやローカルLLMのようなcompute系の負荷では、nvidia-smiでは見えているのにタスクマネージャーでは見えない、ということが起き得ます。

特に今回のように、片方のGPUがヘッドレス、つまり表示出力を持っていない状態では、Windows側のWDDM GPU Engine counterがうまく出ないことがあるようです。

HAGSとは何か

HAGSは、Hardware-Accelerated GPU Schedulingの略です。

日本語のWindows設定では「ハードウェア アクセラレータによる GPU スケジューリング」と表示されます。

設定場所は次のあたりです。

設定
→ システム
→ ディスプレイ
→ グラフィック
→ 既定のグラフィック設定
→ ハードウェア アクセラレータによる GPU スケジューリング

ざっくり言うと、GPUに投げる仕事の順番管理を、従来よりGPUやドライバ側に寄せる機能です。ゲームではレイテンシやCPU負荷が改善することがあります。

一方で、CUDA、録画、リモート操作、マルチGPU監視などでは、環境によってGPU使用率表示や互換性に影響することがあります。

HAGSを無効化したらCUDA engineが見えるようになった

今回の環境では、HAGSを無効化して再起動したところ、Windows側のGPU Engine counterにCUDA負荷が出るようになりました。

無効化後、同じローカルLLM負荷をかけると、Get-Counterで次のように engtype_cuda が見えるようになりました。

GPU A: engtype_cuda 約40%
GPU B: engtype_cuda 約53%

nvidia-smi側でも両GPUに負荷が出ており、タスクマネージャー側の表示とも整合するようになりました。

つまり今回の問題は、GPU本体の故障ではありませんでした。

また、phantom deviceの残骸だけが原因でもありませんでした。

最終的には、HAGS有効時に、ヘッドレス側GPUのCUDA engine使用率がWindowsのタスクマネージャーに出ていなかった、という見立てです。

HAGSを無効化する副作用

HAGSを無効化すると、GPUスケジューリングは従来方式に戻ります。

あり得る影響としては、次のようなものがあります。

  • 一部ゲームでfpsやレイテンシが少し変わる
  • Frame Generationや低遅延系の機能に影響する可能性がある
  • 逆に、CUDA、Blender、ローカルLLM、録画、リモート操作、マルチGPU監視では安定する場合がある

今回のPCはゲーム用メインマシンではなく、ローカルLLMやBlenderなどのGPU作業用サブ機です。そのため、HAGSは無効のまま運用することにしました。

切り分け手順まとめ

同じように「GPUは動いているはずなのにタスクマネージャーで使用率が見えない」という場合、次の順番で確認するとよさそうです。

1. nvidia-smiを見る

まずはタスクマネージャーではなく、nvidia-smiで確認します。

nvidia-smi

ここでGPU使用率、VRAM使用量、温度、消費電力が出ていれば、GPU自体は動いています。

2. WindowsのDisplayデバイスを確認する

過去にGPUを差し替えている場合は、phantom deviceが残っていることがあります。

Get-PnpDevice -Class Display

CM_PROB_PHANTOM の古いGPUが大量に残っている場合は、不要なものを整理する価値があります。ただし、削除対象を間違えると面倒なので、実際に刺さっているGPUのInstance IDと照合してから作業したほうが安全です。

3. WindowsのGPU Engine counterを見る

タスクマネージャーの元になっているカウンタは、次のように見られます。

Get-Counter '\GPU Engine(*)\Utilization Percentage'

CUDA負荷なら engtype_cuda や compute 系のカウンタに値が出ることがあります。

4. HAGSを無効化して再起動する

HAGSが有効で、特にマルチGPUやヘッドレスGPUで使用率が見えない場合は、HAGS無効化を試す価値があります。

UIから変更するのが一番安全です。

設定
→ システム
→ ディスプレイ
→ グラフィック
→ 既定のグラフィック設定
→ ハードウェア アクセラレータによる GPU スケジューリング

変更後は再起動が必要です。

レジストリで確認する場合は、次のキーを見ます。

Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\GraphicsDrivers' -Name HwSchMode

一般に HwSchMode=1 はHAGS無効、HwSchMode=2 はHAGS有効、値がない場合はWindowsの既定設定に従います。

5. それでもだめならドライバ更新やダミープラグを試す

片方のGPUが完全にヘッドレスの場合、HDMIやDisplayPortのダミープラグを挿すことで、Windows側の扱いが変わることがあります。

ただし、これは原因切り分けとしては有用ですが、常用する必要があるかは環境次第です。

また、NVIDIAドライバのバージョンによって挙動が変わる可能性もあります。古いドライバを使っている場合は、Studio Driverなどの更新も試す価値があります。

まとめ

今回の結論はシンプルです。

GPUはちゃんと動いていた。タスクマネージャーが見えていなかった。原因はHAGSだった。

Windowsのタスクマネージャーは便利ですが、CUDAやローカルLLMの監視では万能ではありません。

特に、マルチGPU、ヘッドレスGPU、HAGS有効、CUDA/compute負荷の組み合わせでは、nvidia-smiやHWiNFO、MSI Afterburnerなど、別系統の監視ツールも併用したほうがよさそうです。

ローカルLLMやBlender用のWindowsマシンでGPU使用率が変に見える場合は、HAGSを一度疑ってみる価値があります。

]]>
https://cgbeginner.net/windows11-hags-task-manager-gpu-utilization/feed/ 0 2604
Gugugaga(ぐぐがが,古古嘎嘎)ペンギンまとめ https://cgbeginner.net/gugugaga/ https://cgbeginner.net/gugugaga/#respond Thu, 12 Mar 2026 11:51:10 +0000 https://cgbeginner.net/?p=2572 最近、ショート動画のタイムラインに、妙にかわいい小さなペンギンが流れてきませんか。ぽてぽて歩いて、ちょっと拗ねて、甘えるように「Gugugaga」と鳴く。最近ものすごい勢いで増殖しているミームです。YouTube ShortsやTikTok、抖音では、Gugugaga(古古嘎嘎)やEndministratorのタグがついた同系統の動画がすでにかなりの数出回っています。

かわいいのでまとめました。

Gugugagaペンギンとは何者なのか

「Gugugaga」は英語の “goo goo ga ga” から来ています。赤ちゃんのしゃべり方を表した言い方のようです。この音がミームとして爆発した経緯には諸説ありますが、中国語圏のゲームメディアでは、2025年にBilibiliで拡散した短いネタ動画『gugugaga』が転機として繰り返し言及されています。ここから「ペンギンっぽいものに咕咕嘎嘎の音を重ねる」という遊びが一気に広まったようです。

で、この流れに、ゲーム『Arknights: Endfield』の管理人(Endministrator)というキャラがなぜか混ざりました。このキャラがなんかペンギンっぽくみえることが理由のようです。

https://i.17173cdn.com/2fhnvk/YWxqaGBf/cms3/gRckjnbttbEoqAf.jpg

そしてAI動画ブームが到来し、このキャラクターをかわいくデフォルメしたキャラにGugugagaといわせるミームが発生しました。

情報源まとめ

一个二创梗,居然吵到要收版权费?《终末地》“企鹅管理员”作者向其他人索要授权费_网络游戏新闻_17173.com中国游戏门户站
《终末地》二创梗'企鹅管理员'作者竟索要版权费!AI视频爆火后陷版权争议,网友炸锅。揭秘咕咕嘎嘎梗起源与商业纠纷内幕。
]]>
https://cgbeginner.net/gugugaga/feed/ 0 2572
別PCからWSL2にSSH接続する方法(mirrored mode) https://cgbeginner.net/wsl2-ssh/ https://cgbeginner.net/wsl2-ssh/#respond Wed, 04 Mar 2026 14:04:18 +0000 https://cgbeginner.net/?p=2564 WSL2を使っていると、LAN内の別PCやタブレットからSSH接続したい場面があります。

WSL2はデフォルトでNAT構成になっており、同一PC内からは
localhost
で届くものの、LAN上の別PCからはそのままでは届きません。

対処方法はいくつかありますが、今回はWindows 11で使えるmirrored networking modeを使う方法を選びました。portproxyを使う方法と比べて、WSL2のIPアドレスが再起動のたびに変わる問題を気にしなくていいのがメリットです。

ということで、手順をメモ。


環境

  • Windows 11
  • WSL2 Ubuntu 24.04

目次

  1. WSL2にSSHサーバをインストール
  2. sshdのポートを変更
  3. mirrored modeを有効化
  4. ファイアウォールの設定

1.WSL2にSSHサーバをインストール

WSLのターミナルで実行します。

sudo apt update
sudo apt install -y openssh-server

インストールできたら、systemdでサービスとして登録・起動します。

sudo systemctl enable ssh
sudo systemctl start ssh

起動確認:

systemctl status ssh

Active: active (running) と表示されればOKです。


2. sshdのポートを変更

デフォルトは22番ですが、将来的にWindows側にもsshd(22番)を入れることを想定して、WSL2側は2222番にしておきます。

/etc/ssh/sshd_config の #Port 22 を
Port 2222 に変更します。

sudo sed -i 's/^#Port 22/Port 2222/' /etc/ssh/sshd_config

Ubuntu 24.04はsystemdのsocket
activationを使っているので、ポート変更後は以下の3コマンドが必要です。

sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo systemctl restart ssh

リッスンしているか確認:

ss -tlnp | grep 2222

0.0.0.0:2222 が表示されればOKです。


3. mirrored modeを有効化

Windows側の設定です。

%USERPROFILE%\.wslconfig
に以下を追記します(ファイルがなければ新規作成)。

[wsl2]
networkingMode=mirrored

PowerShellから書き込む場合:

Add-Content "$env:USERPROFILE\.wslconfig" "`n[wsl2]`nnetworkingMode=mirrored"

追記したらWSLを再起動します。

wsl --shutdown

WSLのターミナルを開き直して、WindowsのLAN
IPが含まれているか確認します。

hostname -I

192.168.xxx.xxx が含まれていればmirrored
modeが有効になっています。


4. ファイアウォールの設定

mirrored
modeではWindowsファイアウォールに加えて、Hyper-Vファイアウォールへの許可も必要です。これがないとLANからタイムアウトになります。

管理者権限のPowerShellで以下の2つを実行します。

# Hyper-Vファイアウォール
New-NetFirewallHyperVRule -Name "WSL2 SSH" -DisplayName "WSL2 SSH" -Direction Inbound -VMCreatorId "{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}" -Protocol TCP -LocalPorts 2222

# Windowsファイアウォール
New-NetFirewallRule -Name "WSL2 SSH" -DisplayName "WSL2 SSH" -Direction Inbound -Protocol TCP -LocalPort 2222 -Action Allow

{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
はWSL用に固定されているGUIDです。以下のコマンドで確認できます。

Get-NetFirewallHyperVVMCreator

接続確認

別PCから以下で接続できます。

ssh -p 2222 <ユーザー名>@<WindowsのLAN IP>

まとめ

項目設定内容
WSL2ネットワークmirrored mode
WSL2 sshdポート2222
Hyper-VファイアウォールTCP 2222 許可
WindowsファイアウォールTCP 2222 許可

portproxyと異なり、すべての設定が永続します。WSL再起動後も追加の操作は不要です。

]]>
https://cgbeginner.net/wsl2-ssh/feed/ 0 2564
○時○分から△△を再生すると □□と同時に年を越せます ジェネレーター https://cgbeginner.net/countdown-generator/ https://cgbeginner.net/countdown-generator/#respond Sat, 27 Dec 2025 17:33:56 +0000 https://cgbeginner.net/?p=2531 ○時○分から△△を再生すると □□と同時に年を越せます

というネタ、たまにXで見ますよね。

こういうネタを作るためには、「映画の何時間何分目に、このイベントが起こる」というタイムコードを確認して、そのうえで「元旦0時0分からの逆算を計算する」必要があるので結構めんどくさい。間違えると取り返しつかないですからね。

なので計算機を作りました。

映画タイムコード逆算ツール
現実の「合わせたい日時」(例:元旦 00:00:00)と、映画の「合わせたい瞬間のタイムコード」を入力すると、 その瞬間が現実時刻と一致するように「再生開始すべき日時」を逆算します。
タイムコードは HH:MM:SS / HH:MM:SS.mmm に対応。 フレーム入力は HH:MM:SS:FF(FPS指定)に対応します。
ステップ 秒
SS / MM:SS / HH:MM:SS も受け付けます。 フレーム指定は HH:MM:SS:FF または HH:MM:SS;FF です。
HH:MM:SS:FF を使わないなら無視されます。23.976 を使う場合は 23.976。
秒ズレを吸収するための微調整です(±秒)。
まだ計算していません。
]]>
https://cgbeginner.net/countdown-generator/feed/ 0 2531
Blenderで「OpenGL 4.3 が必要」エラーでクラッシュする問題解決メモ https://cgbeginner.net/blender-opengl-error/ https://cgbeginner.net/blender-opengl-error/#respond Wed, 03 Dec 2025 19:01:29 +0000 https://cgbeginner.net/?p=2519 Windows 11 環境で RTX 5090 を 2 枚(デュアル構成)にしたところ、Blender でレンダリングを実行したタイミングで不定期にクラッシュしました。発生時には、

  • Blender – Unsupported Graphics Card Configuration
  • A graphics card and driver with support for OpenGL 4.3 or higher is required. Plugging all monitors into your primary graphics card might resolve this issue. Installing the latest driver for your graphics card could also help.

というメッセージが出て終了することがありました(GPU 自体は当然 OpenGL 4.3 以上対応のはずなのに、です)。

イベントログ上では Blender 側が ucrtbase.dll で 0xc0000005(アクセス違反)で落ちる記録(Application Error 1000 / WER 1001)が残ることがあり、また WER 側には LiveKernelEvent(WATCHDOG/141 など)が出ることもありました。一方で、典型的な TDR(Display 4101)や nvlddmkm 153 が常に出る、という形ではありませんでした。

この問題が厄介だったのは、再現性が安定しない点です。ある時は同じシーンで通り、再起動を挟むと同じ操作で落ちる、という挙動になりました。

以下、いろいろChatGPTと相談しながら試した記録を記事にしています。

環境の特徴

今回のポイントは「デュアル GPU」だけではなく、モニタが複数枚で、両方の GPU に接続せざるを得ない構成だったことです。私の環境は6モニタなので、RTX 5090 は出力が 4 ポートのため物理的に全モニタを片方の GPU に集約することができません。

この前提があると、Windows と NVIDIA ドライバが「どちらの GPU で OpenGL コンテキストを作るか」を自動判定する場面で、意図しない選択が起きる余地が増えます。

切り分けで試したこと(うまくいかなかった/決定打にならなかったもの)

以下を試しました。

1) 片方ずつの GPU でレンダリング

Blender 側でデバイスを片方だけ有効にしてレンダリングし、さらに両方を有効にしてレンダリングも実施しました。両方を有効にした場合は、レンダリング時間が約半分(例:1分20秒→40秒程度)まで短縮し、計算自体は 2 枚使えていることが確認できました。ここから「2 枚構成そのものが常に破綻する」という状況ではないと判断しました。

2) OCCT などで GPU 負荷テスト(PL 制限含む)

Afterburner で 2 枚とも Power Limit を 80% にした状態でも OCCT の 3D 負荷を継続でき、電力・温度・安定性の観点では致命的な兆候は見えませんでした。さらに、Blender のレンダリング時間が PL 80% と 100% で大差が出ないケースもあり、少なくとも「PL が原因で Blender が落ちる」という説明は成立しにくい状況でした。

3) Blender の設定・インストールを疑う(ポータブル ZIP / config 削除)

ポータブル版を使い、C:\Users\<User>\AppData\Roaming\Blender Foundation\Blender の設定を削除し、起動直後のデフォルトシーンでも試しました。しかし、それでも落ちる時は落ちました。したがって、特定アドオンや設定の破損だけが原因とも言い切れませんでした。

4) CPU レンダリングでも同様に落ちることがある

レンダリングを CPU に切り替えても落ちることがありました。ここが非常に重要で、これにより「CUDA/OptiX の計算部分だけの問題」ではなく、Blender が動作するための描画系(≒OpenGL 側)での問題を強く疑う方向に寄りました。

5) DDU → 最新 Studio Driver をオフラインでクリーンインストール

セーフモードで DDU を使い、LAN ケーブルを抜いてオフラインで Studio Driver を入れ直しました。一時的に改善したように見えましたが、再起動後に再発しました。つまり、これは「やる価値はあるが、今回の決定打ではなかった」という位置づけでした。

6) iGPU を無効化

iGPU(AMD Ryzen 内蔵 GPU)をデバイスマネージャで無効化し、再起動しましたが、問題は継続しました。よって今回の主因が iGPU の混在だと断定するのは難しい状態でした。

最終的に効いた対策(解決策)

NVIDIA コントロールパネルで、グローバル設定の 「OpenGL レンダリング GPU (OpenGL rendering GPU)」 を

  • Auto(自動)→ 特定の RTX 5090(メイン側)に固定

へ変更したところ、Blender のエラーが止まり、少なくとも同一条件での再現ができなくなりました。現状、これが最も説明力の高い「解決策」になりました。

理屈

1) Blender は「UI/ビューポート=OpenGL」で動いている

Blender は UI とビューポート描画を OpenGL で行います。したがって「レンダリングボタンを押した瞬間」に落ちたように見えても、その瞬間に OpenGL が関与する処理は十分にあり得ます。たとえば、レンダリング開始時には進捗 UI 更新、レンダー結果の表示領域の更新、描画コンテキストの再初期化、ウィンドウ移動・モニタ跨ぎに伴うコンテキスト再確立などが起こり得ます。

また、CPU レンダリングでも落ちることがあった点は、「計算(CUDA/OptiX/CPU)ではなく、表示系(OpenGL)起点のクラッシュ」が混ざっている説明と整合します。

2) デュアル GPU + 複数モニタでは「どの GPU が OpenGL を担当するか」が揺れます

モニタが複数で両方の GPU に接続されていると、アプリのウィンドウがどのモニタ上にあるか、OS がどのアダプタを優先として扱うか、ドライバの自動判定がどう働くか、という条件が絡みます。

この自動判定が不安定だったり、特定条件で誤った GPU を選ぶ(あるいは OpenGL コンテキスト作成が失敗する)と、アプリ側は「OpenGL 4.3 以上が必要」という“能力不足に見えるエラー”として落ちることがあります。実際には GPU の能力不足ではなく、OpenGL コンテキストが期待どおりに作れなかったことが本質です。

そこで OpenGL rendering GPU を固定すると、OpenGL の担当 GPU がブレなくなり、コンテキスト作成・維持の条件が安定します。今回の改善はこの説明が最も自然です。

3) 「固定していないほうの GPU 接続モニタでも落ちない」理由

この挙動も矛盾ではありません。OpenGL rendering GPU を固定した場合、ウィンドウがどの GPU に接続されたモニタ上にあっても、OpenGL の実処理は指定 GPU で行われる(または少なくとも指定 GPU を優先)という形になります。そのため「非指定側モニタで操作したら必ず落ちる」ということにはなりません。

どちらの 5090 を指定するべきか

基本的には、次の方針が合理的です。

  • メインモニタが接続されている GPU(普段の UI を担当している側)を指定する
  • もし OC 版と無印があり、さらに PCIe のリンク条件などが良い側が明確なら、その「条件が良い側」を指定する

OpenGL は UI/ビューポートの体感にも関わるため、普段使いの滑らかさや安定性の観点では「メイン側に寄せる」判断が筋が通ります。

これで CUDA / OptiX のレンダリングは 2 枚使えるのか

使えます。OpenGL rendering GPU の設定は、主に OpenGL の描画担当を決める設定です。一方、Blender の CUDA / OptiX レンダリングは、Blender 側の「使用デバイス設定」で 2 枚とも有効にしていれば、基本的に両方を使います。

実際にタスクマネージャー上で 2 枚とも使用率が上がっている観測があるなら、「計算に使われている」という理解で問題ありません。

グローバル固定のデメリット(起こり得ること)

グローバル固定は運用が楽で、今回のように「Auto の揺れが原因」っぽいケースでは有効です。一方、理屈上のデメリットは次の通りです。

  • OpenGL を使う他アプリでも、その指定 GPU に OpenGL が寄るため、用途によっては負荷が偏る可能性があります
  • ごく稀に、特定アプリが「表示している GPU と同じ GPU で OpenGL したい」前提で作られている場合、挙動が変わる可能性があります(この種の問題は一般には多くありませんが、可能性としては残ります)

ただし、挙げられている主要アプリ(Premiere Pro / After Effects / Photoshop / DaVinci Resolve)は、実務上は DirectX/CUDA 側が中心で、OpenGL rendering GPU の固定が直撃するケースは多くありません。したがって、「まずグローバルで固定して安定を取る」という判断は現実的です。

もし将来、特定アプリだけ違和感が出た場合は、その時点で「グローバルは Auto に戻し、Blender(blender.exe)だけプログラム設定で固定する」という運用に切り替えると、影響範囲を最小化できます。

まとめ

今回の問題は、GPU 性能不足ではなく、デュアル GPU + 複数モニタ構成における OpenGL 担当 GPU の自動選択が不安定で、Blender の OpenGL コンテキストが破綻する瞬間があった、という整理が最も整合します。

DDU やドライバクリーンインストール、iGPU 無効化は「やるべき切り分け」ではありますが、決定打は NVIDIA コントロールパネルで OpenGL rendering GPU を固定することでした。これにより、Blender の UI/ビューポート(OpenGL)経路が安定し、レンダリング開始時のクラッシュが止まりました。

]]>
https://cgbeginner.net/blender-opengl-error/feed/ 0 2519
UbuntuにCockpitをインストールしてVM環境構築 https://cgbeginner.net/ubuntu-cockpit-vm/ https://cgbeginner.net/ubuntu-cockpit-vm/#respond Sun, 16 Nov 2025 15:23:36 +0000 https://cgbeginner.net/?p=2510 はじめに

自宅サーバ(Ubuntu)上で、Proxmox のような「全部入りハイパーバイザ」を入れずに、できるだけミニマルな構成で

  • Web UI から VM を操作したい
  • Windows 11 をデスクトップ用途で使える VM として常用したい

という目的で環境を構築したときの手順メモ。

ポイントは次の 3 つ。

  • Ubuntu そのままの上に KVM + libvirt を載せる
  • 管理用 Web UI として Cockpit(cockpit-machines) を使う
  • Windows 11 の要件を満たすために UEFI + vTPM 2.0 をきちんと用意する

この記事は、試行錯誤でハマった部分は省き、「未来の自分が見てすぐ再現できる」ことを狙って整理している。


前提環境

  • ホスト OS:Ubuntu Server 24.04 LTS (noble)
  • 仮想化支援:x86_64 CPU(KVM 対応 / VT-x / AMD-V 有効)
  • ネットワーク:systemd-networkd + netplan(NetworkManager には切り替えない前提)
  • 目的の VM:
    • 作業用 Windows 11(デスクトップ用途)

ここでは、作業用 Windows 11 VM(以降 columbia)を作るところまでを対象とする。


仮想化基盤(KVM / libvirt)と Cockpit の導入

1. 仮想化スタックのインストール

まずは KVM / libvirt / virt-install / OVMF / swtpm を入れる。

sudo apt update

# 仮想化基盤(KVM + libvirt)+ UEFI(OVMF) + vTPM(Windows 11 等向け)
sudo apt install -y \
  qemu-kvm libvirt-daemon-system libvirt-clients virtinst \
  ovmf swtpm swtpm-tools libtpms0

ポイント:

  • qemu-kvm を指定しているが、Ubuntu 24.04 では裏で qemu-system-x86 が選択される。
  • ovmf は UEFI ファームウェア(OVMF)を提供するパッケージ。
  • swtpm + libtpms0 は vTPM 2.0 のエミュレータ。Windows 11 の TPM 要件を満たすために必須。

2. Cockpit と主要アドオンのインストール

Cockpit 本体と、VM 管理に必要なアドオンを backports から入れる。

. /etc/os-release

sudo apt install -y -t ${VERSION_CODENAME}-backports \
  cockpit cockpit-machines cockpit-storaged cockpit-networkmanager
  • cockpit-machines … VM 管理 UI(libvirt のフロントエンド)
  • cockpit-storaged … ストレージ状態の確認・操作
  • cockpit-networkmanager … NetworkManager を使う場合のネットワーク管理 UI
    (今回は systemd-networkd 続投なので、編集機能はあまり使わず、主に状態確認に留める)

3. サービスの有効化

libvirtd は常駐、Cockpit はソケット起動にする。

sudo systemctl enable --now libvirtd cockpit.socket

ファイアウォールに UFW を使っている場合は 9090 番を開けておく。

sudo ufw allow 9090/tcp

4. libvirt グループへの追加

非 root で VM を扱えるように、自分のユーザーを libvirt グループへ。

sudo usermod -aG libvirt $USER

いったんログアウト/ログインし直し、groups で libvirt が含まれていることを確認する。


Windows 11 ISO とストレージの整理

1. ISO 用ディレクトリを用意

qemu:///system の VM は libvirt-qemu ユーザーで動くため、通常ユーザーのホームディレクトリにある ISO はそのままでは読めない。
インストールメディアは libvirt 管理下のディレクトリに置くのが安全。

sudo mkdir -p /var/lib/libvirt/iso

ここに Windows 11 の ISO を配置する。例:

# 手元の PC からサーバへコピーする例(IP は適宜変更)
scp Win11_24H2_Japanese_x64.iso \
    ctrluser@192.168.100.31:/var/lib/libvirt/iso/
    
sudo chown root:root /var/lib/libvirt/iso/Win11_24H2_Japanese_x64.iso
sudo chmod 644       /var/lib/libvirt/iso/Win11_24H2_Japanese_x64.iso

OS ディスク(qcow2)は、デフォルトの /var/lib/libvirt/images に作成する方針とする。


Cockpit へのアクセス

ブラウザから以下にアクセス。

  • https://<サーバIP>:9090/ (例:https://192.168.100.31:9090/)

自己署名証明書の警告は許可する。
Ubuntu のユーザー(例:ctrluser)でログインしておく。

左側メニューの「Virtual Machines(仮想マシン)」を開くと、libvirt の System インスタンス(qemu:///system)が見えるようになる。


Windows 11 VM(columbia)の作成

1. 新規作成ウィザード

Cockpit の「Virtual Machines」画面で「Create new virtual machine」をクリックし、以下のように設定する。

  • Name:columbia(小文字で統一)
  • Connection:System
    (qemu:///system:サーバ側で常時管理される VM 群)
  • Installation source / Type:
    → Local install media (ISO image) のような「ローカルインストールメディア」系を選ぶ
    (※「OS をダウンロード」は Windows が出てこないので使わない)
  • Operating system:
    • Windows 11 が候補に出ればそれを選択
    • 出ない場合は Windows 10 か Generic Windows など近いものを選んでおけばよい
  • Disk:
    • Format:qcow2(デフォルト)
    • Size:128 GiB(論理サイズ。qcow2 なので実際には使用量に応じて増える)
  • Memory:16 GiB(作業用の Windows として余裕を持たせる)
  • vCPU:詳細設定から 4 vCPU にする

重要:
ウィザードの最後で 「Create and edit」(作成して編集する) を選び、
作成直後に詳細画面へ入れるようにしておく。


VM 設定のカスタマイズ(UEFI / TPM / デバイス)

2.1 ファームウェアを UEFI にする

columbia の概要タブで、「Firmware」が UEFI になっていることを確認する。
もし BIOS になっていれば UEFI に変更する。

OVMF のエントリが複数ある場合は、可能なら Secure Boot 付きのものを選ぶ。

例:

  • UEFI x86_64: /usr/share/OVMF/OVMF_CODE.secboot.fd ...

が選べるならこれを使う。

2.2 TPM 2.0(vTPM)の設定

ovmf + swtpm を入れておけば、libvirt は vTPM 2.0 を扱える。
Cockpit に TPM 追加 UI がない場合でも、libvirt の XML に直接書けばよい。

定義を編集:

sudo virsh edit columbia

<devices> ... </devices> ブロック内に、以下のような <tpm> ブロックがあることを確認する。無ければ追加する。

<tpm model='tpm-crb'>
  <backend type='emulator' version='2.0'/>
</tpm>
  • model='tpm-crb' は TPM 2.0 向けのインターフェース
  • backend type='emulator' version='2.0' で swtpm を使う vTPM 2.0 になる

保存に成功すれば、その VM には TPM 2.0 デバイスが追加された状態になる。

2.3 ディスクと NIC の設定方針

初回インストールをスムーズに行うことを優先し、

  • システムディスク:バス sata のまま
  • NIC:モデル e1000e のまま

で進める。

どちらも Windows 標準ドライバで認識されるので、追加ドライバなしでインストーラが動く。

(後から virtio ドライバを入れてディスク/NIC を virtio 化することは可能。この記事ではそこまでは扱わない。)

2.4 Windows 11 ISO を CD-ROM として割り当て

Cockpit の columbia 詳細画面で、「Disks」タブから CD-ROM デバイスを確認・設定する。

  1. 既にインストール元として ISO が設定されていれば、そのままでもよい。
  2. 設定されていない/パスが古い場合は、一度削除し、改めて追加する:
    • 「Add disk」→ CD-ROM を選び、
    • Source ファイルとして
      /var/lib/libvirt/iso/Win11_24H2_Japanese_x64.iso
      を指定する。

これで、VM 起動時に DVD として Windows 11 インストーラがマウントされる。


Windows 11 のインストール

3.1 起動と「Press any key…」への対応

columbia が Shut off 状態であることを確認し、Cockpit から「Install」または「Start」を押す。

  • 直後にコンソールタブ(グラフィカルコンソール)を開き、
    コンソール内をクリックしてフォーカスを合わせておく。
  • 起動直後、画面下部に Press any key to boot from CD or DVD...... が一瞬表示されるので、すぐに Enter などを押す。

ここでキー入力が間に合わないと DVD ブートがスキップされ、

BdsDxe: No bootable option or device was found.

等と表示されてしまう。
その場合は一度シャットダウンして再度起動し、今度はメッセージが出た瞬間に Enter を押す。

3.2 インストーラの基本的な流れ

あとは物理マシンと同じ流れでインストールする。

  1. 言語・キーボードを選択 → 「次へ」→「今すぐインストール」
  2. プロダクトキー入力画面
    • ライセンスキーがあればここで入力
    • 後で入れる場合は「プロダクトキーがありません」を選択
  3. エディション選択(Pro / Home などライセンスに合わせたもの)
  4. ライセンス条項に同意
  5. インストールの種類
    → 「カスタム: Windows のみをインストール(詳細設定)」 を選ぶ

3.3 パーティションの作成

「Windows をインストールする場所」画面では、ディスク 0 に「未割り当ての領域」として ~128GB が見えているはず。

  • ここでは細かいことはせず、**未割り当ての領域を選択して「次へ」**でよい。
    Windows が EFI システムパーティション・MSR・C: ドライブなどを自動で作ってくれる。

その後は通常どおり、コピー → 再起動 → 初期設定(地域 / アカウント / プライバシーなど)を進めればよい。


余談として、最近のWindows11は、なにがなんでもMicrosoftアカウントとの紐づけをさせてくる仕様になってます。私はそれが好きではなく、ローカルアカウントでセットアップしたい。裏技的な作業になってしまうのですが、方法はあります。以下参考。

Windows 11 でのローカルアカウントを用いたセットアップ方法
目次: 概要; ローカルアカウントでのセットアップ手順; FAQ…

インストール後の確認と仕上げ

4.1 TPM 2.0 が認識されているか

Windows 起動後、Win + R から tpm.msc を実行し、TPM 管理画面を開く。

  • TPM のバージョンが 2.0 と表示されていれば OK。

これで Windows 11 の TPM 要件は満たされている。

4.2 ネットワーク

  • NIC を e1000e のままにしているので、特別なドライバを入れなくてもネットワークは動作しているはず。
  • ホスト側では libvirt の default ネットワーク(NAT)が使われる。
    当面はこのままで問題ない。必要に応じて、後から Linux ブリッジを作り、VM を LAN 直結にする設計も検討できる。

4.3 ゲストツール(virtio ドライバ)の導入(任意)

パフォーマンスを詰めたい場合は、別途 virtio-win.iso をダウンロードして

  • 2 枚目の CD-ROM として VM にマウント
  • ゲスト内で virtio-win-guest-tools.exe を実行

することで、virtio 系のドライバをまとめて導入できる。
その後、ディスクや NIC を virtio 化していく、という流れが一般的だが、本記事の範囲外とする。


libvirt System と User session の使い分けメモ

Cockpit 上で VM を作る際、「Connection」として

  • System(qemu:///system)
  • User session(qemu:///session)

が選べるが、本記事のような サーバ上で常用する VM はすべて System に置くのが基本方針。

理由:

  • サーバ再起動後に、System VM は簡単に自動起動・手動起動できる
  • CLI から virsh start columbia などで統一的に扱える
  • ブリッジや物理 NIC、vTPM などのリソース管理が System 側に集中する

User session は「そのユーザーがログインしている間だけ動いていればよい一時 VM」向けと考える。


おまけ:systemd-networkd-wait-online のエラーについて

Cockpit の「Services」で

systemd-networkd-wait-online.service
Failed to start Wait for Network to be Configured.
Timeout occurred while waiting for network connectivity.

といったエラーが出ることがある。

これは

  • systemd-networkd が管理しているすべての NIC が「オンライン」になるまで待ったが、
  • ケーブルが抜けている NIC などがいて、いつまで経っても揃わないためタイムアウト

しただけで、実際に使っている NIC(例:enp1s0f0np0)が正常に上がっているなら実害はほぼない。

気になる場合の対処案:

  1. サービス自体を無効化してしまう sudo systemctl disable --now systemd-networkd-wait-online.service 多くの自宅サーバ用途では、これで問題になることは少ない。
  2. netplan で「必須ではない」NIC を optional にする netplan の YAML にて、使っていない NIC に optional: true を付ける。 network: version: 2 renderer: networkd ethernets: enp4s0: dhcp4: true optional: true その上で sudo netplan generate sudo netplan apply とすれば、その NIC が上がらなくても「オンライン」と判断される。

このエラー自体は Windows 11 VM とは無関係なので、優先度は低い。「気になったら片付ける」程度の認識でよい。


まとめ

  • Ubuntu 24.04 上に KVM + libvirt + Cockpit を載せるだけで、Proxmox 的な日常運用(起動・停止・コンソール・スナップショット)がかなり快適にこなせる。
  • Windows 11 VM を問題なく動かすためには、
    UEFI(OVMF) + vTPM 2.0(swtpm) をきちんと用意してからインストールするのがポイント。
  • ISO の置き場所は /var/lib/libvirt/iso など libvirt からアクセス可能なパスにまとめる。
  • VM 名は columbia のように小文字で統一し、System 接続(qemu:///system)側に揃えておくと、CLI や将来の DNS 管理などがやりやすい。

この状態までできていれば、あとは同じ要領で

  • バックグラウンド処理用 Windows 11
  • ダウンロード専用 Ubuntu VM

なども追加し、用途ごとに VM を分離していくことができる。

]]>
https://cgbeginner.net/ubuntu-cockpit-vm/feed/ 0 2510
USBのWebカメラの映像をRTSP配信する(Raspberry Pi) https://cgbeginner.net/usbcam-rtsp/ https://cgbeginner.net/usbcam-rtsp/#respond Thu, 30 Oct 2025 16:08:40 +0000 https://cgbeginner.net/?p=2499 USB 接続の UVC カメラ(MJPEG を出力)を Raspberry Pi 4 で取り込み、V4L2 M2M ハードウェア H.264 でエンコードした映像を MediaMTX(旧 rtsp-simple-server)で RTSP 配信する方法のメモ。

※自分用メモなので非常に雑ですので役に立つ方のみ自身の責任の上で参考にしてください。

使用するUSBカメラ

バッファロー BSW505MBK

Amazon.co.jp: バッファロー ウェブカメラ WEBカメラ 1080P フルHD 30fps 200万画素 広角約120° マイク内蔵 シンプル BSW505MBK : パソコン・周辺機器
Amazon.co.jp: バッファロー ウェブカメラ WEBカメラ 1080P フルHD 30fps 200万画素 広角約120° マイク内蔵 シンプル BSW505MBK : パソコン・周辺機器

このカメラは、MotionJPEGで出力されるのですが、H264で配信したいので、エンコードもしつつ配信します。Raspberry Pi 4にはハードウェアエンコーダーがあるのでそれを使います。

# カメラの確認
v4l2-ctl --list-devices
v4l2-ctl -d /dev/video0 --list-formats-ext

FFmpegインストール

sudo apt update
sudo apt install -y ffmpeg v4l-utils

私の例

ctrluser@metis:~ $ v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
        /dev/video10
        /dev/video11
        /dev/video12
        /dev/video18
        /dev/video31

bcm2835-isp (platform:bcm2835-isp):
        /dev/video13
        /dev/video14
        /dev/video15
        /dev/video16
        /dev/video20
        /dev/video21
        /dev/video22
        /dev/video23
        /dev/media0
        /dev/media2

rpi-hevc-dec (platform:rpi-hevc-dec):
        /dev/video19
        /dev/media1

USB 2.0 Camera: USB Camera (usb-0000:01:00.0-1.3):
        /dev/video0
        /dev/video1
        /dev/media4

bcm2835-codec (vchiq:bcm2835-codec):
        /dev/media3

ctrluser@metis:~ $ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture

        [0]: 'MJPG' (Motion-JPEG, compressed)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x1024
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 1280x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 848x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 800x600
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 640x400
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 176x144
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
        [1]: 'YUYV' (YUYV 4:2:2)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x1024
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.100s (10.000 fps)
                Size: Discrete 1280x720
                        Interval: Discrete 0.100s (10.000 fps)
                Size: Discrete 1280x480
                        Interval: Discrete 0.067s (15.000 fps)
                Size: Discrete 848x480
                        Interval: Discrete 0.050s (20.000 fps)
                Size: Discrete 800x600
                        Interval: Discrete 0.050s (20.000 fps)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 640x400
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 352x288
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 320x240
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 176x144
                        Interval: Discrete 0.033s (30.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
ctrluser@metis:~ $ ffmpeg -hide_banner -encoders | grep v4l2m2m
 V..... h263_v4l2m2m         V4L2 mem2mem H.263 encoder wrapper (codec h263)
 V..... h264_v4l2m2m         V4L2 mem2mem H.264 encoder wrapper (codec h264)
 V..... hevc_v4l2m2m         V4L2 mem2mem HEVC encoder wrapper (codec hevc)
 V..... mpeg4_v4l2m2m        V4L2 mem2mem MPEG4 encoder wrapper (codec mpeg4)
 V..... vp8_v4l2m2m          V4L2 mem2mem VP8 encoder wrapper (codec vp8)
ctrluser@metis:~ $

MediaMTXインストール

wget https://github.com/bluenviron/mediamtx/releases/download/v1.15.3/mediamtx_v1.15.3_linux_arm64.tar.gz
wget https://github.com/bluenviron/mediamtx/releases/download/v1.15.3/checksums.sha256

# チェックサム確認(行が 'OK' になればOK)
grep mediamtx_v1.15.3_linux_arm64.tar.gz checksums.sha256 | sha256sum --check -

tar xzf mediamtx_v1.15.3_linux_arm64.tar.gz
sudo mv mediamtx /usr/local/bin/

MediaMTXの設定

sudo vi /usr/local/bin/mediamtx.yml
paths:
  cam:
    runOnInit: >
      ffmpeg
      -f v4l2 -thread_queue_size 1024
      -input_format mjpeg -framerate 30 -video_size 1920x1080
      -fflags +discardcorrupt
      -i /dev/video0
      -f alsa -thread_queue_size 1024 -ar 48000 -ac 1
      -i plughw:3,0
      -filter:v "scale=in_range=pc:out_range=tv,format=yuv420p,drawtext=fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf:text='%Y-%m-%d %H\:%M\:%S':expansion=strftime:x=10:y=10:fontsize=26:fontcolor=white:box=1:boxcolor=0x00000088:boxborderw=10"
      -c:v h264_v4l2m2m -b:v 4M -maxrate 4M -bufsize 8M -g 60
      -c:a aac -b:a 128k -ar 48000 -ac 1
      -map 0:v:0 -map 1:a:0
      -max_interleave_delta 0
      -f rtsp -rtsp_transport tcp
      rtsp://127.0.0.1:$RTSP_PORT/$MTX_PATH
    runOnInitRestart: yes

パイプラインの要点

  • 入力:v4l2 で /dev/video0 を MJPEG 1080p/30 で取得。
  • 色・画素フォーマットの正規化:scale=in_range=pc:out_range=tv,format=yuv420p。
    • MJPEG はフルレンジ(PC レンジ, 4:2:2)になりがちなので、TV レンジの yuv420p に確実に整えます。
  • タイムスタンプ焼き込み:drawtext を expansion=strftime で使用。
    • text='%Y-%m-%d %H\:%M\:%S' … ローカル時刻を YYYY-MM-DD HH:MM:SS で描画。
    • box=1:boxcolor=0x00000088:boxborderw=10 … 白文字に半透明黒ボックス+10px の余白(padding)。
    • フォントは DejaVuSansMono を fontfile= で明示(sudo apt install -y fonts-dejavu-core で導入)。
  • エンコード:h264_v4l2m2m(HW H.264)。目安ビットレート 4 Mbps、GOP 60(30fps で約 2 秒)。
  • 発行:Pi 内の MediaMTX に RTSP/TCP でローカル発行。
  • 配信:クライアントは rtsp://<PiのIP>:8554/cam へ接続。

systemd ユニット

MediaMTX 本体と設定ファイルは以下の配置を前提とします。

  • バイナリ:/usr/local/bin/mediamtx
  • 設定:/usr/local/bin/mediamtx.yml

/etc/systemd/system/mediamtx.service:

[Unit]
Description=MediaMTX RTSP server
Wants=network.target


[Service]
ExecStart=/usr/local/bin/mediamtx /usr/local/bin/mediamtx.yml
Restart=always
RestartSec=3


[Install]
WantedBy=multi-user.target

有効化と起動:

sudo systemctl daemon-reload
sudo systemctl enable --now mediamtx

ログ監視は

journalctl -u mediamtx -f
]]>
https://cgbeginner.net/usbcam-rtsp/feed/ 0 2499