Planning to Find an Unpredictable Evader

Brian O'Connor
bgoc@leland.stanford.edu


Project Description

The goal of this project is to use visibility to search for an "evader" with unbounded speed moving through a polygonal environment. The basic strategy for computing this path is to: 1) decompose the environment into convex polygonal cells; 2) compute a visibility polyon for each cell in the environment; 3) build and search a visibility graph from this information to find a solution for a single pursuer; and 4) break the environment into subproblems and solve each with a single pursuer if the first search fails. My solution to the first three parts is fairly robust but my solution to the last problem is incomplete.

  1. Cell Decomposition. To create a cell decomposition based on edge visibility I used the following strategy. I use a quadedge-like structure, the CellEdge, in the last two sections in order to map each cell edge to its dual (the edge with opposite orientation). Then when I create a new cell I keep track of the cell that is reached by crossing this cell edge; generation of a roadmap once this adjacency information is calculated becomes trivial. I also need to remove cells that were created in the last step but which actually correspond to the border or an obstacle; these cells are artifacts caused by the fact that every edge has a dual and can be safely removed. This was definitely the most difficult part of the project; it took me more time to get my cell decomposition working than it did to write the rest of the program. There are still a couple known bugs in my decomposition algorithm. There are a couple problems with extending rays through environments with multiple "holes" or interior obstacles, although worlds with fewer than three holes seem to work fine. Problems can also occur when points on the border are visible to each other but are on the outside of the environment, and in some of these cases a cell can be created that is outside of the environment. This doesn't occur very often though and I was able to create examples with very complex borders that work fine. I created a bunch of line, point, and segment functions for this section that I was able to reuse in the other sections and some error-tolarent floating point routines which I used throughout the project to avoid the rounding errors that occur when I use multiple vector cross-products to compute line intersections. Finally, the fact that my algorithm handles singularities such as multiple colinear points and edges made constructing clean sample environments a lot easier. The downside to this was that I couldn't find an easy way to do this using quadedges and therefore need to make an extra pass over my data to convert from unoriented intersecting lines to oriented segments that only intersect at endpoints.

  2. Computation of Visibility Polyons. Computing a visibility polygon was fairly straightforward. First I shoot rays through every vertex to calculate my gap edges, then I start with one gap edges and follow it to the obstacle edge it ends at, and then I follow that obstacle edge to the next edge it shares a vertex with, and continue the process. If at any time I find that a gap edge intersects the obstacle edge I am processing, then I skip to that gap edge and continue at the other obstacle edge that this gap edge contacts. When I make a cycle and return to the gap edge I started with the visibility polygon is complete. Note that when I traverse a gap edge to an obstacle edge, there will be two different possibilites for which edge I should continue to; however, only one of these possibilities will correspond to a vertex that can be seen from my starting point. Therefore I also keep track of all the vertices that can be seen from the starting point in order to make the right decision in these cases.

  3. Building and Searching a Visibility Graph. My visibility graph consists of nodes represented by (cell,state) pairs, and directed arcs that connect the nodes. I use Dijkstra's algorithm to search the graph, with the enhancement that I don't construct nodes until I've called relax() on a neighboring node. My program runs significantly faster after this optimization, and uses much less memory. When building the graph, my key observations were that I could use a bitmap to represent the state of a node (1's for dirty edges, 0's for clean edges) and that I can represent the change in state when I move from node A to node B by an array of bitmaps. I use the latter observation in conjunction with the adjacency information I built in Part 1 to determine what nodes should be connected by arcs. For example, suppose cell A has 5 gap edges a1,...a5 and cell B has 3 gap edges b1,b2,b3. edges a1, a2, and a4 correspond to b1, and edge a5 corresponds to edge b2. Then given boolean values for a1,...,a5 I can computer the values for B as:

    b1 = a1 | a2 | a4
    b2 = a5
    b3 = 0

    or using bitmaps,

    state(A) = (a1 << 0) | (a2 << 1) | (a3 << 2) | (a4 << 3) | (a5 << 4)
    b1 = 11010 & state(A)
    b2 = 00001 & state(A)
    b3 = 00000 & state(A)
    state(B) = (b1 << 0) | (b2 << 1) | (b3 << 2).

    The 3 bitmaps used in conjunction with the state of A to calculate the state of B are independent of the state of A, so I reduce computation time by sharing this information across arcs that correspond to the same cells with different states. This information isn't symmetric, so I need to calculate a different bitmap array to move from B to A.

  4. Planning for Multiple Pursuers. I ran out of time before I could finish this section. My idea for multiple pursuer planning was to augment my graph search to remember the "best" node it had seen so far in case the search fails. Then I can try to search again with an extra robot, but this time the first robot will stop at its "best" node and the second robot will search the rest of the graph. The second robot will start its search on the opposite side of one of the dirty gap edges in the first robot's "best" node. This idea could be generalized to start more than two pursuers if the first robot has multiple dirty gap edges in its best node, or to continue starting pursuers recursively if the second robot cannot search it's portion of the environment (I didn't have time to implement these extensions though). Right now I'm computing the second robot's environment with an algorithm very similar to my Visibility Polygon algorithm from part 2; beginning with the dirty edge the original pursuer ended on, I traverse all the obstacle edges (and possibly other dirty gap edges) that bound the unsearched region, and make all of these edges the border of a new environment which I pass into my cell decomposition and graph search functions.

Motion Planning Examples

Files suffixed with 'A' show a continuous path and the visibility polygon at each point in the path. Files suffixed with 'B' show the path in cell by cell steps, and also shows the gap edges at each step, color coded to show if the gap is dirty or clean at this step. Files suffixed with 'C' don't show any path planning and instead show the cell decomposition that was used in the other examples. I've had a lot of problems getting these examples to run through the Web, possibly due to Java running out of memory because of the number of ghosts used to illustrate gap edges and visibility polygons. Here are Sun versions of the movies (.mv format)

Source Code


Last modified: Sat Mar 22 23:48:24 PST 1997