教程
維基 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程序!