ROS 創建服務和請求

教程

維基 http://wiki.ros.org/cn/ROS/Tutorials
快速過程
創建包

$ cd ~/catkin_ws
$ mkdir ~/catkin_ws/src
$ cd ~/catkin_ws/src
$ catkin_init_w
$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp

編譯包:

$ cd ~/catkin_ws
$ catkin_make

詳細過程
1安裝環境配置(桌面進入命令行)

echo "source /opt/ros/indigo/setup.bash" >> ~/.bashrc
source ~/.bashrc

2.1創建工作環境並編譯 (生成的在home文件夾下)

mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make

2.2自己創建的包環境 能看到’build’和’devel’這兩個文件夾。
將當前工作環境設置在ROS工作環境

source /home/dongdong/catkin_ws/devel/setup.bash

如果永久性配置

echo "source /home/dongdong/catkin_ws/devel/setup.bash" >> ~/.bashrc
source ~/.bashrc

查看是否配置成功

echo $ROS_PACKAGE_PATH

3 創建包

cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
  • 依賴包文件
    std_msgs
    rospy
    roscpp

4編譯(空 可不編譯)

cd ~/catkin_ws
catkin_make

5 創建ROS消息和ROS服務

  • 一般消息類型
int8, int16, int32, int64 (plus uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C]
  • 特殊的數據類型:Header
    它含有時間戳和座標系信息
Header header
  string child_frame_id
  geometry_msgs/PoseWithCovariance pose
  geometry_msgs/TwistWithCovariance twist

5.1 創建一個msg

$ cd ~/catkin_ws/src/beginner_tutorials
$ mkdir msg
$ echo "int64 num" > msg/Num.msg

上面是最簡單的例子——在.msg文件中只有一行數據。當然,你可以仿造上面的形式多增加幾行以得到更爲複雜的消息:

string first_name
string last_name
uint8 age
uint32 score

接下來,還有關鍵的一步:我們要確保msg文件被轉換成爲C++,Python和其他語言的源代碼:

查看package.xml, 確保它包含一下兩條語句:

 <build_depend>message_generation</build_depend>
 <run_depend>message_runtime</run_depend>

查看CMakeLists.txt

(過程可跳過,直接下面CMakeLists.txt 文件最終內容複製進去beginner_tutorials/CMakeLists.txt 更改)

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare ROS messages and services
add_message_files(DIRECTORY msg FILES Num.msg)
add_service_files(DIRECTORY srv FILES AddTwoInts.srv)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

CMakeLists.txt 具體設置過程
利用find_packag函數,增加對message_generation的依賴

find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)

設置了運行依賴

catkin_package(
  ...
  CATKIN_DEPENDS message_runtime ...
  ...)

找到並去掉註釋#

# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )

替換.msg 文件

add_message_files( FILES Num.msg)

手動添加.msg文件後,我們要確保CMake知道在什麼時候重新配置我們的project。 確保添加了如下代碼:

generate_messages()

現在,你可以生成自己的消息源代碼了

5.2 使用一個msg
通過rosmsg show命令,檢查ROS是否能夠識消息。

$ rosmsg show beginner_tutorials/Num

你將會看到:

int64 num
string first_name
string last_name
uint8 age
uint32 score

6 創建一個服務 srv
在剛剛那個package中創建一個服務:

$ roscd beginner_tutorials
$ mkdir srv

這次我們不再手動創建服務,而是從其他的package中複製一個服務。 roscp是一個很實用的命令行工具,它實現了將文件從一個package複製到另外一個package的功能。

$ roscp [package_name] [file_to_copy_path] [copy_path]

從rospy_tutorials package中複製一個服務文件了:

$ roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv

還有很關鍵的一步:我們要確保srv文件被轉換成C++,Python和其他語言的源代碼。

查看package.xml, 確保它包含一下兩條語句(第5步創建msg時已經添加):

 <build_depend>message_generation</build_depend>
 <run_depend>message_runtime</run_depend>

在CMakeLists.txt文件中
(過程可跳過,直接下面CMakeLists.txt 文件最終內容複製進去beginner_tutorials/CMakeLists.txt 更改,第5步創建msg時如果直接複製了可不用執行):

# Do not just add this line to your CMakeLists.txt, modify the existing line
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)

(對的, message_generation 對msg和srv都起作用)

用你自己的srv文件名替換掉那些Service*.srv文件:

add_service_files( FILES AddTwoInts.srv )

6.2使用rossrv

$ rossrv show beginner_tutorials/AddTwoInts

你將會看到:

int64 a
int64 b
---
int64 sum

7 msg和srv都需要的步驟(可省略 直接講最終CMakeLists.txt內容替換即可)
在CMakeLists.txt中找到如下部分:

# generate_messages(
#   DEPENDENCIES
# #  std_msgs  # Or other packages containing msgs
# )

去掉註釋並附加上所有你消息文件所依賴的那些含有.msg文件的package(這個例子是依賴std_msgs,不要添加roscpp,rospy),結果如下:

generate_messages(DEPENDENCIES std_msgs)

最終CMakeLists.txt文件內容爲(和第五步創建msg的一樣):

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare ROS messages and services
add_message_files(DIRECTORY msg FILES Num.msg)
add_service_files(DIRECTORY srv FILES AddTwoInts.srv)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

重新編譯我們的package:

cd ~/catkin_ws
catkin_make

8編寫簡單的消息發佈器和訂閱器 (C++)

切換到之前創建的 beginner_tutorials package 路徑下:

cd ~/catkin_ws/src/beginner_tutorials

在 beginner_tutorials package 路徑下創建一個src文件夾:

發佈者節點

mkdir -p ~/catkin_ws/src/beginner_tutorials/src

這個文件夾將會用來放置 beginner_tutorials package 的所有源代碼。

在 beginner_tutorials package 裏創建 src/talker.cpp 文件,並將如下代碼粘貼到文件內:

//引用了 ROS 系統中大部分常用的頭文件
#include "ros/ros.h"
//引用了 std_msgs/String 消息, 它存放在 std_msgs package 裏,是由 String.msg 文件自動生成的頭文件
#include "std_msgs/String.h"

#include <sstream>


int main(int argc, char **argv)
{
 //初始化 ROS 。它允許 ROS 通過命令行進行名稱重映射
  ros::init(argc, argv, "talker");

  /**
   爲這個進程的節點創建一個句柄。
   第一個創建的 NodeHandle 會爲節點進行初始化,
   最後一個銷燬的 NodeHandle 則會釋放該節點所佔用的所有資源。
   */
  ros::NodeHandle n;

  /**
   告訴 master 我們將要在 chatter(話題名) 上發佈 std_msgs/String 消息類型的消息。
   這樣 master 就會告訴所有訂閱了 chatter 話題的節點,將要有數據發佈。
   第二個參數是發佈序列的大小。如果我們發佈的消息的頻率太高,緩衝區中的消息在大於 1000 個的時候就會          
   開始丟棄先前發佈的消息。

   NodeHandle::advertise() 返回一個 ros::Publisher 對象,它有兩個作用:
    1) 它有一個   publish() 成員函數可以讓你在topic上發佈消息;  
    2) 如果消息類型不對,它會拒絕發佈。
   */
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  //我們讓它以 10Hz 的頻率運行。
  ros::Rate loop_rate(10);

  /**
   * A count of how many messages we have sent. This is used to create
   * a unique string for each message.
   */
  int count = 0;
  /*
  roscpp 會默認生成一個 SIGINT 句柄,它負責處理 Ctrl-C 鍵盤操作——使得 ros::ok() 返回 false。 
  如果下列條件之一發生,ros::ok() 返回false:

   SIGINT 被觸發 (Ctrl-C)
   被另一同名節點踢出 ROS 網絡
  ros::shutdown() 被程序的另一部分調用
  節點中的所有 ros::NodeHandles 都已經被銷燬
  一旦 ros::ok() 返回 false, 所有的 ROS 調用都會失效。
  */
  while (ros::ok())
  {
    /**
     一個由 msg file 文件產生的『消息自適應』類在 ROS 網絡中廣播消息。現在我們使用標準的String消 
     息,它只有一個數據成員 "data"
     */
    std_msgs::String msg;

    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();

    //ROS_INFO 和其他類似的函數可以用來代替 printf/cout 等函數
    ROS_INFO("%s", msg.data.c_str());

    /**
     我們向所有訂閱 chatter 話題的節點發送消息。
     */
    chatter_pub.publish(msg);
/*
在這個例子中並不是一定要調用 ros::spinOnce(),因爲我們不接受回調。然而,如果你的程序裏包含其他回調函數,最好在這裏加上 ros::spinOnce()這一語句,否則你的回調函數就永遠也不會被調用了。
*/
    ros::spinOnce();

    loop_rate.sleep();
    ++count;
  }


  return 0;
}

訂閱者節點
在 beginner_tutorials package 目錄下創建 src/listener.cpp 文件,並粘貼如下代碼:

#include "ros/ros.h"
#include "std_msgs/String.h"

/**
 這是一個回調函數,當接收到 chatter 話題的時候就會被調用。消息是以 boost shared_ptr 指針的形式傳輸,這就意味着你可以存儲它而又不需要複製數據。
 */
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{

  ros::init(argc, argv, "listener");

  ros::NodeHandle n;

  /**
  告訴 master 我們要訂閱 chatter 話題上的消息。
  當有消息發佈到這個話題時,ROS 就會調用 chatterCallback() 函數。 
  第二個參數是隊列大小,以防我們處理消息的速度不夠快,當緩存達到 1000 條消息後,
  再有新的消息到來就將開始丟棄先前接收的消息。
  NodeHandle::subscribe() 返回 ros::Subscriber 對象,你必須讓它處於活動狀態直到你不再想訂閱該
  消息。當這個對象銷燬時,它將自動退訂 chatter 話題的消息。
   */
  ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

  /**
   ros::spin() 進入自循環,可以儘可能快的調用消息回調函數。如果沒有消息到達,它不會佔用很多 CPU, 
   所以不用擔心。一旦 ros::ok() 返回 false,ros::spin() 就會立刻跳出自循環。這有可能是ros::shutdown() 被調用,或者是用戶按下了 Ctrl-C,使得 master 告訴節點要終止運行。也有可能是 
   節點被人爲關閉的。
   */
  ros::spin();

  return 0;
}

編譯節點
之前教程中使用 catkin_create_pkg 創建了 package.xml 和 CMakeLists.txt 文件。

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare ROS messages and services
add_message_files(DIRECTORY msg FILES Num.msg)
add_service_files(DIRECTORY srv FILES AddTwoInts.srv)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

在 CMakeLists.txt 文件末尾加入幾條語句:

include_directories(include ${catkin_INCLUDE_DIRS})

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

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

最終看起來

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)

## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

## Declare ROS messages and services
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)

## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)

## Declare a catkin package
catkin_package()

## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

這會生成兩個可執行文件, talker 和 listener, 默認存儲到 devel space 目錄下,具體是在
~/catkin_ws/devel/lib/ 中.

現在要爲可執行文件添加對生成的消息文件的依賴:

add_dependencies(talker beginner_tutorials_generate_messages_cpp)

這樣就可以確保自定義消息的頭文件在被使用之前已經被生成。因爲 catkin 把所有的 package 並行的編譯,所以如果你要使用其他 catkin 工作空間中其他 package 的消息,你同樣也需要添加對他們各自生成的消息文件的依賴。

現在運行 catkin_make:

# In your catkin workspace
$ catkin_make  

測試消息發佈器和訂閱器
確保roscore可用,並運行:

roscore

catkin specific 如果使用catkin,確保你在調用catkin_make後,在運行你自己的程序前,已經source了catkin工作空間下的setup.sh文件: (我們在創建工作區的時候已經永久寫入配置文件,不需要在進行設置)

# In your catkin workspace
$ cd ~/catkin_ws
$ source ./devel/setup.bash

啓動發佈者

$ rosrun beginner_tutorials talker      (C++)

你將看到如下的輸出信息:

[INFO] [WallTime: 1314931831.774057] hello world 1314931831.77
[INFO] [WallTime: 1314931832.775497] hello world 1314931832.77
[INFO] [WallTime: 1314931833.778937] hello world 1314931833.78
[INFO] [WallTime: 1314931834.782059] hello world 1314931834.78
[INFO] [WallTime: 1314931835.784853] hello world 1314931835.78
[INFO] [WallTime: 1314931836.788106] hello world 1314931836.79

啓動訂閱者

$ rosrun beginner_tutorials listener     (C++)

你將會看到如下的輸出信息:

[INFO] [WallTime: 1314931969.258941] /listener_17657_1314931968795I heard hello world 1314931969.26
[INFO] [WallTime: 1314931970.262246] /listener_17657_1314931968795I heard hello world 1314931970.26
[INFO] [WallTime: 1314931971.266348] /listener_17657_1314931968795I heard hello world 1314931971.26
[INFO] [WallTime: 1314931972.270429] /listener_17657_1314931968795I heard hello world 1314931972.27
[INFO] [WallTime: 1314931973.274382] /listener_17657_1314931968795I heard hello world 1314931973.27
[INFO] [WallTime: 1314931974.277694] /listener_17657_1314931968795I heard hello world 1314931974.28
[INFO] [WallTime: 1314931975.283708] /listener_17657_1314931968795I heard hello world 1314931975.28

9 編寫簡單的服務和客戶端
9.1 編寫Service節點

進入先前你在catkin workspace教程中所創建的beginner_tutorials包所在的目錄:

cd ~/catkin_ws/src/beginner_tutorials

在beginner_tutorials包中創建src/add_two_ints_server.cpp文件,並複製粘貼下面的代碼:

#include "ros/ros.h"
//編譯系統自動根據我們先前創建的srv文件生成的對應該srv文件的頭文件。
#include "beginner_tutorials/AddTwoInts.h"
/*
int值從request裏面獲取,而返回數據裝入response內,
這些數據類型都定義在srv文件內部,函數返回一個boolean值。
*/
bool add(beginner_tutorials::AddTwoInts::Request  &req,
         beginner_tutorials::AddTwoInts::Response &res)
{
  res.sum = req.a + req.b;
  ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
  ROS_INFO("sending back response: [%ld]", (long int)res.sum);
  return true;
}

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

  ros::ServiceServer service = n.advertiseService("add_two_ints", add);
  ROS_INFO("Ready to add two ints.");
  ros::spin();

  return 0;
}

9.2 編寫Client節點

在beginner_tutorials包中創建src/add_two_ints_client.cpp文件,並複製粘貼下面的代碼:

#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>

int main(int argc, char **argv)
{
  ros::init(argc, argv, "add_two_ints_client");
  if (argc != 3)
  {
    ROS_INFO("usage: add_two_ints_client X Y");
    return 1;
  }

  ros::NodeHandle n;
  ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
  //我們實例化一個由ROS編譯系統自動生成的service類,並給其request成員賦值。一個service類包含兩個成員request和response。同時也包括兩個類定義Request和Response。
  beginner_tutorials::AddTwoInts srv;
  srv.request.a = atoll(argv[1]);
  srv.request.b = atoll(argv[2]);
  if (client.call(srv))
  {
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  }
  else
  {
    ROS_ERROR("Failed to call service add_two_ints");
    return 1;
  }

  return 0;
}

編譯節點
再來編輯一下beginner_tutorials裏面的CMakeLists.txt,文件位於~/catkin_ws/src/beginner_tutorials/CMakeLists.txt,並將下面的代碼添加在文件末尾:

add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)

add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)

這段代碼將生成兩個可執行程序”add_two_ints_server”和”add_two_ints_client”,這兩個可執行程序默認被放在你的devel space下的包目錄下,默認爲~/catkin_ws/devel/lib/share/。你可以直接調用可執行程序,或者使用rosrun命令去調用它們。它們不會被裝在/bin目錄下,因爲當你在你的系統裏安裝這個包的時候,這樣做會污染PATH變量。如果你希望在安裝的時候你的可執行程序在PATH變量裏面,你需要設置一下install target,

運行catkin_make命令:

cd ~/catkin_ws
catkin_make

10 測試簡單的Service和Client

roscore

啓動服務

rosrun beginner_tutorials add_two_ints_server

你將會看到如下類似的信息:

Ready to add two ints.

運行Client

rosrun beginner_tutorials add_two_ints_client 1 3 

你將會看到如下類似的信息:

request: x=1, y=3
sending back response: [4]

現在,你已經成功地運行了你的第一個Service和Client程序!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章