Main Content

Check for Manipulator Self Collisions Using Collision Meshes

This example shows how to check for manipulator self-collisions when executing a trajectory. The collision meshes are loaded via the <collision> tag defined in the URDF of a robot model. For more information about how to load collision data in other ways, see Obtain Collision Data for Manipulator Collision Checking.

Create Robot Representation

Import a URDF file of the KUKA® IIWA-14 serial manipulator as a rigidBodyTree model. The URDF captures the collision mesh files for the rigid bodies in the robot. To individually add collision objects to a rigid body, you can use the addCollision function. To load a provided robot model with collision objects attached, see the loadrobot function.

iiwa = importrobot('iiwa14.urdf');
iiwa.DataFormat = 'column';

Generate Trajectory and Check for Collisions

Specify a start and end configuration as a set of joint positions. These positions should be collision free.

startConfig = [0 -pi/4 pi 3*pi/2 0 -pi/2 pi/8]';
goalConfig = [0 -pi/4 pi 3*pi/4 0 -pi/2 pi/8]';

Find a joint space trajectory that connects the two configurations within three seconds.

q = trapveltraj([startConfig goalConfig],100,'EndTime',3);

To verify this output trajectory does not contain self-collisions, iterate over the output samples and see if any points are in collision using the checkCollision function.

While iterating through the trajectory q, call the checkCollision function on every configuration in the trajectory. Turn on exhaustive checking to continue checking for collisions after the first is detected.

The isConfigInCollision variable tracks the collision status of each configuration. The sepDistForConfig tracks the separation distance between the bodies of the robot. For each collision, the pair of body indices are stored in the configCollisionPairs variable. Note that neighboring bodies are not checked as they are always in contact via the joint that connects them.

isConfigInCollision = false(100,1);
configCollisionPairs = cell(100,1);
sepDistForConfig = zeros(iiwa.NumBodies+1,iiwa.NumBodies+1,100);
for i = 1:length(q)
    [isColliding, sepDist] = checkCollision(iiwa,q(:,i),'Exhaustive','on','SkippedSelfCollisions','parent');
    isConfigInCollision(i) = isColliding;
    sepDistForConfig(:,:,i) = sepDist;
end

To find out the indices of the bodies in collision, find which entries in the sepDistForConfig are NaN. septDist is a symmetric matrix, so the same value is returned in indexes with flipped indexes. Simplify the list by using unique.

for i = 1:length(q)
    sepDist = sepDistForConfig(:,:,i);
    [body1Idx,body2Idx] = find(isnan(sepDist));

    collidingPairs = unique(sort([body1Idx,body2Idx],2));
    configCollisionPairs{i} = collidingPairs;
end

By inspecting the output, you can see the planned trajectory goes through a series of collisions. Visualize the configuration where the first collision occurs and highlight the bodies.

any(isConfigInCollision)
ans = logical
   1

firstCollisionIdx = find(isConfigInCollision,1);

% Visualize the first configuration that is in collision.
figure;
show(iiwa,q(:,firstCollisionIdx));
exampleHelperHighlightCollisionBodies(iiwa,configCollisionPairs{firstCollisionIdx}+1,gca);

Generate a Collision-Free Trajectory

This first collision actually occurs at the starting configuration because a joint position is specified past its limits. Call wrapToPi to limit the starting positions of the joints.

Generate a new trajectory and check for collisions again.

newStartConfig = wrapToPi(startConfig);
q = trapveltraj([newStartConfig goalConfig],100,'EndTime',3);

isConfigInCollision = false(100,1);
configCollisionPairs = cell(100,1);
for i = 1:length(q)
    isColliding = checkCollision(iiwa,q(:,i),'Exhaustive','on','SkippedSelfCollisions','parent');
    isConfigInCollision(i) = isColliding;
end

After checking the whole trajectory, no collisions are found.

any(isConfigInCollision)
ans = logical
   0