Esempio n. 1
0
//
// Removes an observation from the map at position x,y. Node is the index into the
// dynamic array of which observation needs to be removed. This is typically gotten
// from the ancestry node, since the death of a node is currently the only way to 
// call this function.
//
void LowDeleteObservation(short int x, short int y, short int node) {
  int total;

  // We may have already removed this observation previously in the process of 
  // resizing the array at some point. In that case, there's nothing to do here.
  if ((node == -1) || (lowMap[x][y] == NULL))
    return;

  // If this is the last observation left at this location in the map, then we can 
  // revert the whole entry in the map to NULL, indicating that no current particle
  // has observed this location. 
  if (lowMap[x][y]->total - lowMap[x][y]->dead == 1) {
    free(lowMap[x][y]->array);
    free(lowMap[x][y]);
    lowMap[x][y] = NULL;
    return;
  }

  // Look to see if we need to shrink the array
  if ((int)((lowMap[x][y]->total - 1 - lowMap[x][y]->dead)*2.5) <= lowMap[x][y]->size) {
    // Let resizing the array remove this entry (it's what is considered a "dead" entry
    // now, as indicated by the second argument).
    LowResizeArray(lowMap[x][y], lowMap[x][y]->array[node].ID);
    if (lowMap[x][y]->total == 0) {
      free(lowMap[x][y]->array);
      free(lowMap[x][y]);
      lowMap[x][y] = NULL;
    }
    return;
  }

  // If we got this far, we are removing the entry manually. Make a note that we have
  // one less entry here.
  lowMap[x][y]->total--;
  // This variable is here solely to make the code mroe readable by having less 
  // redirections and indexes.
  total = lowMap[x][y]->total;
  // If the ibservation was the last one in the array, our work is done. Otherwise, 
  // we take the last observation in dynamic array, and copy it ontop of the observation
  // we are deleting. Since we don't use any of the entries beyond lowMap[x][y].total,
  // that last observation is essentially already deleted, and we don't have to worry about
  // duplicates.
  if (node != lowMap[x][y]->total) {
    lowMap[x][y]->array[node].hits      = lowMap[x][y]->array[total].hits;
    lowMap[x][y]->array[node].distance  = lowMap[x][y]->array[total].distance;
    lowMap[x][y]->array[node].ID        = lowMap[x][y]->array[total].ID;
    lowMap[x][y]->array[node].source    = lowMap[x][y]->array[total].source;
    lowMap[x][y]->array[node].parentGen = lowMap[x][y]->array[total].parentGen;
    l_particleID[ lowMap[x][y]->array[node].ID ].mapEntries[ lowMap[x][y]->array[node].source ].node = node;
  }
}
Esempio n. 2
0
//
// Finds the appropriate entry in the designated grid square, and then makes a duplicate of that entry
// modified according to the input. 
//
void LowUpdateGridSquare(int x, int y, double distance, int hit, int parentID)
{
  TEntryList *tempEntry;
  int here, i;

  // If the grid square was previously unobserved, then we will need to create a new
  // entry in the observationArray for it, so that later accesses can take full advantage
  // of constant time access.
  if (lowMap[x][y] == NULL) {
    // Check to make sure there is still room left in the observation cache.
    if (observationID >= AREA) 
      fprintf(stderr, "bRoll over!\n");

    // Display ownership of this slot
    flagMap[x][y] = observationID;
    obsX[observationID] = x;
    obsY[observationID] = y;
    observationID++;

    // Since the grid square was unobserved previously, we will also need to create a
    // new entry into the map at this location, that we can then build on.
    // The first step is to create a starter structure, to keep track of the dynamic array
    // of observations.
    lowMap[x][y] = (TMapStarter *) malloc(sizeof(TMapStarter));
    if (lowMap[x][y] == NULL) fprintf(stderr, "Malloc failed in creation of Map Starter at %d %d\n", x, y);
    // No dead or obsolete entries yet.
    lowMap[x][y]->dead = 0;
    // No entries have actually been added to this location yet. We will increment this counter later.
    lowMap[x][y]->total = 0;
    // We will only have room for one observation in this grid square so far. Later, this can grow.
    lowMap[x][y]->size = 1;
    // The actual dynamic array is created here, of exactly the size for one entry.
    lowMap[x][y]->array = (TMapNode *) malloc(sizeof(TMapNode));
    if (lowMap[x][y]->array == NULL) fprintf(stderr, "Malloc failed in making initial map array for %d %d\n", x, y);

    // Initialize the slot
    for (i=0; i < ID_NUMBER; i++) 
      observationArray[flagMap[x][y]][i] = -1;
  }
  // We could have observations here, but this square hasn't been observed yet this iteration.
  // In that case, we need to build an entry into the observationArray for constant time access.
  else if (flagMap[x][y] == 0) 
    LowBuildObservation(x, y, 0);

  // Note where in the dynamic array of observations we need to look for this one particle's 
  // relevent observation. This is indicated to us by the observationArray.
  here = observationArray[flagMap[x][y]][parentID];

  // If the ID of the relevent observation is the same as our altering particle's ID, then the 
  // new observation is merely an amendment to this data, and noone else is using it yet. Just 
  // alter the source directly
  if ((here != -1) && (lowMap[x][y]->array[here].ID == parentID)) {
    lowMap[x][y]->array[here].hits = lowMap[x][y]->array[here].hits + hit;
    lowMap[x][y]->array[here].distance = lowMap[x][y]->array[here].distance + distance;
  }
  // Otherwise, we need to use that relevent observation in order to create a new observation.
  // Otherwise, we can corrupt the data for other particles.
  else {
    // We will be adding a new entry to the list- is there enough room?
    if (lowMap[x][y]->size <= lowMap[x][y]->total) {
      LowResizeArray(lowMap[x][y], -71);
      if (lowMap[x][y]->total == 0) {
	free(lowMap[x][y]->array);
	free(lowMap[x][y]);
	lowMap[x][y] = NULL;
      }
    }

    // Make all changes before incrementing lowMap[x][y]->total, since it's used as an index
    // Update the observationArray, to let it know that this ID will have its own special entry
    // in the global at this location.
    observationArray[flagMap[x][y]][parentID] = lowMap[x][y]->total;

    // Add an entry in to the list of altered map squares for this particle
    // First check to see if the size of that array is big enough to hold another entry
    if (l_particleID[parentID].size == 0) {
      l_particleID[parentID].size = 1;
      l_particleID[parentID].mapEntries = (TEntryList *) malloc(sizeof(TEntryList));
      if (l_particleID[parentID].mapEntries == NULL) fprintf(stderr, "Malloc failed in creation of entry list array\n");
    }
    else if (l_particleID[parentID].size <= l_particleID[parentID].total) {
      l_particleID[parentID].size = (int)(ceil(l_particleID[parentID].total*1.25));
      tempEntry = (TEntryList *) malloc(sizeof(TEntryList)*l_particleID[parentID].size);
      if (tempEntry == NULL) fprintf(stderr, "Malloc failed in expansion of entry list array\n");
      for (i=0; i < l_particleID[parentID].total; i++) {
	tempEntry[i].x = l_particleID[parentID].mapEntries[i].x;
	tempEntry[i].y = l_particleID[parentID].mapEntries[i].y;
	tempEntry[i].node = l_particleID[parentID].mapEntries[i].node;
      }
      free(l_particleID[parentID].mapEntries);
      l_particleID[parentID].mapEntries = tempEntry;
    }

    // Add the location of this new entry to the list in the ancestry node
    l_particleID[parentID].mapEntries[l_particleID[parentID].total].x = x;
    l_particleID[parentID].mapEntries[l_particleID[parentID].total].y = y;
    l_particleID[parentID].mapEntries[l_particleID[parentID].total].node = lowMap[x][y]->total;

    // i is used as a quick reference guide here, in order to make the code easier to read.
    i = lowMap[x][y]->total;
    // The pointers between the ancestry node's list and the map's observation list need
    // to point back towards each other, in order to coordinate data.
    lowMap[x][y]->array[i].source = l_particleID[parentID].total;
    // Assign the appropriate ID to this new observation.
    lowMap[x][y]->array[i].ID = parentID;
    // Note that we now have one more observation at this ancestor node
    l_particleID[parentID].total++;

    // Check to see if this square has been observed by an ancestor
    if (here == -1) {
      // Previously unknown; clean slate. Update directly with the observed data.
      lowMap[x][y]->array[i].hits = hit;
      // Include the strength of the prior.
      lowMap[x][y]->array[i].distance = distance + L_PRIOR_DIST;
      // A value of -2 here indicates that this observation had no preceding observation
      // that it was building off of.
      lowMap[x][y]->array[i].parentGen = -2; 
    }
    else {
      // Include the pertinent info from the old observation in with the new observation.
      lowMap[x][y]->array[i].hits = lowMap[x][y]->array[here].hits + hit;
      lowMap[x][y]->array[i].distance = distance + lowMap[x][y]->array[here].distance;
      lowMap[x][y]->array[i].parentGen = l_particleID[ lowMap[x][y]->array[here].ID ].generation;
    }

    // Now we can acknowledge that there is another observation at this grid square.
    lowMap[x][y]->total++;
  }
}
Esempio n. 3
0
//
// UpdateAncestry
//
// Every iteration, after particles have been resampled and evaluated (ie after Localize has been run),
// we will need to update the ancestry tree. This consists of four main steps.
// a) Remove dead nodes. These are defined as any ancestor node which has no descendents in the current 
//    generation. This is caused by certain particles not being resampled, or by a node's children all
//    dying off on their own. These nodes not only need to be removed, but every one of their observations
//    in their observations in the map also need to be removed.
// b) Collapse branches of the tree. We want to restrict each internal node to having a branching factor
//    of at least two. Therefore, if a node has only one child, we merge the information in that node with
//    the one child node. This is especially common at the root of the tree, as the different hypotheses 
//    coalesce.
// c) Add the new particles into the tree. 
// d) Update the map for each new particle. We needed to wait until they were added into the tree, so that
//    the ancestry tree can keep track of the different observations associated with the particle.
//
void UpdateAncestry(TSense sense, TAncestor particleID[])
{
  int i, j;
  TAncestor *temp, *hold, *parentNode;
  TEntryList *entry, *workArray;
  TMapStarter *node;
  TPath *tempPath, *trashPath;

  // Remove Dead Nodes - 
  // Go through the current particle array, and prune out all particles that did not spawn any particles. We know that the particle
  // had to spawn samples, but those samples may not have become particles themselves, through not generating any samples for the 
  // next generation. Recurse up through there.
  for (i=0; i < l_cur_particles_used; i++) {
    temp = l_particle[i].ancestryNode;

    // This is a "while" loop for purposes of recursing up the tree.
    while (temp->numChildren == 0) {
      // Free up the memory in the map by deleting the associated observations.
      for (j=0; j < temp->total; j++) 
	LowDeleteObservation(temp->mapEntries[j].x, temp->mapEntries[j].y, temp->mapEntries[j].node);

      // Get rid of the memory being used by this ancestor
      free(temp->mapEntries);
      temp->mapEntries = NULL;

      // This is used exclusively for the low level in hierarchical SLAM. 
      // Get rid of the hypothesized path that this ancestor used.
      tempPath = temp->path;
      while (tempPath != NULL) {
	trashPath = tempPath;
	tempPath = tempPath->next;
	free(trashPath);
      }
      temp->path = NULL;

      // Recover the ID, so that it can be used later.
      cleanID++;
      availableID[cleanID] = temp->ID;
      temp->generation = curGeneration;
      temp->ID = -42;

      // Remove this node from the tree, while keeping track of its parent. We need that for recursing
      // up the tree (its parent could possibly need to be removed itself, now)
      hold = temp;
      temp = temp->parent;
      hold->parent = NULL;

      // Note the disappearance of this particle (may cause telescoping of particles, or outright deletion)
      temp->numChildren--;
    }
  }

  // Collapse Branches -
  // Run through the particle IDs, checking for those node IDs that are currently in use. Essentially is 
  // an easy way to pass through the entire tree.  If the node is in use, look at its parent.
  // If the parent has only one child, give all of the altered map squares of the parent to the child, 
  // and dispose of the parent. This collapses those ancestor nodes which have a branching factor of only one.
  for (i = 0; i < ID_NUMBER-1; i++) {
    // These booleans mean (in order) that the ID is in use, it has a parent (ie is not the root of the ancestry tree),
    // and that its parent has only one child (which is necessarily this ID) 
    if ((particleID[i].ID == i) && (particleID[i].parent != NULL) && (particleID[i].parent->numChildren == 1)) {
      // This is a special value for redirections. If the node's parent has already participated in a collapse
      // during this generation, then that node has already been removed from the tree, and we need to go up
      // one more step in the tree. 
      while (particleID[i].parent->generation == -111)
	particleID[i].parent = particleID[i].parent->parent;

      parentNode = particleID[i].parent;

      // Check to make sure that the parent's array is large enough to accomadate all of the entries of the child
      // in addition to its own. If not, we need to increase the dynamic array.
      if (parentNode->size < (parentNode->total + particleID[i].total)) {
	parentNode->size = (int)(ceil((parentNode->size + particleID[i].size)*1.5));
	workArray = (TEntryList *)malloc(sizeof(TEntryList)*parentNode->size);
	if (workArray == NULL) fprintf(stderr, "Malloc failed for workArray\n");

	for (j=0; j < parentNode->total; j++) {
	  workArray[j].x = parentNode->mapEntries[j].x;
	  workArray[j].y = parentNode->mapEntries[j].y;
	  workArray[j].node = parentNode->mapEntries[j].node;
	}
	// Note that parentNode->total hasn't changed- that will grow as the child's entries are added in
	free(parentNode->mapEntries);
	parentNode->mapEntries = workArray;
      }

      // Change all map entries of the parent to have the ID of the child
      // Also check to see if this entry supercedes an entry currently attributed to the parent.
      // Since collapses can merge all of the entries between the parent and the current child into the parent, this check is performed
      // by comparing to see if the generation of the last observation (before the child's update) is at least as recent as parent's
      // generation. If so, note that there is another "dead" entry in the observation array. It will be cleaned up later. If this puts
      // the total number of used slot, minus the number of "dead", below the threshold, shrink the array (which cleans up the dead)
      entry = particleID[i].mapEntries;
      for (j=0; j < particleID[i].total; j++) {
	node = lowMap[entry[j].x][entry[j].y];

	// Change the ID
	node->array[entry[j].node].ID = parentNode->ID;
	node->array[entry[j].node].source = parentNode->total;

	parentNode->mapEntries[parentNode->total].x = entry[j].x;
	parentNode->mapEntries[parentNode->total].y = entry[j].y;
	parentNode->mapEntries[parentNode->total].node = entry[j].node;
	parentNode->total++;

	// Check for pre-existing observation in the parent's list
	if (node->array[entry[j].node].parentGen >= parentNode->generation) {
	  node->array[entry[j].node].parentGen = -1;
	  node->dead++;
	}
      }

      // We do this in a second pass for a good reason. If there are more than one update for a given grid square which uses the child's
      // ID (as a consequence of an earlier collapse), then we want to make certain that the resizing doesn't take place until after all
      // entries have changed their ID appropriately.
      for (j=0; j < particleID[i].total; j++) {
	node = lowMap[entry[j].x][entry[j].y];
	if ((node->total - node->dead)*2.5 < node->size) 
	  LowResizeArray(node, -7);
      }

      // We're done with it- remove the array of updates from the child.
      free(particleID[i].mapEntries);
      particleID[i].mapEntries = NULL;

      // Inherit the path
      tempPath = parentNode->path;
      while (tempPath->next != NULL) 
	tempPath = tempPath->next;
      tempPath->next = particleID[i].path;
      particleID[i].path = NULL;

      // Inherit the number of children
      parentNode->numChildren = particleID[i].numChildren;

      // Subtlety of the ancestry tree: since we only keep pointers up to the parent, we can't exactly change all of the
      // descendents of the child to now point to the parent. What we can do, however, is mark the change for later, and
      // update all of the ancestor particles in a single go, later. That will take a single O(P) pass
      particleID[i].generation = -111;
    }
  }

  // This is the step where we correct for redirections that arise from the collapse of a branch of the ancestry tree
  for (i=0; i < ID_NUMBER-1; i++) 
    if (particleID[i].ID == i) {
      while (particleID[i].parent->generation == -111) 
	particleID[i].parent = particleID[i].parent->parent;
    }

  // Wipe the slate clean, so that we don't get confused by the mechinations of the previous changes
  // from deletes and merges. Updates can make thier own tables, as needed.
  LowInitializeFlags();


  // Add the current savedParticles into the ancestry tree, and copy them over into the 'real' particle array
  j = 0;
  for (i = 0; i < cur_saved_particles_used; i++) {
    // Check for redirection of parent pointers due to collapsing of branches (see above)
    while (savedParticle[i].ancestryNode->generation == -111) 
      savedParticle[i].ancestryNode = savedParticle[i].ancestryNode->parent;

    // A saved particle has ancestryNode denote the parent particle for that saved particle
    // If that parent has only this one child, due to resampling, then we want to perform a collapse
    // of the branch, but it hasn't been done yet, because the savedParticle hasn't been entered into
    // the tree yet. Therefore, we just designate the parent as the "new" entry for this savedParticle.
    // We then update the already created ancestry node as if it were the new node.
    if (savedParticle[i].ancestryNode->numChildren == 1) {
      // Change the generation of the node
      savedParticle[i].ancestryNode->generation = curGeneration;
      // Now that it represents the new node as well, it no longer is considered to have children.
      savedParticle[i].ancestryNode->numChildren = 0;
      // We're copying the savedParticles to the main particle array
      l_particle[j].ancestryNode = savedParticle[i].ancestryNode;

      // Add a new entry to the path of the ancestor node.
      trashPath = (TPath *)malloc(sizeof(TPath));
      trashPath->C = savedParticle[i].C;
      trashPath->D = savedParticle[i].D;
      trashPath->T = savedParticle[i].T;
      trashPath->dist = savedParticle[i].dist;
      trashPath->turn = savedParticle[i].turn;
      trashPath->next = NULL;
      tempPath = l_particle[i].ancestryNode->path;
      while (tempPath->next != NULL)
	tempPath = tempPath->next;
      tempPath->next = trashPath;

      l_particle[j].x = savedParticle[i].x;
      l_particle[j].y = savedParticle[i].y;
      l_particle[j].theta = savedParticle[i].theta;
      l_particle[j].probability = savedParticle[i].probability;
      j++;
    }

    // IF the parent has multiple children, then each child needs its own new ancestor node in the tree
    else if (savedParticle[i].ancestryNode->numChildren > 0) {
      // Find a new entry in the array of ancestor nodes. This is done by taking an unused ID off of the
      // stack, and using that slot. 
      temp = &(particleID[ availableID[cleanID] ]);
      temp->ID = availableID[cleanID];
      // That ID on the top of the stack is now being used.
      cleanID--;

      if (cleanID < 0) {
	fprintf(stderr, " !!! Insufficient Number of Particle IDs : Abandon Ship !!!\n");
	cleanID = 0;
      }

      // This new node needs to have its info filled in
      temp->parent = savedParticle[i].ancestryNode;
      // No updates to the map have been made yet for this node
      temp->mapEntries = NULL;
      temp->total = 0;
      temp->size = 0;
      // The generation of this node is important for collapsing branches of the tree. See above.
      temp->generation = curGeneration;
      temp->numChildren = 0;
      temp->seen = 0;

      // This is where we add a new entry to this node's hypothesized path for the robot
      trashPath = (TPath *)malloc(sizeof(TPath));
      trashPath->C = savedParticle[i].C;
      trashPath->D = savedParticle[i].D;
      trashPath->T = savedParticle[i].T;
      trashPath->dist = savedParticle[i].dist;
      trashPath->turn = savedParticle[i].turn;
      trashPath->next = NULL;
      temp->path = trashPath;

      // Transfer this entry over to the main particle array
      l_particle[j].ancestryNode = temp;
      l_particle[j].x = savedParticle[i].x;
      l_particle[j].y = savedParticle[i].y;
      l_particle[j].theta = savedParticle[i].theta;
      l_particle[j].probability = savedParticle[i].probability;
      j++;
    }
  }

  l_cur_particles_used = cur_saved_particles_used;

  // Here's where we actually go through and update the map for each particle. We had to wait
  // until now, so that the appropriate structures in the ancestry had been created and updated.
  for (i=0; i < l_cur_particles_used; i++) 
    AddToWorldModel(sense, i);

  // Clean up the ancestry particles which disappeared in branch collapses. Also, recover their IDs.
  // We waited until now because we needed to allow for redirection of parents.
  for (i=0; i < ID_NUMBER-1; i++) 
    if (particleID[i].generation == -111) {
      particleID[i].generation = -1;
      particleID[i].numChildren = 0;
      particleID[i].parent = NULL;
      particleID[i].mapEntries = NULL;
      particleID[i].path = NULL;
      particleID[i].seen = 0;
      particleID[i].total = 0;
      particleID[i].size = 0;

      // Recover the ID. 
      cleanID++;
      availableID[cleanID] = i;
      particleID[i].ID = -3;
    }
}