You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
11 KiB
C++

/*
* scanToGrid implementation
*
* Copyright (C) Uwe Hebbelmann, Sebastian Stock, Andre Schemschat
*
* Released under the GPL version 3.
*
*/
#include <cmath>
#include <iostream>
#include <map>
#include "grid/scanToGrid.h"
using std::vector;
using std::map;
/**
* CTor.
*
* @param resolution the resolution of the grid (centimeters per gridunit)
* @param minRelevantHeight The minimal height of a scan that is relevant
* @param maxRelevantHeight The maximal height of a scan that is relevant
* @param maxDistance The maximal relevant distance
* @param minimalWeighting The minimal weighting for the waypoints
* @param createWaypoints If set waypoints will be created
* @param neighbours If set neighbours will be created
*/
scanToGrid::scanToGrid(double resolution,
double minRelevantHeight, double maxRelevantHeight,
int maxDistance, int spotRadius,
bool createWaypoints, bool neighbours)
{
this->resolution = resolution;
this->minRelevantHeight = minRelevantHeight;
this->maxRelevantHeight = maxRelevantHeight;
this->waypoints = createWaypoints;
this->neighbours = neighbours;
this->spot = (float)spotRadius / 3000.0;
// Calculate minimal weighting
this->minimalWeighting = 1;//(1((float)minimalWeighting-1)/
// (float)scaleToGrid(maxDistance));
}
/**
* The method checks if the given point is relevant
* for creating the grid.
* It is not relevant if it is below or above a specified
* height, or if it lies to far away (??)
*
* @param p The point to check (not scaled to grid)
* @return True if the point is relevant
*/
bool scanToGrid::isPointRelevant(const Point &p) const
{
if(p.y < this->minRelevantHeight || p.y > this->maxRelevantHeight)
{
return false;
}
else
return true;
}
/**
* The method scales the absolute coordinate of the scanpoint
* to the matching coordinate of the gridpoint
*
* @param point the absolute coordinate of the scanpoint
*
* @return the matching coordiante of the gridpoint
*/
long scanToGrid::scaleToGrid(double point)
{
return (long)(point / this->resolution);
}
/**
* The methods calculates the norm vector of the vector given
* through x and z.
* The normvector has one direction oflength 1 or -1 and the other
* length in [-1,1]
* @param x The x component of the vector
* @param z the z component of the vector
* @param xnorm Referenz to the double containing the normalized x value afterwards
* @param znorm Referenz to the double containing the normalized z value afterwards
*/
void scanToGrid::calculateNormvector(long x, long z,
double& xnorm, double& znorm)
{
xnorm = 0;
znorm = 0;
// if |X| < |Z|, then norm z to 1 and x to x/z
if(abs(x) < abs(z))
{
if(z == 0)
return;
xnorm = (double)x / (double)abs(z);
znorm = z / abs(z); // now znorm is 1 or -1
}
else
{
if(x == 0)
return;
xnorm = x / abs(x); // now xnorm is 1 or -1
znorm = (double)z / (double)abs(x);
}
}
/**
* The method creates a new grid. It determines the size and offset
* of the new grid and calculates the viewpoint of the roboter during
* the scan.
* The new scangrid is allocated using new, but the pointer is not stored,
* so it must be freed by the calling method.
*
* The scan has to be transformed to absolute values before this method is
* called
*
* @param scan the scan to be transformed
* @param transformation the transformationmatrix needed for calculating
* the viewpoint of the roboter
*
* @return pointer to the created grid.
* (Just gets allocated, does not get freed)
*/
scanGrid* scanToGrid::createGrid(Scan& scan,
const double* transformation)
{
// Calculate the viewpoint of the scan
double rPos[3];
double rPosTheta[3];
Matrix4ToEuler(transformation, rPosTheta, rPos);
long vpX = scaleToGrid(rPos[0]);
long vpZ = scaleToGrid(rPos[2]);
// calculate maximal and minimal coordiantes
double maxX = 0;
double maxZ = 0;
double minX = 0;
double minZ = 0;
DataXYZ xyz(scan.get("xyz"));
for(size_t i = 0; i < xyz.size(); ++i)
{
Point it = xyz[i];
// If the scan is not relevant, skip it
if(!isPointRelevant(it))
continue;
if(it.x < minX) minX = it.x;
if(it.x > maxX) maxX = it.x;
if(it.z < minZ) minZ = it.z;
if(it.z > maxZ) maxZ = it.z;
}
// get offsets and sizes
long offsetX = scaleToGrid(minX);
long offsetZ = scaleToGrid(minZ);
long sizeX = scaleToGrid(maxX) - offsetX + 1;
long sizeZ = scaleToGrid(maxZ) - offsetZ + 1;
//returning new grid
return new scanGrid(vpX, vpZ,
offsetX, offsetZ,
sizeX, sizeZ);
}
/**
*
* !NOTE! Experimental. Currently this is not used! !NOTE!
*
* The method iterates through the grid and checks for each point
* if it has a specified amount of neighbours. If not enough neighbours
* are found, the point is deleted.
*
* @param grid The grid to check
* @param distance
* @param neighbours The amount of neighbours needed for not getting deleted
*/
void scanToGrid::killAlonePoints(scanGrid* grid,
int distance, int neighbours)
{
std::vector< gridPoint* > exPoints;
for(int i=0; i < grid->getSizeX(); ++i)
{
for(int j=0; j < grid->getSizeZ(); ++j)
{
int found = 0;
for(int a=i - distance; a < i + distance; ++a)
{
for(int b=i - distance; b < i + distance; ++b)
{
if(a > 0 && b > 0 &&
a < grid->getSizeX() && b < grid->getSizeZ())
{
if(grid->points[a][b]->getPercent() > 0)
{
++found;
}
}
}
}
// not enough neigbours found
if(found < neighbours)
{
exPoints.push_back(grid->points[i][j]);
}
}
}
vector<gridPoint*>::iterator it = exPoints.begin();
vector<gridPoint*>::iterator end = exPoints.end();
while(it != end)
{
//std::cout << "Deleting " << (*it)->getX() << " " << (*it)->getZ() << " " << std::endl;
(*it)->setFixed(0, 0);
++it;
}
}
/**
* The method adds additional information about the neighbours of
* the given point based on some sort of probabilityfunction.
* Given the degree of the scanner, the function calculates the insecure
* points and weights them
*
* @param grid The grid to add the information to
* @param x The absolute x coordinate
* @param z The absolute z coordinate
* @param weighting The weighting of the point
*/
void scanToGrid::createNeighbours(scanGrid *grid, long x, long z, float weighting)
{
// Calculate direction of the neighbours
long dx = - (grid->getViewpointZ() - z);
long dz = grid->getViewpointX() - x;
// calculating the normvector of them
double xnorm, znorm;
calculateNormvector(dx, dz, xnorm, znorm);
// calculating number of neighbours to weight
int r = (int) (sqrt((double)(dx*dx + dz*dz)) * this->spot);
for(int i = -r; i <= r; ++i)
{
// Skip 0 (0 is the actual point on the vector)
if(i == 0)
continue;
long px = x + (int) (i * xnorm);
long pz = z + (int) (i * znorm);
// if calculated point not in grid, skip
if( !grid->contains(px, pz))
continue;
float weight = weighting / pow(2.0, abs(i*r));
// grid->addPoint(px, pz,
// SOLIDWEIGHT * weight, SOLIDWEIGHT * weight);
grid->addPoint(px, pz, SOLIDWEIGHT, SOLIDWEIGHT);
if(this->waypoints)
createWaypoints(grid, px, pz, weight);
}
}
/**
* The method calculates the free waypoints between the robot and
* the occupied point and adds them to the grid.
* The way is weighted with the weighting-parameter to avoid
* that a path overwrites found obstacles
*
* @param grid pointer to the grid to which the points will be added
* @param x the x-coordinate of the occupied point
* @param z the z-coordinate of the occupied point
* @param weighting The weighting of the point on which the path is based.
*/
void scanToGrid::createWaypoints(scanGrid *grid, long x, long z, float weighting)
{
if(x == 0 || z == 0)
return;
long dx = x - grid->getViewpointX();
long dz = z - grid->getViewpointZ();
// calculating the normal vector
double xnorm, znorm;
calculateNormvector(dx, dz, xnorm, znorm);
int distance = (int)floor(sqrt(pow((double)dx, 2) + pow((double)dz, 2)) /
sqrt(pow(xnorm, 2) + pow(znorm, 2)));
for(int i=0; i < distance; ++i)
{
long px = grid->getViewpointX() + (long)(i * xnorm);
long pz = grid->getViewpointZ() + (long)(i * znorm);
// due to some problems with the viewpoint lieing outside
// of the grid, first check if point is in grid
if(grid->contains(px, pz))
//grid->addPoint(px, pz, (int)(WAYPOINTWEIGHT * weighting), 0);
grid->addPoint(px, pz, WAYPOINTWEIGHT, 0);
}
}
/**
* The method adds the point represented by the 2 coordinates. It weights
* the point based on weighting.
*
* @param grid The grid to add the point to
* @param x The x coordinate (scaled to grid)
* @param z The z coordinate (scaled to grid)
* @param weighting Importance of the point (0 <= w <= 1)
*/
void scanToGrid::createPoint(scanGrid *grid, long x, long z, float weighting)
{
// grid->addPoint(x, z, SOLIDWEIGHT, (int)(SOLIDWEIGHT * weighting));
grid->addPoint(x, z, SOLIDWEIGHT, SOLIDWEIGHT);
}
/**
* Converts a scan(3D) to a grid(2D). It iterates through each
* found point of the scan, translates it to the grid and adds it
* to the grid. Points that are somehow not relevant to the grid (e.g. points
* that are to low or to high are ignored. See isPointRelevant)
*
* If waypoints is true, it will also create waypoints from the origin of
* the scan to the found point and mark the space in between as free.
* If Neighbours should be created, it will also weight the surrounding
* spaces of the found point.
*
* @param scan The scan to be converted
* @param transformation The transformationmatrix
* @return pointer to the grid
*/
scanGrid* scanToGrid::convert(Scan& scan, const double* transformation)
{
scanGrid* grid = createGrid(scan, transformation);
DataXYZ xyz(scan.get("xyz"));
// go through all points and create the grid
for(size_t i = 0; i < xyz.size(); ++i)
{
Point p = xyz[i];
// if the scan is in the relevant area, create Point for it
if(isPointRelevant(p))
{
long x = scaleToGrid(p.x);
long z = scaleToGrid(p.z);
float weighting = calculateWeighting(x - grid->getViewpointX(),
z - grid->getViewpointZ());
createPoint(grid, x, z, weighting);
if(this->waypoints)
createWaypoints(grid, x, z, weighting);
if(this->neighbours)
createNeighbours(grid, x, z, weighting);
}
}
// Kill all points which have less then 8 out of 25 neighbours
/*killAlonePoints(grid, 3, 1);
for(int i = 0; i < grid->getSizeX(); ++i)
{
for(int j = 0; j < grid->getSizeZ(); ++j)
{
gridPoint *p = grid->points[i][j];
float weighting = calculateWeighting(p->getX() - grid->getViewpointX(), p->getZ() - grid->getViewpointZ());
if(this->waypoints)
createWaypoints(grid, p->getX(), p->getZ(), weighting);
if(this->neighbours)
createNeighbours(grid, p->getX(), p->getZ(), weighting);
}
}*/
return grid;
}