ROS/ROS2の環境変数管理

ROS/ROS2 環境変数あるある

1台のPCにROS/ROS2の複数のディストリビューションがインストールされていて, 環境変数を切り替えながら使っていませんか?

自分の場合,趣味のプロジェクトで使っているディストリビューションがバラバラで

  • melodic
  • dashing
  • eloquent

を使い分ける必要があります.

環境変数の切り替え,どうしてる?

以前,こんなツイートを見かけました.

みんな環境変数切り替えで苦労してる. 考えることは一緒!

ちなみに自分は.bashrcに下のように書いて一番上のexportコメントアウトで切り替えてました.
今思うと超ガバガバでウケる(ウケない)

export CHOOSE_ROS_DISTRO=dashing
#export CHOOSE_ROS_DISTRO=melodic
#export CHOOSE_ROS_DISTRO=eloquent

if [ $CHOOSE_ROS_DISTRO = melodic ]; then
    source /opt/ros/melodic/setup.bash
    source ~/catkin_ws/devel/setup.bash
elif [ $CHOOSE_ROS_DISTRO = dashing ]; then
    source /opt/ros/dashing/setup.bash
    source ~/ros2_dashing_ws/install/local_setup.bash
elif [ $CHOOSE_ROS_DISTRO = eloquent ]; then
    source /opt/ros/eloquent/setup.bash
    source ~/ros2_eloquent_ws/install/local_setup.bash
fi

神ツール現る

なんと,ROS2本の著者であるyoutalk氏がROS/ROS2の環境変数管理ツールを作っていたみたいです. github.com

ROSJPのLT大会でも発表されてたみたいですね

ROS / ROS 2開発生産性向上ツール - Speaker Deck

複数のディストリビューションだけでなく複数のワークスペースも考えられていて素晴らしい!

使い方は簡単で

chdistro dashing ~/ros2_ws

のようにchdistroコマンドの後にディストリビューションワークスペースを指定するだけです.

よく使うワークスペースにはエイリアスをかけておけば環境切り替えが爆速になりそうです.

神ツールに補完をつける

素晴らしいchdistroですが,入力補完がないのでディストリビューションワークスペースのパスを全て入力する必要があります.

ということで補完スクリプトを作りました.

/etc/bash-completionに配置すると勝手に読み込まれるはずです.

ディストリビューション/opt/rosにインストールされているものを補完し, ワークスペース$HOME以下のディレクトリを補完してくれるようになっています.

本家の方にも補完を取り入れるようにはたらきかけているのでデフォルトで補完が出るようになるかも知れませんね.

Boost.GeometryでEigenを使う

Boost.Geometry?

皆さん,いきなりですが"Boost.Geometry"をご存知ですか?
一口で言うと「幾何計算をやってくれるC++のライブラリ」なんですが,特筆すべきはその華麗なる設計でC++メタプログラミング大好きマンにとってはバイブルとでも言うべき存在です.そっち系の専門書とかでも良い具体例として引用されています.

具体的なクラス設計とかの話はBoostのドキュメントに書いてあるので興味がある人は読んでみましょう. www.boost.org 英語が苦手という人は日本語訳されている方がいらっしゃいましたのでそちらをどうぞ mmiyano.blogspot.com

Boost.Geometryの柔軟性

さて,そのBoost.Geometryはその"華麗なる設計"によって次の要素を任意に設定することができます.

  • 次元
  • 座標系
  • データ型

例えば,「多倍長浮動小数点数の 4次元極座標系」なんかにも対応することが可能です.(もはやなんでもあり)

Eigenへの対応

さて,今回はみんな大好きEigenの2次元ベクトルをこのBoost.Geometryで使えるようにしていきたいと思います. まず,今回扱う2次元ベクトル,Eigen::Vector2fの基礎情報をチェックしていきますと

項目 Eigen::Vector2fの場合
次元 2
座標系 直交座標系
データ型 float

と言うような感じになります.この情報を元に対応させていきます.

各要素の対応

対応させるときはコードをboost::geometry::traits名前空間に書かないといけないのですが,ここでは省略します.

次元

Eigen::Vecotr2fが2次元であることをBoost.Geometryに教えてあげます.

template<>
struct dimension<Eigen::Vector2f> : boost::mpl::int_<2> {};

座標系

直交座標系であることを教えてあげます.
当然ながら,有名な座標系は予め定義されているのでそれを使います. 頑張れば自己流のオレオレ座標系を作って指定することもできるっぽい!?

template<>
struct coordinate_system<Eigen::Vector2f> {
    typedef cs::cartesian type;
};

データ型

Eigen::Vecotr2ffloatで成り立っていることをBoost.Geometryに教えます.

template<>
struct coordinate_type<Eigen::Vector2f> {
    typedef float type;
};

プリミティブ指定

Eigen::Vector2fがポイント型,すなわち点を表すプリミティブであることを設定します. 点の他にも線やポリゴンなども利用可能です*1

template<>
struct tag<Eigen::Vector2f> {
    typedef point_tag type;
};

要素アクセス

Boost.Geometryの統一的なアクセス法に従って値を取得するgetterと値を設定するsetterを書きます. これによって,Boost.Geometryが自在にEigen::Vector2fを操ることができるようになります.

template<>
struct access<Eigen::Vector2f, 0> {
    static float get(Eigen::Vector2f const &p) {
        return p.x();
    }

    static void set(Eigen::Vector2f &p, float const &value) {
        p.x() = value;
    }
};

template<>
struct access<Eigen::Vector2f, 1> {
    static float get(Eigen::Vector2f const &p) {
        return p.y();
    }

    static void set(Eigen::Vector2f &p, float const &value) {
        p.y() = value;
    }
};

全体像

断片的に解説したのでここでコードの全体像を載せておきます.

#pragma once

#include <boost/geometry.hpp>
#include <Eigen/Core>

namespace boost::geometry::traits {
    template<>
    struct tag<Eigen::Vector2f> {
        typedef point_tag type;
    };
    template<>
    struct coordinate_type<Eigen::Vector2f> {
        typedef float type;
    };

    template<>
    struct coordinate_system<Eigen::Vector2f> {
        typedef cs::cartesian type;
    };

    template<>
    struct dimension<Eigen::Vector2f> : boost::mpl::int_<2> {
    };

    template<>
    struct access<Eigen::zenntaiVector2f, 0> {
        static float get(Eigen::Vector2f const &p) {
            return p.x();
        }

        static void set(Eigen::Vector2f &p, float const &value) {
            p.x() = value;
        }
    };

    template<>
    struct access<Eigen::Vector2f, 1> {
        static float get(Eigen::Vector2f const &p) {
            return p.y();
        }

        static void set(Eigen::Vector2f &p, float const &value) {
            p.y() = value;
        }
    };
}

 

GDBでEigenのデバッグをする

はじめに

GDB?Eigen?な人はお帰りください.
恐らくこの記事はあなたが欲しい情報を提供することができないでしょう.

これから書くことは↓に全て書いてあります.
つまずく人もいそうなので一応日本語情報を残す意味合いで書きました.

stackoverflow.com

因みに筆者の環境はUbuntu18.04です.

GDBでEigenのデバッグができない問題

GDBでEigenのデータを取得しようとすると固まります.
私はCLionにバンドルされているGDBを使っているのですが,
CLionの画面では"Collecting Data..."と出たきりCLionの応答がなくなってしまいます.

f:id:HansRobo:20190924004917p:plain
CLionのGDBでEigen::Vector2fの情報を取得しようとした図

これではデバッグどころかコーディングもできない!!

解決策

冒頭にも言った通り以下のStackOverFlowに書かれています. stackoverflow.com

実はEigenにGDBの拡張プラグインが含まれていてそれをインストールすることで解決できるというオチですw

拡張プラグインのダウンロード

Eigen本体に拡張プラグインが含まれているのでダウンロードします*1.

github.com

以下,/home/hansディレクトリにダウンロードした前提で話をすすめるので適宜置き換えて読んでください.

cd /home/hans/
git clone https://github.com/eigenteam/eigen-git-mirror.git

拡張プラグインの読み込み設定

gdbの設定ファイルを作ってプラグインの読み込み設定を記述します.
ホームディレクトリに.gdbinitという名前のファイルを作成して次の内容をコピーしてください.

python
import sys
sys.path.insert(0, '/home/hans/eigen-git-mirror/debug/gdb')
from printers import register_eigen_printers
register_eigen_printers (None)
end

GDBの再起動

設定ファイルの内容が反映するにはGDBの再起動が必要です.
GDBを起動しているなら一度落としてもう一度起動してください.

Eigenのベクトルや行列をデバッグした時に写真の用に中身が表示できていれば成功です.

f:id:HansRobo:20190924012606p:plain
GDBでEigenのデバッグに成功した図

*1:libeigen3-devパッケージには必要最低限のものしか含まれていないのでダウンロードする必要があります