/// <summary> /// initialise the grid /// </summary> /// <param name="dimension_cells">number of cells across</param> /// <param name="dimension_cells_vertical">number of cells in the vertical axis</param> /// <param name="cellSize_mm">size of each grid cell in millimetres</param> /// <param name="localisationRadius_mm">radius in millimetres used during localisation within the grid</param> /// <param name="maxMappingRange_mm">maximum range in millimetres used during mapping</param> /// <param name="vacancyWeighting">weighting applied to the vacancy model in the range 0.0 - 1.0</param> private void init(int dimension_cells, int dimension_cells_vertical, int cellSize_mm, int localisationRadius_mm, int maxMappingRange_mm, float vacancyWeighting) { this.dimension_cells = dimension_cells; this.dimension_cells_vertical = dimension_cells_vertical; this.cellSize_mm = cellSize_mm; this.localisation_search_cells = localisationRadius_mm / cellSize_mm; this.max_mapping_range_cells = maxMappingRange_mm / cellSize_mm; this.vacancy_weighting = vacancyWeighting; cell = new occupancygridCellMultiHypothesis[dimension_cells][]; navigable_space = new bool[dimension_cells][]; for (int i = 0; i < cell.Length; i++) { cell[i] = new occupancygridCellMultiHypothesis[dimension_cells]; navigable_space[i] = new bool[dimension_cells]; } // make a lookup table for gaussians - saves doing a lot of floating point maths gaussianLookup = stereoModel.createHalfGaussianLookup(10); garbage = new List<occupancygridCellMultiHypothesis>(); // create a lookup table for log odds calculations, to avoid having // to run slow Log calculations at runtime LogOdds = probabilities.CreateLogOddsLookup(LOG_ODDS_LOOKUP_LEVELS); }
/// <summary> /// inserts the given ray into the grid /// There are three components to the sensor model used here: /// two areas determining the probably vacant area and one for /// the probably occupied space /// </summary> /// <param name="ray">ray object to be inserted into the grid</param> /// <param name="origin">the pose of the robot</param> /// <param name="sensormodel">the sensor model to be used</param> /// <param name="leftcam_x">x position of the left camera in millimetres</param> /// <param name="leftcam_y">y position of the left camera in millimetres</param> /// <param name="rightcam_x">x position of the right camera in millimetres</param> /// <param name="rightcam_y">y position of the right camera in millimetres</param> /// <param name="localiseOnly">if true does not add any mapping particles (pure localisation)</param> /// <returns>matching probability, expressed as log odds</returns> public float Insert(evidenceRay ray, particlePose origin, rayModelLookup sensormodel_lookup, pos3D left_camera_location, pos3D right_camera_location, bool localiseOnly) { // some constants to aid readability const int OCCUPIED_SENSORMODEL = 0; const int VACANT_SENSORMODEL_LEFT_CAMERA = 1; const int VACANT_SENSORMODEL_RIGHT_CAMERA = 2; const int X_AXIS = 0; const int Y_AXIS = 1; // which disparity index in the lookup table to use // we multiply by 2 because the lookup is in half pixel steps int sensormodel_index = (int)Math.Round(ray.disparity * 2); // the initial models are blank, so just default to the one disparity pixel model bool small_disparity_value = false; if (sensormodel_index < 2) { sensormodel_index = 2; small_disparity_value = true; } // beyond a certain disparity the ray model for occupied space // is always only going to be only a single grid cell if (sensormodel_index >= sensormodel_lookup.probability.GetLength(0)) sensormodel_index = sensormodel_lookup.probability.GetLength(0) - 1; float xdist_mm=0, ydist_mm=0, zdist_mm=0, xx_mm=0, yy_mm=0, zz_mm=0; float occupied_dx = 0, occupied_dy = 0, occupied_dz = 0; float intersect_x = 0, intersect_y = 0, intersect_z = 0; float centre_prob = 0, prob = 0, prob_localisation = 0; // probability values at the centre axis and outside float matchingScore = occupancygridCellMultiHypothesis.NO_OCCUPANCY_EVIDENCE; // total localisation matching score int rayWidth = 0; // widest point in the ray in cells int widest_point; // widest point index int step_size = 1; particleGridCell hypothesis; // ray width at the fattest point in cells rayWidth = (int)Math.Round(ray.width / (cellSize_mm * 2)); // calculate the centre position of the grid in millimetres int half_grid_width_mm = dimension_cells * cellSize_mm / 2; //int half_grid_width_vertical_mm = dimension_cells_vertical * cellSize_mm / 2; int grid_centre_x_mm = (int)(x - half_grid_width_mm); int grid_centre_y_mm = (int)(y - half_grid_width_mm); int grid_centre_z_mm = (int)z; int max_dimension_cells = dimension_cells - rayWidth; // in turbo mode only use a single vacancy ray int max_modelcomponent = VACANT_SENSORMODEL_RIGHT_CAMERA; if (TurboMode) max_modelcomponent = VACANT_SENSORMODEL_LEFT_CAMERA; float[][] sensormodel_lookup_probability = sensormodel_lookup.probability; // consider each of the three parts of the sensor model for (int modelcomponent = OCCUPIED_SENSORMODEL; modelcomponent <= max_modelcomponent; modelcomponent++) { // the range from the cameras from which insertion of data begins // for vacancy rays this will be zero, but will be non-zero for the occupancy area int starting_range_cells = 0; switch (modelcomponent) { case OCCUPIED_SENSORMODEL: { // distance between the beginning and end of the probably // occupied area occupied_dx = ray.vertices[1].x - ray.vertices[0].x; occupied_dy = ray.vertices[1].y - ray.vertices[0].y; occupied_dz = ray.vertices[1].z - ray.vertices[0].z; intersect_x = ray.vertices[0].x + (occupied_dx * ray.fattestPoint); intersect_y = ray.vertices[0].y + (occupied_dy * ray.fattestPoint); intersect_z = ray.vertices[0].z + (occupied_dz * ray.fattestPoint); xdist_mm = occupied_dx; ydist_mm = occupied_dy; zdist_mm = occupied_dz; // begin insertion at the beginning of the // probably occupied area xx_mm = ray.vertices[0].x; yy_mm = ray.vertices[0].y; zz_mm = ray.vertices[0].z; break; } case VACANT_SENSORMODEL_LEFT_CAMERA: { // distance between the left camera and the left side of // the probably occupied area of the sensor model xdist_mm = intersect_x - left_camera_location.x; ydist_mm = intersect_y - left_camera_location.y; zdist_mm = intersect_z - left_camera_location.z; // begin insertion from the left camera position xx_mm = left_camera_location.x; yy_mm = left_camera_location.y; zz_mm = left_camera_location.z; step_size = 2; break; } case VACANT_SENSORMODEL_RIGHT_CAMERA: { // distance between the right camera and the right side of // the probably occupied area of the sensor model xdist_mm = intersect_x - right_camera_location.x; ydist_mm = intersect_y - right_camera_location.y; zdist_mm = intersect_z - right_camera_location.z; // begin insertion from the right camera position xx_mm = right_camera_location.x; yy_mm = right_camera_location.y; zz_mm = right_camera_location.z; step_size = 2; break; } } // which is the longest axis ? int longest_axis = X_AXIS; float longest = Math.Abs(xdist_mm); if (Math.Abs(ydist_mm) > longest) { // y has the longest length longest = Math.Abs(ydist_mm); longest_axis = Y_AXIS; } // ensure that the vacancy model does not overlap // the probably occupied area // This is crude and could potentially leave a gap if (modelcomponent != OCCUPIED_SENSORMODEL) longest -= ray.width; int steps = (int)(longest / cellSize_mm); if (steps < 1) steps = 1; // calculate the range from the cameras to the start of the ray in grid cells if (modelcomponent == OCCUPIED_SENSORMODEL) { if (longest_axis == Y_AXIS) starting_range_cells = (int)Math.Abs((ray.vertices[0].y - ray.observedFrom.y) / cellSize_mm); else starting_range_cells = (int)Math.Abs((ray.vertices[0].x - ray.observedFrom.x) / cellSize_mm); } // what is the widest point of the ray in cells if (modelcomponent == OCCUPIED_SENSORMODEL) widest_point = (int)(ray.fattestPoint * steps / ray.length); else widest_point = steps; // calculate increment values in millimetres float x_incr_mm = xdist_mm / steps; float y_incr_mm = ydist_mm / steps; float z_incr_mm = zdist_mm / steps; // step through the ray, one grid cell at a time int grid_step = 0; while (grid_step < steps) { // is this position inside the maximum mapping range bool withinMappingRange = true; if (grid_step + starting_range_cells > max_mapping_range_cells) { withinMappingRange = false; if ((grid_step==0) && (modelcomponent == OCCUPIED_SENSORMODEL)) { grid_step = steps; modelcomponent = 9999; } } // calculate the width of the ray in cells at this point // using a diamond shape ray model int ray_wdth = 0; if (rayWidth > 0) { if (grid_step < widest_point) ray_wdth = grid_step * rayWidth / widest_point; else { if (!small_disparity_value) // most disparity values tail off to some point in the distance ray_wdth = (steps - grid_step + widest_point) * rayWidth / (steps - widest_point); else // for very small disparity values the ray model has an infinite tail // and maintains its width after the widest point ray_wdth = rayWidth; } } // localisation rays are wider, to enable a more effective matching score // which is not too narrowly focussed and brittle int ray_wdth_localisation = ray_wdth + 1; //localisation_search_cells; xx_mm += x_incr_mm*step_size; yy_mm += y_incr_mm*step_size; zz_mm += z_incr_mm*step_size; // convert the x millimetre position into a grid cell position int x_cell = (int)Math.Round((xx_mm - grid_centre_x_mm) / (float)cellSize_mm); if ((x_cell > ray_wdth_localisation) && (x_cell < dimension_cells - ray_wdth_localisation)) { // convert the y millimetre position into a grid cell position int y_cell = (int)Math.Round((yy_mm - grid_centre_y_mm) / (float)cellSize_mm); if ((y_cell > ray_wdth_localisation) && (y_cell < dimension_cells - ray_wdth_localisation)) { // convert the z millimetre position into a grid cell position int z_cell = (int)Math.Round((zz_mm - grid_centre_z_mm) / (float)cellSize_mm); if ((z_cell >= 0) && (z_cell < dimension_cells_vertical)) { int x_cell2 = x_cell; int y_cell2 = y_cell; // get the probability at this point // for the central axis of the ray using the inverse sensor model if (modelcomponent == OCCUPIED_SENSORMODEL) centre_prob = 0.5f + (sensormodel_lookup_probability[sensormodel_index][grid_step] * 0.5f); else // calculate the probability from the vacancy model centre_prob = vacancyFunction(grid_step / (float)steps, steps); // width of the localisation ray for (int width = -ray_wdth_localisation; width <= ray_wdth_localisation; width++) { // is the width currently inside the mapping area of the ray ? bool isInsideMappingRayWidth = false; if ((width >= -ray_wdth) && (width <= ray_wdth)) isInsideMappingRayWidth = true; // adjust the x or y cell position depending upon the // deviation from the main axis of the ray if (longest_axis == Y_AXIS) x_cell2 = x_cell + width; else y_cell2 = y_cell + width; // probability at the central axis prob = centre_prob; prob_localisation = centre_prob; // probabilities are symmetrical about the axis of the ray // this multiplier implements a gaussian distribution around the centre if (width != 0) // don't bother if this is the centre of the ray { // the probability used for wide localisation rays prob_localisation *= gaussianLookup[Math.Abs(width) * 9 / ray_wdth_localisation]; // the probability used for narrower mapping rays if (isInsideMappingRayWidth) prob *= gaussianLookup[Math.Abs(width) * 9 / ray_wdth]; } if ((cell[x_cell2][y_cell2] != null) && (withinMappingRange)) { // only localise using occupancy, not vacancy if (modelcomponent == OCCUPIED_SENSORMODEL) { // update the matching score, by combining the probability // of the grid cell with the probability from the localisation ray float score = occupancygridCellMultiHypothesis.NO_OCCUPANCY_EVIDENCE; if (longest_axis == X_AXIS) score = matchingProbability(x_cell2, y_cell2, z_cell, origin, prob_localisation, ray.colour); if (longest_axis == Y_AXIS) score = matchingProbability(x_cell2, y_cell2, z_cell, origin, prob_localisation, ray.colour); if (score != occupancygridCellMultiHypothesis.NO_OCCUPANCY_EVIDENCE) { if (matchingScore != occupancygridCellMultiHypothesis.NO_OCCUPANCY_EVIDENCE) matchingScore += score; else matchingScore = score; } } } if ((isInsideMappingRayWidth) && (withinMappingRange) && (!localiseOnly)) { // add a new hypothesis to this grid coordinate // note that this is also added to the original pose hypothesis = new particleGridCell(x_cell2, y_cell2, z_cell, prob, origin, ray.colour); if (origin.AddHypothesis(hypothesis, max_mapping_range_cells, dimension_cells, dimension_cells_vertical)) { // generate a grid cell if necessary if (cell[x_cell2][y_cell2] == null) cell[x_cell2][y_cell2] = new occupancygridCellMultiHypothesis(dimension_cells_vertical); cell[x_cell2][y_cell2].AddHypothesis(hypothesis); total_valid_hypotheses++; } } } } else grid_step = steps; // its the end of the ray, break out of the loop } else grid_step = steps; // its the end of the ray, break out of the loop } else grid_step = steps; // time to bail out chaps! grid_step += step_size; } } return (matchingScore); }
/// <summary> /// inserts a simulated doorway into the grid /// </summary> /// <param name="tx_cell">start x cell coordinate</param> /// <param name="ty_cell">start y cell coordinate</param> /// <param name="bx_cell">end x cell coordinate</param> /// <param name="by_cell">end y cell coordinate</param> /// <param name="wall_height_cells">height of the wall in cells</param> /// <param name="door_height_cells">height of the doorway in cells</param> /// <param name="door_width_cells">width of the doorway in cells</param> /// <param name="thickness_cells">thickness of the wall in cells</param> /// <param name="probability_variance">variation of probabilities, typically less than 0.2</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public override void InsertDoorwayCells( int tx_cell, int ty_cell, int bx_cell, int by_cell, int wall_height_cells, int door_height_cells, int door_width_cells, int thickness_cells, float probability_variance, int r, int g, int b) { Random rnd = new Random(0); if (wall_height_cells >= dimension_cells_vertical) wall_height_cells = dimension_cells_vertical - 1; if (thickness_cells < 1) thickness_cells = 1; if (door_height_cells > wall_height_cells) door_height_cells = wall_height_cells; int dx = bx_cell - tx_cell; int dy = by_cell - ty_cell; int length_cells = (int)Math.Sqrt(dx*dx + dy*dy); int door_start_index = (length_cells/2) - (door_width_cells/2); int door_end_index = door_start_index + door_width_cells; for (int i = 0; i < length_cells; i++) { int x_cell = tx_cell + (i * dx / length_cells); if ((x_cell > -1) && (x_cell < dimension_cells)) { int y_cell = ty_cell + (i * dy / length_cells); if ((y_cell > -1) && (y_cell < dimension_cells)) { if (cell[x_cell][y_cell] == null) cell[x_cell][y_cell] = new occupancygridCellMultiHypothesis(dimension_cells_vertical); for (int z_cell = 0; z_cell < wall_height_cells; z_cell++) { occupancygridCellMultiHypothesis c = cell[x_cell][y_cell]; float prob = 0.8f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.99f) prob = 0.99f; if (prob < 0.5f) prob = 0.5f; if ((i >= door_start_index) && (i <= door_end_index) && (z_cell < door_height_cells)) { prob = 0.2f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.4f) prob = 0.4f; if (prob < 0.01f) prob = 0.01f; } c.Distill(z_cell, prob, r, g, b); } } } } }
/// <summary> /// inserts a simulated wall into the grid /// </summary> /// <param name="tx_cell">start x cell coordinate</param> /// <param name="ty_cell">start y cell coordinate</param> /// <param name="bx_cell">end x cell coordinate</param> /// <param name="by_cell">end y cell coordinate</param> /// <param name="height_cells">height of the wall in cells</param> /// <param name="thickness_cells">thickness of the wall in cells</param> /// <param name="probability_variance">variation of probabilities, typically less than 0.2</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public override void InsertWallCells( int tx_cell, int ty_cell, int bx_cell, int by_cell, int height_cells, int thickness_cells, float probability_variance, int r, int g, int b) { Random rnd = new Random(0); if (height_cells >= dimension_cells_vertical) height_cells = dimension_cells_vertical - 1; if (thickness_cells < 1) thickness_cells = 1; int dx = bx_cell - tx_cell; int dy = by_cell - ty_cell; int length_cells = (int)Math.Sqrt(dx*dx + dy*dy); for (int i = 0; i < length_cells; i++) { int x_cell = tx_cell + (i * dx / length_cells); if ((x_cell > -1) && (x_cell < dimension_cells)) { int y_cell = ty_cell + (i * dy / length_cells); if ((y_cell > -1) && (y_cell < dimension_cells)) { if (cell[x_cell][y_cell] == null) cell[x_cell][y_cell] = new occupancygridCellMultiHypothesis(dimension_cells_vertical); for (int z_cell = 0; z_cell < height_cells; z_cell++) { occupancygridCellMultiHypothesis c = cell[x_cell][y_cell]; float prob = 0.8f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.99f) prob = 0.99f; if (prob < 0.5f) prob = 0.5f; c.Distill(z_cell, prob, r, g, b); } } } } }
/// <summary> /// inserts a simulated block into the grid /// </summary> /// <param name="tx_cell">start x cell coordinate</param> /// <param name="ty_cell">start y cell coordinate</param> /// <param name="bx_cell">end x cell coordinate</param> /// <param name="by_cell">end y cell coordinate</param> /// <param name="bottom_height_cells">bottom height of the block in cells</param> /// <param name="top_height_cells">bottom height of the block in cells</param> /// <param name="probability_variance">variation of probabilities, typically less than 0.2</param> /// <param name="r">red colour component in the range 0-255</param> /// <param name="g">green colour component in the range 0-255</param> /// <param name="b">blue colour component in the range 0-255</param> public override void InsertBlockCells( int tx_cell, int ty_cell, int bx_cell, int by_cell, int bottom_height_cells, int top_height_cells, float probability_variance, int r, int g, int b) { Random rnd = new Random(0); if (top_height_cells >= dimension_cells_vertical) top_height_cells = dimension_cells_vertical - 1; if (bottom_height_cells < 0) bottom_height_cells = 0; for (int x_cell = tx_cell; x_cell <= bx_cell; x_cell++) { if ((x_cell > -1) && (x_cell < dimension_cells)) { for (int y_cell = ty_cell; y_cell <= by_cell; y_cell++) { if ((y_cell > -1) && (y_cell < dimension_cells)) { if (cell[x_cell][y_cell] == null) cell[x_cell][y_cell] = new occupancygridCellMultiHypothesis(dimension_cells_vertical); for (int z_cell = bottom_height_cells; z_cell <= top_height_cells; z_cell++) { if ((z_cell > -1) && (z_cell < dimension_cells_vertical)) { float prob = 0.8f + ((float)rnd.NextDouble() * probability_variance * 2) - probability_variance; if (prob > 0.99f) prob = 0.99f; if (prob < 0.5f) prob = 0.5f; cell[x_cell][y_cell].Distill(z_cell, prob, r, g, b); } } } } } } }
public void LoadTile(byte[] data) { // read the bounding box int array_index = 0; const int int32_bytes = 4; int tx = BitConverter.ToInt32(data, 0); int ty = BitConverter.ToInt32(data, int32_bytes); int bx = BitConverter.ToInt32(data, int32_bytes * 2); int by = BitConverter.ToInt32(data, int32_bytes * 3); array_index = int32_bytes * 4; // dimensions of the box int w1 = bx - tx; int w2 = by - ty; //Read binary index as a byte array int no_of_bits = w1 * w2; int no_of_bytes = no_of_bits / 8; if (no_of_bytes * 8 < no_of_bits) no_of_bytes++; byte[] indexData = new byte[no_of_bytes]; for (int i = 0; i < no_of_bytes; i++) indexData[i] = data[array_index + i]; bool[] binary_index = ArrayConversions.ToBooleanArray(indexData); array_index += no_of_bytes; int n = 0; int occupied_cells = 0; for (int y = ty; y < by; y++) { for (int x = tx; x < bx; x++) { if (binary_index[n]) { cell[x][y] = new occupancygridCellMultiHypothesis(dimension_cells_vertical); occupied_cells++; } n++; } } if (occupied_cells > 0) { const int float_bytes = 4; // read occupancy values no_of_bytes = occupied_cells * dimension_cells_vertical * float_bytes; byte[] occupancyData = new byte[no_of_bytes]; for (int i = 0; i < no_of_bytes; i++) occupancyData[i] = data[array_index + i]; array_index += no_of_bytes; float[] occupancy = ArrayConversions.ToFloatArray(occupancyData); // read colour values no_of_bytes = occupied_cells * dimension_cells_vertical * 3; byte[] colourData = new byte[no_of_bytes]; for (int i = 0; i < no_of_bytes; i++) colourData[i] = data[array_index + i]; array_index += no_of_bytes; // insert the data into the grid n = 0; for (int y = ty; y < by; y++) { for (int x = tx; x < bx; x++) { if (cell[x][y] != null) { particleGridCellBase[] distilled = new particleGridCellBase[dimension_cells_vertical]; for (int z = 0; z < dimension_cells_vertical; z++) { // set the probability value int index = (n * dimension_cells_vertical) + z; float probLogOdds = occupancy[index]; if (probLogOdds != occupancygridCellMultiHypothesis.NO_OCCUPANCY_EVIDENCE) { // create a distilled grid particle distilled[z] = new particleGridCellBase(); distilled[z].probabilityLogOdds = probLogOdds; // set the colour distilled[z].colour = new byte[3]; for (int col = 0; col < 3; col++) distilled[z].colour[col] = colourData[(index * 3) + col]; } } // insert the distilled particles into the grid cell cell[x][y].SetDistilledValues(distilled); // update the navigable space updateNavigableSpace(null, x, y); n++; } } } } }