/// <summary> /// any old iron... /// </summary> /// <param name="vertical_index">the z position at which to check for garbage</param> /// <returns>the amount of garbage collected</returns> public int GarbageCollect(int vertical_index) { int collected_items = 0; if (Hypothesis[vertical_index] != null) { int i = Hypothesis[vertical_index].Count - 1; while ((i >= 0) && (garbage_entries > 0)) { particleGridCell h = Hypothesis[vertical_index][i]; if (!h.Enabled) // has this hypothesis been marked for deletion? { Hypothesis[vertical_index].RemoveAt(i); garbage_entries--; collected_items++; } i--; } // there is no longer any garbage at this vertical index garbage[vertical_index] = false; // no hypotheses exist, so remove the list to save on memory usage if (Hypothesis[vertical_index].Count == 0) { Hypothesis[vertical_index] = null; } } return(collected_items); }
/// <summary> /// adds a new hypothesis to the map cache /// </summary> /// <param name="hypothesis">grid hypothesis to be added</param> /// <param name="radius_cells">radius of the local map cache</param> /// <param name="grid_dimension">dimension of the occupancy grid</param> /// <param name="grid_dimension_vertical">height of the occupancy grid (z axis) in cells</param> /// <returns>true if the hypothesis was added</returns> public bool Add(particleGridCell hypothesis, int radius_cells, int grid_dimension, int grid_dimension_vertical) { bool added = true; // create a new list for this grid coordinate if necessary // allocating memory as its needed for each grid dimension // is far more efficient than just allocating a big three // dimensional chunk in one go if (map_cache == null) { // store the top left position of the map cache map_cache_tx = hypothesis.x - radius_cells; map_cache_ty = hypothesis.y - radius_cells; // store the width of the map cache map_cache_width_cells = radius_cells * 2; map_cache = new List <particleGridCell> [map_cache_width_cells][][]; } // get the x,y coordinate within the map cache int x = hypothesis.x - map_cache_tx; int y = hypothesis.y - map_cache_ty; if (((x < 0) || (x >= map_cache_width_cells)) || ((y < 0) || (y >= map_cache_width_cells))) { // we're outside of the cache added = false; //resize_map_cache(x, y); } else { // add the hypothesis to the cache map int z = hypothesis.z; // create new lists as they are needed if (map_cache[x] == null) { map_cache[x] = new List <particleGridCell> [map_cache_width_cells][]; } if (map_cache[x][y] == null) { map_cache[x][y] = new List <particleGridCell> [grid_dimension_vertical]; } if (map_cache[x][y][z] == null) { map_cache[x][y][z] = new List <particleGridCell>(); } // add to the list map_cache[x][y][z].Add(hypothesis); } return(added); }
/// <summary> /// distills all grid particles associated with this pose /// </summary> /// <param name="grid">grid to be updated</param> public void Distill(dpslam grid) { for (int i = observed_grid_cells.Count - 1; i >= 0; i--) { particleGridCell hypothesis = observed_grid_cells[i]; grid.Distill(hypothesis); } }
/// <summary> /// add a new occupancy hypothesis to the list /// for this grid cell /// </summary> /// <param name="h">the hypothesis to be added to this grid cell</param> public void AddHypothesis(particleGridCell h) { int vertical_index = h.z; if (Hypothesis[vertical_index] == null) { Hypothesis[vertical_index] = new List <particleGridCell>(); } Hypothesis[vertical_index].Add(h); }
/// <summary> /// add a new grid hypothesis to this pose /// </summary> /// <param name="hypothesis">occupancy hypothesis for a grid cell</param> /// <param name="radius_cells">local map cache radius in cells</param> /// <param name="grid_dimension">dimension of the occupancy grid in cells</param> /// <param name="grid_dimension_vertical">height of the occupancy grid (z axis) in cells</param> /// <returns>true if the hypothesis was added</returns> public bool AddHypothesis( particleGridCell hypothesis, int radius_cells, int grid_dimension, int grid_dimension_vertical) { bool added = false; if (path != null) { added = path.Add(hypothesis, radius_cells, grid_dimension, grid_dimension_vertical); if (added) { observed_grid_cells.Add(hypothesis); } } return(added); }
/// <summary> /// distill an individual grid particle /// </summary> /// <param name="hypothesis">the grid particle to be distilled</param> public void Distill(particleGridCell hypothesis) { int z = hypothesis.z; // create the distilled array if (distilled == null) { distilled = new particleGridCellBase[Hypothesis.Length]; } bool initialised = false; if (distilled[z] == null) { // create a new distilled particle distilled[z] = new particleGridCellBase(); distilled[z].colour = new byte[3]; initialised = true; } // update an existing distilled value distilled[z].probabilityLogOdds += hypothesis.probabilityLogOdds; // and update the distilled colour value for (int col = 2; col >= 0; col--) { if (initialised) { distilled[z].colour[col] = hypothesis.colour[col]; } else { distilled[z].colour[col] = (byte)((hypothesis.colour[col] + distilled[z].colour[col]) / 2); } } }
/// <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 float ray_model_interval_pixels = sensormodel_lookup.ray_model_interval_pixels; int sensormodel_index = (int)Math.Round(ray.disparity / ray_model_interval_pixels); // 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_lookup == null) Console.WriteLine("Sensor models are missing"); 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 = ray_model_interval_pixels + (sensormodel_lookup_probability[sensormodel_index][grid_step] * ray_model_interval_pixels); 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> /// distill this grid particle /// </summary> /// <param name="hypothesis"></param> public void Distill( particleGridCell hypothesis) { occupancygridCellMultiHypothesis c = cell[hypothesis.x][hypothesis.y]; c.Distill(hypothesis); Remove(hypothesis); // occasionally update the navigable space if (rnd.Next(100) < 5) updateNavigableSpace(hypothesis.pose, hypothesis.x, hypothesis.y); }
/// <summary> /// removes an occupancy hypothesis from a grid cell /// </summary> /// <param name="hypothesis">the hypothesis to be removed from the grid</param> public void Remove( particleGridCell hypothesis) { occupancygridCellMultiHypothesis c = cell[hypothesis.x][hypothesis.y]; // add this to the list of garbage to be collected if (c.garbage_entries == 0) garbage.Add(c); c.garbage[hypothesis.z] = true; // increment the heap of garbage c.garbage_entries++; total_garbage_hypotheses++; // mark this hypothesis as rubbish hypothesis.Enabled = false; total_valid_hypotheses--; }
/// <summary> /// distill an individual grid particle /// </summary> /// <param name="hypothesis">the grid particle to be distilled</param> public void Distill(particleGridCell hypothesis) { int z = hypothesis.z; // create the distilled array if (distilled == null) distilled = new particleGridCellBase[Hypothesis.Length]; bool initialised = false; if (distilled[z] == null) { // create a new distilled particle distilled[z] = new particleGridCellBase(); distilled[z].colour = new byte[3]; initialised = true; } // update an existing distilled value distilled[z].probabilityLogOdds += hypothesis.probabilityLogOdds; // and update the distilled colour value for (int col = 2; col >= 0; col--) { if (initialised) distilled[z].colour[col] = hypothesis.colour[col]; else distilled[z].colour[col] = (byte)((hypothesis.colour[col] + distilled[z].colour[col]) / 2); } }
/// <summary> /// add a new occupancy hypothesis to the list /// for this grid cell /// </summary> /// <param name="h">the hypothesis to be added to this grid cell</param> public void AddHypothesis(particleGridCell h) { int vertical_index = h.z; if (Hypothesis[vertical_index] == null) Hypothesis[vertical_index] = new List<particleGridCell>(); Hypothesis[vertical_index].Add(h); }
/// <summary> /// add a new grid hypothesis to this pose /// </summary> /// <param name="hypothesis">occupancy hypothesis for a grid cell</param> /// <param name="radius_cells">local map cache radius in cells</param> /// <param name="grid_dimension">dimension of the occupancy grid in cells</param> /// <param name="grid_dimension_vertical">height of the occupancy grid (z axis) in cells</param> /// <returns>true if the hypothesis was added</returns> public bool AddHypothesis( particleGridCell hypothesis, int radius_cells, int grid_dimension, int grid_dimension_vertical) { bool added = false; if (path != null) { added = path.Add(hypothesis, radius_cells, grid_dimension, grid_dimension_vertical); if (added) observed_grid_cells.Add(hypothesis); } return (added); }
/// <summary> /// adds a new hypothesis to the map cache /// </summary> /// <param name="hypothesis">grid hypothesis to be added</param> /// <param name="radius_cells">radius of the local map cache</param> /// <param name="grid_dimension">dimension of the occupancy grid</param> /// <param name="grid_dimension_vertical">height of the occupancy grid (z axis) in cells</param> /// <returns>true if the hypothesis was added</returns> public bool Add(particleGridCell hypothesis, int radius_cells, int grid_dimension, int grid_dimension_vertical) { bool added = true; // create a new list for this grid coordinate if necessary // allocating memory as its needed for each grid dimension // is far more efficient than just allocating a big three // dimensional chunk in one go if (map_cache == null) { // store the top left position of the map cache map_cache_tx = hypothesis.x - radius_cells; map_cache_ty = hypothesis.y - radius_cells; // store the width of the map cache map_cache_width_cells = radius_cells * 2; map_cache = new List<particleGridCell>[map_cache_width_cells][][]; } // get the x,y coordinate within the map cache int x = hypothesis.x - map_cache_tx; int y = hypothesis.y - map_cache_ty; if (((x < 0) || (x >= map_cache_width_cells)) || ((y < 0) || (y >= map_cache_width_cells))) { // we're outside of the cache added = false; //resize_map_cache(x, y); } else { // add the hypothesis to the cache map int z = hypothesis.z; // create new lists as they are needed if (map_cache[x] == null) map_cache[x] = new List<particleGridCell>[map_cache_width_cells][]; if (map_cache[x][y] == null) map_cache[x][y] = new List<particleGridCell>[grid_dimension_vertical]; if (map_cache[x][y][z] == null) map_cache[x][y][z] = new List<particleGridCell>(); // add to the list map_cache[x][y][z].Add(hypothesis); } return (added); }