Example #1
0
        /// <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);
        }
Example #2
0
        /// <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);
        }
Example #3
0
 /// <summary>
 /// distills all grid particles associated with this pose
 /// </summary>
 /// <param name="grid">grid to be updated</param>
 public void Distill(occupancygridMultiHypothesis grid)
 {
     for (int i = observed_grid_cells.Count - 1; i >= 0; i--)
     {
         particleGridCell hypothesis = observed_grid_cells[i];
         grid.Distill(hypothesis);
     }
 }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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
            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>
        /// 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--;
        }
Example #10
0
        /// <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);
        }
Example #11
0
        /// <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);
        }
Example #12
0
        /// <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);
            }
        }
Example #13
0
 /// <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);
 }