WSLでlibrealsenseをソースビルドしてRealsenseを使用可能にする(pyrealsenseあり)

# 概要
通常のUbuntuではaptでlibrealsenseが入るのだが、WSLだとカーネルヘッダがどうとか言われてインストールに失敗する。ではソースビルドはどうかというと、こちらもやり方によってはカーネルヘッダの問題で失敗してしまう。上手くいく方法を見つけたので、この記事で共有する。

# 動作環境・バージョン
### 動作環境
windows11

### バージョン
Windows11 24H2

WSL バージョン: 2.4.10.0
カーネル バージョン: 5.15.167.4-1

※windows11であってもバージョンが古い場合、wslの一部の機能が正常に動作しない可能性があります

### 使用機器
realsense D455f

# 前提条件
wslを使用した経験があること

# コンテンツ
操作は全てWSL内で行います

## 依存関係のインストール
```
sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade
sudo apt-get install libssl-dev libusb-1.0-0-dev libudev-dev pkg-config libgtk-3-dev
sudo apt-get install git wget cmake build-essential
sudo apt-get install libglfw3-dev libgl1-mesa-dev libglu1-mesa-dev at
sudo apt-get install python3.10 python3-dev
```

## ビルドスクリプトの作成
### 元となるスクリプトのDL
```
wget https://github.com/IntelRealSense/librealsense/raw/master/scripts/libuvc_installation.sh
chmod +x ./libuvc_installation.sh
```
### ビルドスクリプトの修正
ビルドスクリプトを任意のエディタで開き、下記の修正を行ってください（私はnanoを用いました）
- cmakeオプションの変更・追加
`cmake ../  -DFORCE_RSUSB_BACKEND:bool=true -DCMAKE_BUILD_TYPE=release -DBUILD_PYTHON_BINDINGS:bool=true -DBUILD_SHARED_LIBS=false -DPYTHON_EXECUTABLE=/usr/bin/python3`
- makeコマンドオプションの変更
`make -j4`

<details><summary>最終的に出来上がるビルドスクリプト</summary>

```libuvc_installation.sh
#!/bin/bash -xe

#Locally suppress stderr to avoid raising not relevant messages
exec 3>&2
exec 2> /dev/null
con_dev=$(ls /dev/video* | wc -l)
exec 2>&3

if [ $con_dev -ne 0 ];
then
	echo -e "\e[32m"
	read -p "Remove all RealSense cameras attached. Hit any key when ready"
	echo -e "\e[0m"
fi

lsb_release -a
echo "Kernel version $(uname -r)"
sudo apt-get update
cd ~/
sudo rm -rf ./librealsense_build
mkdir librealsense_build && cd librealsense_build

if [ $(sudo swapon --show | wc -l) -eq 0 ];
then
	echo "No swapon - setting up 1Gb swap file"
	sudo fallocate -l 2G /swapfile
	sudo chmod 600 /swapfile
	sudo mkswap /swapfile
	sudo swapon /swapfile
	sudo swapon --show
fi

echo Installing Librealsense-required dev packages
sudo apt-get install git cmake libssl-dev freeglut3-dev libusb-1.0-0-dev pkg-config libgtk-3-dev unzip -y
rm -f ./master.zip

wget https://github.com/IntelRealSense/librealsense/archive/master.zip
unzip ./master.zip -d .
cd ./librealsense-master

echo Install udev-rules
sudo cp config/99-realsense-libusb.rules /etc/udev/rules.d/ 
sudo cp config/99-realsense-d4xx-mipi-dfu.rules /etc/udev/rules.d/
sudo udevadm control --reload-rules && sudo udevadm trigger 
mkdir build && cd build
cmake ../  -DFORCE_RSUSB_BACKEND:bool=true -DCMAKE_BUILD_TYPE=release -DBUILD_PYTHON_BINDINGS:bool=true -DBUILD_SHARED_LIBS=false -DPYTHON_EXECUTABLE=/usr/bin/python3
make -j4
sudo make install
echo -e "\e[92m\n\e[1mLibrealsense script completed.\n\e[0m"
```

</details>

### 修正内容の意味
- `-DFORCE_RSUSB_BACKEND:bool=true` : ビルドバックエンドを変更しなければWSLでは正常なビルドは難しいが、元のビルドスクリプトはやや古く、新しいRSUSB_BACKENDを採用
    ```
    Using RSUSB - user-space implementation of the UVC and HID data protocols, encapsulated and activated by selecting the SDK's -DFORCE_RSUSB_BACKEND flag (a.k.a. -DFORCE_LIBUVC with SDK versions prior to v.2.30).
    ```
    [引用元](https://github.com/IntelRealSense/librealsense/blob/master/doc/installation_jetson.md)

- `DBUILD_PYTHON_BINDINGS:bool=true` : pyrealsense2のビルドを行うために必要
- `-DBUILD_SHARED_LIBS=false` : pyrealseseを静的ライブラリとしてビルド（私の環境ではこれがないと上手く動かなかった）
[上記２つについて、参考](https://github.com/IntelRealSense/librealsense/blob/master/wrappers/python/readme.md)
- `DPYTHON_EXECUTABLE=/usr/bin/python3` : 指定しないとPYTHON_EXECUTABLEが見つからないというエラーになるので追加

## ビルド
1. ビルドに際しては接続されているRealsenseを全て取り外してください
1. ビルドの実行`./libuvc_installation.sh`
1. ビルドが成功した場合、`Librealsense script completed.`と出力され、成果物は`~/librealsense_build/librealsense-master/build/release/`に生成されます

## 動作確認
WSLにrealsenseを接続してください
[参考](https://learn.microsoft.com/ja-jp/windows/wsl/connect-usb)
### librealsense
`rs-enumerate-devices`でデバイス情報が表示されれば成功
```
Device info:
    Name                          :  Intel RealSense D455F
    Serial Number                 :  341522301205
    Firmware Version              :  5.16.0.1
    Recommended Firmware Version  :  5.16.0.1
    Physical Port                 :  2-1-3
    Debug Op Code                 :  15
    Advanced Mode                 :  YES
    Product Id                    :  0B5C
    Camera Locked                 :  YES
    Usb Type Descriptor           :  3.2
    Product Line                  :  D400
    Asic Serial Number            :  349643061773
    Firmware Update Id            :  349643061773
    Dfu Device Path               :
    （略）
```

### pyrealsense
検証のために適当なディレクトリを作成し、そこに以下を**コピー**
```
pyrealsense2.cpython-310-x86_64-linux-gnu.so 
pyrealsense2.cpython-310-x86_64-linux-gnu.so.2.55
```
以下に簡単な検証用スクリプトを用意した（必ずしもこれを用いる必要はない）
<details><summary>検証用スクリプト</summary>

```python
import pyrealsense2 as rs

def print_camera_info(device):
    """
    接続されたRealSenseデバイスの情報を表示する関数

    Args:
        device: pyrealsense2.device オブジェクト
    """
    print("-" * 40)
    print("Device Information:")
    print(f"  Name: {device.get_info(rs.camera_info.name)}")
    print(f"  Serial Number: {device.get_info(rs.camera_info.serial_number)}")
    print(f"  Firmware Version: {device.get_info(rs.camera_info.firmware_version)}")
    print(f"  Product Line: {device.get_info(rs.camera_info.product_line)}")
    print(f"  USB Type: {device.get_info(rs.camera_info.usb_type_descriptor)}")


try:
    # Configure depth and color streams
    pipeline = rs.pipeline()
    config = rs.config()

    # Get device product line for setting a supporting resolution
    pipeline_wrapper = rs.pipeline_wrapper(pipeline)
    pipeline_profile = config.resolve(pipeline_wrapper)
    device = pipeline_profile.get_device()

    # 接続されたデバイスの情報を表示
    print_camera_info(device)

    device_product_line = str(device.get_info(rs.camera_info.product_line))

    found_rgb = False
    for s in device.sensors:
        if s.get_info(rs.camera_info.name) == 'RGB Camera':
            found_rgb = True
            break
    if not found_rgb:
        print("The demo requires Depth camera with Color sensor")
        exit(0)



    config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)

    if device_product_line == 'L500':
        config.enable_stream(rs.stream.color, 960, 540, rs.format.bgr8, 30)
    else:
        config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)

    # Start streaming
    pipeline.start(config)
    print("RealSense camera connected and streaming.")


    try:
        # Allow the camera to warm up
        for _ in range(30): # Get about 30 frames
             pipeline.wait_for_frames()

        print("Capturing a frame...")
        frames = pipeline.wait_for_frames()
        depth_frame = frames.get_depth_frame()
        color_frame = frames.get_color_frame()
        if not depth_frame or not color_frame:
            print("Failed to capture frames.")
            exit(1)

        print("Frames captured successfully!")

    finally:
        # Stop streaming
        pipeline.stop()
        print("RealSense camera disconnected.")

except Exception as e:
    # Handle other potential errors
    print(f"An error occurred: {e}")
    exit(1)
```

</details>

上記を実行してデバイス情報を取得できれば成功
```
Device Information:
  Name: Intel RealSense D455F
  Serial Number: 341522301205
  Firmware Version: 5.16.0.1
  Product Line: D400
  USB Type: 3.2
RealSense camera connected and streaming.
Capturing a frame...
Frames captured successfully!
RealSense camera disconnected.
```

# トラブルシューティング

現状WSLでwebカメラを正しく認識するためにはカーネルのカスタムビルドが必要です。まだの人は[この記事](https://qiita.com/N622/items/5c7cecd625e23c6e3911)を参考にしてください。

また、WSL内で`ls /dev`を実行し、`video0`等が存在するか確認してください。これが無い場合カメラを認識できていません。

pyrealsenseを用いる場合、本記事の検証方法ではスクリプトと.soファイルは同じディレクトリにある必要があります。

# 終わりに
結構手こずったのですが、最終的にうまく動いてくれたので良かったです
pyrealsenseはuv等のパッケージマネージャでも使えるようにしていきたいので、もう少しやるべきことは残されていますね

# 参考文献
[公式ビルドガイド](https://dev.intelrealsense.com/docs/compiling-librealsense-for-linux-ubuntu-guide)
[公式ドキュメント１](https://github.com/IntelRealSense/librealsense/blob/master/doc/libuvc_installation.md)
[公式ドキュメント２](https://github.com/IntelRealSense/librealsense/blob/master/wrappers/python/readme.md)
[公式ドキュメント３](https://github.com/IntelRealSense/librealsense/blob/master/doc/installation_jetson.md)