转载自:https://blog.csdn.net/u014587147/article/details/75647002
小菜虎 2017-07-21 17:02:24 7143 收藏 47
分类专栏: ROS 文章标签: ros 命名空间 参数 重映射
最后发布:2017-07-21 17:02:24首次发布:2017-07-21 17:02:24
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014587147/article/details/75647002
版权
已经学ROS快两个月了,一开始对ROS 命名空间,参数,参数服务器,重映射没认真看,后来发现很重要,它是学习ROS代码的基础。我们都知道ros以topic通信,但是只靠topic通信是远远不够的,于是使用客服端服务器、actionlib、参数服务器来补充,这几种都是通信的机制。但还不够,为了方便又加入了命名空间,重映射。只有清楚了这些,其实才算对ros有了初步了解,下面的几个文章我将分别介绍。
在介绍之前,我先抱怨一下,对于只学过C语言的我,没有写过什么项目,然后自己学习ROS,简直痛苦。想找几个资料,英文的根本看不懂,四级都没过,找中文的基本都是直译过来的,而且大多数人的理解,或多或少的都有点错误,当然,我的理解应该有点错误的,希望明白之人能指出,感激不尽。
在C++中的命名空间,using namespace std,std就是名空间,这里的命名空间跟它差不多,但是比它更多,一层嵌套一层,用的特别灵活。先举个简单的例子,然后细说。
我们先运行一个小乌龟:
rosrun turtlesim turtlesim_node
turtlesim是一个包名,turtlesim_node是执行文件名,可以与节点名相同,也不可以不同。这里的节点名是 /turtlesim。说明可能初始化的时候是这样的,为什么是可能,因为重映射可以改变:
ros::init(argc, argv, "turtlesim");
节点名前面加个“/”,这意味这个节点是全局空间的。另外说下,节点是有类型的,节点的类型就是节点所在包名和可执行文件名的加。一般的定义节点句柄:
ros::NodeHandle n;
NodeHandle是一个类,然后我们实例化的一个对象n.这种情况默认n这个节点是全局命名空间下的。你可以认为"/"这个斜杠就是全局名空间。现在我们来改变这个节点的名空间,通过重映射,我们先用重映射,以后再讲。
你会发现,这个节点的名变为/my/turtlesim,此时,节点所在的空间发生了变化,我们把这个节点从全局名空间放进了一个/my的名空间下。于是节点内的所有参数,服务,话题也都放在了/my的命名空间下。
你会发现,它所发布的和订阅的话题,服务都在这个名空间,这意味着它现在只会订阅和发布这个名空间下的话题。
这个时候运行rosrun turtlesim turtle_teleop_key ,是不会控制小乌龟的,因为它只会发布/turtel1/cmd_vel话题,不会发布/my/turtel1/cmd_vel。
这个时候你可能懂一点了,节点,话题,服务,包括参数都是有名空间的,这个名空间约束着是它们的范围。这个时候你可能会想,那不在一个名空间节点是不是就无法通信了,显然这种想法是错误的,这违背了我们通信的初衷,我们通过重映射和参数服务器,很容易让不同命名空间的节点都能通信。好吧,先简单的通过重映射使他们通信,具体下一篇文再讲,这一篇主要记录命名空间。
看到没,重映射不仅可以把命名空间改变,还可以将发布的话题给改变(还可改变参数的空间),这样/my空间下的乌龟就可以移动了。当然你也可以直接将这个节点的名空间改为/my,这样他们在同一个命名空间,当然可以直接通信了。
上面说的是全局命名空间,还有一个相对命名空间,/turtle1/cmd_vel就是相对命名空间,它在/my的命名空间下,发布/turtle1/cmd_vel话题,解析器会把它解析成/my/turtle1/cmd_vel话题。发布的/turtle1/cmd_vel话题相对/my空间下是/turtle1/cmd_vel话题,对于全局它是/my/turtle1/cmd_vel话题。
接下来我们看看节点句柄的构造函数,也可以跳过:
namespace ros
{
class NodeHandleBackingCollection;
/**
* \brief roscpp's interface for creating subscribers, publishers, etc.
*
* This class is used for writing nodes. It provides a RAII interface
* to this process' node, in that when the first NodeHandle is
* created, it instantiates everything necessary for this node, and
* when the last NodeHandle goes out of scope it shuts down the node.
*
* NodeHandle uses reference counting internally, and copying a
* NodeHandle is very lightweight.
*
* You must call one of the ros::init functions prior to instantiating
* this class.
*
* The most widely used methods are:
* - Setup:
* - ros::init()
* - Publish / subscribe messaging:
* - advertise()
* - subscribe()
* - RPC services:
* - advertiseService()
* - serviceClient()
* - ros::service::call()
* - Parameters:
* - getParam()
* - setParam()
*/
class ROSCPP_DECL NodeHandle
{
public:
/**
* \brief Constructor
*
* When a NodeHandle is constructed, it checks to see if the global
* node state has already been started. If so, it increments a
* global reference count. If not, it starts the node with
* ros::start() and sets the reference count to 1.
*
* \param ns Namespace for this NodeHandle. This acts in addition to any namespace assigned to this ROS node.
* eg. If the node's namespace is "/a" and the namespace passed in here is "b", all
* topics/services/parameters will be prefixed with "/a/b/"
* \param remappings Remappings for this NodeHandle.
* \throws InvalidNameException if the namespace is not a valid graph resource name
*/
NodeHandle(const std::string& ns = std::string(), const M_string& remappings = M_string());
NodeHandle(const NodeHandle& rhs);
NodeHandle(const NodeHandle& parent, const std::string& ns);
NodeHandle(const NodeHandle& parent, const std::string& ns, const M_string& remappings);
~NodeHandle();
}
是不是发现他有多个构造函数,
ros::init(argc, argv, "my_node_name"); ros::NodeHandle nh("/my_node_handle_namespace");
我们创建节点句柄nh时,指明了它的命名空间,所以nh的命名空间为/my_node_handle_namespace,
所以这个节点里的所有的参数,话题前面都应该有/my_node_handle_namespace
最后一种命名空间是私有命名空间,私有名称,以一个波浪字符(~)开始。和相对名称一样,私有名称并不能完全确定它们自身所在的命名空间,而是需要ROS 客户端库将这个名称解析为一个全局名称。与相对名称的主要差别在于,私有名称不是用当前默认命名空间,而是用的它们节点名称作为命名空间。
ros::init(argc, argv, "my_node_name"); ros::NodeHandle nh1("~"); ros::NodeHandle nh2("~foo");
ros::Subscriber sub1 = nh1.subscribe("my_private_topic", ...);
ros::Subscriber sub2 = nh2.subscribe("my_private_topic", ...);
sub1订阅的话题是my_node_name/my_private_topic
sub2订阅的话题是my_node_name/foo/my_private_topic
到这里,我相信大家对命名空间似懂非懂,这个东西到底是干什么的。如果你懂上面说的东西,接下来,看看参数的使用,似乎就懂命名空间到底是干什么的了。