ROS源码学习分享_3_NodeHandle

        

        在初始化模块完成之后我们就可以开始NodeHandle模块代码的学习了。

  ros::NodeHandle n;

      NodeHandle模块是用于管理节点操作和通信的对象。NodeHandle允许节点创建、发布和订阅消息,创建服务和客户端,以及管理节点的参数。

        例如发布者的创建就由NodeHandle进行。

ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

        需要说明的是,使用 nh.advertisenh.subscribe 时,节点会通过 XML-RPC 将这些信息发送给主节点,主节点维护一个全局的发布与订阅关系表。这里只是简单提一下,并不会继续深入,本章主要还是观看NodeHandle模块创建时的源码。

        当NodeHandle没有参数设置直接创建时,他会走头文件的这个重载构造函数。

NodeHandle(const std::string& ns = std::string(), const M_string& remappings = M_string());

        这里的ns 即命名空间, remappings就是之前的重映射表。如果没有参数输入,就将给两个空值给他们。

        需要说明的是,命名空间并不是之前创建的节点名。命名空间允许开发者将话题、服务和参数组织成逻辑分组,以避免命名冲突和管理复杂性。例如:

ros::NodeHandle nh("~");
ros::Publisher cmd_vel_pub = nh.advertise<geometry_msgs::Twist>("cmd_vel", 10);
ros::Subscriber laser_sub = nh.subscribe("scan", 10, laserCallback);

ros::ServiceServer move_base_server = nh.advertiseService("move_base", moveBaseCallback);

nh.setParam("max_speed", 1.0);

        这里的 "~" 表示当前节点的私有命名空间,它会在节点名后面追加一个斜杠("/")。在这种情况下,如果节点名是 "my_node",那么私有命名空间就是 "/my_node/~"在这个例子中,所有的话题(如 "/my_node/~cmd_vel""/my_node/~scan")、服务(如 "/my_node/~move_base")、参数(如 "/my_node/~max_speed")都是在 "/my_node/" 命名空间下定义的。这样做既避免了全局名称冲突,又使得节点资源的定义和使用更加清晰和有条理。如果这里不设置那么使用的是初始化的命名空间/my_node/cmd_vel

        在源代码中他会走这个重载构造函数:

NodeHandle::NodeHandle(const std::string& ns, const M_string& remappings)
  : namespace_(this_node::getNamespace())
  , callback_queue_(0)
  , collection_(0)
{
  std::string tilde_resolved_ns;
  if (!ns.empty() && ns[0] == '~')// starts with tilde
    tilde_resolved_ns = names::resolve(ns);
  else
    tilde_resolved_ns = ns;

  construct(tilde_resolved_ns, true);

  initRemappings(remappings);
}

        那么这里getNamespace 就是获取之前初始化的命名空间了,并且初始化回调队列和集合。下面两行就是之前说的规则了,如果输出~就设置为私有命名空间,之后他进行了非常重要的一个步骤。

1.construct

void NodeHandle::construct(const std::string& ns, bool validate_name)
{
  if (!ros::isInitialized())
  {
    ROS_FATAL("You must call ros::init() before creating the first NodeHandle");
    ROS_BREAK();
  }

  collection_ = new NodeHandleBackingCollection;
  unresolved_namespace_ = ns;
  // if callback_queue_ is nonnull, we are in a non-nullary constructor

  if (validate_name)
    namespace_ = resolveName(ns, true);
  else
    {
      namespace_ = resolveName(ns, true, no_validate());
      // FIXME validate namespace_ now
    }
  ok_ = true;

  boost::mutex::scoped_lock lock(g_nh_refcount_mutex);

  if (g_nh_refcount == 0 && !ros::isStarted())
  {
    g_node_started_by_nh = true;
    ros::start();
  }

  ++g_nh_refcount;
}

        首先他先检查了有没有初始化,没初始化直接退出。下面是一些,验证解析命名空间的操作,我们就不具体看了。当一切准备完成,ok_标志设置为true.这时候他会上一个区域锁,保证多线程安全。如果全局引用计数 g_nh_refcount 为 0 且 ROS 尚未启动,调用 ros::start() 启动 ROS。然后增加全局引用计数 g_nh_refcount

        start()是init模块的函数,将设置一些变量,然后把所有管理功能的节点全部启动,由于start函数拥有过多代码,我们仅放出其关键部分。

        

  PollManager::instance()->addPollThreadListener(checkForShutdown);
  XMLRPCManager::instance()->bind("shutdown", shutdownCallback);

  initInternalTimerManager();

  TopicManager::instance()->start();
  ServiceManager::instance()->start();
  ConnectionManager::instance()->start();
  PollManager::instance()->start();
  XMLRPCManager::instance()->start();

这些管理节点非常重要,我们将在后面的章节详细讲解他们的作用。

最后进行重映射设置参数,但是我们没有设置,所以也不会设置参数。

目前:我们看过代码的流程如下:

总结:该代码的主要作用是启动TopicManager,ConnectionManager和PollManager等模块。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值