ROS中的diagnostics模塊

(關於該模塊的參考資料較少,下面整理的僅是個人的理解,有不恰當的地方歡迎指正,更具體的內容參考ROS官網說明)
diagnostics意爲診斷,該模塊旨在從系統中收集各種狀態信息和硬件信息,以進行分析,故障排除和記錄。 工具鏈中包含用於收集,發佈,分析和查看diagnostics數據的工具。主要內容有:

(1) diagnostics的消息

診斷工具鏈圍繞/diagnostics主題構建。 在此主題上,系統通過diagnostic_msgs/DiagnosticArray類型的消息(定義如下)發佈設備的各種狀態,每個狀態都關聯相應的診斷任務。

Header header #for timestamp
DiagnosticStatus[] status # an array of components being reported on

其中DiagnosticStatus具體信息包括:

byte level               # level of operation enumerated above 
string name              # a description of the test/component reporting
string message           # a description of the status
string hardware_id       # 硬件ID
KeyValue[] values        # an array of values associated with the status

level 的值設定:

byte OK=0
byte WARN=1
byte ERROR=2
byte STALE=3

(2)消息發佈 diagnostic_updater

diagnostic_updater是diagnostics模塊的核心,用於:

  • 在設備驅動程序上發佈傳感器數據主題的狀態
  • 報告硬件設備已關閉
  • 當值超出範圍時報告錯誤,例如溫度

API:

  • diagnostic_updater::DiagnosticStatusWrapper

diagnostic_updater::DiagnosticStatusWrapper 類用於處理與diagnostic_msgs/DiagnosticStatus消息相關的操作如設置DiagnosticStatus中各個字段的信息,具體可以參考文章後面的例子,其成員函數有:

void 	add (const std::string &key, const T &val)
 	# Add a key-value pair. 
void 	add (const std::string &key, const bool &b)
 	# For bool, diagnostic value is "True" or "False". 
void 	addf (const std::string &key, const char *format,...)
 	# Add a key-value pair using a format string. 
void   clear ()
 	# Clear the key-value pairs. 
........
  • diagnostic_updater::Updater

diagnostic_updater::Updater 類本質上是一個診斷任務的向量,通過該類可以添加或刪除diagnostic任務、以及確定任務更新頻率等,其用法可以參看文章最後的例子。該類中的每一個任務均通過DiagnosticStatusWrapper維護一個DiagnosticStatus狀態。

  • diagnostic_updater::DiagnosedPublisher

對於一個標準的ros:Publisher,這個類允許將ros:Publisher和topicDiagnosis組合在一起。如下:

   ros::Publisher pub_;
   pub_ = nh_.advertise<sensor_msgs::PointCloud2>("cloud", 100);

   void produce_diagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat);{
                     .........
}

  diagnostic_updater::DiagnosedPublisher<sensor_msgs::PointCloud2>* diagnosticPub_;

  diagnosticPub_ = new diagnostic_updater::DiagnosedPublisher<sensor_msgs::PointCloud2>(pub_, *diagnostics_,
      diagnostic_updater::FrequencyStatusParam(&expected_frequency_, &expected_frequency_, 0.1, 10),
      diagnostic_updater::TimeStampStatusParam(-1, 1.3 * 1.0 / 12.5));

  diagnostics_->setHardwareID("none");  
  diagnostics_->add("device info", this, &produce_diagnostics);

發佈時調用

   sensor_msgs::PointCloud2 msg;
   diagnosticPub_->publish(msg);

(3)diagnostics數據的分析與整合

diagnostic_aggregator包含一個ros節點aggregator_node,主要用於在運行時對診斷消息進行分類和分析。該節點的訂閱發佈消息如下:

  • Subscribed Topics
    /diagnostics (diagnostic_msgs/DiagnosticArray)

  • Published Topics
    /diagnostics_agg(diagnostic_msgs/DiagnosticArray)
    /diagnostics_toplevel_state (diagnostic_msgs/DiagnosticStatus)

如果機器人有電機驅動器、SICK激光掃描儀、Videre攝像機和幾個電池的診斷信息,診斷輸入可能有狀態名稱:

Left Wheel
Right Wheel
SICK Frequency
SICK Temperature
SICK Connection Status
Stereo Left Camera
Stereo Right Camera
Stereo Analysis
Stereo Connection Status
Battery 1 Level
Battery 2 Level
Battery 3 Level
Battery 4 Level
Voltage Status

使用diagnostic aggregator, 我們可以將這些診斷消息以一種易於理解的形式組織起來,以便在robot_monitor中顯示。即將類別的名稱放在狀態名稱的前面,然後用"/"分割。

My Robot/Wheels/Left
My Robot/Wheels/Right
My Robot/SICK/Frequency
My Robot/SICK/Temperature
My Robot/SICK/Connection Status
My Robot/Stereo/Left Camera
My Robot/Stereo/Right Camera
My Robot/Stereo/Analysis
My Robot/Stereo/Connection Status
My Robot/Power System/Battery 1 Level
My Robot/Power System/Battery 2 Level
My Robot/Power System/Battery 3 Level
My Robot/Power System/Battery 4 Level
My Robot/Power System/Voltage Status

使用上述輸入,機器人監視器將數據分類如下:

My Robot
  -- Wheels
    -- Left
    -- Right
  -- SICK
    -- etc ...
  -- Stereo
  -- Power System

(4)diagnostics數據的查看工具

rqt_robot_monitor 用於直觀的查看 diagnostic_aggregator發佈的消息,作用類似於rqt_bag.

(5)diagnostics數據的轉換工具

diagnostic_analysis工具可以將diagnostics產生的數據bag文件轉換爲CSV文件,以便用現成的電子表格軟件繪製或查看。

參考:
http://wiki.ros.org/diagnostics
http://wiki.ros.org/diagnostic_aggregator#Example
http://wiki.ros.org/diagnostic_updater?distro=melodic

 #include <diagnostic_updater/diagnostic_updater.h>
 #include <std_msgs/Bool.h>
 #include <diagnostic_updater/publisher.h>

 // 解決與windows.h中宏 ERROR定義的衝突
 #ifdef ERROR
 #undef ERROR
 #endif

 double time_to_launch;
 ////////////////////////////////////////////任務函數////////////////////////////////////////////////////
 void dummy_diagnostic(diagnostic_updater::DiagnosticStatusWrapper &stat)
  {
        // summary 和summaryf 函數用於設置 level 與message,不同點在於後者可以添加變量的輸出如%f 。
         if (time_to_launch < 10)
          stat.summaryf(diagnostic_msgs::DiagnosticStatus::ERROR, "Buckle your seat belt. Launch in %f seconds!", time_to_launch);
         else
           stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Launch is in a long time. Have a soda.");
         // add and addf 函數用於增加 與該狀態有關的key-value 對, 另外,add可以自動實現字符串的格式轉換。 addf 支持格式化輸出如%f
        stat.add("Diagnostic Name", "dummy");
        stat.add("Time to Launch", time_to_launch);
        stat.addf("Geeky thing to say", "The square of the time to launch %f is %f",   time_to_launch, time_to_launch * time_to_launch);
       }
 /////////////////////////////////////////用類的方法添加任務////////////////////////////////////////////////////  
class DummyClass
      { 
      public:  void  produce_diagnostics(diagnostic_updater::DiagnosticStatusWrapper &stat)
          {
              stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "This is a silly updater.");
              stat.add("Stupidicity of this updater", 1000.);
           } 
       };

      class DummyTask : public diagnostic_updater::DiagnosticTask
       {
       public:
        DummyTask() : DiagnosticTask("Updater Derived from DiagnosticTask")
        {}
         void run(diagnostic_updater::DiagnosticStatusWrapper &stat)
          {
            stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "This is another silly updater.");
            stat.add("Stupidicity of this updater", 2000.);
         }
        };
   ////////////////////////////////////////////任務函數的組合示例////////////////////////////////////////////////////    
      void check_lower_bound(diagnostic_updater::DiagnosticStatusWrapper &stat)
       {
         if (time_to_launch > 5)
          stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Lower-bound OK");
        else
         stat.summary(diagnostic_msgs::DiagnosticStatus::ERROR, "Too low");
          stat.add("Low-Side Margin", time_to_launch - 5);
       }
     
   void check_upper_bound(diagnostic_updater::DiagnosticStatusWrapper &stat)
     {
       if (time_to_launch < 10)
        stat.summary(diagnostic_msgs::DiagnosticStatus::OK, "Upper-bound OK");
        else
         stat.summary(diagnostic_msgs::DiagnosticStatus::WARN, "Too high");
         stat.add("Top-Side Margin", 10 - time_to_launch);
     }
   
     int main(int argc, char **argv)
     {
        ros::init(argc, argv, "diagnostic_updater_example");
        ros::NodeHandle nh;
   
      //Updater類發佈 /diagnostics消息, 並且有一個~diagnostic_period 參數設置發佈頻率
      
      diagnostic_updater::Updater updater;
      
       //關聯ID,以下兩種方法均可設置ID ,不設置時建議填入none,否則會警告
      updater.setHardwareID("none"); 
      updater.setHardwareIDf("Device-%i-%i", 27, 46);
     
      // 將diagnostic任務添加到Updater中。它們將在調用Updater()函數時運行。
      //本質上, updater.add將傳入內容轉換爲 DiagnosticTask。 
      //以下是兩種添加的方法,任務的名稱對應於任務的DiagnosticStatus的名稱
       updater.add("Function updater", dummy_diagnostic);
       DummyClass dc;
       updater.add("Method updater", &dc&DummyClass::produce_diagnostics);
  
    
      //另外, FunctionDiagnosticTask 可以通過函數創建DiagnosticTask,
      //並且可以通過CompositeDiagnosticTask合併任務的輸出,將 CompositeDiagnosticTask添加到Updater。
      //運行時總體名稱將是子任務的名稱, i.e., "Bound check". 
      
   diagnostic_updater::FunctionDiagnosticTask lower("Lower-bound check",
        boost::bind(&check_lower_bound, _1));
     diagnostic_updater::FunctionDiagnosticTask upper("Upper-bound check",
         boost::bind(&check_upper_bound, _1));
    
      diagnostic_updater::CompositeDiagnosticTask bounds("Bound check");
        bounds.addTask(&lower);
        bounds.addTask(&upper);
       updater.add(bounds);

    // 如果節點/處於特殊狀態,則可以廣播消息到所有的 DiagnosticStatus
         updater.broadcast(0, "Doing important initialization stuff.");
   
       ros::Publisher pub1 = nh.advertise<std_msgs::Bool>("topic1", 1);
    
       ros::Duration(2).sleep(); // 循環時間控制, 通過主函數中的ros::Duration(0.1).sleep()實現;
   
      // FrequencyStatus和TimestampStatus 是分別監視事件頻率和時間戳的Diagnostic任務。
      // 對於沒有header的消息,可以通過 HeaderlessTopicDiagnostic 處理FrequencyStatus (不能處理TimestampStatus)
      //或TopicDiagnostic可以處理FrequencyStatus 和TimestampStatus任務

    double min_freq = 0.5;
    double max_freq = 2;
    diagnostic_updater::HeaderlessTopicDiagnostic pub1_freq("topic1", updater, 
    diagnostic_updater::FrequencyStatusParam(&min_freq, &max_freq, 0.1, 10)); 
      // 注意TopicDiagnostic, HeaderlessDiagnosedPublisher,HeaderlessDiagnosedPublisher 以及 DiagnosedPublisher 
      //都派生自CompositeDiagnosticTask, 因此你可以通過addTask添加自己的任務。
       pub1_freq.addTask(&lower); // **(This wouldn't work if lower was stateful).**    
      // 每次更新pub1_freq時,lower也將得到更新,其輸出將與pub1_freq的輸出合併。
    
     // 如果知道節點狀態改變,可以強制立即更新
      updater.force_update();       
     
     //刪除任務
      if (!updater.removeByName("Bound check"))
     ROS_ERROR("The Bound check task was not found when trying to remove it.");
  
     while (nh.ok())
       {
        std_msgs::Bool msg;
        ros::Duration(0.1).sleep();
    
      // 對pub1的調用必須伴隨對pub1_freq的調用,以保持最新的統計數據。
        msg.data = false;
        pub1.publish(msg);
        pub1_freq.tick();
    
      //可以在程序空閒時隨時調用updater。函數本身將限制更新速度。
         updater.update();
      }
      return 0; 
       }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章