🤪

[Deprecated] WSL2 USBカメラ+他のUSB機器 2022年01月17日版

2022/01/17に公開
16
  • 環境:Windows11 + WSL2 5.10.60.1 + Ubuntu20.04
  • WSL2 Linux Kernel 5.10.60.1 からKernelモジュールにUSBIP対応が標準的に組み込まれた

https://github.com/microsoft/WSL2-Linux-Kernel

  • 2022年01月17日時点の最新カーネルは 5.10.74.3
  • 以下すべての手順の Windows Terminal を使用する箇所は管理者権限で実行
  • WSLのカーネルアップデートと usbipd-win のインストール
  • Windows Terminalで実行
> wsl --update
> wsl --status
カーネル バージョン: 5.10.60.1

> winget install --interactive --exact dorssel.usbipd-win
  • WSLのディストリビューションを起動(WSL2起動用アイコンをマウスでクリックして起動してもよい)
> wsl --list
Linux 用 Windows サブシステム ディストリビューション:
Ubuntu-20.04 (既定)

> wsl -d Ubuntu-20.04
  • 追加パッケージをインストール sudo apt install linux-tools-5.4.0-77-generic hwdata

  • visudosecure_path の先頭に /usr/lib/linux-tools/5.4.0-77-generic: を追記する。

  • visudo で編集するファイルは、ダブルコーテーションの入力漏れやコロンをセミコロンに打ち間違えたりすると sudo が必要なコマンド類が一切使用できなくなるので慎重に実施する

  • 私は深夜に寝ぼけてコロンをセミコロンに打ち間違えて apt コマンドすら実行できなくなりました

  • 参考

https://kledgeb.blogspot.com/2021/11/wsl-227-wsl-2usblinuxusb.html

  • 私のようにミスするひとは居ないと思いますが、一応、私が visudo を復帰させた方法は下記です。

https://zenn.dev/pinto0309/scraps/be6442e2e92932

$ sudo apt install linux-tools-5.4.0-77-generic hwdata
$ sudo visudo
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/lib/linux-tools/5.4.0-77-generic:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
  • Ubuntu側のユーザーをvideoグループに含める。
$ sudo adduser $USER video
  • WSLのコンソールからすぐに抜ける(実行しなくてもよい、Windows Terminalが占有されてうっとおしいときだけ実行する)
$ exit
  • USBカメラとカメラ以外のUSB機器の使用を可能にするためのWSL2用のLinuxカーネルのカスタムビルドを行う。下記の 2. Usage の手順を上から順番に実施する。カスタム値は全てコミット済みなので何も考えずに上から順番に実行する。なお、 <windows username> の部分はWindowsホスト側でログオン中のユーザー名に読み替える必要がある。

https://github.com/PINTO0309/wsl2_linux_kernel_usbcam_enable_conf#2-usage

  • Windows Terminalから usbipd コマンドでホストPCに接続されているUSBカメラの busid を調べる
  • BUSID の部分はカメラを接続したUSBポートによって番号が変わる
  • この記事の環境では 1-1 と認識されている
  • Not attached はWSL2の中にアタッチされていなくて宙ぶらりんであることを示す
> usbipd wsl list
BUSID  DEVICE                                        STATE
1-1    papalook FHD Camera, papalook Microphone      Not attached
1-10   USB 入力デバイス                                Not attached
1-14   インテル(R) ワイヤレス Bluetooth(R)              Not attached
  • バックグラウンドで起動しているWSL2にUSBカメラ BUSID 1-1 をアタッチする
  • コマンドが成功すると、不親切なことに無言で成功する
> usbipd wsl attach --busid 1-1
  • WSL2の中でUSBカメラがアタッチされていることを確認する
  • Windows Terminal上で実行
> usbipd wsl list
BUSID  DEVICE                                        STATE
1-1    papalook FHD Camera, papalook Microphone      Attached - Ubuntu-20.04
1-10   USB 入力デバイス                                Not attached
1-14   インテル(R) ワイヤレス Bluetooth(R)              Not attached
$ ls -l /dev/video*
crw------- 1 root root 81, 0 Jan 17 20:35 /dev/video0
crw------- 1 root root 81, 1 Jan 17 20:35 /dev/video1
  • USBカメラがWSL2の中から認識されることを確認するテストコードを作成する
$ pip3 install opencv-python opencv-contrib-python
$ cat << 'EOT' > ${HOME}/usbcam_test.py
import cv2

W=640
H=480
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, W)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, H)

while True:
    ret, frame = cap.read()
    if not ret:
        continue
    cv2.imshow('usb cam test', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
EOT
  • USBカメラ認識テスト実行
$ sudo chmod 777 /dev/video* && python3 ${HOME}/usbcam_test.py
  • 終わり。起動シーケンスをシェルにしてデスクトップに置いておくとよい。

Discussion

こーのいけこーのいけ

Twitterでもやり取りさせて頂きましたがこちらにも書き残しておきますね。
手順のうち、以下のコマンドを実行するところは管理者権限のコマンドプロンプトで実行する必要があります。

> usbipd wsl attach --busid 1-1

https://github.com/dorssel/usbipd-win/wiki/WSL-support#wsl-convenience-commands
にも "From an administrator command prompt" って書いてありますね。このWikiページを確認するのが良さそうです。
visudoも必要なさそうな気配・・・?

PINTOPINTO

ありがとうございます! 次にまっさらな環境が手に入ったときに改めて試行してみます。

d455d455

初めまして。当該記事を参考にRealSense D455の使用を試みたのですが、どうにもうまくいきません。アドバイスいただけないでしょうか?
【環境】
Windows11
WSL2 5.10.102.1
Ubuntu20.04
RealSense D455

【症状】
本記事の手順についてはすべてうまくいきました。
unameコマンドにてカーネルが5.10.60.1-microsoft-standard-WSL2+になっているのも確認しています。usbipd-winでRealSense D455をUbuntuに認識させ、lsusbでデバイスを認識しているのを確認したのち、/dev/video0~5があるのも確認いたしました。
その後chmod 777をし、pythonスクリプトを起動させると次のように出ます。
「[ WARN:0@0.082] global /io/opencv/modules/videoio/src/cap_v4l.cpp (889) open VIDEOIO(V4L2:/dev/video0): can't open camera by index」
なお、スクリプトを変更し、
cap = cv2.VideoCapture(2)および、cap = cv2.VideoCapture(4)は画が表示されます。

【質問】
RealSense D455において、Video2およびVideo4のみしか表示されないのは正しい挙動でしょうか?
また、PINTO様がRealSenseの動確をしている中で、realsense-viewerでの確認はなされたでしょうか?
手元の環境において別途librealsenseをインストールしたのですが、こちらの環境ではrealsense-viewerがデバイスを認識せず、librealsenseのインストールが本質的な問題ではないかと考えています。
librealsenseのインストールは公式の手順を参考にしています。
https://github.com/IntelRealSense/librealsense/blob/master/doc/distribution_linux.md

PINTOPINTO

@d455 さん

cap = cv2.VideoCapture(2)および、cap = cv2.VideoCapture(4)は画が表示されます。

正しいです。 RealSense D435 でも同様です。RGBと点群の経路が別々に確保されていますので、その挙動で問題がないと判断しています。ちなみに私は WSL2 の中から 3本のRealSenseのRGBに同時にアクセスして正常に動作しています。Video(0) つまり /dev/video0 はRGBではありませんのでエラーになります。

ちなみに私の環境では3本の RealSense D435 が下記のようにWSL2の中から見えています。また、点群も3本とも取得して処理できています。

$ ls -l /dev/video*
/dev/video0
/dev/video1
/dev/video2
/dev/video3
/dev/video4
/dev/video5
/dev/video6
/dev/video7
/dev/video8
/dev/video9
/dev/video10
/dev/video11
/dev/video12
/dev/video13
/dev/video14
/dev/video15
/dev/video16
/dev/video17

環境を作ったのがかなり前ですので、確かRealSenseViwerも動かせるようにしたと記憶していますが、何をしたのかすぐには思い出せません。。。すみません。。。

PINTOPINTO

RealSense は、ファームウェアアップデートで最新のファームウェアを適用しないと確かうまく認識しないときや、動作がとても不安定になることを思い出しました。ファームウェアアップデートをしてみてはいかがでしょうか?

d455d455

ご返信ありがとうございます。
RealSenseのファームウェアはWindows側で最新にしております。
もう少し苦闘してみます。

KuroToraKuroTora

カーネルのカスタムビルド中、
$ cat << 'EOT' > /mnt/c/Users/${WINUSERNAME}/.wslconfig
[wsl2]
kernel=C:\Users\<windows username>\vmlinux
EOT
を実行してシャットダウンさせると、Ubuntuを起動できなくなってしまいました。wslconfigを削除すると元に戻ったのですが、何か対処法をご存知でしたらご教授頂けますと幸いです。
※Windowsサイドで実行してはいけないと赤文字で書いてありましたが、wsl上で実行してもこのような現象が起きてしまいました。解釈が間違っていましたらすいません…

PINTOPINTO

エラーメッセージに表示されている内容しか読み取ることができませんが、「指定されたパスがない」のではないでしょうか?

  1. ${WINUSERNAME} には値が設定されていますか?
  2. <windows username> には値が設定されていますか?
KuroToraKuroTora

返信ありがとうございます(__)
ユーザーネームに空白が含まれており、${WINUSERNAME}ではシングルクォーテーションを付けて打っていたのですが、そのまま<windows username>でもシングルクォーテーションを付けていたため正しく読み取れていなかったようです。
PINTOさんの助言によって成功させることができました(´;ω;`)

negitnegit

初めまして。PINTO様の記事に従い環境を構築していて、動作テストの段階でつまずいてしまったので、ご助言を頂けないでしょうか...m(_ _)m

【環境】

  • Windows11:OS ビルド 22000.318
  • WSL2
  • Ubuntu:20.04.4 LTS
  • Python:3.7.8
  • OpenCV:4.6.0
  • Webカメラ:Logicool C270n HD WEBCAM

【発生している問題】
環境構築が終わり、usbcam_test.pyを実行したら以下のようなエラーが出ました。

[ WARN:0@10.255] global /io/opencv/modules/videoio/src/cap_v4l.cpp (1013) tryIoctl VIDEOIO(V4L2:/dev/video0): select() timeout.
[ WARN:0@20.266] global /io/opencv/modules/videoio/src/cap_v4l.cpp (1013) tryIoctl VIDEOIO(V4L2:/dev/video0): select() timeout.
[ WARN:0@30.276] global /io/opencv/modules/videoio/src/cap_v4l.cpp (1013) tryIoctl VIDEOIO(V4L2:/dev/video0): select() timeout.
^CTraceback (most recent call last):
  File "tools/webcamtest.py", line 29, in <module>
    ret, frame = cap.read()
KeyboardInterrupt

なので、こちらの記事の3. Note に従いコマンドを実行し、実行結果を確認しました。
以下がコマンドの実行結果です。

$ v4l2-ctl -d /dev/video0 --all
Driver Info:
        Driver name      : uvcvideo
        Card type        : C270 HD WEBCAM
        Bus info         : usb-vhci_hcd.0-1
        Driver version   : 5.10.102
        Capabilities     : 0x84a00001
                Video Capture
                Metadata Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps      : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
        Width/Height      : 640/480
        Pixel Format      : 'YUYV' (YUYV 4:2:2)
        Field             : None
        Bytes per Line    : 1280
        Size Image        : 614400
        Colorspace        : sRGB
        Transfer Function : Rec. 709
        YCbCr/HSV Encoding: ITU-R 601
        Quantization      : Default (maps to Limited Range)
        Flags             : 
Crop Capability Video Capture:
        Bounds      : Left 0, Top 0, Width 640, Height 480
        Default     : Left 0, Top 0, Width 640, Height 480
        Pixel Aspect: 1/1
Selection Video Capture: crop_default, Left 0, Top 0, Width 640, Height 480, Flags: 
Selection Video Capture: crop_bounds, Left 0, Top 0, Width 640, Height 480, Flags: 
Streaming Parameters Video Capture:
        Capabilities     : timeperframe
        Frames per second: 30.000 (30/1)
        Read buffers     : 0
                     brightness 0x00980900 (int)    : min=0 max=255 step=1 default=128 value=128
                       contrast 0x00980901 (int)    : min=0 max=255 step=1 default=32 value=32
                     saturation 0x00980902 (int)    : min=0 max=255 step=1 default=32 value=32
 white_balance_temperature_auto 0x0098090c (bool)   : default=1 value=1
                           gain 0x00980913 (int)    : min=0 max=255 step=1 default=0 value=8
           power_line_frequency 0x00980918 (menu)   : min=0 max=2 default=2 value=1
                                0: Disabled
                                1: 50 Hz
                                2: 60 Hz
      white_balance_temperature 0x0098091a (int)    : min=0 max=10000 step=10 default=4000 value=4280 flags=inactive
                      sharpness 0x0098091b (int)    : min=0 max=255 step=1 default=24 value=24
         backlight_compensation 0x0098091c (int)    : min=0 max=1 step=1 default=1 value=1
                  exposure_auto 0x009a0901 (menu)   : min=0 max=3 default=3 value=3
                                1: Manual Mode
                                3: Aperture Priority Mode
              exposure_absolute 0x009a0902 (int)    : min=1 max=10000 step=1 default=156 value=156 flags=inactive
         exposure_auto_priority 0x009a0903 (bool)   : default=0 value=1


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

        [0]: 'YUYV' (YUYV 4:2:2)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 176x144
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                Size: Discrete 1280x720
                        Interval: Discrete 0.133s (7.500 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.133s (7.500 fps)
                        Interval: Discrete 0.200s (5.000 fps)
        [1]: 'MJPG' (Motion-JPEG, compressed)
                Size: Discrete 640x480
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 160x120
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 176x144
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                Size: Discrete 1280x720
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)
                Size: Discrete 1280x960
                        Interval: Discrete 0.033s (30.000 fps)
                        Interval: Discrete 0.040s (25.000 fps)
                        Interval: Discrete 0.050s (20.000 fps)
                        Interval: Discrete 0.067s (15.000 fps)
                        Interval: Discrete 0.100s (10.000 fps)
                        Interval: Discrete 0.200s (5.000 fps)

この実行結果から、usbcam_test.pyのWを640、Hを480にすればいいと良いと思いましたが、そうすると上記のエラーのようにselect() timeout が起きます。

【試したこと】
こちらの記事を参考に強制的にフォーマットを640x480 YUYV にした(?)のですが、同様のエラーが起きました。

しかし、usbcam_test.pyのWを160、Hを120にして再びプログラムを実行すると、Webカメラの映像が出力されました。
そこから徐々にWとHを大きくしていくと、Wが200、Hが200くらいまでは映せますが、それ以上大きくするとselect() timeoutが出てしまいます。W1280、H720などもダメでした。

また、PC内蔵のWebカメラでも試してみましたが、同様にWとHが一定以上大きくなると映らなくなります。

【質問】
どうすれば640×480 かそれ以上の大きさで映像を映せるでしょうか?

【補足】
Webカメラの認識はできているので必要ないかもしれませんが、補足をします。

環境構築の段階で、こちらの2. Usageのコマンドの一部を以下のように変更して実行しました。

  • TAGVERNUM=5.10.60.1TAGVERNUM=5.10.102.1
  • sudo wget -O .config https://github.com/PINTO0309/wsl2_linux_kernel_usbcam_enable_conf/raw/main/${TAGVER}/config && \
    ${TAGVER}linux-msft-wsl-5.10.60.1に変更

また、make install をした時、よくわからない選択肢が2回(多分)出てきたのですが、1回目はy
、2回目は暴発でEnterを押しました。
1回目の選択肢は-DWITH_LIBV4Lみたいなものを聞かれたと思います。

PINTOPINTO

カーネルのバージョンが大きく異なるため、おそらく make コマンド時のビルドオプションが大きく変わっていると思います。したがいまして、リポジトリにコミットされている config ファイルは 5.10.60.1 時点のものですので、こちらからたどれる公式の手順を試していただいたほうが良いかもしれません。
https://github.com/microsoft/WSL2-Linux-Kernel

その昔、同じ症状で一時困っていましたが、そのときは結局USBカメラとの相性の問題でした。5種類試して3種類成功する感じでした。
https://zenn.dev/pinto0309/articles/c3014043502245

negitnegit

ご返信ありがとうございます!m(_ _)m
公式の手順を元にもう一度ビルドをやり直してみます。

negitnegit

usbcam_test.pycap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G'))の1文を加えてフォーマットをYUYVからMJPGに変更したら640×480で映像が出力されました!
ご対応ありがとうございました!m(_ _)m

clearwaterclearwater

有益な記事ありがとうございます。
本手順に沿ってカメラ画像のリアルタイム表示まではできました。
しかしながら、かなりカクカクしてしまいます。
同じusbcam_test.pyでもWindows側で別途作成した環境ではスムーズに描画できているのですが、WSL2側で別途設定が必要なのでしょうか。

<環境>

  • Windows 11 バージョン21H2 (OSビルド22000.918)
  • NVIDIA GeForce RTX 3060 Laptop
  • Ubuntu 20.04.5 LTS (GNU/Linux 5.10.60.1-microsoft-standard-WSL2+ x86_64)
  • Python 3.8.10
  • OpenCV:4.6.0
  • Webカメラ HD Webcam eMeet C960
PINTOPINTO

Microsoft公式が本日時点でWSL2に配信している最新の Linux Kernel 5.10.102.1 でゼロから環境構築し、正常に動作しました。USBカメラ1台目は動作不可。USBカメラ2台目は動作しました。やはりUSBカメラの相性問題だと思います。ダメなカメラは YUYVMJPEG のどちらに変更してもタイムアウトしますし、解像度を下げてもダメでした。

https://zenn.dev/pinto0309/articles/e1432253d29e30