ROS中整体代码是在工作空间中运行,因此我们需要先创建工作空间。假定我们的工作空间为test_ws
,创建步骤如下:
test_ws/src
,或者在某个路径下(自己想要创建工作空间的路径)输入:mkdir -p test_ws/src
;catkin_init_workspace
命令对新空间初始化,完成后在src文件夹下会出现CMakeList.txt
文件;test_ws
目录(可以在第2步的前提下输入cd ..
),在该终端下输入catkin_make
对工作空间进行编译,完成后会在test_ws中出现build和devel文件。创建成功!在test_ws中:
ROS中的包,需要存放在src文件夹下,假设包名为pack_test
,则创建步骤为:
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的一些命令:
ROS的消息传输机制可以分为Message、Action和Server
Message通信双方是Publisher和Subcriber,以下是C++程序示例:
Publisher:在pack_test包中的src文件下,创建Publisher.cpp文件,测试内容如下:
#include // ros头文件
#include // 消息头文件
#include // 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
#include
// 消息回调处理函数
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中打开三个终端运行:
如果出现某个终端找不到pack_test,就在该终端输入source devel/setup.bash
。
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的额外终端,不是很方便。ROS中提供了roslaunch
命令,目的是实现同一个终端运行多个节点的目的,此命令会自动运行roscore节点,具体步骤为:
<?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>
roslaunch pack_test node.launch
通过在新的终端中键入rosnode list
可以查看当前运行的节点,rostopic list
可查看已发布消息的名称。
在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(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_generationbuild_depend>
<exec_depend>message_runtimeexec_depend>