336 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
/**
 | 
						|
 *      @file testOccupancyGrid.cpp
 | 
						|
 *      @date May 14, 2012
 | 
						|
 *      @author Brian Peasley
 | 
						|
 *      @author Frank Dellaert
 | 
						|
 */
 | 
						|
 | 
						|
#include <CppUnitLite/TestHarness.h>
 | 
						|
 | 
						|
#if 0
 | 
						|
 | 
						|
#include <gtsam/discrete/DiscreteFactorGraph.h>
 | 
						|
#include <gtsam/geometry/Pose2.h>
 | 
						|
 | 
						|
#include <vector>
 | 
						|
#include <cmath.>
 | 
						|
#include <random>
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
using namespace std;
 | 
						|
using namespace gtsam;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Laser Factor
 | 
						|
 * @brief factor that encodes a laser measurements likelihood.
 | 
						|
 */
 | 
						|
 | 
						|
class LaserFactor : public DiscreteFactor{
 | 
						|
private:
 | 
						|
  vector<Index>   m_cells;  ///cells in which laser passes through
 | 
						|
 | 
						|
public:
 | 
						|
 | 
						|
  ///constructor
 | 
						|
  LaserFactor(const vector<Index> &cells) : m_cells(cells) {}
 | 
						|
 | 
						|
  /**
 | 
						|
   * Find value for given assignment of values to variables
 | 
						|
   * return 1000 if any of the non-last cell is occupied and 1 otherwise
 | 
						|
   * Values contains all occupancy values (0 or 1)
 | 
						|
   */
 | 
						|
  virtual double operator()(const Values &vals) const{
 | 
						|
 | 
						|
    // loops through all but the last cell and checks that they are all 0.  Otherwise return 1000.
 | 
						|
    for(Index i = 0; i < m_cells.size() - 1; i++){
 | 
						|
      if(vals.at(m_cells[i]) == 1)
 | 
						|
        return 1000;
 | 
						|
    }
 | 
						|
 | 
						|
    // check if the last cell hit by the laser is 1.  return 900 otherwise.
 | 
						|
    if(vals.at(m_cells[m_cells.size() - 1]) == 0)
 | 
						|
      return 900;
 | 
						|
 | 
						|
    return 1;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /// Multiply in a DecisionTreeFactor and return the result as DecisionTreeFactor
 | 
						|
  virtual DecisionTreeFactor operator*(const DecisionTreeFactor&) const{
 | 
						|
    throw runtime_error("operator * not implemented");
 | 
						|
  }
 | 
						|
 | 
						|
  virtual DecisionTreeFactor toDecisionTreeFactor() const{
 | 
						|
    throw runtime_error("DecisionTreeFactor toDecisionTreeFactor not implemented");
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * OccupancyGrid Class
 | 
						|
 * An occupancy grid is just a factor graph.
 | 
						|
 * Every cell in the occupancy grid is a variable in the factor graph.
 | 
						|
 * Measurements will create factors, as well as the prior.
 | 
						|
 */
 | 
						|
class OccupancyGrid : public DiscreteFactorGraph {
 | 
						|
private:
 | 
						|
  size_t      width_;    //number of cells wide the grid is
 | 
						|
  size_t      height_;  //number of cells tall the grid is
 | 
						|
  double      res_;    //the resolution at which the grid is created
 | 
						|
 | 
						|
  vector<Index>   cells_;      //list of keys of all cells in the grid
 | 
						|
  vector<Index>   laser_indices_; //indices of the laser factor in factors_
 | 
						|
 | 
						|
 | 
						|
public:
 | 
						|
 | 
						|
  size_t width() const {
 | 
						|
    return width_;
 | 
						|
  }
 | 
						|
  size_t height() const {
 | 
						|
    return height_;
 | 
						|
  }
 | 
						|
  // should we just not typedef Values Occupancy; ?
 | 
						|
  class Occupancy : public Values {
 | 
						|
  private:
 | 
						|
  public:
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  typedef std::vector<double> Marginals;
 | 
						|
  ///constructor
 | 
						|
  ///Creates a 2d grid of cells with the origin in the center of the grid
 | 
						|
  OccupancyGrid(double width, double height, double resolution){
 | 
						|
    width_     =   width/resolution;
 | 
						|
    height_   =   height/resolution;
 | 
						|
    res_    =  resolution;
 | 
						|
 | 
						|
    for(Index i = 0; i < cellCount(); i++)
 | 
						|
      cells_.push_back(i);
 | 
						|
  }
 | 
						|
 | 
						|
  /// Returns an empty occupancy grid of size width_ x height_
 | 
						|
  Occupancy emptyOccupancy(){
 | 
						|
    Occupancy    occupancy;    //mapping from Index to value (0 or 1)
 | 
						|
    for(size_t i = 0; i < cellCount(); i++)
 | 
						|
      occupancy.insert(pair<Index, size_t>((Index)i,0));
 | 
						|
 | 
						|
    return occupancy;
 | 
						|
  }
 | 
						|
 | 
						|
  ///add a prior
 | 
						|
  void addPosePrior(Index cell, double prior){
 | 
						|
    size_t numStates = 2;
 | 
						|
    DiscreteKey key(cell, numStates);
 | 
						|
 | 
						|
    //add a factor
 | 
						|
    vector<double> table(2);
 | 
						|
    table[0] = 1-prior;
 | 
						|
    table[1] = prior;
 | 
						|
    add(key, table);
 | 
						|
  }
 | 
						|
 | 
						|
  ///add a laser measurement
 | 
						|
  void addLaser(const Pose2 &pose, double range){
 | 
						|
    //ray trace from pose to range t//a >= 1 accept new stateo find all cells the laser passes through
 | 
						|
    double x = pose.x();    //start position of the laser
 | 
						|
    double y = pose.y();
 | 
						|
    double step = res_/8.0;  //amount to step in each iteration of laser traversal
 | 
						|
 | 
						|
    Index       key;
 | 
						|
    vector<Index>   cells;    //list of keys of cells hit by the laser
 | 
						|
 | 
						|
    //traverse laser
 | 
						|
    for(double i = 0; i < range; i += step){
 | 
						|
      //get point on laser
 | 
						|
      x = pose.x() + i*cos(pose.theta());
 | 
						|
      y = pose.y() + i*sin(pose.theta());
 | 
						|
 | 
						|
      //printf("%lf %lf\n", x, y);
 | 
						|
      //get the key of the cell that holds point (x,y)
 | 
						|
      key = keyLookup(x,y);
 | 
						|
 | 
						|
      //add cell to list of cells if it is new
 | 
						|
      if(i == 0 || key != cells[cells.size()-1])
 | 
						|
        cells.push_back(key);
 | 
						|
    }
 | 
						|
 | 
						|
//    for(size_t i = 0; i < cells.size(); i++)
 | 
						|
//      printf("%ld ", cells[i]);
 | 
						|
//    printf("\n");
 | 
						|
 | 
						|
    //add a factor that connects all those cells
 | 
						|
    laser_indices_.push_back(factors_.size());
 | 
						|
    push_back(boost::make_shared<LaserFactor>(cells));
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /// returns the number of cells in the grid
 | 
						|
  size_t cellCount() const {
 | 
						|
    return width_*height_;
 | 
						|
  }
 | 
						|
 | 
						|
  /// returns the key of the cell in which point (x,y) lies.
 | 
						|
  Index keyLookup(double x, double y) const {
 | 
						|
    //move (x,y) to the nearest resolution
 | 
						|
    x *= (1.0/res_);
 | 
						|
    y *= (1.0/res_);
 | 
						|
 | 
						|
    //round to nearest integer
 | 
						|
    x = (double)((int)x);
 | 
						|
    y = (double)((int)y);
 | 
						|
 | 
						|
    //determine index
 | 
						|
    x += width_/2;
 | 
						|
    y = height_/2 - y;
 | 
						|
 | 
						|
    //bounds checking
 | 
						|
    size_t index = y*width_ + x;
 | 
						|
    index = index >= width_*height_ ? -1 : index;
 | 
						|
 | 
						|
    return cells_[index];
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * @brief Computes the value of a laser factor
 | 
						|
   * @param index defines which laser is to be used
 | 
						|
   * @param occupancy defines the grid which the laser will be evaulated with
 | 
						|
   * @ret a double value that is the value of the specified laser factor for the grid
 | 
						|
   */
 | 
						|
  double laserFactorValue(Index index, const Occupancy &occupancy) const{
 | 
						|
    return (*factors_[ laser_indices_[index] ])(occupancy);
 | 
						|
  }
 | 
						|
 | 
						|
  /// returns the sum of the laser factors for the current state of the grid
 | 
						|
  double operator()(const Occupancy &occupancy) const {
 | 
						|
    double value = 0;
 | 
						|
 | 
						|
    // loop over all laser factors in the graph
 | 
						|
    //printf("%ld\n", (*this).size());
 | 
						|
 | 
						|
    for(Index i = 0; i < laser_indices_.size(); i++){
 | 
						|
      value += laserFactorValue(i, occupancy);
 | 
						|
    }
 | 
						|
 | 
						|
    return value;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * @brief Run a metropolis sampler.
 | 
						|
   * @param iterations defines the number of iterations to run.
 | 
						|
   * @return  vector of marginal probabilities.
 | 
						|
   */
 | 
						|
  Marginals runMetropolis(size_t iterations){
 | 
						|
    Occupancy occupancy = emptyOccupancy();
 | 
						|
 | 
						|
    size_t size = cellCount();
 | 
						|
    Marginals marginals(size);
 | 
						|
 | 
						|
    // NOTE: using older interface for boost.random due to interface changes after boost 1.46
 | 
						|
    std::mt19937 rng;
 | 
						|
    std::uniform_int_distribution<> random_cell(0, size - 1);
 | 
						|
 | 
						|
    // run Metropolis for the requested number of operations
 | 
						|
    // compute initial probability of occupancy grid, P(x_t)
 | 
						|
 | 
						|
    double Px = (*this)(occupancy);
 | 
						|
 | 
						|
    for(size_t it = 0; it < marginals.size(); it++)
 | 
						|
      marginals[it] = 0;
 | 
						|
 | 
						|
    for(size_t it = 0; it < iterations; it++){
 | 
						|
      //choose a random cell
 | 
						|
      Index x = random_cell(rng);
 | 
						|
      //printf("%ld:",x);
 | 
						|
      //flip the state of a random cell, x
 | 
						|
         occupancy[x] = 1 - occupancy[x];
 | 
						|
 | 
						|
      //compute probability of new occupancy grid, P(x')
 | 
						|
      //by summing over all LaserFactor::operator()
 | 
						|
         double Px_prime = (*this)(occupancy);
 | 
						|
 | 
						|
      //occupancy.print();
 | 
						|
      //calculate acceptance ratio, a
 | 
						|
        double a = Px_prime/Px;
 | 
						|
 | 
						|
      //if a <= 1 otherwise accept with probability a
 | 
						|
      //if we accept the new state P(x_t) = P(x')
 | 
						|
      //  printf(" %.3lf %.3lf\t", Px, Px_prime);
 | 
						|
        if(a <= 1){
 | 
						|
          Px = Px_prime;
 | 
						|
          //printf("\taccept\n");
 | 
						|
        }
 | 
						|
        else{
 | 
						|
           occupancy[x] = 1 - occupancy[x];
 | 
						|
          // printf("\treject\n");
 | 
						|
        }
 | 
						|
 | 
						|
      //increment the number of iterations each cell has been on
 | 
						|
        for(size_t i = 0; i < size; i++){
 | 
						|
          if(occupancy[i] == 1)
 | 
						|
            marginals[i]++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    //compute the marginals
 | 
						|
    for(size_t it = 0; it < size; it++)
 | 
						|
      marginals[it] /= iterations;
 | 
						|
 | 
						|
    return marginals;
 | 
						|
  }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
/* ************************************************************************* */
 | 
						|
TEST( OccupancyGrid, Test1) {
 | 
						|
  //Build a small grid and test optimization
 | 
						|
 | 
						|
  //Build small grid
 | 
						|
  double width     =  3;     //meters
 | 
						|
  double height     =   2;     //meters
 | 
						|
  double resolution   =   0.5;   //meters
 | 
						|
  OccupancyGrid occupancyGrid(width, height, resolution); //default center to middle
 | 
						|
 | 
						|
  //Add measurements
 | 
						|
  Pose2 pose(0,0,0);
 | 
						|
  double range = 1;
 | 
						|
 | 
						|
  occupancyGrid.addPosePrior(0, 0.7);
 | 
						|
  EXPECT_LONGS_EQUAL(1, occupancyGrid.size());
 | 
						|
 | 
						|
  occupancyGrid.addLaser(pose, range);
 | 
						|
  EXPECT_LONGS_EQUAL(2, occupancyGrid.size());
 | 
						|
 | 
						|
  OccupancyGrid::Occupancy occupancy = occupancyGrid.emptyOccupancy();
 | 
						|
  EXPECT_LONGS_EQUAL(900, occupancyGrid.laserFactorValue(0,occupancy));
 | 
						|
 | 
						|
 | 
						|
  occupancy[16] = 1;
 | 
						|
  EXPECT_LONGS_EQUAL(1, occupancyGrid.laserFactorValue(0,occupancy));
 | 
						|
 | 
						|
  occupancy[15] = 1;
 | 
						|
  EXPECT_LONGS_EQUAL(1000, occupancyGrid.laserFactorValue(0,occupancy));
 | 
						|
 | 
						|
  occupancy[16] = 0;
 | 
						|
  EXPECT_LONGS_EQUAL(1000, occupancyGrid.laserFactorValue(0,occupancy));
 | 
						|
 | 
						|
 | 
						|
  //run MCMC
 | 
						|
  OccupancyGrid::Marginals occupancyMarginals = occupancyGrid.runMetropolis(50000);
 | 
						|
  EXPECT_LONGS_EQUAL( (width*height)/pow(resolution,2), occupancyMarginals.size());
 | 
						|
 | 
						|
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/* ************************************************************************* */
 | 
						|
int main() {
 | 
						|
  TestResult tr;
 | 
						|
  return TestRegistry::runAllTests(tr);
 | 
						|
}
 | 
						|
/* ************************************************************************* */
 | 
						|
 |