cartographer-project / cartographer

Cartographer is a system that provides real-time simultaneous localization and mapping (SLAM) in 2D and 3D across multiple platforms and sensor configurations.
Apache License 2.0
7.09k stars 2.25k forks source link

Editing Cell Values From a known Global pose #1792

Closed JakeInit closed 3 years ago

JakeInit commented 3 years ago

It possible to edit cell values that corresponds to probabilities of hit or misses in the map by viewing the mutable gird data of the pbstream.

Cells are stored in submaps correct? So if the global pose is known for the cell value that I want changed, how do I know which submap to look at in order to change it? Can the same cell appear in multiple submaps?

I believe I'm having difficulty imagining how submaps are stacked or what features are matched in order to match them? Do the constraints/nodes play a role in this? My assumption was yes, but I was not able to understand why when reading the paper "Real-Time Loop Closure in 2D LIDAR SLAM". I saw matching is done using ceres. Do I have to reverse-engineer ceres to be able to understand the matching.

https://storage.googleapis.com/pub-tools-public-publication-data/pdf/45466.pdf.

Any understanding of submaps would be appreciated. I'm mainly looking for clues on how to pinpoint the cell values based on global poses. Thanks.

wohe commented 3 years ago

Could you provide some context on what you are trying to achieve? This would make it easier to give you a good answer.

That said:

  1. Submaps can overlap, i.e. there can be multiple submaps which have data for a single point in space
  2. The general setup is still the same as described in the paper: constraints are between a node and a submap, they get computed using a branch-and-bound approach, see fast_correlative_scan_matcher_3d.h
  3. For the purpose of constraints to submaps, the Ceres-based scan matcher is merely used to compute a refinement after a match was found, not to find the match in the first place
  4. We depend on Ceres to solve our optimization problems, but you do not have to understand its implementation to make sense of Cartographer's source code

Hope that helps a bit.

JakeInit commented 3 years ago

The above does help me understand further what I need to do to accomplish my task and I have made a decent amount of progress now.

I am trying to be able to remove objects from a map. On the PGM file, the user should be able to remove a crate, for example, that was included in the map and maybe was not supposed to be there. So far I have been able to read in all the pixel values of the PGM file, and then convert those values to Cairo, and then to a cell value. I would like to then edit the appropriate mutable cell value in the pbstream. This would mean looking at each individual submap and editing the appropriate cell. Based on what you mentioned above, some cells may need edited in multiple submaps and some may not exist in a submap.

So I would need a way to determine if the pixel should even exist in a submap. Here is what I have.

  1. All pixel values (converted to cell values) stored in a vector. Image origin is at the top left corner of the image (so first element of vector) and increase to the end of each row and down one column starting on the left again.

  2. From some tests, it seems cell values in the pbstream start in the top right corner of the submap image. This is the first element of the vector of cell values in the pbstream for each submap. The elements seem to correlate by moving down the image and then shifting left one column back at the top and then traversing down again.

  3. I know the origin of the global map is referenced by the first point of the first submap.

  4. It seems I have a global pose for each submap from the pbstream. I assume this is in relation to the origin of the global map (or first submap). Therefore I can rotate and translate each submap to a common frame. Example, if submap 1 has global pose {1, 1, 90deg}, then the rotation matrix to place points in submap 1 to be relative to submap 0 would be:

submap1_to_submap0 = cos(90) -sin(90) 1 sin(90) cos(90) 1 0 0 1

  1. I have stored the x and y offset from the image origin to the global map origin into a YAML file when I created the map pbstream and pgm image.
io::PaintSubmapSlicesResult result = io::PaintSubmapSlices(submap_slices, resolution);
const Eigen::Vector2d origin(result.origin.x() * resolution, result.origin.y() * resolution);
io::StreamFileWriter yaml_writer("FileName.yaml");

const std::string output =
      "image: " + pgm_filename + "\n" +
          "resolution: " + std::to_string(resolution) + "\n" + "origin: [" +
          std::to_string(origin.x()) + ", " + std::to_string(origin.y()) +
          ", 0.0]\nnegate: 0\noccupied_thresh: 0.65\nfree_thresh: 0.196\n";

  file_writer->Write(output.data(), output.size());
  1. I should now be able to transform image origin to global, and to submap.

So from this point I believe this means I need to get the global bounds of each submap using the transform methods above, determine if a pixel value can fall within the submap based on its global x, y coordinate, and then edit the appropriate cell value based if it can exist. It is a lot of transforms, but this is the only way I have found to be able to accomplish my task.

From the paper it seems nodes are based on found features that will be given a pose and this helps build constraints that represent "edges of a graph". So that means the boundaries of the submap? I wasn't sure if this could provide any advantage.

Also, I do not see much difference in the global pose of a submap vs. the local pose of a submap. The global pose seems to make sense in that it is the pose of the submap in reference to submap 0. What does local pose tell us?

It seems the best solution is to just let Cartographer do its part matching the submaps , storing submap information (submap poses, map origin...), and just use that to my advantage.

If anymore information is required, I'm happy to provide it.

JakeInit commented 3 years ago

My issue seems to correspond closely now with Submap Frame Question #1507. I left a comment there in hopes the original author may have discovered a solution to their original problem or may be able to comment on mine. It is possible the original author moved on seeing they did not get an answer to their last question.

JakeInit commented 3 years ago

Cartographer seems to already have matched the submaps up with the global maps. The only requirement on the user part is to verify if a global point can exist on a submap and then determine which cell it corresponds to. No other transformations are needed once the global coordinate is determined.