ROS學習筆記(二)---Beginner Level
初學ROS做的筆記,權當備忘錄
旨在梳理beginner level的常用操作,以便後續參考。
1.安裝並配置ROS環境
1.1安裝
- 設置source.list
首先Ctrl+Alt+T打開終端,輸入以下指令:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
- 設置keys
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
也可以使用另外的倉庫hkp://pgp.mit.edu:80 or hkp://keyserver.ubuntu.com:80代替
- 安裝
首先update源:
sudo apt-get update
然後安裝全功能包:
sudo apt-get install ros-kinetic-desktop-full
- 設置環境變量
echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
- 安裝依賴
sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential
- 安裝ros-dep
sudo apt install python-rosdep
- 初始化ros-dep
sudo rosdep init
rosdep update
1.2 管理環境
確認ROS的環境變量是否設置好了,ROS_PACKAGE_PATH.
printenv | grep ROS
如果沒有設置,需要source(根據實際安裝的版本來)
source /opt/ros/kinetic/setup.bash
1.3 創建ROS空間
創建一個catkin workspace空間
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make #編譯指令
2.ROS文件系統介紹
3.創建並編譯ROS程序包
在當前目錄創建一個package的命令如下,也可以同時指明這個package所依賴的其他package:
roscreate-pkg [package_name] [depend1] [depend2] [depend3]
3.1 創建新的ROS package
切換到~/catkin_ws 並創建package
cd ~/ros_workspace
roscreate-pkg beginner_tutorials std_msgs rospy roscpp
創建package成功以後可以使用roscd切換到package目錄
roscd beginner_tutorials
如果不能使用roscd命令,則需要再次source
source ./devel/setup.sh
3.2編譯package
在catkin_ws目錄下
catkin_make
build 目錄是build space的默認所在位置,同時cmake 和 make也是在這裏被調用來配置並編譯你的程序包。devel 目錄是devel space的默認所在位置, 同時也是在你安裝程序包之前存放可執行文件和庫文件的地方。
4.理解ROS節點和話題
安裝一個輕量級的模擬器:
sudo apt-get install ros-kinetic-ros-tutorials
4.1圖的概念
-
Nodes:節點,一個節點即爲一個可執行文件,它可以通過ROS與其它節點進行通信。
-
Messages:消息,消息是一種ROS數據類型,用於訂閱或發佈到一個話題。
-
Topics:話題,節點可以發佈消息到話題,也可以訂閱話題以接收消息。
-
Master:節點管理器,ROS名稱服務 (比如幫助節點找到彼此)。
-
rosout: ROS中相當於stdout/stderr。
-
roscore: 主機+ rosout + 參數服務器 (參數服務器會在後面介紹)。
4.2節點
一個節點其實只不過是ROS程序包中的一個可執行文件。ROS節點可以使用ROS客戶庫與其他節點通信。節點可以發佈或接收一個話題。節點也可以提供或使用某種服務。
(節點是ros中非常重要的一個概念,爲了幫助初學者理解這個概念,這裏舉一個通俗的例子:
例如,咱們有一個機器人,和一個遙控器,那麼這個機器人和遙控器開始工作後,就是兩個節點。遙控器起到了下達指 令的作用;機器人負責監聽遙控器下達的指令,完成相應動作。從這裏我們可以看出,節點是一個能執行特定工作任 務的工作單元,並且能夠相互通信,從而實現一個機器人系統整體的功能。在這裏我們把遙控器和機器人簡單定義爲兩個節點,實際上在機器人中根據控制器、傳感器、執行機構等不同組成模塊,還可以將其進一步細分爲更多的節點,這個是根據用戶編寫的程序來定義的。)
4.3客戶端庫
ROS客戶端庫允許使用不同編程語言編寫的節點之間互相通信:
- rospy = python 客戶端庫
- roscpp = c++ 客戶端庫
4.4 roscore
roscore 是你在運行所有ROS程序前首先要運行的命令。
roscore
4.5使用rosnode
rosnode 顯示當前運行的ROS節點信息。rosnode list 指令列出活躍的節點:
rosnode list
rosnode info 命令返回的是關於一個特定節點的信息。
$ rosnode info /rosout
4.6使用rosrun
rosrun 允許你使用包名直接運行一個包內的節點(而不需要知道這個包的路徑)。用法:
rosrun [package_name] [node_name]
用一下命令運行一個turtlesim_node
rosrun turtlesim turtlesim_node
5.理解ROS服務和參數
5.1 ROS Services
服務(services)是節點之間通訊的另一種方式。服務允許節點發送請求(request) 並獲得一個響應(response)
rosservice可以很輕鬆的使用 ROS 客戶端/服務器框架提供的服務。rosservice提供了很多可以在topic上使用的命令,如下所示:
使用方法:
rosservice list #輸出可用服務的信息
rosservice call #調用帶參數的服務
rosservice type #輸出服務類型
rosservice find #依據類型尋找服務find services by service type
rosservice uri #輸出服務的ROSRPC uri
rosservice call [service] [args]
rosservice type [service]
5.2 Using rosparam
rosparam使得我們能夠存儲並操作ROS 參數服務器(Parameter Server)上的數據。參數服務器能夠存儲整型、浮點、布爾、字符串、字典和列表等數據類型。rosparam使用YAML標記語言的語法。一般而言,YAML的表述很自然:1 是整型, 1.0 是浮點型, one是字符串, true是布爾, [1, 2, 3]是整型列表, {a: b, c: d}是字典. rosparam有很多指令可以用來操作參數,如下所示:
使用方法:
rosparam set #設置參數
rosparam get #獲取參數
rosparam load #從文件讀取參數
rosparam dump #向文件中寫入參數
rosparam delete #刪除參數
rosparam list #列出參數名
rosparam set [param_name]
rosparam get [param_name]
rosparam dump [file_name]
rosparam load [file_name] [namespace]
6. 使用rqt_console和roslaunch
安裝:
sudo apt-get install ros-<distro>-rqt ros-<distro>-rqt-common-plugins ros-<distro>-turtlesim
6.1使用rqt_console和rqt_logger_level
開兩個終端分別運行:
rosrun rqt_console rqt_console
rosrun rqt_logger_level rqt_logger_level
再開一個新的終端運行:
rosrun turtlesim turtlesim_node
6.2使用roslaunch
roslaunch可以用來啓動定義在launch文件中的多個節點。用法:
roslaunch [package] [filename.launch]
先切換到beginner_tutorials程序包目錄下:
roscd beginner_tutorials
如果roscd執行失敗了,記得設置你當前終端下的ROS_PACKAGE_PATH環境變量,設置方法如下
export ROS_PACKAGE_PATH=~/catkin_ws/:$ROS_PACKAGE_PATH
創建一個launch文件夾
mkdir launch
cd launch
創建一個名爲turtlemimic.launch的launch文件,件裏面
touch turtlemimic.launch
並複製粘貼以下內容到該文件
<launch> <!--以launch標籤開頭以表明這是一個launch文件 -->
<!--
在這裏我們創建了兩個節點分組並以'命名空間(namespace)'標籤來區分,其中一個名爲turtulesim1,另一個名爲turtlesim2,兩個組裏面都使用相同的turtlesim節點並命名爲'sim'。這樣可以讓我們同時啓動兩個turtlesim模擬器而不會產生命名衝突。
-->
<group ns="turtlesim1">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>
<group ns="turtlesim2">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>
<!--
在這裏我們啓動模仿節點,並將所有話題的輸入和輸出分別重命名爲turtlesim1和turtlesim2,這樣就會使turtlesim2模仿turtlesim1。
-->
<node pkg="turtlesim" name="mimic" type="mimic">
<remap from="input" to="turtlesim1/turtle1"/>
<remap from="output" to="turtlesim2/turtle1"/>
</node>
</launch>
現在讓我們通過roslaunch命令來啓動launch文件:
roslaunch beginner_tutorials turtlemimic.launch
在將會有兩個turtlesims被啓動,然後我們在一個新終端中使用rostopic命令發送速度設定消息
rostopic pub /turtlesim1/turtle1/command_velocity turtlesim/Velocity -r 1 -- 2.0 -1.8
7.創建ROS消息和ROS服務
-
消息(msg): msg文件就是一個描述ROS中所使用消息類型的簡單文本。它們會被用來生成不同語言的源代碼。
-
服務(srv): 一個srv文件描述一項服務。它包含兩個部分:請求和響應。
msg文件存放在package的msg目錄下,srv文件則存放在srv目錄下。
msg文件實際上就是每行聲明一個數據類型和變量名。可以使用的數據類型如下:
- int8, int16, int32, int64 (plus uint*)
- float32, float64
- string
- time, duration
- other msg files
- variable-length array[] and fixed-length array[C]
在ROS中有一個特殊的數據類型:Header,它含有時間戳和座標系信息。在msg文件的第一行經常可以看到Header header的聲明.
7.1 使用msg
在之前創建的package裏定義新的消息
cd ~/catkin_ws/src/beginner_tutorials
mkdir msg
echo "int64 num" > msg/Num.msg
接下來,還有關鍵的一步:我們要確保msg文件被轉換成爲C++,Python和其他語言的源代碼:
查看package.xml, 確保它包含一下兩條語句:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
在 CMakeLists.txt文件中,利用find_packag函數,增加對message_generation的依賴,這樣就可以生成消息了。 你可以直接在COMPONENTS的列表裏增加message_generation,就像這樣:
find_package(catkin REQUIRED COMPONENTS
roscpp
std_msgs
message_generation
)
還有運行依賴
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES beginner_tutorials
CATKIN_DEPENDS roscpp std_msgs
# DEPENDS system_lib
message_runtime
)
add_message_files(
FILES
Num.msg
)
手動添加.msg文件後,我們要確保CMake知道在什麼時候重新配置我們的project。 確保添加了如下代碼:
generate_messages()
具體文件見GitHub:CMakeList.txt
使用
rosmsg show [message type]
具體方法
rosmsg show beginner_tutorials/Num #rosmsg show Num
7.2 使用msv
在剛剛那個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和其他語言的源代碼。
還是再CMakeLists.txt中增加:
add_service_files(
FILES
AddTwoInts.srv
)
使用
rossrv show <service type>
rossrv show beginner_tutorials/AddTwoInts # rossrv show AddTwoInts
修改MakeLists.txt
generate_messages(
DEPENDENCIES
std_msgs
)
編譯
cd ~/catkin_ws/
catkin_make
8.編寫簡單的消息發佈器和訂閱器並測試(C++)
8.1 編寫消息發佈器
『節點』(Node) 是指 ROS 網絡中可執行文件。接下來,我們將會創建一個發佈器節點(“talker”),它將不斷的在 ROS 網絡中廣播消息
切換到之前創建的 beginner_tutorials package 路徑下:
cd ~/catkin_ws/src/beginner_tutorials
創建一個src文件
mkdir -p ~/catkin_ws/src/beginner_tutorials/src
在src中新建一個talker.cpp文件,複製talker.cpp的內容
//ros/ros.h 是一個實用的頭文件,它引用了 ROS 系統中大部分常用的頭文件。
#include "ros/ros.h"
//這引用了 std_msgs/String 消息, 它存放在 std_msgs package 裏,是由 String.msg 文件自動生成的頭文件。需要關於消息的定義,可以參考 msg 頁面。
#include "std_msgs/String.h"
#include <sstream>
int main(int argc,char **argv)
{
/*
初始化 ROS 。它允許 ROS 通過命令行進行名稱重映射——然而這並不是現在討論的重點。在這裏,我們也可以指定節點的名稱——運行過程中,節點的名稱必須唯一。
這裏的名稱必須是一個 base name ,也就是說,名稱內不能包含 / 等符號
*/
ros::init(argc,argv,"talker");
/*
爲這個進程的節點創建一個句柄。第一個創建的 NodeHandle 會爲節點進行初始化,最後一個銷燬的 NodeHandle 則會釋放該節點所佔用的所有資源。
*/
ros::NodeHandle n;
/*
chatter 話題的節點,將要有數據發佈。第二個參數是發佈序列的大小。如果我們發佈的消息的頻率太高,緩衝區中的消息在大於 1000 個的時候就會開始丟棄先前發佈的消息。
NodeHandle::advertise() 返回一個 ros::Publisher 對象,它有兩個作用: 1) 它有一個 publish() 成員函數可以讓你在topic上發佈消息; 2) 如果消息類型不對,它會拒絕發佈。
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter",1000);
/*
ros::Rate 對象可以允許你指定自循環的頻率。它會追蹤記錄自上一次調用 Rate::sleep() 後時間的流逝,並休眠直到一個頻率週期的時間。
在這個例子中,我們讓它以 10Hz 的頻率運行。
*/
ros::Rate loop_rate(10);
/*
roscpp 會默認生成一個 SIGINT 句柄,它負責處理 Ctrl-C 鍵盤操作——使得 ros::ok() 返回 false。
如果下列條件之一發生,ros::ok() 返回false:
SIGINT 被觸發 (Ctrl-C)
被另一同名節點踢出 ROS 網絡
ros::shutdown() 被程序的另一部分調用
節點中的所有 ros::NodeHandles 都已經被銷燬
一旦 ros::ok() 返回 false, 所有的 ROS 調用都會失效。
*/
int count=0;
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_pub.publish(msg);//這裏,我們向所有訂閱 chatter 話題的節點發送消息。
/*
在這個例子中並不是一定要調用 ros::spinOnce(),因爲我們不接受回調。然而,如果你的程序裏包含其他回調函數,最好在這裏加上 ros::spinOnce()這一語句,否則你的回調函數就永遠也不會被調用了。
*/
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
8.2 編寫訂閱器
在src下新建listener.cpp文件,複製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 話題的消息。
有各種不同的 NodeHandle::subscribe() 函數,允許你指定類的成員函數,甚至是 Boost.Function 對象可以調用的任何數據類型。roscpp overview 提供了更爲詳盡的信息。
*/
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;
}
編譯:
在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})
切換到catkin_ws目錄編譯
cd ~/catkin_ws
catkin_make
8.3 測試
確保roscore可用,並運行:
roscore
catkin specific 如果使用catkin,確保你在調用catkin_make後,在運行你自己的程序前,已經source了catkin工作空間下的setup.sh文件:
cd ~/catkin_ws
source ./devel/setup.bash
運行發佈器
rosrun beginner_tutorials talker
運行訂閱器
rosrun beginner_tutorials listener
9.編寫簡單的服務器和客戶端並測試(C++)
9.1 服務器
進入先前你在catkin workspace教程中所創建的beginner_tutorials包所在的目錄:
cd ~/catkin_ws/src/beginner_tutorials
創建src/add_two_ints_server.cpp文件
#include "ros/ros.h"
//beginner_tutorials/AddTwoInts.h是由編譯系統自動根據我們先前創建的srv文件生成的對應該srv文件的頭文件。
#include "beginner_tutorials/AddTwoInts.h"
/*
這個函數提供兩個int值求和的服務,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 客戶端
創建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");
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
添加下面代碼
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)
編譯
cd ~/catkin_ws
catkin_make
9.3 測試
- 運行service
rosrun beginner_tutorials add_two_ints_server
- 運行client
rosrun beginner_tutorials add_two_ints_client 1 3