ROS源代码阅读(3):ROS程序的初始化——this_node::init()

来源:互联网 发布:junit数据库自动回滚 编辑:程序博客网 时间:2024/06/08 13:23

接着上一篇博文ROS程序的初始化——从ros:init()出发,我们接着来分析ROS程序的初始化问题。在此文中我们探讨的是节点环境的初始化——this_node::init()

this_node::init()函数的源代码在./src/ros_comm/roscpp/src/libros/this_node.cpp
中,具体实现如下:

//./src/ros_comm/roscpp/src/libros/this_node.cppvoid init(const std::string& name, const M_string& remappings, uint32_t options){  ThisNode::instance().init(name, remappings, options);}

其中,ThisNode是文件./src/ros_comm/roscpp/src/libros/this_node.cpp中定义的一个类,该类的具体定义如下:

//./src/ros_comm/roscpp/src/libros/this_node.cppclass ThisNode{  std::string name_;  std::string namespace_;  ThisNode() : name_("empty") {}//构造对象时将name_置为空public:  static ThisNode& instance()  {    static ThisNode singleton;//整个ROS程序只有singleton一个拷贝,详见编程中的“单例模式”。    return singleton;  }  const std::string& getName() const  {    return name_;  }  const std::string& getNamespace() const  {    return namespace_;  }  void init(const std::string& name, const M_string& remappings, uint32_t options);};

this_node::init()函数实际上直接调用的是ThisNode类中的void init(const std::string& name, const M_string& remappings, uint32_t options);函数。该函数的定义如下:

//./src/ros_comm/roscpp/src/libros/this_node.cppvoid ThisNode::init(const std::string& name, const M_string& remappings, uint32_t options){  char *ns_env = NULL;#ifdef _MSC_VER  _dupenv_s(&ns_env, NULL, "ROS_NAMESPACE");#else  ns_env = getenv("ROS_NAMESPACE");//获取ROS_NAMESPACE的环境变量名#endif  if (ns_env)//如果环境变量ns_env已被赋值  {    namespace_ = ns_env;//将ROS_NAMESPACE的环境变量名赋值给namespace_    //namespace_是类ThisNode的成员变量#ifdef _MSC_VER    free(ns_env);#endif  }  //检测通过参数传入的节点名不能为空  if (name.empty()) {    throw InvalidNameException("The node name must not be empty");  }  name_ = name; //将传入的节点名赋值给变量name_  //name_是类ThisNode的成员变量  bool disable_anon = false;  //在输入参数remappings查找键为"__name"的项  M_string::const_iterator it = remappings.find("__name");  if (it != remappings.end())//如果找到了  {    name_ = it->second;//将对应项的值赋值给name_    disable_anon = true;  }  //在输入参数remappings查找键为"__ns"的项  it = remappings.find("__ns");//如果找到了  if (it != remappings.end())  {    namespace_ = it->second;//将对应项的值赋值给变量namespace_  }  if (namespace_.empty())//如果namespace_为空  {    namespace_ = "/";  }  namespace_ = (namespace_ == "/")    ? std::string("/")     : ("/" + namespace_)    ;  std::string error;  //对照命名规则检查namespace_,看看是否合法。  if (!names::validate(namespace_, error))  {    std::stringstream ss;    ss << "Namespace [" << namespace_ << "] is invalid: " << error;    throw InvalidNameException(ss.str());  }  // names must be initialized here, because it requires the namespace to already be known so that it can properly resolve names.  // It must be done before we resolve g_name, because otherwise the name will not get remapped.  names::init(remappings);//将remappings映射为g_remappings和g_unresolved_remappings两个变量  //检查name_的合法性  if (name_.find("/") != std::string::npos)  {    throw InvalidNodeNameException(name_, "node names cannot contain /");  }  if (name_.find("~") != std::string::npos)  {    throw InvalidNodeNameException(name_, "node names cannot contain ~");  }  name_ = names::resolve(namespace_, name_);//进行格式化整理  if (options & init_options::AnonymousName && !disable_anon)  {    char buf[200];    snprintf(buf, sizeof(buf), "_%llu", (unsigned long long)WallTime::now().toNSec());    name_ += buf;  }  ros::console::setFixedFilterToken("node", name_);}

从代码可以看出,该函数完成了以下几个功能:

  1. 获取ROS_NAMESPACE的环境变量名;
  2. 给变量name_赋值,并进行一些格式化处理。name_是类ThisNode的成员变量;
  3. 给变量namespace_赋值,并进行一些格式化处理。namespace_是类ThisNode的成员变量;

根据类ThisNode的定义,该类的成员变量就只有name_和namespace_两个变量。因此,该函数可以看做是根据输入参数,对ThisNode的对象进行初始化。

而根据ThisNode::instance()函数,该类在程序中只有唯一的一个对象。即调用this_node::init()的时候完成对该类唯一对象的初始化。

另外,上述函数调用了

  • names::validate(namespace_, error)(上述代码第57行)
  • names::init(remappings)(上述代码第66行)
  • ros::console::setFixedFilterToken(上述代码第86行)

三个函数。为了更好理解代码,我们下面对这三个函数做一简述。

插曲1:names::validate(namespace_, error)

上述代码调用了函数names::validate(namespace_, error),该函数定义在./src/ros_comm/roscpp/src/libros/names.cpp
中。具体实现如下。

bool validate(const std::string& name, std::string& error){  if (name.empty())  {    return true; //如果name为空,则返回true  }  //检查首字符,首字符只能是~ / 或 alpha  char c = name[0];  if (!isalpha(c) && c != '/' && c != '~')  {    std::stringstream ss;    ss << "Character [" << c << "] is not valid as the first character in Graph Resource Name [" << name << "].  Valid characters are a-z, A-Z, / and in some cases ~.";    error = ss.str();    return false;  }  //逐个检查name中的每个字符是否为合法字符  for (size_t i = 1; i < name.size(); ++i)  {    c = name[i];    if (!isValidCharInName(c))    {      std::stringstream ss;      ss << "Character [" << c << "] at element [" << i << "] is not valid in Graph Resource Name [" << name <<"].  Valid characters are a-z, A-Z, 0-9, / and _.";      error = ss.str();      return false;    }  }  return true;}

插曲2:names::init(remappings)

该函数定义在./src/ros_comm/roscpp/src/libros/names.cpp文件中,作用是将remappings映射为g_remappings和g_unresolved_remappings两个变量,其中g_remappings是按照一定规则整理过的remappings,而g_unresolved_remappings是初始传入的remappings参数

//./src/ros_comm/roscpp/src/libros/names.cppvoid init(const M_string& remappings){  //该函数的作用是将remappings映射为g_remappings和g_unresolved_remappings两个变量  M_string::const_iterator it = remappings.begin();  M_string::const_iterator end = remappings.end();  for (; it != end; ++it) //遍历M_string中的每个元素  {    const std::string& left = it->first; //left为键    const std::string& right = it->second; //right为值    //键不为空 且 键的第一个字符不为“_” 且 键不等于ThisNode对象的name_成员变量    if (!left.empty() && left[0] != '_' && left != this_node::getName())    {      std::string resolved_left = resolve(left, false);      std::string resolved_right = resolve(right, false);      g_remappings[resolved_left] = resolved_right;      g_unresolved_remappings[left] = right;    }  }}

其中调用了resolve()函数,该函数也定义在./src/ros_comm/roscpp/src/libros/names.cpp中,执行一些简答的格式化操作,在此不进行详述。

插曲3:ros::console::setFixedFilterToken

该文件的实现在./src/ros_comm/rosconsole/src/rosconsole/rosconsole.cpp文件中,具体代码如下:

void setFixedFilterToken(const std::string& key, const std::string& val){  g_extra_fixed_tokens[key] = val;}

从代码可以看出,该函数主要是对变量g_extra_fixed_tokens进行赋值。

总结

当初始化函数this_node::init()被ros::init()调用时,实际上调用了ROS程序中ThisNode类唯一的实例中的init(name, remappings, options)函数,作用是对该唯一的实例进行初始化。
该函数的具体作用如下:
1. 获取ROS_NAMESPACE的环境变量名;
2. 给变量name_赋值,并进行一些格式化处理。name_是类ThisNode的成员变量;
3. 给namespace_赋值,并进行一些格式化处理。namespace_是类ThisNode的成员变量;
4. 将remappings映射为g_remappings和g_unresolved_remappings两个变量;

是ros::init()函数的五个主要初始化操作中的第三个,后续我们将解析剩余的两个初始化操作file_log::init和param::init。然后,对ros::init()函数做一个综合的分析。

阅读全文
1 0