Register internal metrics and provide a public interface. (#917)

[RFC 24](https://github.com/googlecartographer/rfcs/blob/master/text/0024-monitoring-ros.md)

Public API:
  - adds `cartographer_ros::metrics::FamilyFactory`
  - compatible with `::cartographer::metrics::RegisterAllMetrics`

Public RPC interface:
  - adds the ROS service `/read_metrics`
  - response contains the latest values of all available metric families
master
Michael Grupp 2018-07-04 17:38:17 +02:00 committed by gaschler
parent bb4aec395f
commit be7ecf2e0d
9 changed files with 204 additions and 6 deletions

View File

@ -15,12 +15,16 @@
*/ */
#include "cartographer/cloud/client/map_builder_stub.h" #include "cartographer/cloud/client/map_builder_stub.h"
#include "cartographer/common/make_unique.h"
#include "cartographer_ros/node.h" #include "cartographer_ros/node.h"
#include "cartographer_ros/node_options.h" #include "cartographer_ros/node_options.h"
#include "cartographer_ros/ros_log_sink.h" #include "cartographer_ros/ros_log_sink.h"
#include "gflags/gflags.h" #include "gflags/gflags.h"
#include "tf2_ros/transform_listener.h" #include "tf2_ros/transform_listener.h"
DEFINE_bool(collect_metrics, false,
"Activates the collection of runtime metrics. If activated, the "
"metrics can be accessed via a ROS service.");
DEFINE_string(configuration_directory, "", DEFINE_string(configuration_directory, "",
"First directory in which configuration files are searched, " "First directory in which configuration files are searched, "
"second is always the Cartographer installation to allow " "second is always the Cartographer installation to allow "
@ -56,7 +60,8 @@ void Run() {
auto map_builder = auto map_builder =
cartographer::common::make_unique<::cartographer::cloud::MapBuilderStub>( cartographer::common::make_unique<::cartographer::cloud::MapBuilderStub>(
FLAGS_server_address); FLAGS_server_address);
Node node(node_options, std::move(map_builder), &tf_buffer); Node node(node_options, std::move(map_builder), &tf_buffer,
FLAGS_collect_metrics);
if (!FLAGS_load_state_filename.empty()) { if (!FLAGS_load_state_filename.empty()) {
node.LoadState(FLAGS_load_state_filename, true /* load_frozen_state */); node.LoadState(FLAGS_load_state_filename, true /* load_frozen_state */);

View File

@ -0,0 +1,71 @@
/*
* Copyright 2018 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cartographer_ros/metrics/family_factory.h"
#include "cartographer/common/make_unique.h"
namespace cartographer_ros {
namespace metrics {
using BucketBoundaries = ::cartographer::metrics::Histogram::BucketBoundaries;
::cartographer::metrics::Family<::cartographer::metrics::Counter>*
FamilyFactory::NewCounterFamily(const std::string& name,
const std::string& description) {
auto wrapper =
::cartographer::common::make_unique<CounterFamily>(name, description);
auto* ptr = wrapper.get();
counter_families_.emplace_back(std::move(wrapper));
return ptr;
}
::cartographer::metrics::Family<::cartographer::metrics::Gauge>*
FamilyFactory::NewGaugeFamily(const std::string& name,
const std::string& description) {
auto wrapper =
::cartographer::common::make_unique<GaugeFamily>(name, description);
auto* ptr = wrapper.get();
gauge_families_.emplace_back(std::move(wrapper));
return ptr;
}
::cartographer::metrics::Family<::cartographer::metrics::Histogram>*
FamilyFactory::NewHistogramFamily(const std::string& name,
const std::string& description,
const BucketBoundaries& boundaries) {
auto wrapper = ::cartographer::common::make_unique<HistogramFamily>(
name, description, boundaries);
auto* ptr = wrapper.get();
histogram_families_.emplace_back(std::move(wrapper));
return ptr;
}
void FamilyFactory::ReadMetrics(
::cartographer_ros_msgs::ReadMetrics::Response* response) const {
for (const auto& counter_family : counter_families_) {
response->metric_families.push_back(counter_family->ToRosMessage());
}
for (const auto& gauge_family : gauge_families_) {
response->metric_families.push_back(gauge_family->ToRosMessage());
}
for (const auto& histogram_family : histogram_families_) {
response->metric_families.push_back(histogram_family->ToRosMessage());
}
}
} // namespace metrics
} // namespace cartographer_ros

View File

@ -0,0 +1,61 @@
/*
* Copyright 2018 The Cartographer Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CARTOGRAPHER_ROS_METRICS_FAMILY_FACTORY_H
#define CARTOGRAPHER_ROS_METRICS_FAMILY_FACTORY_H
#include <memory>
#include <string>
#include "cartographer/metrics/family_factory.h"
#include "cartographer_ros/metrics/internal/counter.h"
#include "cartographer_ros/metrics/internal/family.h"
#include "cartographer_ros/metrics/internal/gauge.h"
#include "cartographer_ros/metrics/internal/histogram.h"
#include "cartographer_ros_msgs/ReadMetrics.h"
namespace cartographer_ros {
namespace metrics {
// Realizes the factory / registry interface for the metrics in libcartographer
// and provides a wrapper to collect ROS messages from the metrics it owns.
class FamilyFactory : public ::cartographer::metrics::FamilyFactory {
public:
::cartographer::metrics::Family<::cartographer::metrics::Counter>*
NewCounterFamily(const std::string& name,
const std::string& description) override;
::cartographer::metrics::Family<::cartographer::metrics::Gauge>*
NewGaugeFamily(const std::string& name,
const std::string& description) override;
::cartographer::metrics::Family<::cartographer::metrics::Histogram>*
NewHistogramFamily(const std::string& name, const std::string& description,
const ::cartographer::metrics::Histogram::BucketBoundaries&
boundaries) override;
void ReadMetrics(
::cartographer_ros_msgs::ReadMetrics::Response* response) const;
private:
std::vector<std::unique_ptr<CounterFamily>> counter_families_;
std::vector<std::unique_ptr<GaugeFamily>> gauge_families_;
std::vector<std::unique_ptr<HistogramFamily>> histogram_families_;
};
} // namespace metrics
} // namespace cartographer_ros
#endif // CARTOGRAPHER_ROS_METRICS_FAMILY_FACTORY_H

View File

@ -28,9 +28,11 @@
#include "cartographer/common/time.h" #include "cartographer/common/time.h"
#include "cartographer/mapping/pose_graph_interface.h" #include "cartographer/mapping/pose_graph_interface.h"
#include "cartographer/mapping/proto/submap_visualization.pb.h" #include "cartographer/mapping/proto/submap_visualization.pb.h"
#include "cartographer/metrics/register.h"
#include "cartographer/sensor/point_cloud.h" #include "cartographer/sensor/point_cloud.h"
#include "cartographer/transform/rigid_transform.h" #include "cartographer/transform/rigid_transform.h"
#include "cartographer/transform/transform.h" #include "cartographer/transform/transform.h"
#include "cartographer_ros/metrics/family_factory.h"
#include "cartographer_ros/msg_conversion.h" #include "cartographer_ros/msg_conversion.h"
#include "cartographer_ros/sensor_bridge.h" #include "cartographer_ros/sensor_bridge.h"
#include "cartographer_ros/tf_bridge.h" #include "cartographer_ros/tf_bridge.h"
@ -88,10 +90,15 @@ using TrajectoryState =
Node::Node( Node::Node(
const NodeOptions& node_options, const NodeOptions& node_options,
std::unique_ptr<cartographer::mapping::MapBuilderInterface> map_builder, std::unique_ptr<cartographer::mapping::MapBuilderInterface> map_builder,
tf2_ros::Buffer* const tf_buffer) tf2_ros::Buffer* const tf_buffer, const bool collect_metrics)
: node_options_(node_options), : node_options_(node_options),
map_builder_bridge_(node_options_, std::move(map_builder), tf_buffer) { map_builder_bridge_(node_options_, std::move(map_builder), tf_buffer) {
carto::common::MutexLocker lock(&mutex_); carto::common::MutexLocker lock(&mutex_);
if (collect_metrics) {
metrics_registry_ = carto::common::make_unique<metrics::FamilyFactory>();
carto::metrics::RegisterAllMetrics(metrics_registry_.get());
}
submap_list_publisher_ = submap_list_publisher_ =
node_handle_.advertise<::cartographer_ros_msgs::SubmapList>( node_handle_.advertise<::cartographer_ros_msgs::SubmapList>(
kSubmapListTopic, kLatestOnlyPublisherQueueSize); kSubmapListTopic, kLatestOnlyPublisherQueueSize);
@ -114,6 +121,8 @@ Node::Node(
kWriteStateServiceName, &Node::HandleWriteState, this)); kWriteStateServiceName, &Node::HandleWriteState, this));
service_servers_.push_back(node_handle_.advertiseService( service_servers_.push_back(node_handle_.advertiseService(
kGetTrajectoryStatesServiceName, &Node::HandleGetTrajectoryStates, this)); kGetTrajectoryStatesServiceName, &Node::HandleGetTrajectoryStates, this));
service_servers_.push_back(node_handle_.advertiseService(
kReadMetricsServiceName, &Node::HandleReadMetrics, this));
scan_matched_point_cloud_publisher_ = scan_matched_point_cloud_publisher_ =
node_handle_.advertise<sensor_msgs::PointCloud2>( node_handle_.advertise<sensor_msgs::PointCloud2>(
@ -620,6 +629,22 @@ bool Node::HandleWriteState(
return true; return true;
} }
bool Node::HandleReadMetrics(
::cartographer_ros_msgs::ReadMetrics::Request& request,
::cartographer_ros_msgs::ReadMetrics::Response& response) {
carto::common::MutexLocker lock(&mutex_);
response.timestamp = ros::Time::now();
if (!metrics_registry_) {
response.status.code = cartographer_ros_msgs::StatusCode::UNAVAILABLE;
response.status.message = "Collection of runtime metrics is not activated.";
return true;
}
metrics_registry_->ReadMetrics(&response);
response.status.code = cartographer_ros_msgs::StatusCode::OK;
response.status.message = "Successfully read metrics.";
return true;
}
void Node::FinishAllTrajectories() { void Node::FinishAllTrajectories() {
carto::common::MutexLocker lock(&mutex_); carto::common::MutexLocker lock(&mutex_);
for (const auto& entry : map_builder_bridge_.GetTrajectoryStates()) { for (const auto& entry : map_builder_bridge_.GetTrajectoryStates()) {

View File

@ -29,11 +29,13 @@
#include "cartographer/mapping/map_builder_interface.h" #include "cartographer/mapping/map_builder_interface.h"
#include "cartographer/mapping/pose_extrapolator.h" #include "cartographer/mapping/pose_extrapolator.h"
#include "cartographer_ros/map_builder_bridge.h" #include "cartographer_ros/map_builder_bridge.h"
#include "cartographer_ros/metrics/family_factory.h"
#include "cartographer_ros/node_constants.h" #include "cartographer_ros/node_constants.h"
#include "cartographer_ros/node_options.h" #include "cartographer_ros/node_options.h"
#include "cartographer_ros/trajectory_options.h" #include "cartographer_ros/trajectory_options.h"
#include "cartographer_ros_msgs/FinishTrajectory.h" #include "cartographer_ros_msgs/FinishTrajectory.h"
#include "cartographer_ros_msgs/GetTrajectoryStates.h" #include "cartographer_ros_msgs/GetTrajectoryStates.h"
#include "cartographer_ros_msgs/ReadMetrics.h"
#include "cartographer_ros_msgs/SensorTopics.h" #include "cartographer_ros_msgs/SensorTopics.h"
#include "cartographer_ros_msgs/StartTrajectory.h" #include "cartographer_ros_msgs/StartTrajectory.h"
#include "cartographer_ros_msgs/StatusResponse.h" #include "cartographer_ros_msgs/StatusResponse.h"
@ -58,7 +60,7 @@ class Node {
public: public:
Node(const NodeOptions& node_options, Node(const NodeOptions& node_options,
std::unique_ptr<cartographer::mapping::MapBuilderInterface> map_builder, std::unique_ptr<cartographer::mapping::MapBuilderInterface> map_builder,
tf2_ros::Buffer* tf_buffer); tf2_ros::Buffer* tf_buffer, bool collect_metrics);
~Node(); ~Node();
Node(const Node&) = delete; Node(const Node&) = delete;
@ -142,6 +144,9 @@ class Node {
bool HandleGetTrajectoryStates( bool HandleGetTrajectoryStates(
::cartographer_ros_msgs::GetTrajectoryStates::Request& request, ::cartographer_ros_msgs::GetTrajectoryStates::Request& request,
::cartographer_ros_msgs::GetTrajectoryStates::Response& response); ::cartographer_ros_msgs::GetTrajectoryStates::Response& response);
bool HandleReadMetrics(
cartographer_ros_msgs::ReadMetrics::Request& request,
cartographer_ros_msgs::ReadMetrics::Response& response);
// Returns the set of SensorIds expected for a trajectory. // Returns the set of SensorIds expected for a trajectory.
// 'SensorId::id' is the expected ROS topic name. // 'SensorId::id' is the expected ROS topic name.
@ -212,11 +217,14 @@ class Node {
// We have to keep the timer handles of ::ros::WallTimers around, otherwise // We have to keep the timer handles of ::ros::WallTimers around, otherwise
// they do not fire. // they do not fire.
std::vector<::ros::WallTimer> wall_timers_; std::vector<::ros::WallTimer> wall_timers_;
// The timer for publishing local trajectory data (i.e. pose transforms and // The timer for publishing local trajectory data (i.e. pose transforms and
// range data point clouds) is a regular timer which is not triggered when // range data point clouds) is a regular timer which is not triggered when
// simulation time is standing still. This prevents overflowing the transform // simulation time is standing still. This prevents overflowing the transform
// listener buffer by publishing the same transforms over and over again. // listener buffer by publishing the same transforms over and over again.
::ros::Timer publish_local_trajectory_data_timer_; ::ros::Timer publish_local_trajectory_data_timer_;
std::unique_ptr<cartographer_ros::metrics::FamilyFactory> metrics_registry_;
}; };
} // namespace cartographer_ros } // namespace cartographer_ros

View File

@ -38,6 +38,7 @@ constexpr char kSubmapQueryServiceName[] = "submap_query";
constexpr char kStartTrajectoryServiceName[] = "start_trajectory"; constexpr char kStartTrajectoryServiceName[] = "start_trajectory";
constexpr char kWriteStateServiceName[] = "write_state"; constexpr char kWriteStateServiceName[] = "write_state";
constexpr char kGetTrajectoryStatesServiceName[] = "get_trajectory_states"; constexpr char kGetTrajectoryStatesServiceName[] = "get_trajectory_states";
constexpr char kReadMetricsServiceName[] = "read_metrics";
constexpr char kTrajectoryNodeListTopic[] = "trajectory_node_list"; constexpr char kTrajectoryNodeListTopic[] = "trajectory_node_list";
constexpr char kLandmarkPosesListTopic[] = "landmark_poses_list"; constexpr char kLandmarkPosesListTopic[] = "landmark_poses_list";
constexpr char kConstraintListTopic[] = "constraint_list"; constexpr char kConstraintListTopic[] = "constraint_list";

View File

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include "cartographer/common/make_unique.h"
#include "cartographer/mapping/map_builder.h" #include "cartographer/mapping/map_builder.h"
#include "cartographer_ros/node.h" #include "cartographer_ros/node.h"
#include "cartographer_ros/node_options.h" #include "cartographer_ros/node_options.h"
@ -21,6 +22,9 @@
#include "gflags/gflags.h" #include "gflags/gflags.h"
#include "tf2_ros/transform_listener.h" #include "tf2_ros/transform_listener.h"
DEFINE_bool(collect_metrics, false,
"Activates the collection of runtime metrics. If activated, the "
"metrics can be accessed via a ROS service.");
DEFINE_string(configuration_directory, "", DEFINE_string(configuration_directory, "",
"First directory in which configuration files are searched, " "First directory in which configuration files are searched, "
"second is always the Cartographer installation to allow " "second is always the Cartographer installation to allow "
@ -53,9 +57,10 @@ void Run() {
LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename); LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);
auto map_builder = auto map_builder =
cartographer::common::make_unique<cartographer::mapping::MapBuilder>( ::cartographer::common::make_unique<cartographer::mapping::MapBuilder>(
node_options.map_builder_options); node_options.map_builder_options);
Node node(node_options, std::move(map_builder), &tf_buffer); Node node(node_options, std::move(map_builder), &tf_buffer,
FLAGS_collect_metrics);
if (!FLAGS_load_state_filename.empty()) { if (!FLAGS_load_state_filename.empty()) {
node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state); node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
} }

View File

@ -32,6 +32,9 @@
#include "tf2_ros/static_transform_broadcaster.h" #include "tf2_ros/static_transform_broadcaster.h"
#include "urdf/model.h" #include "urdf/model.h"
DEFINE_bool(collect_metrics, false,
"Activates the collection of runtime metrics. If activated, the "
"metrics can be accessed via a ROS service.");
DEFINE_string(configuration_directory, "", DEFINE_string(configuration_directory, "",
"First directory in which configuration files are searched, " "First directory in which configuration files are searched, "
"second is always the Cartographer installation to allow " "second is always the Cartographer installation to allow "
@ -130,7 +133,8 @@ void RunOfflineNode(const MapBuilderFactory& map_builder_factory) {
tf_buffer.setUsingDedicatedThread(true); tf_buffer.setUsingDedicatedThread(true);
Node node(node_options, std::move(map_builder), &tf_buffer); Node node(node_options, std::move(map_builder), &tf_buffer,
FLAGS_collect_metrics);
if (!FLAGS_load_state_filename.empty()) { if (!FLAGS_load_state_filename.empty()) {
node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state); node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
} }

View File

@ -0,0 +1,18 @@
# Copyright 2018 The Cartographer Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
---
cartographer_ros_msgs/StatusResponse status
cartographer_ros_msgs/MetricFamily[] metric_families
time timestamp