ros::NodeHandle
构造函数会调用ros::start()
, 最后一个ros::NodeHandle
销毁时,将调用ros::shutdown()
。如果想自定义节点生存期,可以用这两个函数。
检查关闭节点的两种方法是ros::ok()
和ros::isShuttingDown()
NodeHandle源码
NodeHandle类是非常重要的一个类,可以说是ROS程序的核心,发布、订阅就是它完成的,分析一下它的源码。
NodeHandle类中有几个私有成员变量:1
2
3
4
5std::string namespace_;
// 回调接口指针,主要用于advertise, subscribe,advertiseService和createTimer函数
CallbackQueueInterface* callback_queue_;
NodeHandleBackingCollection* collection_;
构造函数
NodeHandle的构造函数调用层级如下:
源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15NodeHandle::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和ros::names::resolve
首先看初始化时的getNamespace
,其原型是1
2const std::string & ros::this_node::getNamespace()
{ return namespace_; } //Returns the namespace of the current node.
返回当前节点的namespace
。比如节点初始化和启动是这样实现的:1
2ros::init(argc,argv,"locateTag");
ros::NodeHandle n("~node");
那么getNamespace()
的结果就是/locateTag/node
,但是节点名称有命名规范,否则编译正确但运行会报错:1
2
3terminate called after throwing an instance of 'ros::InvalidNameException'
what(): Character [-] at element [6] is not valid in Graph Resource Name [health---Status]. Valid characters are a-z, A-Z, 0-9, / and _.
已放弃 (核心已转储)
接下来的names::resolve
是对参数ns
进行处理。如果ns
不为空而且以~
开头,使用resolve
函数解析,还以上面的例子,解析结果仍然是/locateTag/node
,否则直接赋给tilde_resolved_ns
。resolve
函数返回的叫做Graph Resource Names,这是ROS中的继承性命名系统,命名必须符合下面特征:
- 第一个字符只能是a-z|A-Z或者~ /
- 之后的字符是0-9|a-z|A-Z,_ /
- 基本名称不能有 / ~
上面说了这么多,其实在使用时,一般ns
都是空,此时的getNamespace()
和resolveName
都返回/
construct函数
1 | void NodeHandle::construct(const std::string& ns, bool validate_name) |
若此时没有调用ros::init
,报严重错误然后终止,所以 ros::init()必须在NodeHandle创建之前。 有时先调用了ros::init
也会出这个错误,很可能是在NodeHandle
之前先用了ROS的东西
然后创建NodeHandleBackingCollection
指针,下面是一些赋值,g_nh_refcount
为初始为0的全局变量,即全局引用计数,如果此时没有启动ros,将调用ros::start()
,然后将g_nh_refcount
加1
ros::start()
太复杂了,它是ROS架构的核心,在另一篇文章分析
initRemappings
比较简单,而且不重要,就不分析了
析构函数
类的析构函数只调用了destruct()
:1
2
3
4
5
6
7
8
9
10
11
12
13void NodeHandle::destruct()
{
delete collection_;
boost::mutex::scoped_lock lock(g_nh_refcount_mutex);
--g_nh_refcount;
if (g_nh_refcount == 0 && g_node_started_by_nh)
{
ros::shutdown();
}
}
析构函数更简单,释放collection_
,引用计数减1,如果变成了0,就关闭ros