00001
00020 #pragma once
00021
00022 #include <ros/ros.h>
00023 #include <nav_msgs/GetMap.h>
00024 #include <nav_msgs/OccupancyGrid.h>
00025 #include <nav_msgs/Path.h>
00026 #include <nav_msgs/GetPlan.h>
00027 #include <geometry_msgs/PoseStamped.h>
00028 #include <cmath>
00029 #include <vector>
00030 #include <boost/tuple/tuple.hpp>
00031 #include <costmap_2d/costmap_2d_ros.h>
00032 #include <tf/transform_listener.h>
00033 #include "robot_model_services/helper/DebugHelper.hpp"
00034 #include "robot_model_services/helper/MathHelper.hpp"
00035 #include "robot_model_services/helper/TypeHelper.hpp"
00036 #include "robot_model_services/typedef.hpp"
00037
00038 namespace robot_model_services {
00039 struct RayTracingIndex {
00040 int32_t x;
00041 int32_t y;
00042 int8_t occupancy;
00043 public:
00044 static bool topologicalCompare(const RayTracingIndex &source, const RayTracingIndex &firstIndex, const RayTracingIndex &secondIndex) {
00045 SimpleVector2 sourceVector(source.x, source.y);
00046 SimpleVector2 firstIndexVector(firstIndex.x, firstIndex.y);
00047 SimpleVector2 secondIndexVector(secondIndex.x, secondIndex.y);
00048
00049 return (firstIndexVector - sourceVector).squaredNorm() < (secondIndexVector - sourceVector).squaredNorm();
00050 }
00051
00052 static bool equals(const RayTracingIndex &firstIndex, const RayTracingIndex &secondIndex) {
00053 SimpleVector2 firstIndexVector(firstIndex.x, firstIndex.y);
00054 SimpleVector2 secondIndexVector(secondIndex.x, secondIndex.y);
00055 return firstIndexVector == secondIndexVector;
00056 }
00057 };
00058 typedef boost::shared_ptr<RayTracingIndex> RayTracingIndexPtr;
00059 typedef std::vector<RayTracingIndex> Ray;
00060
00061
00062 class MapHelper {
00063 private:
00064 DebugHelperPtr mDebugHelperPtr;
00065
00066 bool mMapReceived;
00067 int8_t mCollisionThreshold;
00068 ros::NodeHandle mGlobalNodeHandle;
00069 ros::ServiceClient mGetPlanServiceClient;
00070 nav_msgs::OccupancyGrid mMap;
00071 costmap_2d::Costmap2D mCostmap;
00072 nav_msgs::OccupancyGrid mRaytracingMap;
00073
00074 int8_t mCostTranslationTable[256];
00075 public:
00076 MapHelper(const std::string &mapTopicName = "map", const std::string &getPlanServiceName = "move_base/make_plan") : mMapReceived(false), mCollisionThreshold(45) {
00077 mDebugHelperPtr = DebugHelper::getInstance();
00078
00079
00080 mCostTranslationTable[0] = 0;
00081 mCostTranslationTable[253] = 99;
00082 mCostTranslationTable[254] = 100;
00083 mCostTranslationTable[255] = -1;
00084
00085 for (int i = 1; i < 253; i++) {
00086 mCostTranslationTable[i] = int8_t(1 + (97 * (i - 1)) / 251);
00087 }
00088
00089 ros::Subscriber mapSubscriber = mGlobalNodeHandle.subscribe<nav_msgs::OccupancyGrid>(mapTopicName, 1, &MapHelper::mapReceived, this);
00090 while(ros::ok() && !mMapReceived) {
00091 mDebugHelperPtr->write(std::stringstream() << "Waiting for map to arrive on topic '" << mapSubscriber.getTopic() << "'",
00092 DebugHelper::MAP);
00093
00094 ros::spinOnce();
00095 ros::Duration(0.5).sleep();
00096 }
00097
00098 mDebugHelperPtr->write(std::stringstream() << "waiting for service: " << getPlanServiceName, DebugHelper::MAP);
00099 ros::service::waitForService(getPlanServiceName, -1);
00100 mGetPlanServiceClient = mGlobalNodeHandle.serviceClient<nav_msgs::GetPlan>(getPlanServiceName, true);
00101 while(ros::ok() && !mGetPlanServiceClient.exists()) {
00102 mDebugHelperPtr->write(std::stringstream() << "Waiting for planning server to start on service '"
00103 << mGetPlanServiceClient.getService(), DebugHelper::MAP);
00104 ros::spinOnce();
00105 ros::Duration(0.5).sleep();
00106 }
00107 this->setCostmap();
00108 this->aggregateRaytracingMap();
00109 }
00110
00111 virtual ~MapHelper() { }
00112 private:
00113 void mapReceived(nav_msgs::OccupancyGrid map) {
00114 mDebugHelperPtr->write("Map received", DebugHelper::MAP);
00115 mMap = map;
00116 mMapReceived = true;
00117 mDebugHelperPtr->write(std::stringstream() << "Map resolution: " << mMap.info.resolution, DebugHelper::MAP);
00118 mDebugHelperPtr->write(std::stringstream() << "Map translation: " << this->getTranslation(), DebugHelper::MAP);
00119 mDebugHelperPtr->write(std::stringstream() << "Map orientation: " << this->getOrientation().w() << ", "
00120 << this->getOrientation().x() << ", "
00121 << this->getOrientation().y() << ", "
00122 << this->getOrientation().z(),
00123 DebugHelper::MAP);
00124 mDebugHelperPtr->write(std::stringstream() << "Map metric width: " << this->getMetricWidth(), DebugHelper::MAP);
00125 mDebugHelperPtr->write(std::stringstream() << "Map metric height: " << this->getMetricHeight(), DebugHelper::MAP);
00126 }
00127
00128 void setCostmap() {
00129 tf::TransformListener tf(ros::Duration(10));
00130 std::string name = "global_costmap";
00131 costmap_2d::Costmap2DROS costmapRos(name, tf);
00132 costmapRos.start();
00133 costmapRos.updateMap();
00134 costmapRos.stop();
00135
00136 mCostmap = *(costmapRos.getCostmap());
00137
00138
00139 std::string path;
00140 mGlobalNodeHandle.getParam("/nbv/mMapsImagePath", path);
00141
00142
00143 std::string costmapPath = path + "/costmap.pgm";
00144 mDebugHelperPtr->write("Outputting calculated costmap to " + costmapPath,
00145 DebugHelper::MAP);
00146 mCostmap.saveMap(costmapPath);
00147 }
00148
00149 void aggregateRaytracingMap() {
00150 mDebugHelperPtr->write("Aggregating raytracing map.", DebugHelper::MAP);
00151 if (mMap.info.width != mCostmap.getSizeInCellsX() || mMap.info.height != mCostmap.getSizeInCellsY()) {
00152 ROS_ERROR("Cannot aggregate raytracing map. Dimensions of map and costmap do not match!");
00153 mDebugHelperPtr->write(std::stringstream() << "Map size: " << mMap.info.width << "x" << mMap.info.height, DebugHelper::MAP);
00154 mDebugHelperPtr->write(std::stringstream() << "Costmap size: " << mCostmap.getSizeInCellsX() << "x" << mCostmap.getSizeInCellsY(), DebugHelper::MAP);
00155 assert(mMap.info.width == mCostmap.getSizeInCellsX() && mMap.info.height == mCostmap.getSizeInCellsY());
00156 }
00157
00158 mRaytracingMap.info = mMap.info;
00159 mRaytracingMap.data.reserve(mMap.data.size());
00160 for (unsigned int y = 0; y < mMap.info.height; y++) {
00161 for (unsigned int x = 0; x < mMap.info.width; x++) {
00162 unsigned int index = y * mMap.info.width + x;
00163 int8_t mapOccupancyValue = mMap.data[index];
00164 int8_t costmapOccupancyValue = mCostTranslationTable[mCostmap.getCost(x, y)];
00165 int8_t aggregatedOccupancyValue = mapOccupancyValue == -1 ? -1 : costmapOccupancyValue;
00166
00167 mRaytracingMap.data[index] = aggregatedOccupancyValue;
00168 }
00169 }
00170 mDebugHelperPtr->write("Aggregation done.", DebugHelper::MAP);
00171 }
00172
00173 int8_t getOccupancyValue(const nav_msgs::OccupancyGrid &map, const SimpleVector3 &position) {
00174 int32_t mapX, mapY;
00175 this->worldToMapCoordinates(position, mapX, mapY);
00176 return this->getOccupancyValue(map, mapX, mapY);
00177 }
00178
00179 int8_t getOccupancyValue(const costmap_2d::Costmap2D &costmap, const SimpleVector3 &position) {
00180 int32_t mapX, mapY;
00181 this->worldToMapCoordinates(position, mapX, mapY);
00182 return this->getOccupancyValue(costmap, mapX, mapY);
00183 }
00184
00185 int8_t getOccupancyValue(const nav_msgs::OccupancyGrid &map, const int32_t &mapX, const int32_t &mapY) {
00186 uint32_t width = map.info.width;
00187 uint32_t height = map.info.height;
00188
00189 if (mapX < 0 || mapY < 0 || mapX >= (int32_t)width || mapY >= (int32_t)height) {
00190 return -1;
00191 }
00192
00193 return map.data[mapX + mapY * width];
00194 }
00195
00196 int8_t getOccupancyValue(const costmap_2d::Costmap2D &costmap, const int32_t &mapX, const int32_t &mapY) {
00197 uint32_t width = costmap.getSizeInCellsX();
00198 uint32_t height = costmap.getSizeInCellsY();
00199
00200 if (mapX < 0 || mapY < 0 || mapX >= (int32_t)width || mapY >= (int32_t)height) {
00201 return -1;
00202 }
00203
00204 unsigned char cost = costmap.getCost(mapX, mapY);
00205 return mCostTranslationTable[cost];
00206 }
00207
00208 public:
00209 int8_t getMapOccupancyValue(const SimpleVector3 &position) {
00210 return this->getOccupancyValue(mMap, position);
00211 }
00212
00213 int8_t getMapOccupancyValue(const int32_t &mapX, const int32_t &mapY) {
00214 return this->getOccupancyValue(mMap, mapX, mapY);
00215 }
00216
00217
00218 int8_t getCostmapOccupancyValue(const SimpleVector3 &position) {
00219 return this->getOccupancyValue(mCostmap, position);
00220 }
00221
00222 int8_t getCostmapOccupancyValue(const int32_t &mapX, const int32_t &mapY) {
00223 return this->getOccupancyValue(mCostmap, mapX, mapY);
00224 }
00225
00226
00227 int8_t getRaytracingMapOccupancyValue(const SimpleVector3 &position) {
00228 return this->getOccupancyValue(mRaytracingMap, position);
00229 }
00230
00231 int8_t getRaytracingMapOccupancyValue(const int32_t &mapX, const int32_t &mapY) {
00232 return this->getOccupancyValue(mRaytracingMap, mapX, mapY);
00233 }
00234
00235 bool doRaytracing(const SimpleVector3 &fromPoint, const SimpleVector3 &toPoint) {
00236 Ray ray;
00237 bool retVal = this->doRaytracing(fromPoint, toPoint, ray);
00238 return retVal;
00239 }
00240
00241 bool doRaytracing(const SimpleVector3 &fromPoint, const SimpleVector3 &toPoint, Ray &ray) {
00242
00243 ray.clear();
00244
00245
00246 int32_t fromMapX, fromMapY;
00247 this->worldToMapCoordinates(fromPoint, fromMapX, fromMapY);
00248
00249 int32_t toMapX, toMapY;
00250 this->worldToMapCoordinates(toPoint, toMapX, toMapY);
00251
00252
00253 int32_t distanceX = toMapX - fromMapX;
00254 int32_t distanceY = toMapY - fromMapY;
00255
00256
00257 if (distanceX == 0 && distanceY == 0) {
00258 int8_t occupancyValue = this->getRaytracingMapOccupancyValue(fromMapX, fromMapY);
00259 return this->isOccupancyValueAcceptable(occupancyValue);
00260 }
00261
00262
00263 int32_t minX = std::min(fromMapX, toMapX);
00264 int32_t maxX = std::max(fromMapX, toMapX);
00265 int32_t minY = std::min(fromMapY, toMapY);
00266 int32_t maxY = std::max(fromMapY, toMapY);
00267
00268
00269 float resolution = mRaytracingMap.info.resolution;
00270
00271
00272 int32_t signumX = distanceX < 0 ? - 1 : 1;
00273 int32_t signumY = distanceY < 0 ? - 1 : 1;
00274
00275
00276 SimpleVector2 diff = (toPoint - fromPoint).block<2, 1>(0, 0);
00277
00278
00279 bool rayTracingSucceeded = true;
00280
00281
00282 for (int32_t offsetX = 0; offsetX <= abs(distanceX) + 1 && diff[0] != 0; offsetX += 1) {
00283 double x = (fromMapX + signumX * offsetX) * resolution;
00284 double t = (x - fromPoint[0]) / diff[0];
00285 double y = fromPoint[1] + t * diff[1];
00286
00287 RayTracingIndex idx;
00288 idx.x = (int32_t) (x / resolution);
00289 idx.y = (int32_t) (y / resolution);
00290 idx.occupancy = this->getRaytracingMapOccupancyValue(idx.x, idx.y);
00291
00292 if (idx.x < minX || idx.x > maxX || idx.y < minY || idx.y > maxY) {
00293 continue;
00294 }
00295
00296 if (!this->isOccupancyValueAcceptable(idx.occupancy)) {
00297 rayTracingSucceeded = false;
00298 }
00299
00300 ray.push_back(idx);
00301 }
00302
00303
00304 for (int32_t offsetY = 0; offsetY <= abs(distanceY) + 1 && diff[1] != 0; offsetY += 1) {
00305 double y = (fromMapY + signumY * offsetY) * resolution;
00306 double t = (y - fromPoint[1]) / diff[1];
00307 double x = fromPoint[0] + t * diff[0];
00308
00309 RayTracingIndex idx;
00310 idx.x = (int32_t) (x / resolution);
00311 idx.y = (int32_t) (y / resolution);
00312 idx.occupancy = this->getRaytracingMapOccupancyValue(idx.x, idx.y);
00313
00314 if (idx.x < minX || idx.x > maxX || idx.y < minY || idx.y > maxY) {
00315 continue;
00316 }
00317
00318 if (!this->isOccupancyValueAcceptable(idx.occupancy)) {
00319 rayTracingSucceeded = false;
00320 }
00321
00322 ray.push_back(idx);
00323 }
00324
00325
00326 RayTracingIndex source = {fromMapX, fromMapY, 0};
00327 std::stable_sort(ray.begin(), ray.end(), boost::bind(&RayTracingIndex::topologicalCompare, source, _1, _2));
00328
00329
00330 Ray::iterator endIterator = std::unique(ray.begin(), ray.end(), &RayTracingIndex::equals);
00331 ray.resize(std::distance(ray.begin(), endIterator));
00332
00333 return rayTracingSucceeded;
00334 }
00335
00336 float getMetricWidth() {
00337 return mMap.info.width * mMap.info.resolution;
00338 }
00339
00340 float getMetricHeight() {
00341 return mMap.info.height * mMap.info.resolution;
00342 }
00343
00344 void worldToMapCoordinates(const SimpleVector3 &position, int32_t &x, int32_t &y) {
00345 SimpleVector3 continuousMapCoords;
00346 worldToMapCoordinates(position, continuousMapCoords);
00347
00348
00349 x = (int32_t) floor(continuousMapCoords[0]);
00350 y = (int32_t) floor(continuousMapCoords[1]);
00351 }
00352
00353 void worldToMapCoordinates(const SimpleVector3 &position, SimpleVector3 &result) {
00354 SimpleVector3 translation = this->getTranslation();
00355 SimpleQuaternion orientation = this->getOrientation();
00356 SimpleMatrix3 rotationMatrix = orientation.toRotationMatrix();
00357
00358
00359 result = (rotationMatrix.transpose() * position - translation) / mMap.info.resolution;
00360 }
00361
00362 void mapToWorldCoordinates(const int32_t &x, const int32_t &y, SimpleVector3 &result) {
00363 mapToWorldCoordinates(SimpleVector3(x, y, 0.0), result);
00364 }
00365
00366 void mapToWorldCoordinates(const SimpleVector3 &position, SimpleVector3 &result) {
00367 SimpleVector3 translation = this->getTranslation();
00368 SimpleQuaternion orientation = this->getOrientation();
00369 result = orientation.toRotationMatrix() * position * mMap.info.resolution + translation;
00370 }
00371
00372 void mapToWorldSize(const double &size, double &result) {
00373 result = size * mMap.info.resolution;
00374 }
00375
00376 void worldToMapSize(const double &size, double &result) {
00377 result = size / mMap.info.resolution;
00378 }
00379
00380 SimpleVector3 getTranslation() {
00381 return SimpleVector3(mMap.info.origin.position.x, mMap.info.origin.position.y, mMap.info.origin.position.z);
00382 }
00383
00384 SimpleQuaternion getOrientation() {
00385 return TypeHelper::getSimpleQuaternion(mMap.info.origin.orientation);
00386 }
00387
00388 bool isOccupancyValueAcceptable(const int8_t &occupancyValue) {
00389 return occupancyValue >= 0 && occupancyValue <= mCollisionThreshold;
00390 }
00391
00392 void setCollisionThreshold(int8_t thresholdValue) {
00393 mCollisionThreshold = thresholdValue;
00394 }
00395
00396 int8_t getCollisionThreshold () {
00397 return mCollisionThreshold;
00398 }
00399
00400 float getSquaredDistance(const SimpleVector3 &start, const SimpleVector3 &goal) {
00401 return (goal - start).squaredNorm();
00402 }
00403 };
00404
00405 typedef boost::shared_ptr<MapHelper> MapHelperPtr;
00406 }