はじめに
すいません,私もこのネタ(includeパス問題)です.
Go言語(以下,golang)はクラウドネイティブな各モジュールと相性が良い.Webサーバ機能がPy○○onなどと違ってテスト用ではなく商用にも耐えられるモノが標準で採用されてる点が大きい.その他諸々からもREST APIファーストな用途には欠かすことが出来ない言語と言える.
そんなgolangをROS2でも利用したい!と考えるのは最早,必然である(?)
2022年現在,目についたrclgo実装は次の通り.
- https://github.com/tiiuae/rclgo
- https://github.com/juaruipav/rclgo
- https://github.com/PiusNyakoojo/rclgo
いずれの実装もhumbleには未対応である点は共通している.ただし,更新が積極的に行われており比較的利用者が多いことが想定できる tiiuae/rclgo
のhumble対応に挑戦してみようと思った次第である.
tiiuae/rclgoについて
このrclgoは,ROS2コアライブラリ(ros-galactic-ros-coreで入るような,rclcppや各種Pub/Subのメッセージ定義,rmw等)に依存している.golangのスタンダード・ライブラリの一つであるcgoを利用することによって,C/C++言語で書かれたコード(rclcpp)を呼び出すgolangラッパーの役割を果たす.
cgoについて
以下は,https://pkg.go.dev/cmd/cgo にあるベーシックな例である.
package main
// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
// printf("%s\n", s);
// }
import "C"
import "unsafe"
func main() {
cs := C.CString("Hello from stdio")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}
こんな感じで,golangソースコードにコメントでC実装を書き込み, import C
を宣言する(プリアンブル).Go ツールは,1 つ以上の Go ファイルが特別な import C
を使用していることを確認すると,非 Go ファイルを探し,それらを Go パッケージの一部としてコンパイルする.
また,Cのコンパイラに伝えるオプションCFLAGS,CPPFLAGS,CXXFLAGS,FFLAGS,および LDFLAGS は,コメント内で疑似 #cgo ディレクティブを使用して定義できる.
// #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
その他,環境変数としてもC用のコンパイラオプションの設定が可能.
tiiuae/rclgoでのcgo利用と直面する諸問題
表題のgolangパッケージでは2つのパッケージエントリがある.
- gogen
- メッセージ定義毎の相互変換用パッケージを自動生成するためのツール.(rclcppからrclgoへ)
- rclgo
- 今回目的のrcl
rclgoを使うためには,gogenであらかじめパッケージを生成しておく必要がある.これで生成されるメッセージ定義の一例としてstd_msgs/msg/string
のcgo利用箇所を抜粋する.
/*
#include <rosidl_runtime_c/message_type_support_struct.h>
#include <std_msgs/msg/string.h>
*/
import "C"
/*
#cgo LDFLAGS: "-L/opt/ros/galactic/lib" "-Wl,-rpath=/opt/ros/galactic/lib"
#cgo LDFLAGS: -lrcl -lrosidl_runtime_c -lrosidl_typesupport_c -lrcutils -lrmw_implementation
#cgo LDFLAGS: -lstd_msgs__rosidl_typesupport_c -lstd_msgs__rosidl_generator_c
#cgo LDFLAGS: -lbuiltin_interfaces__rosidl_typesupport_c -lbuiltin_interfaces__rosidl_generator_c
#cgo CFLAGS: "-I/opt/ros/galactic/include"
*/
import "C"
もう,お分かりですね.
そう,数々のROS2戦士達が一度は「🤔🤔🤔🤔🤔🤔」となったであろう,includeパス変更問題に直面するのである.
std_msgs/msg/string
の場合は,メッセージ定義上位パッケージでLDFLAGS
とCFLAGS: -I/opt/ros/galactic/include
を使用して,依存関係を無事解決!!となっていたわけであるが,,,
私はどうしたか?
とりあえず,一刻でも早くhumbleで動くことを確認をしたかったので,環境変数を使った力技で押し切るためのスペシャルな(??)ラッピングBashスクリプトcommander.bash
を作った.
(多分,パッケージジェネレーターのテンプレートエンジンをいじるのが最も根本的な解決なのであろうが,人のコードは読むのが辛い....時間を優先した結果として許して欲しい.)
#!/bin/bash
BASE_DIR=/opt/ros/humble
CGO_CFLAGS="-L${BASE_DIR}/lib -Wl,-rpath=$BASE_DIR/lib -lrcl -lrmw -lrosidl_runtime_c -lrosidl_typesupport_c -lrcutils -lrcl_action -lrmw_implementation"
include_list=
for dir in $( ls -d $BASE_DIR/include/* ); do
if [ -d $dir ]; then
include_list="$include_list -I$dir"
fi
done
CGO_CFLAGS="$CGO_CFLAGS $include_list"
echo $CGO_CFLAGS
# Exec
CGO_ENABLED=1 CC=gcc CGO_CFLAGS=$CGO_CFLAGS make $1
exit $?
使うときは,Makefile
のオプションを指定すればよい.
./commander.bash build
./commander.bash test
他の問題
この他,humble移行に伴い,いくつかの変更があったことから,rclgoに影響のある範囲に対しての修正が必要である.
- 各パスで
galactic
になっているの部分をhumble
に置換が必要 - 構造体のサフィックスが
_t
から_s
へ(struct type name suffix changed from _t to _s)-
sizeof_struct_rcl_service_t
からsizeof_struct_rcl_service_s
-
struct_rmw_service_info_t
からstruct_rmw_service_info_s
-
struct_rcl_client_t
からstruct_rcl_client_s
-
- rcl_clock_initのコンストラクタに渡す型が変わった.
-
uint32
からrcl_clock_type_t
-
動作確認結果
Pub/Sub共に動いた模様.
おわりに
この辺までやったところで,本業やらなんやらが忙しくなったのでテンプレートエンジンをイジるところまで着手できていない.
テンプレートエンジンをいじったら,本家にPRを送ってみようかなと思う(その前に,対応をしてこられそうですが.)