ROSソース読解(2):ROSプログラムの初期化--ros:init()から

19864 ワード

ros::init()から


次のブログのROSソースコードを読みます(1):切り込み点を探して、ros::init()関数から、ROSソースコードの探索を始めました.ros::init()関数の宣言は、ROSコードの./src/ros_comm/roscpp/include/ros/init.hファイルにあります.リロードは次の3つの形式です.
//./src/ros_comm/roscpp/include/ros/init.h
void init(int &argc, char **argv, const std::string& name, uint32_t options = 0);
void init(const M_string& remappings, const std::string& name, uint32_t options = 0);
void init(const VP_string& remapping_args, const std::string& name, uint32_t options = 0);

この関数の具体的な実装は./src/ros_comm/roscpp/src/libros/init.cppファイルにある.まず、最も簡単な形式の実装を選択して分析します.
//./src/ros_comm/roscpp/src/libros/init.cpp
void init(const M_string& remappings, const std::string& name, uint32_t options)
{
  if (!g_atexit_registered)
  {
    g_atexit_registered = true;
    atexit(atexitCallback);
  }
  if (!g_global_queue)
  {
    g_global_queue.reset(new CallbackQueue);
  }
  // , :
  if (!g_initialized)
  {
    g_init_options = options;
    g_ok = true;

    ROSCONSOLE_AUTOINIT; // console.h :Initializes the rosconsole library. 
    // Disable SIGPIPE
#ifndef WIN32
    signal(SIGPIPE, SIG_IGN);
#endif
    network::init(remappings);// , network.cpp 
    master::init(remappings); // master
    // names:: namespace is initialized by this_node
    this_node::init(name, remappings, options); // 
    file_log::init(remappings);
    param::init(remappings);
    g_initialized = true;// 
  }
}

ROSプログラムの初期化関数ros::init()は主に以下のいくつかの関数を呼び出して初期化を完了していることがわかります.
  • network::init(remappings);
  • master::init(remappings);
  • this_node::init(name, remappings, options);
  • file_log::init(remappings);
  • param::init(remappings); ここで、最初の2つの関数(network::initとmaster::init)は比較的簡単で、このドキュメントではまずこの2つの関数について説明します.

  • 1. network::init()


    名前から見ると、この関数はネットワークの初期化に使用されます.実は今./src/ros_comm/roscpp/src/libros/network.cpp中です.実装コードは次のとおりです.
    //./src/ros_comm/roscpp/src/libros/network.cpp
    void init(const M_string& remappings) // init.cpp 
    {
      // 1:
      M_string::const_iterator it = remappings.find("__hostname");
      if (it != remappings.end())
      {
        g_host = it->second;
      }
      else
      {
        it = remappings.find("__ip");
        if (it != remappings.end())
        {
          g_host = it->second;
        }
      }
      // 2
      it = remappings.find("__tcpros_server_port");
      if (it != remappings.end())
      {
        try
        {
          g_tcpros_server_port = boost::lexical_cast<uint16_t>(it->second);
        }
        catch (boost::bad_lexical_cast&)
        {
          throw ros::InvalidPortException("__tcpros_server_port [" + it->second + "] was not specified as a number within the 0-65535 range");
        }
      }
      // 3
      if (g_host.empty())
      {
        g_host = determineHost();
      }
    }
    
    } // namespace network
    } // namespace ros

    上記のコードによれば,この関数を正式に解析する前に,入力パラメータのデータ型がM_であることに注目する.stringの引用ですが、まずM_を見てみましょう.stringはどのようなデータ型ですか.

    エピソード1:データ型M_についてstring


    この関数の入力パラメータはconst M_string& remappingsです.データ型M_stringの定義./src/roscpp_core/cpp_common/include/ros/datatypes.hにおいて、このファイルは、ROS実装に用いられるいくつかのデータ型を定義する.コードは次のとおりです.
    //./src/roscpp_core/cpp_common/include/ros/datatypes.h
    namespace ros {
    typedef std::vector<std::pair<std::string, std::string> > VP_string;
    typedef std::vector<std::string> V_string;
    typedef std::set<std::string> S_string;
    typedef std::map<std::string, std::string> M_string;
    typedef std::pair<std::string, std::string> StringPair;
    typedef boost::shared_ptr M_stringPtr;
    }

    見えますM_stringがキー/値のデータ型はstd::stringのmapコンテナです.ここでmapコンテナに関する内容については、私のブログstd:mapと反復器を参照してください.
    データ型M_を解決stringの問題は、コードをよく見ています.コードの最初の機能モジュールは以下の通りであり、このモジュールは主に変数g_に対してhostは、コードとコメントを参照して値を割り当てます.
    // remappings “__hostname” 
    M_string::const_iterator it = remappings.find("__hostname");
       // , “g_host”
      if (it != remappings.end())
      {
        g_host = it->second;
      }
       // , :
      else
      {
        // “__ip” 
        it = remappings.find("__ip");
        // , “g_host”
        if (it != remappings.end())
        {
          g_host = it->second;
        }
      }

    ただし、g_hostはstd::stringタイプの変数で、network.cppファイルの最初から定義されています.
    std::string g_host;

    この関数の2番目の機能モジュールと対応するコメントは次のとおりです.
    // "__tcpros_server_port" 
    it = remappings.find("__tcpros_server_port");
      // 
      if (it != remappings.end())
      {
        try// (std::string) uint16_t 
        {
          g_tcpros_server_port = boost::lexical_cast<uint16_t>(it->second);
        }
        catch (boost::bad_lexical_cast&)// 
        {
          throw ros::InvalidPortException("__tcpros_server_port [" + it->second + "] was not specified as a number within the 0-65535 range");
        }
      }

    C++のタイプ変換器boost::lexical_cast.

    エピソード2:タイプ変換器boost::lexical_についてcast


    boost::lexical_castは、数値間の変換(conversion)のための包括的なスキームを提供し、例えば、文字列「712」を整数712に変換し、コードは以下の通りである.
    string s = "712";  
    int a = lexical_cast<int>(s); 

    この方法の利点は、変換に予期せぬ事故が発生した場合、lexical_キャストはbadを投げ出すlexical_cast異常、プログラムでスナップできます.
    以上のモジュールはboost::lexical_castは、ヘッダファイルboost/lexical_cast.hppに含まれる変換を行い、bad_をキャプチャするlexical_cast異常.上記コードから分かるように、このモジュールの役割は変数g_であるtcpros_server_portはnetworkに定義する値を付与する.cppの先頭で、デフォルト値は0です.
    uint16_t g_tcpros_server_port = 0;

    3番目のモジュールのコードは次のとおりです.
    // g_host , determineHost() 。
    if (g_host.empty())
      {
        g_host = determineHost();
      }

    このコードによって呼び出されたdetermineHost()も./src/ros_comm/roscpp/src/libros/network.cppファイルに定義される.この関数については、ここでは詳しく説明しません.このコードの役割は、g_hostが空の場合(モジュール1では一時的に値が割り当てられていない)、determineHostを呼び出して値を割り当てます.
    以上、network:init()関数は主にg_を完成した.hostとg_tcpros_server_port 2つの変数の付与.

    2. master::init()


    master::init()関数は./src/ros_comm/roscpp/src/libros/master.cppファイルに定義されます.具体的な実装コードは以下の通りである.入力パラメータタイプM_についてstring、上記で紹介しました.このコードの実装の詳細についてnetwork::initは似ていて、入力パラメータに基づいていくつかの変数に値を付与して、私は直接コード注釈を添付して、モジュールを分けて詳しく説明しません.
    void init(const M_string& remappings)
    {
      // , remappings "__master" 。
      M_string::const_iterator it = remappings.find("__master");
      // , g_uri
      if (it != remappings.end())
      {
        g_uri = it->second;
      }
      // g_uri ( )
      if (g_uri.empty())
      {
        char *master_uri_env = NULL;
        // master_uri_env 
        #ifdef _MSC_VER
          _dupenv_s(&master_uri_env, NULL, "ROS_MASTER_URI");
        #else
          master_uri_env = getenv("ROS_MASTER_URI");
        #endif
    
        if (!master_uri_env)// master_uri_env 
        {
          ROS_FATAL( "ROS_MASTER_URI is not defined in the environment. Either " \
                     "type the following or (preferrably) add this to your " \
                     "~/.bashrc file in order set up your " \
                     "local machine as a ROS master:

    "
    \ "export ROS_MASTER_URI=http://localhost:11311

    "
    \ "then, type 'roscore' in another shell to actually launch " \ "the master program."); ROS_BREAK(); } g_uri = master_uri_env; #ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/ms175774(v=vs.80).aspx free(master_uri_env); #endif }//if(g_uri.empty()) // g_uri , g_uri g_host, g_port。 if (!network::splitURI(g_uri, g_host, g_port)) { ROS_FATAL( "Couldn't parse the master URI [%s] into a host:port pair.", g_uri.c_str()); ROS_BREAK();// } }

    ここで、2つの未知の関数ROS_について説明するBREAK()およびnetwork::splitURI(g_uri,g_host,g_port).

    エピソード2.1 ROS_BREAK()関数


    ROS_BREAK()関数の定義は./src/ros_comm/rosconsole/include/ros/assert.h
    ファイルにあり、具体的には以下の通りである.
    #define ROS_BREAK() \
      do { \
        ROS_FATAL("BREAKPOINT HIT
    \tfile = %s
    \tline=%d
    "
    , __FILE__, __LINE__); \ ROS_ISSUE_BREAK() \ } while (0)

    この関数は、ROSプログラムの実行を中断し、どのファイルのどの行で中断が発生したかを表示するために使用されます.この関数の詳細は、ここでは詳しく説明しません.

    エピソード2.2 network::splitURI()


    この関数は./src/ros_comm/roscpp/src/libros/network.cppファイルに定義されており、具体的には以下のように実現されています.私は同時に注釈を添付します.
    bool splitURI(const std::string& uri, std::string& host, uint32_t& port)
    {
      //  uri , host
      if (uri.substr(0, 7) == std::string("http://"))
        host = uri.substr(7);
      else if (uri.substr(0, 9) == std::string("rosrpc://"))
        host = uri.substr(9);
      //  uri (: ) , port_str
      std::string::size_type colon_pos = host.find_first_of(":");
      if (colon_pos == std::string::npos)
        return false; // uri , 
      std::string port_str = host.substr(colon_pos+1);
      // “/”
      std::string::size_type slash_pos = port_str.find_first_of("/");
      if (slash_pos != std::string::npos)
        port_str = port_str.erase(slash_pos);
      port = atoi(port_str.c_str());// 
      host = host.erase(colon_pos);// host “:”
      return true;
    }

    すなわち,uriを解析し,プロトコル部分を除いてhostに割り当て,ポート番号をportに割り当てる役割を果たす.
    以上より、master::init()の役割は、パラメータremappingsから変数g_を抽出することであるuriの値をg_uriはg_に解析するhostとg_port.

    まとめ


    本稿では,ROSの初期化関数ros::init()を簡潔に解析した.この関数は主に以下の関数を呼び出して初期化を完了していることがわかりました.
  • network::init(remappings);
  • master::init(remappings);
  • this_node::init(name, remappings, options);
  • file_log::init(remappings);
  • param::init(remappings);

  • 次に,その中の最初の2つの関数の簡単な解析を行った.

    次のようになります。

  • 残りの3つの初期化関数の解析を続行します:this_node::init,file_log::initとparam::init
  • 関数network::init()を解析するときに尻尾、すなわちdetermineHost()関数を残した.その後determineHost()を深く分析します
  • 上記の分析を完了した後、ros:init()が完了した仕事を全体的な観点から完全にまとめた.