つくばチャレンジの環境を Unity 上で再現したシミュレータです。
本リポジトリは Field-Robotics-Japan/vtc_unity の派生版で、 ROS 2 Humble で動作させるための修正と、2D LiDAR / GNSS センサーの追加を行っています。
オリジナルの Virtual Tsukuba Challenge (Unreal Engine 版) は fuRo によって開発されました。
利用している上流パッケージ:
- vtc_world_unity : つくばチャレンジの環境モデル
- kashiwanoha_world_unity : 柏の葉キャンパスの環境モデル
- unit04_unity : ロボット (unit04) モデル
- sensors_unity : センサーモデル
以下のバージョンで動作を確認しています。
| 項目 | バージョン |
|---|---|
| OS | Ubuntu 22.04.5 LTS |
| ROS | ROS 2 Humble Hawksbill |
| Unity Editor | 2021.3.45f1 |
| Git LFS | 3.0.2 |
| パッケージ | 確認バージョン | 用途 |
|---|---|---|
ros-humble-rosbridge-suite |
2.0.0 | Unity との WebSocket 通信 |
ros-humble-velodyne-msgs |
2.5.1 | Velodyne パケット型 |
ros-humble-velodyne-pointcloud |
2.5.1 | パケット → 点群 変換 |
ros-humble-tf2-ros |
0.25.12 | 静的 TF の配信 |
ros-humble-rviz2 |
11.2.16 | 可視化 |
ros-humble-teleop-twist-keyboard |
2.4.0 | キーボード操作 |
sudo apt install ros-humble-rosbridge-suite ros-humble-velodyne \
ros-humble-tf2-ros ros-humble-rviz2 ros-humble-teleop-twist-keyboard注意: 上流の README は ROS 1 (
roslaunch,rosbridge_websocket.launch) を前提としています。 本リポジトリでは ROS 2 用の手順に置き換わっています。
本プロジェクトの Unity 側通信は ROS# (RosSharp) が担っており、ROS 1 形式のヘッダを送信します。
ROS 1 の std_msgs/Header には seq フィールドがありますが、ROS 2 には存在しません。
そのため rosbridge がヘッダ付きメッセージを全て拒否し、以下のエラーでセンサーデータが一切流れませんでした。
[ERROR] [rosbridge_websocket]: [id: /imu] publish:
Message type sensor_msgs/Imu does not have a field header.seq
対策として、seq のみを無視する rosbridge のパッチを用意しました (ros2/setup_rosbridge_patch.sh)。
seq 以外の未知フィールドは従来どおりエラーになるため、他の不具合を隠すことはありません。
/opt/ros は変更せず、パッチ済みコピーを PYTHONPATH で優先させる方式です。
なお、タイムスタンプ形式 (secs/nsecs ↔ sec/nanosec) は rosbridge 側が変換に対応済みのため問題になりません。
ただし タイムスタンプは rosbridge の受信時刻で上書きされます。
Assets/unit04_unity/Scripts/LiDAR/LrfScanPublisher.cs を新規追加し、
front_lrf_link / rear_lrf_link に取り付けました。sensor_msgs/LaserScan を配信します。
角度範囲とレイ数から角度分解能を自動計算し、測定範囲外は range_max + 1(ROS 上の無効値)を返します。
ROS# 付属の
LaserScanPublisher+LaserScanReaderも存在しますが、そちらは未計測のレイを0のままにし、samplesとangle_incrementを手動で整合させる必要があるため、別実装としています。
Assets/unit04_unity/Scripts/GNSS/GnssPublisher.cs を新規追加し、gps_link に取り付けました。
sensor_msgs/NavSatFix を配信します。実機 (u-blox) を想定した RTK シミュレーションに対応しています。
| モード | status |
水平σ | 垂直σ | 想定状況 |
|---|---|---|---|---|
RtkFixed (既定) |
2 (GBAS) | 0.02 m | 0.03 m | 上空が開けた場所 |
RtkFloat |
2 (GBAS) | 0.30 m | 0.50 m | 樹木・建物の近く |
Single |
0 (FIX) | 1.50 m | 3.00 m | 補正データ途絶 |
NoFix |
-1 | - | - | 測位不能 |
NavSatStatus に RTK 専用の値が無いため、ROS ドライバの慣例に従い RTK Fixed / Float とも STATUS_GBAS_FIX とし、
精度差は共分散で表現しています。Inspector から実行中でも切り替えられます。
Unity 座標から WGS84 への変換は局所平面近似で行い、Unity の +x を東・+z を北・+y を上として扱います。
OriginLatitude/OriginLongitude: Unity 原点 (0,0,0) に対応する実座標。既定値は推定値で、 ロボット初期位置がつくば市役所前 (36.0833N / 140.0764E) になるよう逆算したものです。測量値ではありません。OriginAltitude: 既定 40.0。シーンの Y 軸が実標高 (m) を持つため、 つくば周辺のジオイド高を加算して WGS84 楕円体高 (u-blox が出力する基準) に合わせています。
unit04.prefab の gps_link は m_IsActive: 0 (無効) だったため Start() が呼ばれず、
GNSS が advertise されませんでした。有効化しています (質量 0.25 kg・重力無効・FixedJoint 固定のため挙動への影響はありません)。
Unity 側は TF を配信しないため、rviz2 が全センサーのメッセージを破棄していました
(Message Filter dropping message ... queue is full)。
prefab から実際のリンク位置を読み取り、静的 TF として配信する launch を追加しています (ros2/launch/unit04_tf.launch.xml)。
テクスチャとモデルは Git LFS で管理されています。LFS を入れずに clone すると、テクスチャが ポインタファイルのままになり、環境が白黒で表示されます。
sudo apt install git-lfs
git lfs install
git clone <this-repository>
cd vtc_unity
git lfs pull # 約 840 MB既に clone 済みで白黒になっている場合:
git lfs pull
rm -rf Library/ # インポート結果のキャッシュを破棄 (再インポートに 10-20 分)Library/ は生成物なので削除して問題ありません。
Unity Hub から 2021.3.45f1 をインストールします。
# Unity Hub (未導入の場合)
sudo apt install unityhubUnity Hub でプロジェクト (vtc_unity ディレクトリ) を開きます。初回のインポートには時間がかかります。
source /opt/ros/humble/setup.bash
./ros2/setup_rosbridge_patch.shros2/rosbridge_patched/ にパッチ済みの rosbridge_library が生成されます (Git 管理外)。
./ros2/run_rosbridge.sh必ずこのスクリプトを使ってください。 素の
ros2 launch rosbridge_server rosbridge_websocket_launch.xmlで起動するとheader.seqエラーで全センサーが止まります。
Assets/vtc_unity/Scenes/VirtualTsukubaChallenge.unity を開き、▶ を押します。
(柏の葉キャンパスは Assets/vtc_unity/Scenes/KashiwaNoHa.unity)
rosbridge を再起動した場合は、Unity の Play を停止 → 再開してください。ROS# は自動再接続しません。
接続確認:
ros2 topic echo /client_count --once # 1 以上なら接続済みros2 launch ros2/launch/unit04_tf.launch.xmlUnity が配信するのは生パケットなので、変換ノードが必要です。
ros2 launch velodyne_pointcloud velodyne_transform_node-VLP16-launch.pyros2 run teleop_twist_keyboard teleop_twist_keyboard \
--ros-args -r /cmd_vel:=/diff_drive_controller/cmd_velリマップは必須です (Unity 側は /cmd_vel を購読していません)。
rviz2Fixed Frame は base_footprint に設定してください。
| 方向 | トピック | 型 | frame_id |
|---|---|---|---|
| ROS → Unity | /diff_drive_controller/cmd_vel |
geometry_msgs/Twist |
- |
| Unity → ROS | velodyne_packets |
velodyne_msgs/VelodyneScan |
velodyne |
| Unity → ROS | /front_lrf/scan |
sensor_msgs/LaserScan |
front_lrf_link |
| Unity → ROS | /rear_lrf/scan |
sensor_msgs/LaserScan |
rear_lrf_link |
| Unity → ROS | /gps/fix |
sensor_msgs/NavSatFix |
gps_link |
| Unity → ROS | /imu |
sensor_msgs/Imu |
Unity |
| Unity → ROS | /camera0/compressed |
sensor_msgs/CompressedImage |
Camera |
ロボットは差動二輪 (車輪半径 0.20316 m / トレッド 0.43515 m) で、cmd_vel を左右ホイールの速度に変換して走行します。
IMU とカメラの
frame_idは上流由来でUnity/Cameraという汎用名のままです。
重い場合は以下を調整してください (いずれも Inspector から変更できます)。
| 対象 | 設定項目 | 既定値 | 備考 |
|---|---|---|---|
| Unity 全体 | Quality Settings | - | Edit > Project Settings > Quality を下げるのが最も手軽 |
| Velodyne | Lidar.numberOfIncrements |
3600 | 1800 で負荷が半減。24 の倍数にすること (パケット構造の制約) |
| 2D LiDAR | LrfScanPublisher.NumberOfRays |
1081 | 271 (1度刻み) で 1/4 に |
| 2D LiDAR | LrfScanPublisher.ScanRateHz |
40 | 20 Hz でも多くの用途では十分 |
| カメラ | CameraSensor |
- | レート制限が無く毎フレーム JPEG 圧縮するため、未使用なら無効化 |
Raycast 数の目安: Velodyne が 16 × 3600 × 10 Hz = 576,000 本/秒、2D LiDAR が 1081 × 40 Hz = 43,240 本/秒 (1台あたり)。
Git LFS のファイルがポインタのままです。
git lfs pull
rm -rf Library/Library/ を削除して開き直すと、シーンが読み込まれず空の状態で起動します。
Project ウィンドウからシーンファイルを開き直してください。
素の rosbridge が起動している可能性があります。
grep -c "header.seq" $(ls -t ~/.ros/log/python3_*.log | head -1) # 0 なら正常
pgrep -c -f "lib/rosbridge_server/rosbridge_websocket" # 1 なら正常./ros2/run_rosbridge.sh で起動し直してください。
Message Filter dropping message ... queue is full が出る場合は TF がありません。
ros2 launch ros2/launch/unit04_tf.launch.xml を起動し、Fixed Frame を base_footprint にしてください。
ss -ltn | grep 9090 # rosbridge が待ち受けているか
ros2 topic echo /client_count --once # 0 なら Unity が未接続rosbridge の再起動後は Unity の Play を停止 → 再開してください。
- オドメトリと TF が Unity から配信されません。 そのため rviz2 上でロボットは移動しません
(
odom → base_footprintが存在しないため)。センサーはロボット座標系で正しく表示されます。 - タイムスタンプは rosbridge の受信時刻であり、Unity のシミュレーション時刻ではありません。
- GNSS の原点座標は推定値です。実コースの実測データがあれば
GnssPublisherのOriginLatitude/OriginLongitudeを修正してください。 Packages/manifest.jsonに ROS-TCP-Connector と UnitySensors が含まれていますが、 シーンでは使用していません (通信は全て ROS# 経由)。
- Stencil2 PointCloud data : Apache2.0 by 防衛大学校ソフトウェア工学講座
This data is referenced to create the world model. - City Hall.fbx : Apache2.0 by Tomoaki Yoshida
City Hall.prefab is modified from above FBX model. - FBX data for Kashiwa No Ha Campus : CC BY 4.0 by National Institute of Advanced Industrial Science and Technology (AIST)
We utilize following Assets from Unity Asset Store.
Every package are Free now (2020/05/13).
We partly modify and utilize those Assets.
- RosSharp : Apache2.0
- Realistic Tree 9
- Mobile Tree Package
- Yughues Free Bushes
- Yughues Free Ground Materials
- PBR Tile Texture Floor
- Birch Tree Pack vol. 1
- Forester Pro Sycamore
- Asphalt materials
- Easy Grass Substance
- First Person All-in-One
- Simple Cars Pack
- UAA - City Props - Vehicles
- Stylized Vehicles Pack - FREE
Copyright [2020] Ryodo Tanaka groadpg@gmail.com
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

