AirSimをUbuntuで使う方法をまとめました。

UbuntuでAirSimを使う【C++, ROS】

AirSimをUbuntuで使うのに大苦戦したので、使用方法を残しておきます。記事後半では、ROS Wrapperの使用方法もまとめます。

目次

テスト環境

  • Ubuntu 16.04
  • ROS Kinetic

AirSimの使用方法

v1.2.0 - Linuxの環境「Block」を例として以下で紹介します。

ビルド済みのバイナリをダウンロード

公式Releaseからzipファイルをダウンロードして解凍します。

AirSim公式Release

$ wget https://github.com/microsoft/AirSim/releases/download/v1.2.0Linux/Blocks.zip
$ unzip Blocks.zip

AirSim(Unreal Engine)の起動

$ cd Block
$ ./Block.sh

オプションなしだとフルスクリーンで他の作業ができなくなるので、下記のように、ウィンドウサイズを指定するのがオススメです。

$ ./Block.sh -ResX=640 -ResY=480 -windowed

起動するとドローンか自動車か選択するポップアップが出ます。ドローンを動かすにはコントローラやAPIが必要ですが、自動車はキーボードで運転することができます。操縦方法の詳細はF1で表示できるので確認してみてください。

Dockerコンテナ内では上手く起動できなかった

ちなみに、Dockerコンテナ内でダウンロードして同様に起動しようとしたら、以下のエラーが出たので、Dockerコンテナ内での起動はあきらめました。

Refusing to run with the root privileges.

ROS Wrapperの使用方法

ROS Wrapperを使用することで、ROSを通してAirSim(Unreal Engine)と通信できます。

AirSimのビルド

準備

AirSimのビルドに必要なライブラリなどをインストールします。

$ apt-get update
$ apt-get install -y rsync g++-8 python-catkin-tools ros-kinetic-mavros*

ビルド

$ git clone https://github.com/Microsoft/AirSim.git
$ cd AirSim
$ ./setup.sh
$ ./build.sh

ROS Wrapperのビルド

CMake3.10以上が必要

公式Releaseからtarファイルをダウンロードして解凍します。(例:v3.17.3)

CMake公式Release

$ wget https://github.com/Kitware/CMake/releases/download/v3.17.3/cmake-3.17.3.tar.gz
$ tar xvf cmake-3.17.3.tar.gz
$ cd cmake-3.17.3
$ ./bootstrap && make && make install

filesystem関連のエラー回避

filesystem関連のビルドエラーを回避するために、cppファイルの中身を以下のように書き換える必要があります。

  • AirSim/MavLinkCom/MavLinkTest/Commands.cpp
    • #include <filesystem>
      → #include <experimental>
    • using namespace std::filesystem;
      → using namespace std::experimental::filesystem;
  • AirSim/MavLinkCom/MavLinkTest/main.cpp
    • #include <filesystem>
      → #include <experimental/filesystem>
    • using namespace std::filesystem;
      → using namespace std::experimental::filesystem

ビルド

$ cd ~/AirSim/ros
$ catkin build

セットアップ

roslaunchなどを利用するために以下のセットアップが必要です。

$ source ~/AirSim/ros/devel/setup.bash
$ echo "source ~/AirSim/ros/devel/setup.bash" >> ~/.bashrc

起動手順

settingファイルを指定

settingファイルを既定の場所に置きます。settingファイルでは、センサの条件などを指定することができます。まずは、以下のように、公式チュートリアルで用意されているsettingファイルを使ってみます。

$ mkdir -p ~/Documents/AirSim
$ roscd airsim_tutorial_pkgs
$ cp settings/front_stereo_and_center_mono.json ~/Documents/AirSim/settings.json

AirSim(Unreal Engine)の起動

$ cd Block
$ ./Block.sh

settingファイルの通りに環境が構築されていることが確認できると思います。

airsim_drone

ROS WrapperをLaunch

$ roslaunch airsim_ros_pkgs airsim_node.launch

rvizやrostopic echoで、ROS Wrapperが機能していることを確認できると思います。

おまけ:ROS WrapperをDockerで使う

ROS WrapperをDockerで使う方法を紹介します。

Dockerfile

Dockerfileの実装例です。

FROM osrf/ros:kinetic-desktop-full

########## basis ##########
RUN apt-get update && apt-get install -y \
	vim \
	wget \
	unzip \
	git
########## AirSim ##########
RUN apt-get update &&\
	apt-get install -y \
		rsync \
		g++-8 \
		python-catkin-tools \
		ros-kinetic-mavros* &&\
	mkdir /home/airsim_ws/ &&\
	cd /home/airsim_ws/ &&\
	git clone https://github.com/Microsoft/AirSim.git &&\
	cd AirSim &&\
	./setup.sh &&\
	./build.sh
########## AirSim ROS wrapper ##########
##### CMake >= 3.10 #####
RUN mkdir /home/cmake_ws &&\
	cd /home/cmake_ws &&\
	wget https://github.com/Kitware/CMake/releases/download/v3.17.3/cmake-3.17.3.tar.gz &&\
	tar xvf cmake-3.17.3.tar.gz &&\
	cd cmake-3.17.3 &&\
	./bootstrap && make && make install
##### Build #####
RUN sed -i "s/#include <filesystem>/#include <experimental\/filesystem>/g" /home/airsim_ws/AirSim/MavLinkCom/MavLinkTest/Commands.cpp &&\
	sed -i "s/using namespace std::filesystem;/using namespace std::experimental::filesystem;/g" /home/airsim_ws/AirSim/MavLinkCom/MavLinkTest/Commands.cpp &&\
	sed -i "s/#include <filesystem>/#include <experimental\/filesystem>/g" /home/airsim_ws/AirSim/MavLinkCom/MavLinkTest/main.cpp &&\
	sed -i "s/using namespace std::filesystem;/using namespace std::experimental::filesystem;/g" /home/airsim_ws/AirSim/MavLinkCom/MavLinkTest/main.cpp &&\
	cd /home/airsim_ws/AirSim/ros &&\
	/bin/bash -c "source /opt/ros/kinetic/setup.bash; catkin build" &&\
	echo "source /home/airsim_ws/AirSim/ros/devel/setup.bash" >> ~/.bashrc
##### Setting json #####
RUN mkdir -p /root/Documents/AirSim &&\
	cp /home/airsim_ws/AirSim/ros/src/airsim_tutorial_pkgs/settings/front_stereo_and_center_mono.json /root/Documents/AirSim/settings.json
######### initial position ##########
WORKDIR /home/airsim_ws

Build

$ docker build -t airsim_ros:latest .

Run

$ docker run -it --rm --net=host airsim_ros:latest

使い方

ホスト側での操作

settingファイルを指定

$ mkdir -p ~/Documents/AirSim
$ roscd airsim_tutorial_pkgs
$ cp settings/front_stereo_and_center_mono.json ~/Documents/AirSim/settings.json

AirSim(Unreal Engine)の起動

$ cd Block
$ ./Block.sh

Docker側での操作

$ docker run -it --rm --net=host airsim_ros:latest
(コンテナ内)$ roslaunch airsim_ros_pkgs airsim_node.launch

ROS Wrapperでドローンを操縦

ros::ServiceClientやros::Publisherを用いることでシミュレーション内のドローンに指令を与えることができます。以下の手順で、ドローンを操縦するためのパッケージを新たにつくっていきます。

パッケージを作成

airsim_ros_pkgsで定義されているメッセージやサービスを使うので、以下のように依存関係を指定する必要があります。

$ cd ~/AirSim/ros/src
$ catkin_create_pkg my_pkg std_msgs rospy roscpp airsim_ros_pkgs

ソースコードを実装

離陸して直進する実装例をご紹介します。ファイル名はgo_straight.cppとしました。topicやserviceの名前は、rostopic listやrosservice listでそれぞれ確認してください。

#include <ros/ros.h>
#include <airsim_ros_pkgs/Takeoff.h>
#include <airsim_ros_pkgs/VelCmd.h>

int main(int argc, char** argv)
{
	ros::init(argc, argv, "test_flight");
	ros::NodeHandle nh;

	/*pub srv*/
	ros::ServiceClient client_takeoff = nh.serviceClient<airsim_ros_pkgs::Takeoff>("/airsim_node/drone_1/takeoff");
	ros::Publisher pub_vel = nh.advertise<airsim_ros_pkgs::VelCmd>("/airsim_node/drone_1/vel_cmd_body_frame", 1);

	/*take off*/
	airsim_ros_pkgs::Takeoff srv_takeoff;
	if(client_takeoff.call(srv_takeoff)){
		std::cout << "takeoff: true" << std::endl;
	}
	else{
		std::cout << "takeoff: false" << std::endl;
		return 1;
	}
	/*go straight*/
	double mps = 1.0;
	airsim_ros_pkgs::VelCmd vel_msg;
	vel_msg.twist.linear.x = mps;
	vel_msg.twist.linear.y = 0.0;
	vel_msg.twist.linear.z = 0.0;
	vel_msg.twist.angular.x = 0.0;
	vel_msg.twist.angular.y = 0.0;
	vel_msg.twist.angular.z = 0.0;
	pub_vel.publish(vel_msg);

	return 0;
}

CMakelists.txtに追加

cmake_minimum_required(VERSION 2.8.3)
project(my_pkg)

find_package(catkin REQUIRED COMPONENTS
  airsim_ros_pkgs
  roscpp
  rospy
  std_msgs
)

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES my_pkg
#  CATKIN_DEPENDS airsim_ros_pkgs roscpp rospy std_msgs
#  DEPENDS system_lib
)

include_directories(
  ${catkin_INCLUDE_DIRS}
)

add_executable(go_straight src/go_straight.cpp)
target_link_libraries(go_straight ${catkin_LIBRARIES})

ビルド

$ cd ~/AirSim/ros
$ catkin build

TAKE OFF!!!

AirSim(Unreal Engine)の起動

$ cd Block
$ ./Block.sh

ROS WrapperをLaunch

$ roslaunch airsim_ros_pkgs airsim_node.launch

指令を送信

$ rosrun my_pkg go_straight

ドローンが浮上して直進するはずです。

C++ APIでドローンを操縦

本当は上記のROS Wrapperで操縦したかったのですが、ロール、ピッチの指令が上手く機能しなかったので、C++のAPIも使ってみました。

ソースコードを実装

公式チュートリアルに載っているソースコードを使います。ファイル名はhello_drone.cppとしました。

#include <iostream>
#include "vehicles/multirotor/api/MultirotorRpcLibClient.hpp"

int main() 
{
	using namespace std;
	msr::airlib::MultirotorRpcLibClient client;

	cout << "Press Enter to enable API control" << endl; cin.get();
	client.enableApiControl(true);

	cout << "Press Enter to arm the drone" << endl; cin.get();
	client.armDisarm(true);

	cout << "Press Enter to takeoff" << endl; cin.get();
	client.takeoffAsync(5)->waitOnLastTask();

	cout << "Press Enter to move 5 meters in x direction with 1 m/s velocity" << endl; cin.get();  
	auto position = client.getMultirotorState().getPosition(); // from current location
	client.moveToPositionAsync(position.x() + 5, position.y(), position.z(), 1)->waitOnLastTask();

	cout << "Press Enter to land" << endl; cin.get();
	client.landAsync()->waitOnLastTask();

	return 0;
}

CMakeLitsts.txtに追加

cmake_minimum_required(VERSION 3.5.0)
project(my_project)

add_compile_options(-std=c++11)

# set(AIRSIM_ROOT ../AirSim)
find_path(AIRSIM_ROOT
	NAMES AirSim.sln 
	PATHS "./AirSim" "../AirSim" "../../AirSim" "../../../AirSim" "../../../../AirSim"
)
message(STATUS "found AIRSIM_ROOT=${AIRSIM_ROOT}")

add_subdirectory("${AIRSIM_ROOT}/cmake/AirLib" AirLib)
add_subdirectory("${AIRSIM_ROOT}/cmake/rpclib_wrapper" rpclib_wrapper)
add_subdirectory("${AIRSIM_ROOT}/cmake/MavLinkCom" MavLinkCom)

include_directories(
	${AIRSIM_ROOT}/AirLib/include
	${AIRSIM_ROOT}/external/rpclib/rpclib-2.2.1/include
	${AIRSIM_ROOT}/MavLinkCom/include
	${AIRSIM_ROOT}/MavLinkCom/common_utils
	${AIRSIM_ROOT}/AirLib/deps/eigen3
)

add_executable(hello_drone src/hello_drone.cpp)
target_link_libraries(hello_drone AirLib rpc)

find_path(またはset)で、AirSimのroot pathを${AIRSIM_ROOT}として定義して使っています。自身の環境に合わせて、調整する必要があります。その他の部分は変更する必要ありません。後述のディレクトリ構造の通りにファイルを置けば変更なしでビルドできるはずです。

ディレクトリ構造の確認

ディレクトリ構造の例です。

airsim_ws/
 ┣ AirSim
 ┗ my_project
   ┣ CMakeLitsts.txt
   ┗ src/
     ┗ hello_drone.cpp

ビルド

$ cd ~/airsim_ws/my_project
$ mkdir build
$ cd build
$ cmake ..
$ make

TAKE OFF!!!

AirSim(Unreal Engine)の起動

$ cd Block
$ ./Block.sh

APIを起動

$ cd ~/airsim_ws/my_project/build
$ ./hello_drone

確認されたエラー

現時点(2020年7月)では、Linux用のバイナリーのバージョンは、v1.2.0~v1.3.1がリリースされています。v1.2.0では、以下のエラーが確認されました。

  • ROS WrapperでIMUのデータを取得できない
    $ roslaunch airsim_ros_pkgs airsim_node.launch


    error
    Exception raised by the API:
    rpclib: server could not find function 'getImuData' with argument count 2.
  • C++のAPIでロール、ピッチ、ヨーの指令が出せない
    client.moveByRollPitchYawZAsync(roll, pitch, yaw, z, duration)->waitOnLastTask();


    terminate called after throwing an instance of 'rpc::rpc_error'
      what():  rpc::rpc_error during call
    Aborted (core dumped)

v1.3.1では上記のエラーが出ないのですが、環境の種類が少なかったり、全体的に処理が重いといった問題があります。

※もし私側のミスによるエラーでしたら申し訳ありません。

NOTE:座標系

ちなみに、AirSimの座標系は下図の通りになっているようです。姿勢などを考えるときは座標系に注意してください。

airsim_coordinate

さいごに

AirSimをUbuntuで使う方法をまとめてみました。参考になれば幸いです。


以上です。

Ad.