The objective of this work is to introduce some key algorithm of motion planning: collision checking, probabilistic roadmaps, visibility PRM, \(A^*\) (a-star), random shortcut.
A robot model with simple (static actuated) dynamics, like a UR5 manipulator robot. See Lab 1 for loading the UR5 robot.
A collision checking library, provided by Pinocchio.
Pinocchio provides collision checking for a large class of 3D objects (sphere, capsule, box, triangle soup) using library FCL. Any 3D object of FCL can be loaded using the C++ interface or the URDF model. Although, only capsules can yet be added through the Python API, and only URDF-loaded meshes and Python-loaded capsules can be easily connected with Gepetto viewer (help is welcome to correct this, it would requires 4h work).
An example of loading a capsule in Pinocchio and Gepetto viewer is below:
URDF specifications does not allow to define which collision pairs should be tested. By default, Pinocchio does not load any collision pair. A simple strategy is to add all pairs, but often, some meshes of the models induce wrong collision. Then manually remove them by testing valid configurations. To be clean, you can store the valid collision pair in a SRDF file. For UR5:
Collision checking are done through the following algorithms:
Both collision algorithms requires a preliminary update of placement and return True
if configuration is in collision (False
otherwise).
We need to define a simple function to check whether a configuration is respecting the robot constraints (joint limits and collision, plus any other inequality-defined constraints you might want).
Implement the function check
taking a configuration q
in argument and return True
if and only if q
is acceptable – The solution only uses the 2 collision algorithms of Pinocchio listed above and standard python otherwise.
We need to define a local controller, aka a steering method, to define the behavior of the robot when it tries to connect to configuration together. Here we will simply use linear interpolation. More complex controllers (like optimal trajectories) might be preferred if more knowledge about the system is available.
In the meantime, we will also need a method to check whether a local path is collision free. We will do that be simply regularly sampling the path with a given step length. Continuous collision detection might be implemented for better and safer performances.
Here we propose to implement the steering method and the path validation in a single connected method. More versatile implementation is obtained by defining two different functions.
Implement a connect
function, that takes as argument an initial q1
and a final q2
configuration, and return True
is it is possible to connect both using linear interpolation while avoiding collision. Optionally, the function should also returns the sampling of the path as a list of intermediate configurations – The solution does not need any new Pinocchio calls.
Finally, we need a k-nearest-neighbors algorithms.
Implement a function nearest_neighbors
that takes as argument a new configuration q
, a list of candidates qs
, and the number of requested neighbors k
, and returns the list of the k
nearest neighbors of q
in qs
. Optionally, the distance function that scores how close a configuration q1
is close to a configuration q2
might be also provided. If qs
contains less that k
elements, simply returns them all – no new Pinocchio method is needed for the solution.
Basically, probabilistic roadmaps are build by maintaining a graph of configurations. At each iteration, a new configuration is randomly sampled and connected to the nearest configurations already in the graph. The algorithm stops when both start configuration qstart and goal configuration qgoal can be directly connected to some elements of the graph.
We propose here to implement the visibility PRM algorithm. This algorithm also maintains the connected components of the graph. When a new configuration qnew is sampled, we try to connect it to its nearest neighbor in each of the already-sampled connected component. Configuration qnew is added to the graph only if one of the two following conditions is respected:
In the second case, the connected components that can be connected are also merged.
A graph structure with connected components is provided here.
Implement a visibilityPRM
that takes in argument two start and goal configurations qstart
and qgoal
, and the number of random sampling that must be achieved before failing. The returns True
if qgoal
can be connected to qstart
. The graph must also be returned – no fancy Pinocchio algorithm is needed here.
The PRM can be visualized in Gepetto-viewer using the function display_prm
provided here.
\(A^*\) is an algorithm to find the shortest path in a graph (discrete problem). \(A^*\) iterativelly explore the nodes of the graph starting for the given start. One a node gets explored, its exact cost from start (cost to here) is exactly known. Then, all its children are added to the "frontier" of the set of already-explored nodes. Cost from nodes of the frontier to the goal is not (yet) exactly known but is anticipated through a heuristic function (also provided). At next iteration, the algorithm examines a node of the frontier, looking first at the node that is the most likely to be in the shortest path using the heuristic distance.
See the fairly complete description of \(A^*\) provided here.
Implement the \(A^*\) algorithm. The \(A^*\) returns a sequence of node ID from start to goal. We only work here with the existing graph of configuration, meaning that no new nodes a sampled, and no new collision test are computed. Pinocchio is not necessary here. \(A^*\) returns a list containing the indexes of the nodes of the PRM graph that one should cross to reach qgoal from qstart.
Being given a list of configurations, a random shortcut simply randomly tries to shortcut some subpart of the list: it randomly selects the start and end of the subpart, and tries to directly connect them (using the above-defined steering method). In case of success, it skips the unnecessary subpart. The algorithm iterates a given number of time.
The shortcut can be run on either the list of configuration output by the \(A^*\), or on a sampling of the trajectory connecting the nodes of the \(A^*\). We propose here the second version.
Defines a function sample_path
to uniformly sample that trajectory connecting the nodes selected by \(A^*\): for each edge of the \(A^*\) optimal sequence, call connect
and add the resulting sampling to the previously-computed sample. It takes as argument the PRM graph and the list of indexes computed by \(A^*\) and returns a list of robot configuration starting by qstart and ending by qgoal – no Pinocchio method is needed here.
The sampled path can be displayed in Gepetto-viewer using the function displayPath
provided here.
Implement the shortcut
algorithm that tries to randomly connect two configuration of the sampled path. It takes the list of configuration output by sample_path
and the number of random shortcut it should tries. It returns an optimized list of configurations – no Pinocchio method is needed here.