Main Content

Using Pretrained DLCHOMP Optimizer to Predict Higher Number of Waypoints

Since R2024a

This example shows you how to a pretrained dlCHOMP optimizer to work with a different number of waypoints than what it was trained on.

In this example, you'll start with a dlCHOMP object that has a network that has been pretrained for a robot with an appropriate obstacle set. However, the pretrained network also uses a specific number of waypoints. To change these number of waypoints, you'll need to modify the network using transfer learning.

In this example it is assumed that the pretrained dlCHOMP optimizer is trained for the target robot kukaIiwa7 as specified by its RigidBodyTree property and has the desired basis point set encoder as specified by its BPSEncoder property. Since the RigidBodyTree and the BPSEncoder properties meet the desired requirements, but the NumWaypoints property does not, transfer learning can be performed by tweaking the network architecture stored in the underlying Network property of the pretrained dlCHOMP object. If you would like to change the optimization options such as SmoothnessOptions, CollisionOptions or SolverOptions properties instead, refer to the Using Pretrained DLCHOMP Optimizer in Unseen Obstacle Environment example.

To try out pretrained networks for other robots, see Pretrained Optimizers.

This example uses a pretrained network to initialize a new dlCHOMP optimizer and then trains it on a training dataset that meets our target trajectory requirement of a higher number of waypoints.

Background

Transfer learning is a deep learning approach in which a model that has been trained for one task is used as a starting point for a model that performs a similar task. Updating and retraining a network with transfer learning is usually much faster and easier than training a network from scratch.

Transfer learning is used because:

  • It enables you to train models with less labeled data by reusing popular models that have already been trained on large data sets.

  • It can reduce training time and computing resources. With transfer learning, the weights are not learned from scratch because the pretrained model has already learned the weights based on previous learnings.

For more information about transfer learning, see What Is Transfer Learning?

Download Pretrained DLCHOMP Optimizer

The pretrained DLCHOMP optimizer to be downloaded was trained for the target robot kukaIiwa7 as specified by its RigidBodyTree property and has the desired basis point set encoder as specified by its BPSEncoder property. Since the RigidBodyTree and the BPSEncoder properties meet the desired requirements, but the NumWaypoints property does not, transfer learning can be performed by tweaking the network architecture stored in the underlying Network property of the pretrained dlCHOMP object. This will be covered in the next section.

For now, download the pretrained DLCHOMP optimizer. Also obtain the dlCHOMPDataOptions object that was used to generate the training dataset used to train this pretrained optimizer.

dlCHOMPComponent = "rst/data/dlCHOMP/R2024a";
pretrainedDLCHOMPFilename = "kukaIiwa7DLCHOMPTrained.zip";
disp("Downloading previously trained kukaIiwa7 dlCHOMP optimizer (46 MB)...");
Downloading previously trained kukaIiwa7 dlCHOMP optimizer (46 MB)...
pretrainedDLCHOMPFolder = exampleHelperDownloadData(dlCHOMPComponent,pretrainedDLCHOMPFilename);
pretrainedDLCHOMPMATPath = fullfile(pretrainedDLCHOMPFolder,"trainedDLCHOMP.mat");  
loadedData = load(pretrainedDLCHOMPMATPath,"robotName","trainedDLCHOMP","trainInfo","trainOptions","trainLossType","dataOptions");
pretrainedRobotName = loadedData.robotName;
pretrainedDLCHOMP = loadedData.trainedDLCHOMP;
pretrainedTrainInfo = loadedData.trainInfo;
pretrainedTrainOptions = loadedData.trainOptions;
pretrainedTrainLossType = loadedData.trainLossType;
pretrainedDataOptions = loadedData.dataOptions;

As can be seen in the training plot above, the pretrainedDLCHOMP optimizer took 1500 iterations to get trained, and hence converge, from scratch.

Modify Last Layer of Pretrained Network to Predict Higher Number of Waypoints

First, specify the desired target number of waypoints.

desiredNumWaypoints = 50;

Compare the desiredNumWaypoints with the NumWaypoints property of the pretrainedDLCHOMP object.

isequal(desiredNumWaypoints,pretrainedDLCHOMP.NumWaypoints)
ans = logical
   0

As can be seen, the number of waypoints do not match as the pretrainedDLCHOMP was trained to predict 40 waypoints which does not match the desired 50 waypoints.

Since the NumWaypoints property only affects the last FullyConnectedLayer of the neural network property Network of a given dlCHOMP object, the rest of the network can still be reused from the pretrainedDLCHOMP object.

To do this, first, convert the Network property of the pretrainedDLCHOMP object into a LayerGraph.

pretrainedLayerGraph = layerGraph(pretrainedDLCHOMP.Network);

Then determine the configuration size of the robot of interest.

configurationSize = numel(pretrainedDLCHOMP.RigidBodyTree.homeConfiguration());

Then create a new FullyConnectedLayer object which meets the desired target trajectory requirement of outputting a higher number of waypoints than before.

desiredFinalLayerName = pretrainedDLCHOMP.Network.OutputNames{1};
desiredOutputSize = configurationSize * (desiredNumWaypoints-2); % The network only predicts the intermediate waypoints
newFinalLayer = fullyConnectedLayer( ...
    desiredOutputSize,"Name",desiredFinalLayerName)
newFinalLayer = 
  FullyConnectedLayer with properties:

          Name: 'FinalFCLayer'

   Hyperparameters
     InputSize: 'auto'
    OutputSize: 336

   Learnable Parameters
       Weights: []
          Bias: []

  Show all properties

As can be seen, this new layer has empty Weights and Bias properties, which indicates that it has not learned any parameters since it has not been trained yet.

Change the learning rates so that learning is faster in this new layer than in the transferred layers by setting its WeightLearnRateFactor and BiasLearnRateFactor to 10.

newFinalLayer.WeightLearnRateFactor = 10;
newFinalLayer.BiasLearnRateFactor = 10;

Now, replace the existing final FullyConnectedLayer with this new layer that was created.

modifiedLayerGraph = pretrainedLayerGraph.replaceLayer( ...
    desiredFinalLayerName, newFinalLayer);
modifiedNetwork = dlnetwork(modifiedLayerGraph);

Finally, create a new dlCHOMP object by setting its NumWaypoints property to desiredNumWaypoints and setting its Network property to modifiedNetwork at object construction time. All other properties will stay the same as that of the pretrainedDLCHOMP object.

There is a need to create a new object since the NumWaypoints and Network properties of a dlCHOMP object are read-only and cannot be modified post object construction.

modifiedDLCHOMP = dlCHOMP( ...
    pretrainedDLCHOMP.RigidBodyTree, ...
    pretrainedDLCHOMP.BPSEncoder, ...
    desiredNumWaypoints, ...
    CollisionOptions=pretrainedDLCHOMP.CollisionOptions, ...
    SmoothnessOptions=pretrainedDLCHOMP.SmoothnessOptions, ...
    SolverOptions=pretrainedDLCHOMP.SolverOptions, ...
    RigidBodyTreeSpheres=pretrainedDLCHOMP.RigidBodyTreeSpheres, ...
    Network=modifiedNetwork)
modifiedDLCHOMP = 
  dlCHOMP with properties:

           RigidBodyTree: [1×1 rigidBodyTree]
    RigidBodyTreeSpheres: [11×1 table]
       SmoothnessOptions: [1×1 chompSmoothnessOptions]
           SolverOptions: [1×1 chompSolverOptions]
        CollisionOptions: [1×1 chompCollisionOptions]
      SphericalObstacles: [4×0 double]
              BPSEncoder: [1×1 bpsEncoder]
            NumWaypoints: 50
                 Network: [1×1 dlnetwork]
               NumInputs: [14 10000]
              NumOutputs: 336

Now, for using the modifiedDLCHOMP optimizer for inference, it needs to have all of the learnable parameters in its Network property in a learned state.

Since the final FullyConnectedLayer of the pretrained network was replaced with a new layer with non-learned parameters, there is a need to generate a small dataset for retraining the optimizer to meet this one last unsatisfied objective.

Generate Training Dataset Matching Desired Number of Waypoints

First, create a new desiredDataOptions object which is a copy of the pretrainedDataOptions object that was used to generate the training dataset for the pretrainedDLCHOMP object.

desiredDataOptions = pretrainedDataOptions
desiredDataOptions = 
  dlCHOMPDataOptions with properties:

    RadiusRangeOfObstacles: [0.0500 0.2000]
     CountRangeOfObstacles: [20 25]
       MinDistanceFromBase: 0.2100
                NumSamples: 5000
           ValidationSplit: 0.2000
              RandomStream: 'threefry'
                      Seed: 0

Modify the dlCHOMPDataOptions properties of the desiredDataOptions object that affect environment diversity and the training versus validation data set splits.

As explained in the Background section, there is a need to generate a much smaller training dataset as compared to what the pretrainedDLCHOMP object was trained on.

For this, decrease the training dataset size in the desiredDataOptions object to be much smaller than that of the pretrainedDataOptions object. Set the NumSamples property of desiredDataOptions to be 1/5th of that of pretrainedDataOptions which amounts to 1000 as compared to 5000 data samples. For more information on these properties, see dlCHOMPDataOptions.

desiredDataOptions.NumSamples = pretrainedDataOptions.NumSamples/5
desiredDataOptions = 
  dlCHOMPDataOptions with properties:

    RadiusRangeOfObstacles: [0.0500 0.2000]
     CountRangeOfObstacles: [20 25]
       MinDistanceFromBase: 0.2100
                NumSamples: 1000
           ValidationSplit: 0.2000
              RandomStream: 'threefry'
                      Seed: 0

Keep 20% of the generated data set for validation and the rest for training. To do this set the ValidationSplit property to 0.2.

desiredDataOptions.ValidationSplit = 0.2;

Now, execute the following code snippet to generate the 1000 samples. Note that this takes 4 hrs on a Windows 10 system with 48 GB of RAM. Hence, this dataset is downloaded by default as generateData is set to false below.

If you want to wait for data generation, set generateData to true. Otherwise, set generateData to false.

generateData = false;

If generateData is false, download the required data. Otherwise, generate the data.

dataSamplesFolder = "allData";
if generateData == false
    dataSamplesFilename = "kukaIiwa7DataForRetrainingDLCHOMPToHigherWaypoints.zip";
    disp("Downloading data for retraining kukaIiwa7 dlCHOMP optimizer (244 MB)...");
    dataSamplesFolder = exampleHelperDownloadData(dlCHOMPComponent, ...
        dataSamplesFilename, 'DataFolder', dataSamplesFolder);
    validIndices = 1:floor(desiredDataOptions.ValidationSplit * desiredDataOptions.NumSamples);
    trainIndices = (numel(validIndices)+1):desiredDataOptions.NumSamples;
    validDS = dlCHOMPDatastore(dataSamplesFolder,validIndices);
    trainDS = dlCHOMPDatastore(dataSamplesFolder,trainIndices);
else
    seed = 100;
    rng(seed,"twister");
    [trainDS,validDS] = generateSamples(modifiedDLCHOMP,desiredDataOptions,dataSamplesFolder);
end
Downloading data for retraining kukaIiwa7 dlCHOMP optimizer (244 MB)...

Retrain Modified dlCHOMP Optimizer

Let's retrain the modifiedDLCHOMP optimizer object using the new data that was obtained.

Use the same training options object pretrainedTrainOptions by setting its ValidationData property to the new validation datastore validDS that was obtained earlier. Keep the other properties the same.

trainOptions = pretrainedTrainOptions;
trainOptions.ValidationData = validDS;

Use the loss type pretrainedTrainLossType that was used to train the pretrainedDLCHOMP object.

Now, retrain the modifiedDLCHOMP optimizer object using the training datastore trainDS, loss type pretrainedTrainLossType and the training options trainOptions.

Retraining takes around 10 minutes on a Windows 10 system with 48 GB RAM using 8 parallel pool workers. So download this previously retrained dlCHOMP object by default as the doRetraining is set to false below.

If you want to retrain the object yourself, set doRetraining to true. Otherwise, set doRetraining to false.

doRetraining = false;

If doRetraining is false, download a new pretrained dlCHOMP optimizer object. Otherwise, retrain the existing object.

if doRetraining == false
    retrainedDLCHOMPFilename = "kukaIiwa7DLCHOMPRetrainedForHigherWaypoints.zip";
    disp("Downloading previously retrained kukaIiwa7 dlCHOMP optimizer (44 MB)...");
    retrainedDLCHOMPFolder = exampleHelperDownloadData(dlCHOMPComponent,retrainedDLCHOMPFilename);
    retrainedDLCHOMPMATPath = fullfile(retrainedDLCHOMPFolder,"retrainedDLCHOMP.mat");
    load(retrainedDLCHOMPMATPath,"retrainedDLCHOMP","retrainInfo");
else
    retrainInfo = trainDLCHOMP(modifiedDLCHOMP,trainDS,pretrainedTrainLossType,trainOptions);
    retrainedDLCHOMP = modifiedDLCHOMP;
end
Downloading previously retrained kukaIiwa7 dlCHOMP optimizer (44 MB)...

As can be seen above, the dlCHOMP optimizer converged much faster while training, within about 150 iterations, which is much smaller than the nearly 1500 iterations that were needed to train the pretrained dlCHOMP optimizer from scratch as was shown in the training plot under the Download Pretrained DLCHOMP Optimizer section above. This indicates that the network had already learned weights based on previous learnings and simply had to be tuned to predict a higher number of waypoints than before.

Infer Using Retrained dlCHOMP Optimizer

Now that the retrained optimizer is available, it can be used for inference on an unseen target environment of interest.

Generate One Unseen Target Environment

Let's generate an unseen target environment using the same dlCHOMPDataOptions object were chosen for generating the training and validation data sets to ensure the same environment complexity. However, change the properties that affect the environment diversity to ensure that an unseen data sample is generated.

First, to generate only 1 data sample, set the NumSamples property to 1 and the ValidationSplit property to 0 respectively.

desiredDataOptions.NumSamples = 1;
desiredDataOptions.ValidationSplit = 0;

Next, to ensure an unseen data sample is generated, change the Seed value to differ it from the one used when generating the training and validation data sets used to train the optimizer. Since the default Seed value of 0 was used then, let's use a value of 300 now.

desiredDataOptions.Seed = 300
desiredDataOptions = 
  dlCHOMPDataOptions with properties:

    RadiusRangeOfObstacles: [0.0500 0.2000]
     CountRangeOfObstacles: [20 25]
       MinDistanceFromBase: 0.2100
                NumSamples: 1
           ValidationSplit: 0
              RandomStream: 'threefry'
                      Seed: 300

Choose a new location for saving the generated unseen sample.

folderForUnseenData = "unseenData";

Generate an unseen target environment.

rng(desiredDataOptions.Seed,"twister");
testDS = retrainedDLCHOMP.generateSamples(desiredDataOptions,folderForUnseenData);
------------------------
Starting data generation
[1/1] Data Samples Written	|	┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃┃ 100.00% complete	|	
Total Run Time: 00:00:02 secs
Estimated Time Remaining: 00:00:00 secs
------------------------
filePathToUnseenSample = resolve(testDS.FileSet).FileName(1);
[unseenStart,unseenGoal,unseenObstacles,chompTrajectory] = exampleHelperExtractDataFromDLCHOMPSample(filePathToUnseenSample);

Infer On Unseen Target Environment

Make the retrainedDLCHOMP optimizer aware of the unseen spherical obstacles.

retrainedDLCHOMP.SphericalObstacles = unseenObstacles;

Finally, use the retrainedDLCHOMP optimizer for inference on this unseen target environment.

[optimWptsDLCHOMPNew,optimTptsDLCHOMPNew,solninfoDLCHOMPNew] = retrainedDLCHOMP.optimize(unseenStart,unseenGoal);

Let's visualize this trajectory in an animated fashion.

% Create new figure
figure;

% Show animated robot trajectory
ax = show(retrainedDLCHOMP,unseenStart);
title(sprintf("dlCHOMP Optimized End-Effector Trajectory with %d Waypoints\nUnseen Target Environment",desiredNumWaypoints));
axis(ax,"equal");
exampleHelperShowAnimatedRobotTrajectory(ax,retrainedDLCHOMP.RigidBodyTree,optimWptsDLCHOMPNew);

Figure contains an axes object. The axes object with title dlCHOMP Optimized End-Effector Trajectory with 50 Waypoints Unseen Target Environment, xlabel X, ylabel Y contains 133 objects of type patch, line. These objects represent world, iiwa_link_0, iiwa_link_1, iiwa_link_2, iiwa_link_3, iiwa_link_4, iiwa_link_5, iiwa_link_6, iiwa_link_7, iiwa_link_ee, iiwa_link_ee_kuka, iiwa_link_0_mesh, iiwa_link_1_mesh, iiwa_link_2_mesh, iiwa_link_3_mesh, iiwa_link_4_mesh, iiwa_link_5_mesh, iiwa_link_6_mesh, iiwa_link_7_mesh, iiwa_link_0_coll_mesh, iiwa_link_1_coll_mesh, iiwa_link_2_coll_mesh, iiwa_link_3_coll_mesh, iiwa_link_4_coll_mesh, iiwa_link_5_coll_mesh, iiwa_link_6_coll_mesh, iiwa_link_7_coll_mesh.

Conclusion

In conclusion, you learned how to tweak a pretrained dlCHOMP optimizer's network architecture meant to predict a low number of waypoints, to meet the target requirement of predicting a higher number of waypoints. You also learned how to retrain this modified dlCHOMP optimizer to tune it to predict a higher number of waypoints. Finally, you used this retrained dlCHOMP optimizer for motion planning in an unseen target obstacle environment.

See Also

Related Topics