WSL2でROSを使う(2021/4)

2023年現在、GWSLの登場により、本記事の内容は不要となりました。  
WSL2をインストールすると、X Windowシステムがデフォルトで使用できます。  
GWSLを使用している例: https://qiita.com/1024xx4/items/45a5cce13eafa17ca6f1
したがって、WSL2をインストールして、通常通りROSをインストールするだけでRvizなどが使用可能となりました。

# 方針
WSL2(Windows Subsystem for Linux 2)を使って、Windows上でUbuntuを起動し、そのUbuntu上でROSを動かす。
> WSL 2 は、Linux ディストリビューションと Windows の対話方法が変更された、WSL の新しいバージョンのアーキテクチャです。([Microsoft](https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-index))

想定する環境：
　- Windows10 HOME (OSビルド 19041.207)
　- Ubuntu16.04
　- ROS-Kinetic

変更履歴:
  - 2020/11/27 vcXsrvの設定を修正
  - 2021/04/10 firewallについて追記
  - 2021/05/04 vcXsrvとの接続不良の調査方法を追記

# 手順
1. WSL2を用いてUbuntu(X Window System※なし)をインストールする
2. Windows10側でX Window Systemをインストールし、Ubuntuから使えるようにする
3. ROSをインストールする
4. ROSパッケージが使えることを確認する
5. ROSがバグってた時のTroubleShooting

※ X Window Systemは、ウィンドウを画面上に描画したり、移動させたり、マウスやキーボードを使ってやり取りする([Wikipedia](https://ja.wikipedia.org/wiki/X_Window_System))。今回は、Ubuntu側の処理の結果を、Windows側のXで描画する。

## 1. WSL2を用いてUbuntuをインストールする
「レガシーコンソールを使用する」のチェックを解除する。
<img width="400px" alt=image.png src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108813/74b66210-5325-20ab-e4c1-6353df701439.png>

後は、[このページ](https://qiita.com/poramal/items/3562472d52fe60f61c56)に従う。

Ubuntuがインストールできたら、以下のコマンドを実行する。

```bash
# Xサーバー通信用のアプリ群を入れる
sudo apt update
sudo apt install x11-apps -y 
# 環境変数を設定する
## rvizがXサーバーと正しく通信できるようにする
echo 'export LIBGL_ALWAYS_INDIRECT=""' >> ~/.bashrc 
## GazeboがXサーバーと正しく通信できるようにする
echo 'export GAZEBO_IP=127.0.0.1' >> ~/.bashrc
source ~/.bashrc 
```

## 2. Windows10側でX Window Systemをインストールし、Ubuntuから使えるようにする
### 2.1 Windows側へのVcXsrvのインストールと設定
VcXsrvを以下の公式ページからダウンロードしてインストールした後、以下の手順で設定する([参考](https://qiita.com/ryoi084/items/0dff11134592d0bb895c))(2021/02/20追記)。

[VcXsrvのダウンロードページ](https://sourceforge.net/projects/vcxsrv/)

1. 「スタート」からxlaunch.exeを起動。
1. multiple windowを選択し, 次へ。
1. start no client を選択し, 次へ.
1. Additional parameters for VcXsrv という入力フォームに 「-ac -nowgl」と入力し、次へ。
1. `Save configuration`を押下して、設定ファイルをどこかに保存。
1. `完了`を押下して、VcXsrvを起動。

<img width="400px" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108813/bfaffd93-25de-ac88-288b-bdbcfa1b0d0c.png">

なお、上記オプションの意味は以下の通り([参考](https://gist.github.com/stowler/9921780))。
`-ac`: disable access control restrictions
`-nowgl`: disable the GLX extension to use the native Windows WGL interface for hardware-accelerated OpenGL(参考「[WSL: RvizやGazeboが起動しない](https://demura.net/lecture/15304.html)」)

### 2.2 Windows側でVcXsrvをスタートアップに登録
必要に応じて、PCが起動したときVcXsrvが自動で起動するように、上記設定ファイルをスタートアップに保存する。
方法は以下の通り([参考](https://qiita.com/ryoi084/items/0dff11134592d0bb895c))。

1. キーボードで`Windows`+`R`を押下して, `shell:startup`と入力して検索
1. `スタートアップ`というフォルダが開いたら、上記設定ファイルを同フォルダにコピペ

### 2.3 Ubuntu側での設定
`~/.bashrc`に以下の行を追記([参考](https://github.com/microsoft/WSL/issues/4106#issuecomment-501885675))。
この設定は、XサーバのIPを指定しているのだが、上記`-ac`の設定を忘れると有効にならないので注意が必要。

```bash
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0
```

### 2.4 動作確認
ここで、以下のコマンドを実行し、動作確認をする。

```bash
xeyes
```

<img width="400px" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108813/e3105de7-e623-6c8f-b30a-ffaff1d2dd3a.png">

## 3. ROSをインストールする
[ROS公式HP](http://wiki.ros.org/ja/kinetic/Installation/Ubuntu)に従う。


## 4. ROSパッケージが使えることを確認する
起動時にファイアウォールの警告が出るが、
同一PC内での通信に過ぎないので、「キャンセル」で問題ない。

```bash
# roscore起動
$ roscore
# 別ターミナルでrviz起動
$ rviz
```

## 5. ROSがバグった時のTroubleShooting
### 5.1 uuidがないと怒られる
WSL内のUbuntuにuuidがないので怒られた。

```bash
# バグの内容
$ rviz
[ INFO] [1587018027.496482200]: rviz version 1.12.17
[ INFO] [1587018027.496589000]: compiled against Qt version 5.5.1
[ INFO] [1587018027.496632400]: compiled against OGRE version 1.9.0 (Ghadamon)
process 14451: D-Bus library appears to be incorrectly set up; failed to read machine uuid: UUID file '/etc/machine-id' should contain a hex
 string of length 32, not length 0, with no other text
See the manual page for dbus-uuidgen to correct this issue.
Segmentation fault (core dumped)

# 解決法
# uuidを生成して設定する
$ dbus-uuidgen |sudo tee /etc/machine-id
```


### 5.2 rvizがセグフォる
rvizがセグフォる。gdbで確認してもよくわからない。

```bash
# rvizが`Segmentation fault`で落ちる
$ gdb rviz
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from rviz...(no debugging symbols found)...done.
(gdb) run
Starting program: /opt/ros/kinetic/bin/rviz
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffe4630700 (LWP 16921)]
[ INFO] [1587020884.912756500]: rviz version 1.12.17
[ INFO] [1587020884.912855300]: compiled against Qt version 5.5.1
[ INFO] [1587020884.912904600]: compiled against OGRE version 1.9.0 (Ghadamon)
[New Thread 0x7fffe3a00700 (LWP 16924)]
[New Thread 0x7fffe31f0700 (LWP 16925)]
[New Thread 0x7fffe29e0700 (LWP 16926)]
[New Thread 0x7fffe21d0700 (LWP 16931)]

Thread 1 "rviz" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) quit
A debugging session is active.

        Inferior 1 [process 16917] will be killed.

```
解決策: Windows側のX Window Systemsの追加オプションの問題だった。
OpenGLの描画を正しく行えるように、VcXsrvの起動時の追加のオプション"-nowgl"を追加する（参考「[WSL: RvizやGazeboが起動しない](https://demura.net/lecture/15304.html)」）。本投稿の[#2](https://qiita.com/rkoyama1623/items/de467c6b954a6df638c8#2-windows10%E5%81%B4%E3%81%A7x-window-system%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97ubuntu%E3%81%8B%E3%82%89%E4%BD%BF%E3%81%88%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%99%E3%82%8B)を設定していれば起こらないはず。
**補足**: rvizがリンクしているUbuntu側のOpenGLのライブラリファイル`libGL.so`と、Windows側の`libGL.so`の不一致が原因。`-nowgl`はwgl(Windows-GL)を使わない(mesaのOpenGLを使う)設定である。

```bash
$ ldd `which rviz`|grep libGL.so
libGL.so.1 => /usr/lib/x86_64-linux-gnu/mesa/libGL.so.1 (0x00007fff32cd0000)
```



### 5.3 Qtのバージョンが古い?
UbuntuのQtが、rvizの想定しているバージョンより古いのが良くなさそう。
これは、問題ないかもしれない。

```
# バグの内容
$ qmake -query QT_VERSION
4.8.7
$ rviz
[ INFO] [1587019259.002021100]: rviz version 1.12.17
[ INFO] [1587019259.002127600]: compiled against Qt version 5.5.1
[ INFO] [1587019259.002177600]: compiled against OGRE version 1.9.0 (Ghadamon)
Segmentation fault (core dumped)

# 解決法
# Qtを最新にする
sudo add-apt-repository universe
sudo apt-get update
sudo apt-get install qt5-default qtcreator -y
```

### 5.4 Gazeboが真っ暗
WSLのissue [ROS Gazebo black screen](https://github.com/microsoft/WSL/issues/3368)で解決していた。

```bash
# バグの内容
# Gazeboの画面が真っ暗になる

# 解決方法
# GAZEBO_IPを設定する
echo 'export GAZEBO_IP=127.0.0.1' >> ~/.bashrc
source ~/.bashrc
```

### 5.5 xeyesが表示されないでタイムアウトする
以下は、X serverであるvcXsrvへアプリケーションが接続できずに、タイムアウトして起こるエラー。原因は必ずしも一つではないが、セキュリティソフトなどが原因なのか、firewallがvcXsrvのポートに対する受信をブロックしてしまうケースがらしい([参考](https://qiita.com/Decwest/items/86f0246f60e28461c4c8))。
筆者の環境でも、PCによって起こったり起こらなかったりする印象である。

```bash
# バグの内容
$ xeyes
Error: Can't open display: 192.168.220.209:0

# 解決方法
# WindowsのFirewallでvcXsrvへの接続を許可する
# 以下の記事を参照のこと
# https://qiita.com/Decwest/items/86f0246f60e28461c4c8
```

vcXsrvとの通信エラーが起こる場合、vcXsrvの設定ではなく、そもそもゲストOSであるUbuntuとホストWindowsとのTCP/IP通信で失敗している場合もある。
具体的な解決方法を提示するものではないか、現象の調査方法を以下に例示する。
正常な状態では、以下のような結果が得られるはず。

```bash
# (1) ホストWindows側でvcXsrvが動作している(ポート6000をListenしている)ことを確認
# WindowsのPowershellで実行
> Get-Process -Id (Get-NetTCPConnection -LocalPort 6000).OwningProcess
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    472      25    24744      26732       0.36   5012   1 vcxsrv

# (2) ホストwindows側のIPを確認
# WindowsのPowershellで実行
# 以下の例では、172.18.192.1がホストWindows側のIP
> ipconfig
...
イーサネット アダプター vEthernet (WSL):

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::fcc4:b3f9:8028:ba12%60
   IPv4 アドレス . . . . . . . . . . . .: 172.18.192.1
   サブネット マスク . . . . . . . . . .: 255.255.240.0
   デフォルト ゲートウェイ . . . . . . .:

# (3) 上記のIPがWSL2側から見えているか確認
# WSL2のターミナルで以下を実行
$ cat /etc/resolv.conf
...
nameserver 172.18.192.1 # ホストWindowsのIPが見えた

# (4) WSL2側のIPを確認
# WSL2のターミナルで以下を実行
# 以下の例では、172.18.203.153がWSL2側のIP
# ホストWindowsと同じネットワークに属しているが異なるIPであることを確認
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.18.203.153  netmask 255.255.240.0  broadcast 172.18.207.255
        ...

# (5) WSL2側からホストWindows側へTCP/IP接続可能か確認
# WSL2のターミナルで以下を実行
# telnet <ホストWindowsのIP> <Xサーバのポート番号>
$ telnet 172.18.192.1 6000
Trying 172.18.192.1...
Connected to 172.18.192.1. # 接続された
Escape character is '^]'.  # Ctrl + ]を押下
telnet> q                  # qまたはquitと入力しEnter
Connection closed.
```
失敗例として、以下があり得る。

- 手順(1)でvcxsrvが見えない  
  →vcXsrvの起動に失敗している
- 手順(2)でIPが空  
  →ホストWindowsにIPが割り振られていない  
  →Windowsのネットワーク設定を再確認してください
- 手順(3)で手順(2)のIPと異なるIPが見えているor手順(4)でIPが空等  
  →WSL2側のネットワーク設定がおかしい
- 手順(5)でconnectedにならない  
  →FirewallでvcXsrvとの接続をブロックされている可能性あり

# 6. 最後に
無事、`hrpsys_ros_bridge`の`SampleRobot`のデモも実行できた。
シミュレーションは快適に動いているし、Microsoft Officeの隣でSimulationが回る最高の環境が完成した。

```bash
rtmlaunch hrpsys_ros_bridge samplerobot.launch
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108813/e1953ce0-dcb0-be91-1b4d-411092ce4848.png)