/* * InterpolationDemo.cpp * * Created on: Mar 16, 2020 * Author: Edo Jelavic * Institute: ETH Zurich, Robotic Systems Lab */ #include "grid_map_demos/InterpolationDemo.hpp" #include "grid_map_core/iterators/GridMapIterator.hpp" #include "grid_map_msgs/GridMap.h" #include "grid_map_ros/GridMapRosConverter.hpp" namespace grid_map_demos { AnalyticalFunctions createWorld(Worlds world, double highResolution, double lowResolution, double length, double width, grid_map::GridMap *groundTruthHighRes, grid_map::GridMap *groundTruthLowRes) { const grid_map::Length mapLength(length, width); const grid_map::Position mapPosition(0.0, 0.0); *groundTruthHighRes = createMap(mapLength, highResolution, mapPosition); *groundTruthLowRes = createMap(mapLength, lowResolution, mapPosition); AnalyticalFunctions groundTruth; switch (world) { case Worlds::Sine: { groundTruth = createSineWorld(groundTruthHighRes); createSineWorld(groundTruthLowRes); break; } case Worlds::Tanh: { groundTruth = createTanhWorld(groundTruthHighRes); createTanhWorld(groundTruthLowRes); break; } case Worlds::GaussMixture: { groundTruth = createGaussianWorld(groundTruthHighRes); createGaussianWorld(groundTruthLowRes); break; } case Worlds::Poly: { groundTruth = createPolyWorld(groundTruthHighRes); createPolyWorld(groundTruthLowRes); break; } default: throw std::runtime_error("Interpolation demo: Unknown world requested."); } return groundTruth; } AnalyticalFunctions createPolyWorld(grid_map::GridMap *map) { AnalyticalFunctions func; func.f_ = [](double x,double y) { return (-x*x -y*y +2.0*x*y +x*x*y*y); }; fillGridMap(map, func); return func; } AnalyticalFunctions createSineWorld(grid_map::GridMap *map) { AnalyticalFunctions func; const double w1 = 1.0; const double w2 = 2.0; const double w3 = 3.0; const double w4 = 4.0; func.f_ = [w1,w2,w3,w4](double x,double y) { return std::cos(w1*x) + std::sin(w2*y) + std::cos(w3*x) + std::sin(w4*y); }; fillGridMap(map, func); return func; } AnalyticalFunctions createTanhWorld(grid_map::GridMap *map) { AnalyticalFunctions func; const double s = 5.0; func.f_ = [s](double x,double y) { const double expZx = std::exp(2 *s* x); const double tanX = (expZx - 1) / (expZx + 1); const double expZy = std::exp(2 *s* y); const double tanY = (expZy - 1) / (expZy + 1); return tanX + tanY; }; fillGridMap(map, func); return func; } AnalyticalFunctions createGaussianWorld(grid_map::GridMap *map) { struct Gaussian { double x0, y0; double varX, varY; double s; }; AnalyticalFunctions func; constexpr int numGaussians = 7; std::array, numGaussians> vars = { { { 1.0, 0.3 }, { 0.25, 0.25 }, { 0.1, 0.1 }, { 0.1, 0.1 }, { 0.1, 0.1 }, { 0.1, 0.05 }, { 0.05, 0.05 } } }; std::array, numGaussians> means = { { { 1, -1 }, { 1, 1.7 }, { -1, 1.6 }, { -1.8, -1.8 }, { -1, 1.8 }, { 0, 0 }, { -1.2, 0 } } }; std::array scales = { -2.0, -1.0, 2.0, 1.0, 3.0, 4.0, 1.0 }; std::array g; for (int i = 0; i < numGaussians; ++i) { g.at(i).x0 = means.at(i).first; g.at(i).y0 = means.at(i).second; g.at(i).varX = vars.at(i).first; g.at(i).varY = vars.at(i).second; g.at(i).s = scales.at(i); } func.f_ = [g](double x,double y) { double value = 0.0; for (size_t i{0}; i < g.size(); ++i) { const double x0 = g.at(i).x0; const double y0 = g.at(i).y0; const double varX = g.at(i).varX; const double varY = g.at(i).varY; const double s = g.at(i).s; value += s * std::exp(-(x-x0)*(x-x0) / (2.0*varX) - (y-y0)*(y-y0) / (2.0 * varY)); } return value; }; fillGridMap(map, func); return func; } void fillGridMap(grid_map::GridMap *map, const AnalyticalFunctions &functions) { grid_map::Matrix& data = (*map)[demoLayer]; for (grid_map::GridMapIterator iterator(*map); !iterator.isPastEnd(); ++iterator) { const grid_map::Index index(*iterator); grid_map::Position pos; map->getPosition(index, pos); data(index(0), index(1)) = functions.f_(pos.x(), pos.y()); } } grid_map::GridMap createMap(const grid_map::Length &length, double resolution, const grid_map::Position &pos) { grid_map::GridMap map; map.setGeometry(length, resolution, pos); map.add(demoLayer, 0.0); map.setFrameId("map"); return map; } grid_map::GridMap createInterpolatedMapFromDataMap(const grid_map::GridMap &dataMap, double desiredResolution) { grid_map::GridMap interpolatedMap; interpolatedMap.setGeometry(dataMap.getLength(), desiredResolution, dataMap.getPosition()); const std::string &layer = demoLayer; interpolatedMap.add(layer, 0.0); interpolatedMap.setFrameId(dataMap.getFrameId()); return interpolatedMap; } void interpolateInputMap(const grid_map::GridMap &dataMap, grid_map::InterpolationMethods interpolationMethod, grid_map::GridMap *interpolatedMap) { for (grid_map::GridMapIterator iterator(*interpolatedMap); !iterator.isPastEnd(); ++iterator) { const grid_map::Index index(*iterator); grid_map::Position pos; interpolatedMap->getPosition(index, pos); const double interpolatedHeight = dataMap.atPosition(demoLayer, pos, interpolationMethod); interpolatedMap->at(demoLayer, index) = interpolatedHeight; } } Error computeInterpolationError(const AnalyticalFunctions &groundTruth, const grid_map::GridMap &map) { unsigned int count = 0; Error error; const int nRow = map.getSize().x(); const int nCol = map.getSize().y(); for (grid_map::GridMapIterator iterator(map); !iterator.isPastEnd(); ++iterator) { const auto row = (*iterator).x(); const auto col = (*iterator).y(); const bool skipEvaluation = row < 2 || col < 2 || col > (nCol - 3) || row > (nRow - 3); if (skipEvaluation) { continue; } grid_map::Position pos; map.getPosition(*iterator, pos); const double f = map.at(demoLayer, *iterator); const double f_ = groundTruth.f_(pos.x(), pos.y()); const double e = std::fabs(f - f_); error.meanSquare_ += e * e; error.meanAbs_ += e; ++count; if (e > error.max_) { error.max_ = e; } } error.meanSquare_ /= count; error.meanAbs_ /= count; return error; } InterpolationDemo::InterpolationDemo(ros::NodeHandle *nh) { nh->param("interpolation_type", interpolationMethod_, "Nearest"); nh->param("world", world_, "Sine"); nh->param("groundtruth_resolution", groundTruthResolution_, 0.02); nh->param("interpolation/data_resolution", dataResolution_, 0.1); nh->param("interpolation/interpolated_resolution", interpolatedResolution_, 0.02); nh->param("world_size/length", worldLength_, 4.0); nh->param("world_size/width", worldWidth_, 4.0); groundTruthMapPub_ = nh->advertise("ground_truth", 1, true); dataSparseMapPub_ = nh->advertise("data_sparse", 1, true); interpolatedMapPub_ = nh->advertise("interpolated", 1, true); runDemo(); } void InterpolationDemo::runDemo() { // visualize stuff groundTruth_ = createWorld(worlds.at(world_), groundTruthResolution_, dataResolution_, worldLength_, worldWidth_, &groundTruthMap_, &dataSparseMap_); interpolatedMap_ = createInterpolatedMapFromDataMap(dataSparseMap_, interpolatedResolution_); interpolateInputMap(dataSparseMap_, interpolationMethods.at(interpolationMethod_), &interpolatedMap_); publishGridMaps(); std::cout << "\n \n visualized the world: " << world_ << std::endl; // print some info const auto statistics = computeStatistics(); printStatistics(statistics); } InterpolationDemo::Statistics InterpolationDemo::computeStatistics() const { Statistics stats; for (auto world = worlds.cbegin(); world != worlds.cend(); ++world) { std::map methodsStats; for (auto method = interpolationMethods.cbegin(); method != interpolationMethods.cend(); ++method) { const auto errorAndDuration = interpolateAndComputeError(world->first, method->first); Statistic statistic; statistic.duration_ = errorAndDuration.second; statistic.error_ = errorAndDuration.first; statistic.interpolationMethod_ = method->first; statistic.world_ = world->first; methodsStats.insert( { method->first, statistic }); } stats.insert( { world->first, methodsStats }); } return stats; } InterpolationDemo::ErrorAndDuration InterpolationDemo::interpolateAndComputeError( const std::string world, const std::string &method) const { grid_map::GridMap highResMap, dataMap; auto groundTruth = createWorld(worlds.at(world), groundTruthResolution_, dataResolution_, worldLength_, worldWidth_, &highResMap, &dataMap); auto interpolatedMap = createInterpolatedMapFromDataMap(dataMap, interpolatedResolution_); const auto start = clk::now(); interpolateInputMap(dataMap, interpolationMethods.at(method), &interpolatedMap); const auto end = clk::now(); const auto count = std::chrono::duration_cast(end - start).count(); const unsigned int numElements = interpolatedMap_.getSize().x() * interpolatedMap_.getSize().y(); ErrorAndDuration errorAndDuration; errorAndDuration.first = computeInterpolationError(groundTruth, interpolatedMap); errorAndDuration.second = static_cast(count) / numElements; return errorAndDuration; } void InterpolationDemo::printStatistics(const Statistics &stats) const { std::cout << " \n \n ================================== \n"; printf("Interpolated map of size: %f x %f \n", interpolatedMap_.getLength().x(), interpolatedMap_.getLength().y()); printf("Resolution of the data: %f, resolution of the interpolated map: %f, \n \n", dataSparseMap_.getResolution(), interpolatedMap_.getResolution()); for (auto world = worlds.cbegin(); world != worlds.cend(); ++world) { std::cout << "Stats for the world: " << world->first << std::endl; for (auto method = interpolationMethods.cbegin(); method != interpolationMethods.cend(); ++method) { Statistic s = stats.at(world->first).at(method->first); printf( "Method: %-20s Mean absolute error: %-10f max error: %-10f avg query duration: %-10f microsec \n", method->first.c_str(), s.error_.meanAbs_, s.error_.max_, s.duration_); } std::cout << std::endl; } } void InterpolationDemo::publishGridMaps() const { grid_map_msgs::GridMap highResMsg, lowResMsg, interpolatedMsg; grid_map::GridMapRosConverter::toMessage(groundTruthMap_, highResMsg); grid_map::GridMapRosConverter::toMessage(dataSparseMap_, lowResMsg); grid_map::GridMapRosConverter::toMessage(interpolatedMap_, interpolatedMsg); groundTruthMapPub_.publish(highResMsg); dataSparseMapPub_.publish(lowResMsg); interpolatedMapPub_.publish(interpolatedMsg); } } /* namespace grid_map_demos */