ラベル ros の投稿を表示しています。 すべての投稿を表示
ラベル ros の投稿を表示しています。 すべての投稿を表示

2020/05/23

ROSでcatkin_makeする際にundefined referenceが出る原因まとめ

背景


ROS melodicでOSSパッケージを利用した開発を行う中で、make時にundefined reference to...(に対する定義されていない参照です)のエラーが発生する場合があったため、原因をまとめておく。

記事の目的


ROSのOSSパッケージを利用する際、make時にundefined reference to...のエラーが出る原因をまとめる

undefined reference


現象

GitHubでOSSのパッケージをcloneしてcatkin_makeする際、下記のようなエラーが出る場合がある
$ catkin_make ...
...
/opt/ros/melodic/lib/libtf.so: undefined reference to tf2_ros::TransformListener::TransformListener(tf2::BufferCore&, ros::NodeHandle const&, bool)'
collect2: error: ld returned 1 exit status
...

原因

原因として、下記が考えられる。
  • OSSパッケージのバージョン違い
  • rosには、ディストリビューション毎にバージョンが変更され、tfなどの標準パッケージの仕様が変更される場合がある。 よって、git cloneしたパッケージのバージョンが異なると、make時にエラーとなる場合がある。

対処方法

  • OSSパッケージのバージョン違い
  • OSSパッケージのgitブランチを確認し、バージョンを合わせる
    1. ブランチを確認する
    2. $ cd < OSSパッケージののルートディレクトリ >
      $ git branch
      noetic-devel # ros noetic用のブランチ
    3. ブランチを変更する
    4. $ git checkout <現在のROSのブランチ>
      <現在のROSのブランチ>

まとめ


  • ROSのOSSパッケージを利用する際、make時にundefined reference to...のエラーが出る原因をまとめた


参考文献




変更履歴


  1. 2020/05/23: 新規作成

2019/09/12

google testでrosパッケージの単体テストを行う方法

背景


rosパッケージを作成する際に、品質担保のために通常単体テストを行う。
googleテストの使用方法やCMakeListsの書き方を備忘録として記載する。

記事の目的


google testでrosパッケージの単体テストを行う

google test


google testでrosパッケージの単体テストを行う方法について記載する。

google testとは

google testは、google社が提供するオープンソースのテストフレームワークである。

特徴

  • マクロを使用してテストケースを容易に記述できる
  • rosの標準のテストフレームワークである

テンプレートのパッケージ構成

google testでrosパッケージの単体テストを行うrosパッケージの構成を記載する。
$ tree
.
├── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake
└── sample_pkg                # google testでテストを行うサンプルパッケージ
     ├── CMakeLists.txt       # CMakeListsファイル
     ├── include
     │   └── sample_pkg
     │       └── sample.hpp  # headerファイル
     ├── launch
     │   └── sample.launch   #(オプション)launchファイル
     ├── package.xml          # Packageファイル
     ├── src
     │   ├── main.cpp        # ソースファイル(main関数のみ)
     │   └── sample.cpp      # ソースファイル(その他関数, クラス)
     └── test
          └── utest.cpp       # 単体テストファイル

テンプレートのheaderファイルとソースファイル

テンプレートのheaderファイルとソースファイルを記載する。
// sample.hpp
#include <ros/ros.h>

namespace sample
{
    int  func1(int a, int b);
    bool func2(int c, int d);
    class class3
    {
        public:
            class3();
            ~class3();
            void func4(int e);
            void func5(int &f);
        protected:
            int internal_variable_ = 0;
    };
};

// sample.cpp
#include <sample_pkg/sample.hpp>

int sample::func1(int a, int b)
{
    return a+b;
}

bool sample::func2(int a, int b)
{
    if (a == b)
    {
        return true;
    }
    return false;
}

sample::class3::class3()
{}

sample::class3::~class3()
{}

void sample::class3::func4(int e)
{
    internal_variable_ = e;
}

void sample::class3::func5(int &f)
{
    f = internal_variable_;
}

// main.cpp
#include <sample_pkg/sample.hpp>

int main(int argc, char **argv){
    int A = sample::func1(1,3);
    
    return A;
}


テンプレートの単体テストファイル

テンプレートの単体テストファイルを記載する。
// utest.cpp
#include <sample_pkg/sample.hpp>
#include <gtest/gtest.h>

// sample::func1のテスト
TEST(Factorial1Test, func1Test)
{
    int A = sample::func1(1,2);
    EXPECT_EQ(3, A); // A == 3 かチェック
}

// sample::func2のテスト
TEST(Factorial2Test, func2Test)
{
    EXPECT_TRUE(sample::func2(1,1)); // a == bの時、戻り値がtrueかチェック
    EXPECT_FALSE(sample::func2(1,2)); // a != bの時、戻り値がfalseかチェック
}

// sample::class3のテスト
TEST(Class3Test, func4_5Test1)
{
    sample::class3 c;
    int a;
    c.func5(a);
    EXPECT_EQ(0, a); // 初期状態でc.func5(a)のa==0かチェック
}

// sample::class3のテスト
TEST(Class3Test, func4_5Test2)
{
    sample::class3 c;
    int a = 4;
    int b = 0;
    c.func4(a);
    c.func5(b);
    EXPECT_EQ(a, b); // c.func4(a)を行った後、c.func5(b)のb==aかチェック
}

// Run all the tests that were declared with TEST()
int main(int argc, char **argv){
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

テンプレートのCMakeListsファイルとPackageファイル

テンプレートのCMakeListsファイルとPackageファイルを記載する。
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(sample_pkg)

## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11 -Wall -g -O3)

## Find catkin macros and libraries
find_package(catkin REQUIRED COMPONENTS
             roscpp
)

###################################
## catkin specific configuration ##
###################################

## Declare things to be passed to dependent projects
catkin_package(
   INCLUDE_DIRS include
   LIBRARIES sample_pkg
)

###########
## Build ##
###########

## Specify additional locations of header files
include_directories(include
                    /usr/local/include
                    ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
add_library(${PROJECT_NAME}_lib
            src/sample.cpp
)

## Declare a C++ executable
add_executable(${PROJECT_NAME}
               src/main.cpp
)

## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_lib
                      ${catkin_LIBRARIES}
)

target_link_libraries(${PROJECT_NAME}
                      ${PROJECT_NAME}_lib
                      ${catkin_LIBRARIES}
)

#############
## Install ##
#############

## Mark executables and/or libraries for installation
install(TARGETS ${PROJECT_NAME}
        ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
        LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
        RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

## Mark cpp header files for installation
install(DIRECTORY include/${PROJECT_NAME}/
        DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
        FILES_MATCHING PATTERN "*.hpp"
)

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
catkin_add_gtest(${PROJECT_NAME}_test test/utest.cpp)
target_link_libraries(${PROJECT_NAME}_test
                      ${PROJECT_NAME}_lib
                      ${catkin_LIBRARIES}
)

<!-- package.xml -->
<?xml version="1.0"?>
<package format="2">
  <name>sample_pkg</name>
  <version>0.0.0</version>
  <description>The sample package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <maintainer email="xxx@yyy.zz">EmptySet</maintainer>

  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>MIT</license>

  <!-- Url tags are optional, but multiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <url type="website">https://ittechnicalmemos.blogspot.com</url>

  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <author email="xxx@yyy.zz">EmptySet</author>

  <!-- The *depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <exec_depend>roscpp</exec_depend>
</package>

テンプレートの単体テスト実行方法

テンプレートの単体テスト実行方法及び結果を記載する。
$ catkin_make run_tests
...
- run_tests.py: execute commands
  /home/emptySet/work_space/devel/lib/sample_pkg/sample_pkg_test --gtest_output=xml:/home/emptySet/work_space/build/test_results/sample_pkg/gtest-sample_pkg_test.xml
[==========] Running 4 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Factorial1Test
[ RUN      ] Factorial1Test.func1Test
[       OK ] Factorial1Test.func1Test (0 ms)
[----------] 1 test from Factorial1Test (0 ms total)

[----------] 1 test from Factorial2Test
[ RUN      ] Factorial2Test.func2Test
[       OK ] Factorial2Test.func2Test (0 ms)
[----------] 1 test from Factorial2Test (0 ms total)

[----------] 2 tests from Class3Test
[ RUN      ] Class3Test.func4_5Test1
[       OK ] Class3Test.func4_5Test1 (0 ms)
[ RUN      ] Class3Test.func4_5Test2
[       OK ] Class3Test.func4_5Test2 (0 ms)
[----------] 2 tests from Class3Test (0 ms total)

[----------] Global test environment tear-down
[==========] 4 tests from 3 test cases ran. (0 ms total)
[  PASSED  ] 4 tests.
-- run_tests.py: verify result "/home/emptySet/work_space/build/test_results/sample_pkg/gtest-sample_pkg_test.xml"
...

テスト結果がNGの場合は、下記のように表示される。
$ catkin_make run_tests
...
/home/emptySet/work_space/src/sample_pkg/test/utest.cpp:31: Failure
Value of: a
  Actual: 4
Expected: 2
[  FAILED  ] Class3Test.func4_5Test2 (0 ms)
...
[  FAILED  ] 1 test, listed below:
[  FAILED  ] Class3Test.func4_5Test2

 1 FAILED TEST
...

備考

  • google testのマクロはここを参照。

まとめ


  • google testでrosパッケージの単体テストを行う方法を調査、記載した

参考文献



変更履歴


  1. 2019/09/12: 新規作成

2019/09/05

実行中のrosノードを全て終了させるスクリプトの作成方法

背景


実行している複数rosノードをCtr+Cでkillする際、時々一部のノードが終了されずにバックグラウンドで実行される場合があった。このような場合、次にrosノードを起動した際に不具合が生じる可能性がある。そこで、実行中のrosノードを全てkillするスクリプトを作成した。

記事の目的


実行中のrosノードを全てkillするスクリプトを作成する

kill_all_rosnode.sh


ここでは、実行中のrosノードを全てkillするスクリプトの記述方法について記載する。

スクリプトの作成方法

スクリプトの作成方法は以下の通りである。
  1. スクリプトkill_all_rosnode.shを作成する
  2. #!/bin/bash
    echo "Kill all ros nodes!"
    ps aux | grep ros | grep -v grep | awk '{ print "kill -9", $2 }' | sh
    exit 0
  3. スクリプトに実行権限を与える
  4. $ chmod +x ./kill_all_rosnode.sh 

スクリプトの実行方法

実行方法は以下の通りである。
$ ./kill_all_rosnode.sh
Kill all ros nodes!

まとめ


  • 実行中のrosノードを全てkillするスクリプトを作成した

参考文献



変更履歴


  1. 2019/09/05: 新規作成

2019/08/24

CUDA10のコンパイラでrosのパッケージがコンパイルできない問題の対処法

背景


CUDA10のnvccを用いてrosのパッケージをコンパイルする際、エラーが発生してコンパイルできない問題が生じた。

記事の目的


CUDA10のnvccでrosパッケージをコンパイルできるようにする

nvccのバグ


ここでは、CUDA10のnvccでrosパッケージをコンパイルできない現象、原因及び対策について記載する。

現象

CUDA10のnvccでrosパッケージをコンパイルすると、下記のエラーが発生する場合がある。
$ catkin_make
...
/usr/include/pcl-1.7/pcl/point_cloud.h:586:100 error: template-id ‘getMapping’ used as a declarator
friend boost::shared_ptr& detail::getMapping(pcl::PointCloud &p);
^
/usr/include/pcl-1.7/pcl/point_cloud.h:586:100 error: ‘getMapping’ is neither function nor member function; cannot be declared friend
cc1plus: error: expected ‘;’ at end of member declaration
/usr/include/pcl-1.7/pcl/point_cloud.h:586:111: error: expected ‘)’ before ‘&’ token
...
make: *** [all] Error 2

原因

nvccが利用しているgnuコンパイラのバグが原因である。このバグは、「friend関数によりnamespace内で定義された関数を使用する」記述があるコードで発生する。ところで、rosは独自に手を加えたPointCloudLibrary(PCL)を使用しているが、PCLのコード内に上記の記述が存在する('/usr/include/pcl-1.7/pcl/point_cloud.h'内 l.586)。nvccのコンパイラは、上記箇所をコンパイルする際に、関数のnamespace部分を削除してしまう(detail::getMapping→getMapping)。そのため、コンパイラ自身が関数を見つけられなくなり、エラーとなる。
$less /usr/include/pcl-1.7/pcl/point_cloud.h 
'point_cloud.h'内の該当箇所
 
namespace detail
  {
    template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
    getMapping (pcl::PointCloud<PointT>& p);
  } // namespace detail
 
    protected:
      // This is motivated by ROS integration. Users should not need to access mapping_.
      boost::shared_ptr<MsgFieldMap> mapping_;

      friend boost::shared_ptr<MsgFieldMap>& detail::getMapping<PointT>(pcl::PointCloud<PointT> &p);

    public:
      EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  };

  namespace detail
  {
    template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
    getMapping (pcl::PointCloud<PointT>& p)
    {
      return (p.mapping_);
    }
  } // namespace detail

対策

'/usr/include/pcl-1.7/pcl/point_cloud.h'を下記のように書き換えることで回避できる。
// Add ---------------------------------------------------------------------- //
template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
    getMapping (pcl::PointCloud<PointT>& p);
// -------------------------------------------------------------------------- //
namespace detail
  {
    template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
    getMapping (pcl::PointCloud<PointT>& p);
  } // namespace detail
 
    protected:
      // This is motivated by ROS integration. Users should not need to access mapping_.
      boost::shared_ptr<MsgFieldMap> mapping_;
      // Change ------------------------------------------------------------------- //
      friend boost::shared_ptr<MsgFieldMap>& getMapping<PointT>(pcl::PointCloud<PointT> &p);
      // friend boost::shared_ptr<MsgFieldMap>& detail::getMapping<PointT>(pcl::PointCloud<PointT> &p);
      // -------------------------------------------------------------------------- //
    public:
      EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  };

// Add ---------------------------------------------------------------------- //
template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
    getMapping (pcl::PointCloud<PointT>& p)
{
    return (p.mapping_);
}
// -------------------------------------------------------------------------- //
namespace detail
  {
    template <typename PointT> boost::shared_ptr<pcl::MsgFieldMap>&
    getMapping (pcl::PointCloud<PointT>& p)
    {
      return (p.mapping_);
    }
  } // namespace detail

備考

  • ros-kinetic、cuda 10.1で検証した
  • コンパイルが通ることと、実行可能であることを確認した

まとめ


  • rosをapt install時にインストールされるpclのヘッダーファイルを変更することにより、CUDA10のnvccでrosパッケージがコンパイルできなくなる問題を回避した

参考文献



変更履歴


  1. 2019/08/24: 新規作成

MQTTの導入

背景 IoTデバイスの接続環境構築のため、MQTT(mosquitto)の導入を行った。 記事の目的 MQTT(mosquitto)をUbuntuに導入する mosquitto ここではmosquittoについて記載する。 MQTT MQTT(Message Qu...