normal_filter_comparison_node.cpp
Go to the documentation of this file.
1 
7 #pragma GCC diagnostic push
8 #pragma GCC diagnostic ignored "-Wpedantic"
9 #pragma GCC diagnostic ignored "-Wformat"
10 #include <filters/filter_chain.hpp>
11 #pragma GCC diagnostic pop
12 #include <ros/ros.h>
13 #include <Eigen/Core>
14 #include <cmath>
15 #include <vector>
16 
19 
20 // Function headers
21 namespace grid_map {
32 void normalsErrorCalculation(grid_map::GridMap& map, const grid_map::Size& gridMapSize, double& directionalErrorAreaSum,
33  double& directionalErrorRasterSum);
34 
43 void mapAddNoise(grid_map::GridMap& map, const grid_map::Size& gridMapSize, const double& noise_on_map);
44 
53 void mapAddOutliers(grid_map::GridMap& map, const grid_map::Size& gridMapSize, const double outlierPercentage);
54 
63 void mapAverageFiltering(grid_map::GridMap& map, const grid_map::Size& gridMapSize, const double filterRadius);
64 } // namespace grid_map
65 
66 int main(int argc, char** argv) {
67  // Initialize node and publisher.
68  ros::init(argc, argv, "normal_filter_comparison_demo");
69  ros::NodeHandle nh("~");
70  ros::Publisher publisher = nh.advertise<grid_map_msgs::GridMap>("grid_map", 1, true);
71 
72  // Start time.
73  double begin = ros::Time::now().toSec();
74 
75  // Load filter chain, defined in grid_map_demos/config/normal_filter_comparison.yaml.
76  filters::FilterChain<grid_map::GridMap> filterChain_("grid_map::GridMap");
77  std::string filterChainParametersName_;
78  nh.param("filter_chain_parameter_name", filterChainParametersName_, std::string("grid_map_filters"));
79 
80  // Read noise amount, in meters, from parameters server.
81  double noise_on_map;
82  nh.param("noise_on_map", noise_on_map, 0.015);
83  ROS_INFO("noise_on_map = %f", noise_on_map);
84 
85  double outliers_percentage;
86  ;
87  nh.param("outliers_percentage", outliers_percentage, 0.0);
88  ROS_INFO("outliers_percentage = %f", outliers_percentage);
89 
90  // Configuration of chain filter.
91  if (!filterChain_.configure(filterChainParametersName_, nh)) {
92  ROS_ERROR("Could not configure the filter chain!");
93  }
94 
95  // Parameters for grid map dimensions.
96  const double mapLength = 2.5;
97  const double mapWidth = 4.0;
98  const double mapResolution = 0.02;
99 
100  // Grid map object creation.
101  grid_map::GridMap map({"elevation", "normal_analytic_x", "normal_analytic_y", "normal_analytic_z"});
102  map.setFrameId("map");
103  map.setGeometry(grid_map::Length(mapLength, mapWidth), mapResolution, grid_map::Position(0.0, 0.0));
104  const grid_map::Size gridMapSize = map.getSize();
105  ROS_INFO("Created map with size %f x %f m (%i x %i cells).\n The center of the map is located at (%f, %f) in the %s frame.",
106  map.getLength().x(), map.getLength().y(), map.getSize()(0), map.getSize()(1), map.getPosition().x(), map.getPosition().y(),
107  map.getFrameId().c_str());
108 
109  // Initialize variables for normal quality comparison.
110  double directionalErrorAreaSum = 0;
111  double directionalErrorRasterSum = 0;
112 
113  // Surface shape variables.
114  const double surfaceSpeed = 0.5;
115  const double surfaceSlope = 0.2;
116  const double surfaceBias = -0.04;
117  const double wavePeriod = 5.0;
118 
119  // Work with grid map in a loop. Grid map and analytic normals are continuously generated using exact functions.
120  ros::Rate rate(10.0);
121  while (nh.ok()) {
122  ros::Time time = ros::Time::now();
123 
124  // Calculate wave shaped elevation and analytic surface normal.
125  for (grid_map::GridMapIterator it(map); !it.isPastEnd(); ++it) {
126  grid_map::Position position;
127  map.getPosition(*it, position);
128  map.at("elevation", *it) =
129  surfaceBias + surfaceSlope * std::sin(surfaceSpeed * time.toSec() + wavePeriod * position.y()) * position.x();
130 
131  // Analytic normals computation.
132  grid_map::Vector3 normalAnalytic(-surfaceSlope * std::sin(surfaceSpeed * time.toSec() + wavePeriod * position.y()),
133  -position.x() * std::cos(surfaceSpeed * time.toSec() + wavePeriod * position.y()), 1.0);
134  normalAnalytic.normalize();
135  map.at("normal_analytic_x", *it) = normalAnalytic.x();
136  map.at("normal_analytic_y", *it) = normalAnalytic.y();
137  map.at("normal_analytic_z", *it) = normalAnalytic.z();
138  }
139 
140  // elevation_filtered layer is used for visualization, initialize it here and if there is noise and filtering it will be updated.
141  map.add("elevation_filtered", map.get("elevation"));
142 
143  // Perturb and then filter map only if noise != 0.
144  if (noise_on_map != 0.0) {
145  grid_map::mapAddNoise(map, gridMapSize, noise_on_map);
146  }
147  if (outliers_percentage != 0.0) {
148  grid_map::mapAddOutliers(map, gridMapSize, outliers_percentage);
149  }
150  if ((noise_on_map != 0.0) || (outliers_percentage != 0.0)) {
151  grid_map::mapAverageFiltering(map, gridMapSize, 0.1);
152  }
153 
154  // Computation of normals using filterChain_.update function.
155  if (!filterChain_.update(map, map)) {
156  ROS_ERROR("Could not update the grid map filter chain!");
157  }
158 
159  // Normals error computation
160  grid_map::normalsErrorCalculation(map, gridMapSize, directionalErrorAreaSum, directionalErrorRasterSum);
161  ROS_INFO_THROTTLE(2.0, "directionalErrorArea = %f", directionalErrorAreaSum);
162  ROS_INFO_THROTTLE(2.0, "directionalErrorRaster = %f", directionalErrorRasterSum);
163 
164  // Publish grid map.
165  map.setTimestamp(time.toNSec());
166  grid_map_msgs::GridMap message;
168  publisher.publish(message);
169  double end = ros::Time::now().toSec();
170 
171  // Limit simulation length to 1 minute.
172  if ((end - begin) > 60) {
173  break;
174  }
175  }
176 
177  return 0;
178 }
179 
180 namespace grid_map {
181 void normalsErrorCalculation(grid_map::GridMap& map, const grid_map::Size& gridMapSize, double& directionalErrorAreaSum,
182  double& directionalErrorRasterSum) {
183  // If layers saved as matrices accessing values is faster.
184  const grid_map::Matrix mapNormalAnalyticX = map["normal_analytic_x"];
185  const grid_map::Matrix mapNormalAnalyticY = map["normal_analytic_y"];
186  const grid_map::Matrix mapNormalAnalyticZ = map["normal_analytic_z"];
187  const grid_map::Matrix mapNormalAreaX = map["normal_area_x"];
188  const grid_map::Matrix mapNormalAreaY = map["normal_area_y"];
189  const grid_map::Matrix mapNormalAreaZ = map["normal_area_z"];
190  const grid_map::Matrix mapNormalRasterX = map["normal_raster_x"];
191  const grid_map::Matrix mapNormalRasterY = map["normal_raster_y"];
192  const grid_map::Matrix mapNormalRasterZ = map["normal_raster_z"];
193 
194  // Initialize vector to use eigen scalar product (Vector3 = Eigen::Vector3d).
195  grid_map::Vector3 normalVectorAnalytic = Vector3::Zero();
196  grid_map::Vector3 normalVectorArea = Vector3::Zero();
197  grid_map::Vector3 normalVectorRaster = Vector3::Zero();
198 
199  // Add layer for possible future visualization in RViz to understand where error is.
200  map.add("directionalErrorArea", grid_map::Matrix::Zero(gridMapSize(0), gridMapSize(1)));
201  map.add("directionalErrorRaster", grid_map::Matrix::Zero(gridMapSize(0), gridMapSize(1)));
202 
203  // Raster normals not defined for outermost layer of cells.
204  const grid_map::Index submapStartIndex(1, 1);
205  const grid_map::Index submapBufferSize(gridMapSize(0) - 2, gridMapSize(1) - 2);
206  for (grid_map::SubmapIterator iterator(map, submapStartIndex, submapBufferSize); !iterator.isPastEnd(); ++iterator) {
207  const grid_map::Index index(*iterator);
208 
209  normalVectorAnalytic(0) = mapNormalAnalyticX(index(0), index(1));
210  normalVectorAnalytic(1) = mapNormalAnalyticY(index(0), index(1));
211  normalVectorAnalytic(2) = mapNormalAnalyticZ(index(0), index(1));
212 
213  normalVectorArea(0) = mapNormalAreaX(index(0), index(1));
214  normalVectorArea(1) = mapNormalAreaY(index(0), index(1));
215  normalVectorArea(2) = mapNormalAreaZ(index(0), index(1));
216 
217  normalVectorRaster(0) = mapNormalRasterX(index(0), index(1));
218  normalVectorRaster(1) = mapNormalRasterY(index(0), index(1));
219  normalVectorRaster(2) = mapNormalRasterZ(index(0), index(1));
220 
221  // Error(alpha) = 1.0 - abs(cos(alpha)), where alpha = angle(normalVectorAnalytic, normalVectorArea)
222  // Error of perfect normals will be 0.0.
223  map.at("directionalErrorArea", *iterator) = 1.0 - std::abs(normalVectorAnalytic.dot(normalVectorArea));
224  map.at("directionalErrorRaster", *iterator) = 1.0 - std::abs(normalVectorAnalytic.dot(normalVectorRaster));
225  }
226 
227  // Directional error defined as sum of absolute cosines of normal calculated with different methods
228  const double directionalErrorArea = map["directionalErrorArea"].array().sum();
229  const double directionalErrorRaster = map["directionalErrorRaster"].array().sum();
230 
231  // Calculate mean value of directional error of last 20 cycles.
232  directionalErrorAreaSum = (directionalErrorAreaSum * 19.0 + directionalErrorArea) / 20.0;
233  directionalErrorRasterSum = (directionalErrorRasterSum * 19.0 + directionalErrorRaster) / 20.0;
234 }
235 
236 void mapAddNoise(grid_map::GridMap& map, const grid_map::Size& gridMapSize, const double& noise_on_map) {
237  // Add noise (using Eigen operators).
238  map.add("noise", noise_on_map * Matrix::Random(gridMapSize(0), gridMapSize(1)));
239  map.add("elevation_noisy", map.get("elevation") + map["noise"]);
240 }
241 
242 void mapAddOutliers(grid_map::GridMap& map, const grid_map::Size& gridMapSize, const double outlierPercentage) {
243  // Adding outliers at infinite height (accessing cell by position).
244  const double numberInfPoints = outlierPercentage * gridMapSize(0) * gridMapSize(1);
245 
246  for (int i = 0; i < static_cast<int>(numberInfPoints); ++i) {
247  grid_map::Position randomPosition = grid_map::Position::Random();
248  if (map.isInside(randomPosition)) {
249  map.atPosition("elevation_noisy", randomPosition) = std::numeric_limits<float>::infinity();
250  }
251  }
252 }
253 
254 void mapAverageFiltering(grid_map::GridMap& map, const grid_map::Size& gridMapSize, const double filterRadius) {
255  grid_map::Index startIndex(0, 0);
256  grid_map::SubmapIterator it(map, startIndex, gridMapSize);
257  // Iterate over whole map.
258  for (; !it.isPastEnd(); ++it) {
259  Position currentPosition;
260  map.getPosition(*it, currentPosition);
261  double mean = 0.0;
262  double sumOfWeights = 0.0;
263 
264  // Compute weighted mean.
265  for (grid_map::CircleIterator circleIt(map, currentPosition, filterRadius); !circleIt.isPastEnd(); ++circleIt) {
266  if (!map.isValid(*circleIt, "elevation_noisy")) {
267  continue;
268  }
269  grid_map::Position currentPositionInCircle;
270  map.getPosition(*circleIt, currentPositionInCircle);
271 
272  // Computed weighted mean based on Euclidian distance.
273  double distance = (currentPosition - currentPositionInCircle).norm();
274  double weight = pow(filterRadius - distance, 2);
275  mean += weight * map.at("elevation_noisy", *circleIt);
276  sumOfWeights += weight;
277  }
278  if (sumOfWeights!=0) {
279  map.at("elevation_filtered", *it) = mean / sumOfWeights;
280  } else {
281  map.at("elevation_filtered", *it) = std::numeric_limits<float>::infinity();
282  }
283  }
284 }
285 } // namespace grid_map
Eigen::Array2i Index
float & atPosition(const std::string &layer, const Position &position)
void publish(const boost::shared_ptr< M > &message) const
bool getPosition(const Index &index, Position &position) const
Eigen::Array2i Size
Eigen::MatrixXf Matrix
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
static void toMessage(const grid_map::GridMap &gridMap, grid_map_msgs::GridMap &message)
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
int main(int argc, char **argv)
bool isValid(const Index &index) const
Node for comparing different normal filters performances.
bool isInside(const Position &position) const
void mapAverageFiltering(grid_map::GridMap &map, const grid_map::Size &gridMapSize, const double filterRadius)
Eigen::Vector2d Position
#define ROS_INFO(...)
const Matrix & get(const std::string &layer) const
bool param(const std::string &param_name, T &param_val, const T &default_val) const
uint64_t toNSec() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
float & at(const std::string &layer, const Index &index)
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
void add(const std::string &layer, const double value=NAN)
void mapAddNoise(grid_map::GridMap &map, const grid_map::Size &gridMapSize, const double &noise_on_map)
void mapAddOutliers(grid_map::GridMap &map, const grid_map::Size &gridMapSize, const double outlierPercentage)
bool update(const T &data_in, T &data_out)
bool configure(std::string param_name, ros::NodeHandle node=ros::NodeHandle())
static Time now()
Eigen::Vector3d Vector3
#define ROS_INFO_THROTTLE(rate,...)
bool ok() const
void setFrameId(const std::string &frameId)
void normalsErrorCalculation(grid_map::GridMap &map, const grid_map::Size &gridMapSize, double &directionalErrorAreaSum, double &directionalErrorRasterSum)
#define ROS_ERROR(...)
Eigen::Array2d Length


grid_map_demos
Author(s): Péter Fankhauser
autogenerated on Tue Jun 1 2021 02:13:55