编写发布者节点:
“节点”是连接到ROS网络的可执行文件的ROS术语。在这里,我们将创建一个将继续广播消息的发布者(“talker”)节点。
将目录更改为您在catkin工作区之前的教程中创建的beginner_tutorials包:
首先使用指令更改一下roscd的绝对路径:
$ . ~/catkin_ws/devel/setup.bash
然后使用
$ roscd beginner_tutorials
在beginner_tutorials包目录中创建一个src目录:
$ mkdir -p src
该目录将包含beginner_tutorials包的任何源文件。
在beginner_tutorials包中创建src / talker.cpp文件并在其中粘贴以下内容:
#include "ros/ros.h"
/*ros/ros.h是一个方便的包括,包括使用ROS系统最常见的公共部分所需的所有标题。*/
#include "std_msgs/String.h"
#include <sstream>
/*这包括std_msgs / String消息,该消息驻留在std_msgs包中。
这是从该包中的String.msg文件自动生成的标头。*/
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
/*初始化ROS。这允许ROS通过命令行进行名称重映射 - 现在不重要。这也是我们
指定节点名称的地方。节点名称在正在运行的系统中必须是唯一的.
此处使用的名称必须是基本名称,即。它不能有/在其中。*/
ros::NodeHandle n;
/*为此进程的节点创建句柄。创建的第一个NodeHandle实际上将执行节点的初始化*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
/*告诉主人我们将在主题chatter中发布类型为std_msgs / String的消息。
这让主人告诉任何监听chatter节点的节点,我们将发布有关该主题的数据。
第二个参数是我们的发布队列的大小。在这种情况下,如果我们发布得太快,
它将在开始丢弃旧消息之前缓冲最多1000条消息。
NodeHandle::advertise()返回一个ros::Publisher对象,它有两个目的:
1)它包含一个publish()方法,它允许你将消息发布到它创建的主题上,以及
2)当它超出范围时,它会自动取消广告。*/
ros::Rate loop_rate(10);
/*使用ros :: Rate对象可以指定要循环的频率。它将跟踪自上次调用
Rate :: sleep()以来的持续时间,并在正确的时间内休眠。在这种情况下,
我们告诉它我们想要以10Hz运行。*/
int count = 0;
/*默认情况下,roscpp将安装一个SIGINT处理程序,该处理程序提供Ctrl-C处理,
如果发生这种情况,将导致ros :: ok()返回false。如果出现以下情况,
ros :: ok()将返回false:
收到SIGINT(Ctrl-C)
我们已被另一个具有相同名称的节点踢出网络
ros :: shutdown()已被应用程序的另一部分调用。
所有ros ::NodeHandles都被销毁了
一旦ros :: ok()返回false,所有ROS调用都将失败。
*/
while (ros::ok())
{
/*我们使用消息适配类在ROS上广播消息,通常从msg文件生成。
更复杂的数据类型是可能的,但是现在我们将使用标准的String消息,
它有一个成员:“data”。*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
/*ROS_INFO和friend是printf/cout的替代品。*/
ROS_INFO("%s", msg.data.c_str());
/*将消息广播给任何已连接的人。*/
chatter_pub.publish(msg);
/*这个简单的程序不需要调用ros :: spinOnce(),因为我们没有收到任何回调。
但是,如果您要在此应用程序中添加订阅,并且此处没有ros::spinOnce(),
则永远不会调用您的回调。所以,添加它是为了衡量。*/
ros::spinOnce();
/*现在我们使用ros::Rate对象在剩余时间内休眠,让我们达到10Hz的发布速度。*/
loop_rate.sleep();
++count;
}
return 0;
}
这是正在发生的事情的浓缩版本:
初始化ROS系统
宣传我们将把聊天主题上的std_msgs / String消息发布给master
循环发布消息,每秒10次聊天
现在我们需要编写一个节点来接收这些消息。
编写订阅者节点
在beginner_tutorials包中创建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;
/*与主人订阅chatter主题。每当有新消息到达时,ROS都会调用chatterCallback()
函数。第二个参数是队列大小,以防我们无法足够快地处理消息。在这种情况下,
如果队列达到1000条消息,我们将在新消息到达时开始丢弃旧消息。
NodeHandle :: subscribe()返回一个ros::Subscriber对象,您必须保留该对象,
直到您想取消订阅为止。订阅者对象被销毁后,它将自动取消订阅chatter主题。
NodeHandle::subscribe()函数有一些版本允许您指定类成员函数,甚至可以指定
Boost.Function对象可调用的任何函数。该roscpp概述包含更多的信息。*/
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;
}
再次,这是一个精简版的正在发生的事情:
初始化ROS系统
订阅聊天主题
旋转,等待消息到达
当消息到达时,将调用chatterCallback()函数
构建节点:
您在之前的教程中使用了catkin_create_pkg,它为您创建了一个package.xml和一个CMakeLists.txt文件。
生成的CMakeLists.txt应该如下所示(修改了创建Msgs和Srvs教程,删除了未使用的注释和示例):
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
add_message_files(DIRECTORY msg FILES Num.msg)
add_service_files(DIRECTORY srv FILES AddTwoInts.srv)
generate_messages(DEPENDENCIES std_msgs)
catkin_package()
不要担心修改注释(#)示例,只需将这几行添加到CMakeLists.txt的底部:
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)
添加后的CMakeLists.txt文件大概是这样的:
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)
generate_messages(DEPENDENCIES std_msgs)
catkin_package()
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空间的package目录,默认位于〜/catkin_ws/devel/lib/。
请注意,您必须将可执行目标的依赖项添加到(message generation targets)信息生成目标:(以下代码已经添加到CMakeLists.txt文件)
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
这可以确保在使用之前生成此包的(message head)邮件头。如果您使用catkin工作区内其他包的消息,则还需要将依赖项添加到各自的生成目标,因为catkin并行构建所有项目。从* Groovy *开始,您可以使用以下变量来依赖所有必需的目标:
target_link_libraries(talker $ {catkin_LIBRARIES})
您可以直接调用可执行文件,也可以使用rosrun来调用它们。它们不会放在’ / bin’中,因为在将软件包安装到系统时会污染PATH。
之后就运行catkin_make啦!
进入catkin工作区:
$ cd ~/catkin_ws
$ catkin_make