gtsam/gtsam_unstable/slam/tests/testOccupancyGrid.cpp

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(std::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);
}
/* ************************************************************************* */