SignedDistanceField.cpp
Go to the documentation of this file.
1 /*
2  * SignedDistanceField.cpp
3  *
4  * Created on: Jul 10, 2020
5  * Author: Ruben Grandia
6  * Institute: ETH Zurich
7  */
8 
10 
11 #include <iostream>
12 
15 
16 namespace grid_map {
17 
18 // Import from the signed_distance_field namespace
20 using signed_distance_field::Gridmap3dLookup;
24 
25 SignedDistanceField::SignedDistanceField(const GridMap& gridMap, const std::string& elevationLayer, double minHeight, double maxHeight)
26  : frameId_(gridMap.getFrameId()), timestamp_(gridMap.getTimestamp()) {
27  assert(maxHeight >= minHeight);
28 
29  // Determine origin of the 3D grid
30  Position mapOriginXY;
31  gridMap.getPosition(Eigen::Vector2i(0, 0), mapOriginXY);
32  Position3 gridOrigin(mapOriginXY.x(), mapOriginXY.y(), minHeight);
33 
34  // Round up the Z-discretization. We need a minimum of two layers to enable finite difference in Z direction
35  const auto numZLayers = static_cast<size_t>(std::max(std::ceil((maxHeight - minHeight) / gridMap.getResolution()), 2.0));
36  const size_t numXrows = gridMap.getSize().x();
37  const size_t numYrows = gridMap.getSize().y();
38  Gridmap3dLookup::size_t_3d gridsize = {numXrows, numYrows, numZLayers};
39 
40  // Initialize 3D lookup
41  gridmap3DLookup_ = Gridmap3dLookup(gridsize, gridOrigin, gridMap.getResolution());
42 
43  // Allocate the internal data structure
45 
46  // Check for NaN
47  const auto& elevationData = gridMap.get(elevationLayer);
48  if (elevationData.hasNaN()) {
49  std::cerr
50  << "[grid_map_sdf::SignedDistanceField] elevation data contains NaN. The generated SDF will be invalid! Apply inpainting first"
51  << std::endl;
52  }
53 
54  // Compute the SDF
55  computeSignedDistance(elevationData);
56 }
57 
58 double SignedDistanceField::value(const Position3& position) const noexcept {
59  const auto nodeIndex = gridmap3DLookup_.nearestNode(position);
60  const auto nodePosition = gridmap3DLookup_.nodePosition(nodeIndex);
61  const auto nodeData = data_[gridmap3DLookup_.linearIndex(nodeIndex)];
62  const auto jacobian = derivative(nodeData);
63  return distance(nodeData) + jacobian.dot(position - nodePosition);
64 }
65 
67  const auto nodeIndex = gridmap3DLookup_.nearestNode(position);
68  const auto nodeData = data_[gridmap3DLookup_.linearIndex(nodeIndex)];
69  return derivative(nodeData);
70 }
71 
72 std::pair<double, SignedDistanceField::Derivative3> SignedDistanceField::valueAndDerivative(const Position3& position) const noexcept {
73  const auto nodeIndex = gridmap3DLookup_.nearestNode(position);
74  const auto nodePosition = gridmap3DLookup_.nodePosition(nodeIndex);
75  const auto nodeData = data_[gridmap3DLookup_.linearIndex(nodeIndex)];
76  const auto jacobian = derivative(nodeData);
77  return {distance(nodeData) + jacobian.dot(position - nodePosition), jacobian};
78 }
79 
81  const auto gridOriginZ = static_cast<float>(gridmap3DLookup_.gridOrigin_.z());
82  const auto resolution = static_cast<float>(gridmap3DLookup_.resolution_);
83  const auto minHeight = elevation.minCoeff();
84  const auto maxHeight = elevation.maxCoeff();
85 
86  /*
87  * General strategy to reduce the amount of transposing:
88  * - SDF at a height is in transposed form after computing it.
89  * - Take the finite difference in dx, now that this data is continuous in memory.
90  * - Transpose the SDF
91  * - Take other finite differences. Now dy is efficient.
92  * - When writing to the 3D structure, keep in mind that dx is still transposed.
93  */
94 
95  // Memory needed to compute the SDF at a layer
96  Matrix tmp; // allocated on first use
97  Matrix tmpTranspose; // allocated on first use
98  Matrix sdfTranspose; // allocated on first use
99 
100  // Memory needed to keep a buffer of layers. We need 3 due to the central difference
101  Matrix currentLayer; // allocated on first use
102  Matrix nextLayer; // allocated on first use
103  Matrix previousLayer; // allocated on first use
104 
105  // Memory needed to compute finite differences
106  Matrix dxTranspose = Matrix::Zero(elevation.cols(), elevation.rows());
107  Matrix dxNextTranspose = Matrix::Zero(elevation.cols(), elevation.rows());
108  Matrix dy = Matrix::Zero(elevation.rows(), elevation.cols());
109  Matrix dz = Matrix::Zero(elevation.rows(), elevation.cols());
110 
111  // Compute SDF of first layer
112  computeLayerSdfandDeltaX(elevation, currentLayer, dxTranspose, sdfTranspose, tmp, tmpTranspose, gridOriginZ, resolution, minHeight,
113  maxHeight);
114 
115  // Compute SDF of second layer
116  computeLayerSdfandDeltaX(elevation, nextLayer, dxNextTranspose, sdfTranspose, tmp, tmpTranspose, gridOriginZ + resolution, resolution,
117  minHeight, maxHeight);
118 
119  // First layer: forward difference in z
120  layerFiniteDifference(currentLayer, nextLayer, dz, resolution); // dz / layer = +resolution
121  columnwiseCentralDifference(currentLayer, dy, -resolution); // dy / dcol = -resolution
122 
123  emplacebackLayerData(currentLayer, dxTranspose, dy, dz);
124 
125  // Middle layers: central difference in z
126  for (size_t layerZ = 1; layerZ + 1 < gridmap3DLookup_.gridsize_.z; ++layerZ) {
127  // Circulate layer buffers
128  previousLayer.swap(currentLayer);
129  currentLayer.swap(nextLayer);
130  dxTranspose.swap(dxNextTranspose);
131 
132  // Compute SDF of next layer
133  computeLayerSdfandDeltaX(elevation, nextLayer, dxNextTranspose, sdfTranspose, tmp, tmpTranspose,
134  gridOriginZ + (layerZ + 1) * resolution, resolution, minHeight, maxHeight);
135 
136  // Compute other finite differences
137  layerCentralDifference(previousLayer, nextLayer, dz, resolution);
138  columnwiseCentralDifference(currentLayer, dy, -resolution);
139 
140  // Add the data to the 3D structure
141  emplacebackLayerData(currentLayer, dxTranspose, dy, dz);
142  }
143 
144  // Circulate layer buffers one last time
145  previousLayer.swap(currentLayer);
146  currentLayer.swap(nextLayer);
147  dxTranspose.swap(dxNextTranspose);
148 
149  // Last layer: backward difference in z
150  layerFiniteDifference(previousLayer, currentLayer, dz, resolution);
151  columnwiseCentralDifference(currentLayer, dy, -resolution);
152 
153  // Add the data to the 3D structure
154  emplacebackLayerData(currentLayer, dxTranspose, dy, dz);
155 }
156 void SignedDistanceField::computeLayerSdfandDeltaX(const Matrix& elevation, Matrix& currentLayer, Matrix& dxTranspose, Matrix& sdfTranspose,
157  Matrix& tmp, Matrix& tmpTranspose, float height, float resolution, float minHeight,
158  float maxHeight) const {
159  // Compute SDF + dx of layer: compute sdfTranspose -> take dxTranspose -> transpose to get sdf
160  signedDistanceAtHeightTranspose(elevation, sdfTranspose, tmp, tmpTranspose, height, resolution, minHeight, maxHeight);
161  columnwiseCentralDifference(sdfTranspose, dxTranspose, -resolution); // dx / drow = -resolution
162  currentLayer = sdfTranspose.transpose();
163 }
164 
165 void SignedDistanceField::emplacebackLayerData(const Matrix& signedDistance, const Matrix& dxTranspose, const Matrix& dy,
166  const Matrix& dz) {
167  for (size_t colY = 0; colY < gridmap3DLookup_.gridsize_.y; ++colY) {
168  for (size_t rowX = 0; rowX < gridmap3DLookup_.gridsize_.x; ++rowX) {
169  data_.emplace_back(node_data_t{signedDistance(rowX, colY), dxTranspose(colY, rowX), dy(rowX, colY), dz(rowX, colY)});
170  }
171  }
172 }
173 
174 size_t SignedDistanceField::size() const noexcept {
175  return gridmap3DLookup_.linearSize();
176 }
177 
178 const std::string& SignedDistanceField::getFrameId() const noexcept {
179  return frameId_;
180 }
181 
183  return timestamp_;
184 }
185 
186 void SignedDistanceField::filterPoints(std::function<void(const Position3&, float, const Derivative3&)> func, size_t decimation) const {
187  for (size_t layerZ = 0; layerZ < gridmap3DLookup_.gridsize_.z; layerZ += decimation) {
188  for (size_t colY = 0; colY < gridmap3DLookup_.gridsize_.y; colY += decimation) {
189  for (size_t rowX = 0; rowX < gridmap3DLookup_.gridsize_.x; rowX += decimation) {
190  const Gridmap3dLookup::size_t_3d index3d = {rowX, colY, layerZ};
191  const auto index = gridmap3DLookup_.linearIndex(index3d);
192  func(gridmap3DLookup_.nodePosition(index3d), distanceFloat(data_[index]), derivative(data_[index]));
193  }
194  }
195  }
196 }
197 
198 } // namespace grid_map
const std::string & getFrameId() const noexcept
const Matrix & get(const std::string &layer) const
const Size & getSize() const
static float distanceFloat(const node_data_t &nodeData) noexcept
void signedDistanceAtHeightTranspose(const Matrix &elevationMap, Matrix &sdfTranspose, Matrix &tmp, Matrix &tmpTranspose, float height, float resolution, float minHeight, float maxHeight)
signed_distance_field::Gridmap3dLookup gridmap3DLookup_
Object encoding the 3D grid.
Time timestamp_
Timestamp of the grid map (nanoseconds).
uint64_t Time
double value(const Position3 &position) const noexcept
double getResolution() const
Position3 nodePosition(const size_t_3d &index) const noexcept
std::pair< double, Derivative3 > valueAndDerivative(const Position3 &position) const noexcept
size_t linearIndex(const size_t_3d &index) const noexcept
Eigen::Vector2d Position
std::string frameId_
Frame id of the grid map.
void computeSignedDistance(const Matrix &elevation)
Eigen::MatrixXf Matrix
static double distance(const node_data_t &nodeData) noexcept
void columnwiseCentralDifference(const Matrix &data, Matrix &centralDifference, float resolution)
Derivative3 derivative(const Position3 &position) const noexcept
void layerCentralDifference(const Matrix &data_km1, const Matrix &data_kp1, Matrix &result, float resolution)
void computeLayerSdfandDeltaX(const Matrix &elevation, Matrix &currentLayer, Matrix &dxTranspose, Matrix &sdfTranspose, Matrix &tmp, Matrix &tmpTranspose, float height, float resolution, float minHeight, float maxHeight) const
Eigen::Vector3d Position3
std::array< float, 4 > node_data_t
Data structure to store together {signed distance value, derivative}.
void filterPoints(std::function< void(const Position3 &, float, const Derivative3 &)> func, size_t decimation=1) const
std::vector< node_data_t > data_
Object encoding the signed distance value and derivative in the grid.
void layerFiniteDifference(const Matrix &data_k, const Matrix &data_kp1, Matrix &result, float resolution)
SignedDistanceField(const GridMap &gridMap, const std::string &elevationLayer, double minHeight, double maxHeight)
bool getPosition(const Index &index, Position &position) const
void emplacebackLayerData(const Matrix &signedDistance, const Matrix &dxTranspose, const Matrix &dy, const Matrix &dz)
size_t_3d nearestNode(const Position3 &position) const noexcept
Position3 gridOrigin_
Origin position of the grid.


grid_map_sdf
Author(s): Takahiro Miki , Péter Fankhauser
autogenerated on Wed Jul 5 2023 02:23:42