ROS學習快速索引
基礎相關
創建工作空間(workspace)
ROS中整體代碼是在工作空間中運行,因此我們需要先創建工作空間。假定我們的工作空間爲test_ws
,創建步驟如下:
- 創建文件夾
test_ws/src
,或者在某個路徑下(自己想要創建工作空間的路徑)輸入:mkdir -p test_ws/src
; - 進入src目錄,打開終端輸入
catkin_init_workspace
命令對新空間初始化,完成後在src文件夾下會出現CMakeList.txt
文件; - 終端返回
test_ws
目錄(可以在第2步的前提下輸入cd ..
),在該終端下輸入catkin_make
對工作空間進行編譯,完成後會在test_ws中出現build和devel文件。
創建成功!在test_ws中:
- bulid:是編譯的中間文件;
- devel:(developing縮寫)是最終的一個目標文件(可執行文件、腳本等);
- src:源代碼存放位置;
src/package1/include:存放庫文件(*.hpp,*.cpp,*.h,*.c)
src/package1/action:存放動作(*.action)文件;
src/package1/msg:存放消息(*.msg)
src/package1/srv:存放服務器相關信息文件(*.srv)
src/package1/launch:存放啓動文件(*.launch)
src/package1/scripts:存放python文件(*.py)
src/package1/src:存放package的源文件(*.cpp,*.c)
src/package1/CMakeList.txt:catkin_make文件
src/package1/package.xml:package的描述文件
創建ROS package
ROS中的包,需要存放在src文件夾下,假設包名爲pack_test
,則創建步驟爲:
- 進入test_ws/src文件夾下,打開終端;
- 輸入
catkin_create_pkg pack_test std_msgs roscpp
創建包pack_test,其依賴爲std_msgs和roscpp(ROS標準消息類型和C++編譯環境);
命令結構爲:catkin_create_pkg [package_name] [depend1] [depend2] [depend3]
關於ROS package的一些命令:
- rospack profile:查看新添加包的信息;
- rospack find [package_name]:查找指定包的位置;
- rospack depends [package_name]:查看指定包的依賴;
消息傳輸機制
ROS的消息傳輸機制可以分爲Message、Action和Server
Publisher & Subcriber(Message)
Message通信雙方是Publisher和Subcriber,以下是C++程序示例:
Publisher:在pack_test包中的src文件下,創建Publisher.cpp文件,測試內容如下:
#include <ros/ros.h> // ros頭文件
#include <std_msgs/String.h> // 消息頭文件
#include <sstream> // stl字符串頭文件
int main(int argc, char **argv)
{
// ROS初始化,名稱爲Publisher
ros::init(argc, argv, "Publisher");
// 創建節點句柄
ros::NodeHandle n;
// 創建消息發佈變量,消息類型爲std_msgs::String,名稱爲message,緩存1000字節
ros::Publisher pub = n.advertise<std_msgs::String>("message", 1000);
// 創建發佈頻率變量,頻率爲10Hz
ros::Rate loop_rate(10);
// 如果ROS出於開啓狀態,則保持循環
while (ros::ok()){
//ros::ok()返回false,代表可能發生了以下事件
//1.SIGINT被觸發(Ctrl-C)調用了ros::shutdown()
//2.被另一同名節點踢出 ROS 網絡
//3.ros::shutdown()被程序的另一部分調用
//4.節點中的所有ros::NodeHandles 都已經被銷
// 創建消息變量並,輸入信息: I am the publish node
std_msgs::String msg;
std::stringstream ss;
ss << " I am the publish node ";
msg.data = ss.str();
// 發佈消息
pub.publish(msg);
// 啓動ROS消息回調處理函數
ros::spinOnce();
// 等待
loop_rate.sleep();
}
return 0;
}
Subcriber:在pack_test包中的src文件下,創建Subcriber.cpp文件,測試內容爲:
#include <ros/ros.h>
#include <std_msgs/String.h>
// 消息回調處理函數
void Callback(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, "Subscriber");
ros::NodeHandle n;
// 創建消息訂閱變量,消息名稱爲message,緩存1000字節,回調函數爲CallBack
ros::Subscriber sub = n.subscribe("message", 1000, Callback);
// 啓動ROS消息回調處理函數
ros::spin();
return 0;
}
之後,我們要在pack_test/CMakeList.txt中(最下面)添加如下內容:
# 添加路徑搜索目錄
include_directories(
include
${catkin_INCLUDE_DIRS}
)
# 添加節點的可執行文件
add_executable(subscirber_node src/Subscriber_node.cpp)
add_executable(publisher_node src/Publisher_node.cpp)
# 添加節點可指定文件作需要鏈接的庫文件
target_link_libraries(subscriber_node ${catkin_LIBRARIES})
target_link_libraries(publisher_node ${catkin_LIBRARIES})
最後,在test_ws中輸入catkin_make,編譯成功後,分別在test_ws中打開三個終端運行:
- roscore
- rosrun pack_test publisher_node
- rosrun pack_test subcriber_node
如果出現某個終端找不到pack_test,就在該終端輸入source devel/setup.bash
。
Publisher & Subcriber(Message Python版本)
ROS中可以使用Python作爲編程語言,其好處是不用編譯,不用寫CMakeLists.txt(只需要在CMakeList.txt中find_package內添加rospy,然後重新對包進行編譯,以獲得對python的支持),獲得腳本語言快速的編寫能力。我們將python腳本放入test_ws/src/pack_test/scripts
,下面則是python對以上兩個節點進行重寫:
Publisher.py
#!/usr/bin/env python
# 添加ros函數庫
import rospy
# 添加std_msgs中String類型
from std_msgs.msg import String
# 初始化節點
rospy.init_node('publisher_node')
# 初始化發佈者,話題名稱messaage,類型String,緩存1000字節
pub = rospy.Publisher('message', String, queue_size = 1000)
# 發佈頻率10Hz
rate = rospy.Rate(10)
# 定義發佈的信息
msg = String()
msg = ' I am the talker node '
# 循環發佈
while not rospy.is_shutdown():
pub.publish(msg)
rate.sleep()
subscriber.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
# 話題回調函數
def CallBack(msg):
rospy.info('I heard %s"', msg.data)
rospy.init_node('subscriber_node')
# 初始化訂閱者,訂閱話題爲message,類型String,回調函數CallBack
sub = rospy.Subscriber('message', String, CallBack)
rospy.spin()
最後,我們在三個終端中依次運行:
- roscore
- rosrun pack_test publisher.py
- rosrun pack_test subscriber.py
launch文件初探
在終端中運行任何一個節點,我們都需要添加roscore的額外終端,不是很方便。ROS中提供了roslaunch
命令,目的是實現同一個終端運行多個節點的目的,此命令會自動運行roscore節點,具體步驟爲:
- 在包(比如pack_test)中新建launch文件夾,並在其中創建node.launch文件(文件名自擬)。
- 在node.launch中添加以下內容:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launch>
<!-- Subscriber node, 節點名稱,包名,節點。output=“screen“將輸出打印到終端,如果是python的話,要在type中輸入python腳本名稱 -->
<node name="subscirber_node" pkg="test" type="subscriber_node" output="screen"/>
<!-- Publisher node -->
<node name="publisher_node" pkg="test" type="publisher_node"/>
</launch>
- 在test_ws中新建終端運行
roslaunch pack_test node.launch
通過在新的終端中鍵入rosnode list
可以查看當前運行的節點,rostopic list
可查看已發佈消息的名稱。
自定義message
在test_ws/src/pack_test/msg
中新建Pose.msg文件,寫入如下內容
float64 x
float64 y
float64 z
std_msgs文件中的變量類型與C++中的變量類型有如下的對應關係:
msg type | c++ | msg type | C++ |
---|---|---|---|
bool | bool | int64 | long long |
int8 | char | unit64 | unsigned long |
int8 | unsigned char | float32 | float |
int16 | short | float64 | double |
uint16 | unsigned short | string | std::string |
int32 | int | time | ros::Time |
uint32 | unsigned int | duration | ros::Duration |
此外msg還可以使用其他包中的消息類型,比如senor_msgs中的Image.msg等等,或者用戶自定義的消息類型,此時需要在generate_message中添加對這些包的依賴,並且在去掉add_dependencies註釋。
注意:如果當前包中使用了其他包中所定義的消息,服務和動作類型,需要添加add_dependencies項。
比如,在以上我們的自定消息Pose.msg文件,我們需要修改CMakeList.txt文件:
- 在find_package中加入message_generation的依賴;
- 在add_message_files中添加Pose.msg
- generate_messages中添加自定義消息的依賴
比如:
find_package(catkin REQUIRED COMPONENTS
std_msgs
roscpp
rospy
...
message_generation
)
add_message_files(
FILES
Pose.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
修改package.xml文件,去掉註釋:
<!-- <build_depend>message_generation</build_depend> -->
<!-- <exec_depend>message_runtime</exec_depend> -->
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>