ROS之命名空间

已经学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。

这个时候你可能懂一点了,节点,话题,服务,包括参数都是有名空间的,这个名空间约束着是它们的范围。这个时候你可能会想,那不在一个名空间节点是不是就无法通信了,显然这种想法是错误的,这违背了我们通信的初衷,我们通过重映射和参数服务器,很容易让不同命名空间的节点都能通信。好吧,先简单的通过重映射使他们通信,具体下一篇文再讲,这一篇主要记录命名空间。

ROS之命名空间_第1张图片

看到没,重映射不仅可以把命名空间改变,还可以将发布的话题给改变(还可改变参数的空间),这样/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

到这里,我相信大家对命名空间似懂非懂,这个东西到底是干什么的。如果你懂上面说的东西,接下来,看看参数的使用,似乎就懂命名空间到底是干什么的了。

你可能感兴趣的:(ROS)