pixiで始めるmacOSにおけるROS 2開発環境構築（Gazebo Fortressとnav2を添えて）

## TL;DR

- これまでROS 2のmacOSサポート状況は決して手厚いとは言えない状況でした
- pixiの登場によってmacOSでもROS 2ソフトウェア開発ができるようになってきました
- 今後のpixiの発展に期待しましょう！

## 1. 昨今のROS 2のサポート事情

ROS 2 Jazzy時点でmacOS(かつ、amd64)のサポートはTier3でソースビルドのみとなっています。そのため、ROS 2のmacOSサポート状況は決して手厚い状況ではありません。

![ros2_support.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/91fed3dc-8cc4-418c-8b59-e6737ac5e9bd.png)

※<https://www.ros.org/reps/rep-2000.html#jazzy-jalisco-may-2024-may-2029>より引用

## 2. macOS上におけるROS 2環境構築

一方、普段使い慣れているmacOSでROS 2ソフトウェア開発をしたい方も一定数居ます。macOS上におけるROS 2環境構築は以下のパターンがあります。

- ソースビルド
- Dockerコンテナ
- pixi
- pixi + Dockerコンテナ

それぞれの概要、メリット、デメリットを述べていきます。

### 2.1 ソースビルド

公式ドキュメントは<https://docs.ros.org/en/jazzy/Installation/Alternatives/macOS-Development-Setup.html>にあります。先人たちの~~涙ぐましい~~取り組みは以下にあります。

- <https://roscon.jp/2023/presentations/019.pdf>
- <https://github.com/TakanoTaiga/ros2_m1_native>
- <https://www.docswell.com/s/dandelion1124/KM1PJL-2024-02-13-093736>
- <https://ar-ray.hatenablog.com/entry/2023/12/24/115051>

「ソースビルド」におけるメリット、デメリットについては後述します。

#### メリット

- GPUなどの各種アクセラレータが使える
- USBカメラなどのセンサデバイスが使える
- ネイティブで動作させるため、シミュレータ環境、RViz2などが高速に動作する

#### デメリット

- [Homebrew](https://brew.sh/)でROS 2関連のパッケージは管理されていないため、必要なパッケージは基本的にソースビルドが必要
- パッケージ依存解決にrosdepが使えない
- Homebrewのパッケージ更新によって頻繁にビルドできなくなるため、定期的なメンテナンスが必要
- ホスト環境が汚れる

### 2.2 Dockerコンテナ

macOS上のDockerコンテナ上でROS 2を動かす方法があります。CUIのみであれば<https://hub.docker.com/_/ros>で十分ですが、GUIツール（Rqtツール、RViz2、Gazeboなど）を使う場合、<https://github.com/Tiryoh/docker-ros2-desktop-vnc>で動かすのが楽ちんです。

「Dockerコンテナ」におけるメリット、デメリットについては後述します。

#### メリット

- apt、rosdepが使えるため、パッケージインストール、依存解決が簡単
- Ubuntuと同じ操作コマンドのため、そちらに慣れている方にはハードルが低い
- ホスト環境が汚れない

#### デメリット

- GPUなどの各種アクセラレータが使えない
- USBカメラなどのセンサデバイスが使えない
- GUIアプリケーション（シミュレータ環境、RViz2）をDockerコンテナ上で動かすと低速

GUIありでGazebo Fortressを起動するとCPU使用率が以下のようになります。Gazebo FortressのプロセスのCPU使用率が高くなっていることがわかります。

![docker_gazebo_gui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/337976cd-3678-4d40-8ba2-7b313dbad06b.png)

GUIなし（headless mode）でGazebo Fortressを起動するとCPU使用率が以下のようになります。GUIありよりもCPU使用率が下がっているものの、Gazebo FortressのプロセスのCPU使用率が高くなっていることがわかります。

![docker_gazebo_headless.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/a0d83540-4639-492d-83f5-239aaea4a654.png)

### 2.3 pixi

クロスプラットフォーム対応のパッケージ管理システム[pixi](https://pixi.sh/latest/)があり、ROS 2パッケージも配布しています。対応しているパッケージが気になる方は<https://prefix.dev/channels/robostack-staging>を読むとよいでしょう。

本記事ではpixiそのものの詳細は取り上げないので気になる方は以下のページを参照ください。

- <https://pixi.sh/latest/tutorials/ros2/>
- <https://note.com/hiroyuki_okada/n/n619b16943f0a>
- <https://zenn.dev/yahooshiken/articles/getting-started-ros2-with-pixish>
- <https://www.docswell.com/s/zibaanyan/KWWRYD-2024-12-28-113147>

「pixi」におけるメリット、デメリットについては後述します。

#### メリット

- `pixi add`が使えるため、パッケージインストール、依存解決が簡単
- ワークスペース単位で分離されているのでホスト環境が汚れない

#### デメリット

- パッケージ依存解決にrosdepが使えない
- コマンドに`pixi run`を付ける必要があるため、少し慣れが必要

### 2.4 pixi + Dockerコンテナ

pixiとDockerコンテナを併用することでそれぞれの欠点を補う方法です。詳細は<https://qiita.com/dandelion1124/items/cf6399071744049b7370>を参照ください。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">macOSのDockerコンテナ上でIgnition Gazebo、ナビゲーションを動かし、macOSネイティブでRViz2使って可視化するという全部macOS上で動かすやつができた。これで謎のビルド修行からかなり解放される。 <a href="https://t.co/4kEnhyBAio">pic.twitter.com/4kEnhyBAio</a></p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/1806013265556001232?ref_src=twsrc%5Etfw">June 26, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

「pixi + Dockerコンテナ」におけるメリット、デメリットについては後述します。

#### メリット

- pixi
  - ワークスペース単位で分離されているのでホスト環境が汚れない
- Dockerコンテナ
  - apt、rosdepが使えるため、パッケージインストール、依存解決が簡単
  - ホスト環境が汚れない

#### デメリット

- pixi
  - パッケージ依存解決にrosdepが使えない
  - コマンドに`pixi run`を付ける必要があるため、少し慣れが必要
- Dockerコンテナ
  - GUIアプリケーション（シミュレータ環境、RViz2）をDockerコンテナ上で動かすと低速
- 全般
  - 何をpixiで動かして、何をDockerで動かすかの判断、パッケージ構成を工夫する必要がある

## 4. pixi上でGazebo、navigation2を動かす

pixi上でどこまでできそうかを掴むためにGazebo、navigation2を動かしてみます。

### 4.1 動作確認環境

今回動作確認に用いた開発環境は以下の通りです。

- MacBook Air (15インチ, M3, 2024)
- macOS Sonoma 14.4
- Docker Desktop on Mac 4.39.0
- pixi 0.45.0

### 4.2 GitHubリポジトリ

以下の図からpixi上でGazebo Fortressとnav2を動かせるソースコード一式を以下のリポジトリに格納しています。
<https://github.com/atinfinity/pixi_ros2_gazebo_example>

### 4.3 動作確認

pixi上でGazebo Fortressとnav2が動かせていることがわかります。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Gazebo Ignition、navigation2全部macOS上のpixiで動かすことに成功！ <a href="https://t.co/NVkE7sjbGe">pic.twitter.com/NVkE7sjbGe</a></p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/1910393458113863952?ref_src=twsrc%5Etfw">April 10, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

また、pixi上でGUIなし（headless mode）でGazebo Fortressを起動するとCPU使用率が以下のようになります。macOSネイティブ上で実行しているため、Gazebo FortressのプロセスのCPU使用率が減っていることがわかります。

![pixi_gazebo_headless.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/e8c55bd2-ef26-44be-a1fc-744fc37ea028.png)

追加で3D LiDARのシミュレーションも追加してみましたが、こちらもmacOSネイティブ上で実行しているため、快適に動作しています、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">macOSネイティブ実行だとGazeboの3D LiDARもさくさく動くよの図。 <a href="https://t.co/hfpeYSBbu0">pic.twitter.com/hfpeYSBbu0</a></p>&mdash; dandelion (@dandelion1124) <a href="https://twitter.com/dandelion1124/status/1916474091638542669?ref_src=twsrc%5Etfw">April 27, 2025</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### 4.4 制限事項

- Gazebo FortressのGUIが使えない <https://github.com/gazebosim/gz-sim/issues/2848>
- macOSのpixiでOgre 2が正常に動作しないのでOgre 1にする必要があります

また、はっきりと原因を特定できていませんが、2D LRFのURDFを以下のように記述していました。

```xml
<scan>
  <horizontal>
    <samples>1000</samples>
    <resolution>1</resolution>
    <min_angle>-3.141592</min_angle>
    <max_angle>3.141592</max_angle>
  </horizontal>
</scan>
```

ただし、これをそのまま使うと以下のようになってしまいました・・・。

![invalid_scan.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31158/667247d5-d630-4f41-832a-6b467039e011.jpeg)

そのため、verticalタグを追加して回避しています。

```xml
<scan>
  <horizontal>
    <samples>1000</samples>
    <resolution>1</resolution>
    <min_angle>-3.141592</min_angle>
    <max_angle>3.141592</max_angle>
  </horizontal>
  <vertical>
    <samples>2</samples>
    <resolution>0.01</resolution>
    <min_angle>-0.01</min_angle>
    <max_angle>0.01</max_angle>
  </vertical>
</scan>
```