Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <algorithm>
00018 #include <fstream>
00019 #include <iostream>
00020 #include <string>
00021 #include <vector>
00022
00023 #include "cartographer/common/math.h"
00024 #include "cartographer/io/proto_stream_deserializer.h"
00025 #include "cartographer/mapping/proto/pose_graph.pb.h"
00026 #include "cartographer/transform/transform_interpolation_buffer.h"
00027 #include "cartographer_ros/msg_conversion.h"
00028 #include "cartographer_ros/time_conversion.h"
00029 #include "gflags/gflags.h"
00030 #include "glog/logging.h"
00031 #include "ros/ros.h"
00032 #include "ros/time.h"
00033 #include "rosbag/bag.h"
00034 #include "rosbag/view.h"
00035 #include "tf2_eigen/tf2_eigen.h"
00036 #include "tf2_msgs/TFMessage.h"
00037
00038 DEFINE_string(bag_filename, "",
00039 "Bag file containing TF messages of the trajectory that will be "
00040 "compared against the trajectory in the .pbstream file.");
00041 DEFINE_string(tf_parent_frame, "map",
00042 "The parent frame ID of the TF trajectory from the bag file.");
00043 DEFINE_string(tf_child_frame, "base_link",
00044 "The child frame ID of the TF trajectory from the bag file.");
00045 DEFINE_string(pbstream_filename, "",
00046 "Proto stream file containing the pose graph. The last "
00047 "trajectory will be used for comparison.");
00048
00049 namespace cartographer_ros {
00050 namespace {
00051
00052 double FractionSmallerThan(const std::vector<double>& v, double x) {
00053 return static_cast<double>(std::count_if(
00054 v.begin(), v.end(), [=](double value) { return value < x; })) /
00055 v.size();
00056 }
00057
00058 std::string QuantilesToString(std::vector<double>* v) {
00059 if (v->empty()) return "(empty vector)";
00060 std::sort(v->begin(), v->end());
00061 std::stringstream result;
00062 const int kNumQuantiles = 10;
00063 for (int i = 0; i < kNumQuantiles; ++i) {
00064 auto value = v->at(v->size() * i / kNumQuantiles);
00065 auto percentage = 100 * i / kNumQuantiles;
00066 result << percentage << "%: " << value << "\n";
00067 }
00068 result << "100%: " << v->back() << "\n";
00069 return result.str();
00070 }
00071
00072 void Run(const std::string& pbstream_filename,
00073 const std::string& bag_filename) {
00074 cartographer::mapping::proto::PoseGraph pose_graph_proto =
00075 cartographer::io::DeserializePoseGraphFromFile(pbstream_filename);
00076 const cartographer::mapping::proto::Trajectory& last_trajectory_proto =
00077 *pose_graph_proto.mutable_trajectory()->rbegin();
00078 const cartographer::transform::TransformInterpolationBuffer
00079 transform_interpolation_buffer(last_trajectory_proto);
00080
00081 rosbag::Bag bag;
00082 bag.open(bag_filename, rosbag::bagmode::Read);
00083 rosbag::View view(bag);
00084 std::vector<double> deviation_translation, deviation_rotation;
00085 const double signal_maximum = std::numeric_limits<double>::max();
00086 for (const rosbag::MessageInstance& message : view) {
00087 if (!message.isType<tf2_msgs::TFMessage>()) {
00088 continue;
00089 }
00090 auto tf_message = message.instantiate<tf2_msgs::TFMessage>();
00091 for (const auto& transform : tf_message->transforms) {
00092 if (transform.header.frame_id != FLAGS_tf_parent_frame ||
00093 transform.child_frame_id != FLAGS_tf_child_frame) {
00094 continue;
00095 }
00096 const cartographer::common::Time transform_time =
00097 FromRos(message.getTime());
00098 if (!transform_interpolation_buffer.Has(transform_time)) {
00099 deviation_translation.push_back(signal_maximum);
00100 deviation_rotation.push_back(signal_maximum);
00101 continue;
00102 }
00103 auto optimized_transform =
00104 transform_interpolation_buffer.Lookup(transform_time);
00105 auto published_transform = ToRigid3d(transform);
00106 deviation_translation.push_back((published_transform.translation() -
00107 optimized_transform.translation())
00108 .norm());
00109 deviation_rotation.push_back(
00110 published_transform.rotation().angularDistance(
00111 optimized_transform.rotation()));
00112 }
00113 }
00114 bag.close();
00115 LOG(INFO) << "Distribution of translation difference:\n"
00116 << QuantilesToString(&deviation_translation);
00117 LOG(INFO) << "Distribution of rotation difference:\n"
00118 << QuantilesToString(&deviation_rotation);
00119 LOG(INFO) << "Fraction of translation difference smaller than 1m: "
00120 << FractionSmallerThan(deviation_translation, 1);
00121 LOG(INFO) << "Fraction of translation difference smaller than 0.1m: "
00122 << FractionSmallerThan(deviation_translation, 0.1);
00123 LOG(INFO) << "Fraction of translation difference smaller than 0.05m: "
00124 << FractionSmallerThan(deviation_translation, 0.05);
00125 LOG(INFO) << "Fraction of translation difference smaller than 0.01m: "
00126 << FractionSmallerThan(deviation_translation, 0.01);
00127 }
00128
00129 }
00130 }
00131
00132 int main(int argc, char** argv) {
00133 FLAGS_alsologtostderr = true;
00134 google::InitGoogleLogging(argv[0]);
00135 google::SetUsageMessage(
00136 "\n\n"
00137 "This compares a trajectory from a bag file against the "
00138 "last trajectory in a pbstream file.\n");
00139 google::ParseCommandLineFlags(&argc, &argv, true);
00140 CHECK(!FLAGS_bag_filename.empty()) << "-bag_filename is missing.";
00141 CHECK(!FLAGS_pbstream_filename.empty()) << "-pbstream_filename is missing.";
00142 ::cartographer_ros::Run(FLAGS_pbstream_filename, FLAGS_bag_filename);
00143 }