【ROS2概念】系列(十三)——ROS2如何創(chuàng)建自定義msg、srv和action文件

目錄

一认然、ROS2創(chuàng)建自定義msg、srv和action文件的思路

ROS2通過自帶rosidl_default_generators包來為自定義的msg漫萄、srv和action文件生成各個語言的頭文件卷员!

ROS2通過在CMakeLists.txt中調(diào)用新增的宏rosidl_generate_interfaces來為msg、srv和action文件生成各個語言的頭文件腾务!而這個宏的實(shí)現(xiàn)定義在rosidl_default_generators包毕骡。

二、基本數(shù)據(jù)類型

利用ROS2的原始基本數(shù)據(jù)類型和已有的數(shù)據(jù)類型岩瘦,可以自定義需要的數(shù)據(jù)類型挺峡。

2.1 ROS2的基本原始數(shù)據(jù)類型

ROS2目前支持的內(nèi)置類型:

每種內(nèi)置類型都可以用來定義數(shù)組:

所有比其ROS定義更寬松的類型均由軟件強(qiáng)制執(zhí)行ROS的范圍和長度約束。

使用數(shù)組和有界類型的消息定義示例:

int32[] unbounded_integer_array
int32[5] five_integers_array
int32[<=5] up_to_five_integers_array

string string_of_unbounded_size
string<=10 up_to_ten_characters_string

string[<=5] up_to_five_unbounded_strings
string<=10[] unbounded_array_of_string_up_to_ten_characters each
string<=10[<=5] up_to_five_strings_up_to_ten_characters_each

2.2 ROS2中的基本msg類型

2.3 ROS2中的基本srv類型

2.4 ROS2中的基本action類型

三担钮、創(chuàng)建自定義的msg橱赠、srv和action

3.1 創(chuàng)建功能包

在本文中,將在自己的包中創(chuàng)建自定義的.msg箫津、.srv.action文件狭姨,然后在另外的包中使用它們,這兩個包應(yīng)該在同一個工作空間dev_ws/src目錄中,然后運(yùn)行以下命令創(chuàng)建一個新包苏遥。

ros2 pkg create --build-type ament_cmake tutorial_interfaces

tutorial_interfaces是新包的名稱饼拍。注意,它是一個CMake包田炭,目前還沒有辦法在純Python包中生成.msg师抄、.srv.action文件。但是教硫,可以在CMake包中創(chuàng)建自定義接口叨吮,然后在Python節(jié)點(diǎn)中使用它。

.msg瞬矩、.srv.action文件保存在自己的包中是一種很好的做法茶鉴。在dev_ws/src/tutorial_interfaces目錄下創(chuàng)建:

mkdir msg srv action

3.2 創(chuàng)建自定義接口文件

3.2.1 自定義msg文件

tutorial_interfaces/msg文件夾下,創(chuàng)建一個名為Num.msg的新文件景用,用一行代碼聲明它的數(shù)據(jù)結(jié)構(gòu):

int64 num

這是自定義的消息涵叮,它傳輸一個名稱為num的64位整數(shù)。

3.2.2 自定義srv文件

srv文件和msg文件相似,除了它包含兩個部分:請求和響應(yīng)割粮。這兩部分用“---”線分隔盾碗。

tutorial_interfaces/srv文件夾下,創(chuàng)建一個名為AddThreeInts.srv的新文件舀瓢,并聲明他的請求和響應(yīng)結(jié)構(gòu):

int64 a
int64 b
int64 c
---
int64 sum

這是自定義的服務(wù)置尔,它請求三個名為a、b和c的整數(shù)氢伟,并以一個名為sum的整數(shù)進(jìn)行響應(yīng)榜轿。

3.2.3 自定義action文件

按照以下格式定義.action文件:

Request
---
Result
---
Feedback

動作定義由三個消息定義組成,它們之間用--分隔朵锣。

請求(request)消息從動作客戶端發(fā)送到動作服務(wù)器谬盐,初始化一個新目標(biāo)。

當(dāng)目標(biāo)完成時诚些,結(jié)果(result)消息從動作服務(wù)器發(fā)送到動作客戶端飞傀。

反饋(feedback)消息定期從動作服務(wù)器發(fā)送到動作客戶端,其中包含關(guān)于目標(biāo)的更新。

一個動作的實(shí)例通常被稱為目標(biāo)(goal)。

假設(shè)想要定義一個新的動作“Fibonacci”來計(jì)算Fibonacci序列库正。

tutorial_interfaces/action目錄中,創(chuàng)建一個名為Fibonacci.action的文件幢痘,內(nèi)容如下:

int32 order
---
int32[] sequence
---
int32[] partial_sequence

目標(biāo)請求是想要計(jì)算的斐波那契數(shù)列的order,結(jié)果是最終的sequence家破,而反饋是到目前為止計(jì)算的partial_sequence颜说。

3.3 編譯生成

3.3.1 CMakeLists.txt

要將自定義的接口(interface)轉(zhuǎn)換成基于語言的代碼(如C++和Python),以便它們可以在這些語言中使用汰聋,請將以下代碼添加到CMakeLists.txt中:

find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
  "msg/Num.msg"
  "srv/AddThreeInts.srv"
  "action/Fibonacci.action"
 )

注意:需要依賴于action_msgs门粪,因?yàn)閯幼鞫x包括額外的元數(shù)據(jù)(例如目標(biāo)id)。

3.3.2 package.xml

因?yàn)榻涌谝蕾囉趓osidl_default_generators來生成基于語言的代碼烹困,所以需要聲明對它的依賴關(guān)系玄妈。將以下行添加到package.xml中:

<depend>action_msgs</depend>

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

3.3.3 編譯

在工作空間的根目錄(~/dev_ws)中,運(yùn)行以下命令:

colcon build --packages-select tutorial_interfaces

現(xiàn)在接口將被其他ROS 2包發(fā)現(xiàn)髓梅。

四拟蜻、測試

4.1 驗(yàn)證msg、srv和action的創(chuàng)建

打開新終端女淑,在dev_ws下運(yùn)行:

source install/setup.bash

可以使用ros2 interface show命令確認(rèn)接口創(chuàng)建工作:

查看msg文件:

ros2 interface show tutorial_interfaces/msg/Num

返回:

int64 num

查看srv文件:

ros2 interface show tutorial_interfaces/srv/AddThreeInts

返回:

int64 a
int64 b
int64 c
---
int64 sum

查看action文件:

ros2 interface show tutorial_interfaces/srv/AddThreeInts

返回

int32 order
---
int32[] sequence
---
int32[] partial_sequence

4.2 使用pub/sub測試Num.msg

  • Publisher代碼
#include <chrono>
#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp"     // CHANGE

using namespace std::chrono_literals;

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher()
  : Node("minimal_publisher"), count_(0)
  {
    publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10);    // CHANGE
    timer_ = this->create_wall_timer(
      500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

private:
  void timer_callback()
  {
    auto message = tutorial_interfaces::msg::Num();                               // CHANGE
    message.num = this->count_++;                                        // CHANGE
    RCLCPP_INFO(this->get_logger(), "Publishing: '%d'", message.num);    // CHANGE
    publisher_->publish(message);
  }
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr publisher_;         // CHANGE
  size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}
  • Subscriber代碼
#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/msg/num.hpp"     // CHANGE
using std::placeholders::_1;

class MinimalSubscriber : public rclcpp::Node
{
public:
  MinimalSubscriber()
  : Node("minimal_subscriber")
  {
    subscription_ = this->create_subscription<tutorial_interfaces::msg::Num>(          // CHANGE
      "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
  }

private:
  void topic_callback(const tutorial_interfaces::msg::Num::SharedPtr msg) const       // CHANGE
  {
    RCLCPP_INFO(this->get_logger(), "I heard: '%d'", msg->num);              // CHANGE
  }
  rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr subscription_;       // CHANGE
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalSubscriber>());
  rclcpp::shutdown();
  return 0;
}
  • CMakeLists.txt
#...

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED)                         # CHANGE

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp tutorial_interfaces)         # CHANGE

add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp tutorial_interfaces)     # CHANGE

install(TARGETS
  talker
  listener
  DESTINATION lib/${PROJECT_NAME})

ament_package()
  • package.xml

添加

<depend>tutorial_interfaces</depend>

  • 編譯

colcon build --packages-select cpp_pubsub

  • 運(yùn)行

打開新終端瞭郑,source dev_ws環(huán)境,并運(yùn)行:

ros2 run cpp_pubsub talker
ros2 run cpp_pubsub listener

因?yàn)镹um.msg只傳遞一個整數(shù)鸭你,所以它應(yīng)該只發(fā)布整數(shù)值:

[INFO] [minimal_publisher]: Publishing: '0'
[INFO] [minimal_publisher]: Publishing: '1'
[INFO] [minimal_publisher]: Publishing: '2'

4.3 使用service/client測試AddThreeInts.srv

  • Service
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/srv/add_three_ints.hpp"     // CHANGE

#include <memory>

void add(const std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Request> request,     // CHANGE
          std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Response>       response)  // CHANGE
{
  response->sum = request->a + request->b + request->c;                                       // CHANGE
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld" " c: %ld",   // CHANGE
                request->a, request->b, request->c);                                          // CHANGE
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_server");  // CHANGE

  rclcpp::Service<tutorial_interfaces::srv::AddThreeInts>::SharedPtr service =                 // CHANGE
    node->create_service<tutorial_interfaces::srv::AddThreeInts>("add_three_ints",  &add);     // CHANGE

  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add three ints.");      // CHANGE

  rclcpp::spin(node);
  rclcpp::shutdown();
}
  • Client
#include "rclcpp/rclcpp.hpp"
#include "tutorial_interfaces/srv/add_three_ints.hpp"        // CHANGE

#include <chrono>
#include <cstdlib>
#include <memory>

using namespace std::chrono_literals;

int main(int argc, char **argv)
{
  rclcpp::init(argc, argv);

  if (argc != 4) { // CHANGE
      RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_three_ints_client X Y Z");      // CHANGE
      return 1;
  }

  std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_client"); // CHANGE
  rclcpp::Client<tutorial_interfaces::srv::AddThreeInts>::SharedPtr client =                        // CHANGE
    node->create_client<tutorial_interfaces::srv::AddThreeInts>("add_three_ints");                  // CHANGE

  auto request = std::make_shared<tutorial_interfaces::srv::AddThreeInts::Request>();               // CHANGE
  request->a = atoll(argv[1]);
  request->b = atoll(argv[2]);
  request->c = atoll(argv[3]);               // CHANGE

  while (!client->wait_for_service(1s)) {
    if (!rclcpp::ok()) {
      RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
      return 0;
    }
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
  }

  auto result = client->async_send_request(request);
  // Wait for the result.
  if (rclcpp::spin_until_future_complete(node, result) ==
    rclcpp::executor::FutureReturnCode::SUCCESS)
  {
    RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
  } else {
    RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_three_ints");    // CHANGE
  }

  rclcpp::shutdown();
  return 0;
}
  • CMakeLists.txt
#...

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED)        # CHANGE

add_executable(server src/add_two_ints_server.cpp)
ament_target_dependencies(server
  rclcpp tutorial_interfaces)                      #CHANGE

add_executable(client src/add_two_ints_client.cpp)
ament_target_dependencies(client
  rclcpp tutorial_interfaces)                      #CHANGE

install(TARGETS
  server
  client
  DESTINATION lib/${PROJECT_NAME})

ament_package()
  • package.xml

添加

<depend>tutorial_interfaces</depend>

  • 編譯

colcon build --packages-select cpp_srvcli

  • 運(yùn)行

打開新終端, source dev_ws環(huán)境,并運(yùn)行:

ros2 run cpp_srvcli server
ros2 run cpp_srvcli client 2 3 1

4.4 使用service/client測試Fibonacci.action

參考:
ROS2的原始基本數(shù)據(jù)類型:https://zhuanlan.zhihu.com/p/353268822
ROS2學(xué)習(xí)之旅(16)——創(chuàng)建自定義ROS2 msg和srv文件:https://www.cnblogs.com/love-robot/p/15014734.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袱巨,一起剝皮案震驚了整個濱河市阁谆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愉老,老刑警劉巖场绿,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嫉入,居然都是意外死亡焰盗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門咒林,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熬拒,“玉大人,你說我怎么就攤上這事垫竞∨焖冢” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵欢瞪,是天一觀的道長活烙。 經(jīng)常有香客問我,道長遣鼓,這世上最難降的妖魔是什么啸盏? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮骑祟,結(jié)果婚禮上宫补,老公的妹妹穿的比我還像新娘。我一直安慰自己曾我,他們只是感情好粉怕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抒巢,像睡著了一般贫贝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛉谜,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天稚晚,我揣著相機(jī)與錄音,去河邊找鬼型诚。 笑死客燕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狰贯。 我是一名探鬼主播也搓,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赏廓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了傍妒?” 一聲冷哼從身側(cè)響起幔摸,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颤练,沒想到半個月后既忆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗦玖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年患雇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宇挫。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡苛吱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捞稿,到底是詐尸還是另有隱情又谋,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布娱局,位于F島的核電站彰亥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衰齐。R本人自食惡果不足惜任斋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耻涛。 院中可真熱鬧废酷,春花似錦、人聲如沸抹缕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卓研。三九已至趴俘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奏赘,已是汗流浹背寥闪。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留磨淌,地道東北人疲憋。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像梁只,于是被迫代替她去往敵國和親缚柳。 傳聞我的和親對象是個殘疾皇子埃脏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容