Introduction

Optima is an easy to set up and easy to use robotics toolbox. Its primary use-case is robot motion generation (e.g., trajectory optimization, motion planning, optimization-based inverse kinematics, etc), though its underlying structures are general and can apply to many planning and optimization problem domains. The core library is written in Rust, though high quality support for other targets such as Python and Webassembly are afforded via Rust's flexible compiler.

Optima features an extensive suite of efficient and flexible robotics subroutines, including forward kinematics, jacobian computations, etc. Optima also features implementations of state-of-the-art robotics algorithms related to planning, optimization, and proximity computations, such as RelaxedIK, CollisionIK, and Proxima (with more algorithms on the way). These robotics features are designed to be easy to set up and access. Models of many popular robots such as UR5, Franka Emika Panda, Kinova Gen3, etc., are included out-of-the-box and can be instantiated with a single line of code. If your robot model is not included in our provided assets, a robot can be added to the library in just minutes.

High-level Features

  • The underlying non-linear optimization engine has several backend options, such as the Open Engine and NLopt. All algorithms support a non-linear objective function, non-linear equality constraints, and non-linear inequality constraints. Most provided algorithms are local solvers, though several global optimization algorithms are provided via NLopt as well. These optimization algorithm options can be swapped in and out by changing a single parameter, making testing and benchmarking over optimization algorithms easy to conduct.

  • The library presents a powerful Rust trait called OptimaTensorFunction; any function that implements this trait can automatically compute derivatives (up to the fourth derivative) using a derivative composition graph that automatically selects either analytical derivatives, when provided, or finite-difference approximate derivatives, by default, through its traversal. Thus, instead of depending on low-level automatic differentiation libraries that often fail on important subroutines, this approach provides a highly efficient and flexible way to perform derivatives for any implemented function. High order derivatives are even computable on multi-dimensional functions as the library supports tensors of any arbitrary dimension. For instance, a function \( \mathit{f}: \mathbb{R}^{3 \times 5} \rightarrow {R}^{4 \times 4} \) implemented as an OptimaTensorFunction can automatically provide derivatives \( \frac{\partial f}{\partial \mathbf{X}} \in {R}^{4 \times 4 \times 3 \times 5} \), \( \frac{\partial^2 f}{\partial \mathbf{X}^2} \in {R}^{4 \times 4 \times 3 \times 5 \times 3 \times 5} \), etc.

  • Optima uses flexible transform and rotation implementations that make SE(3) computations a breeze. For instance, any computations over transforms can use homogeneous matrices, implicit dual quaternions, a rotation matrix + vector, unit quaternion + vector, euler angles + vector, etc. No matter what SE(3) representation is chosen, the library will automatically ensure that all conversions and computations are handled correctly under the hood.

  • In just a few lines of code, individual robot models can be combined to form a "robot set", allowing for easy planning and optimization over several robots all at once. For instance, a UR5 (6DOF) can be easily combined with two Rethink Sawyer (7DOF) robots to form a robot set with 20 total DOF. In addition, any robot in the library (including those in a robot set) can be easily supplemented with a mobile base of numerous types (e.g., floating base, planar base, etc.). The extra degrees of freedom accompanying these mobile base options are automatically added to the robot (or robot set) model.

Sub-repositories

The Optima Toolbox is comprised of four primary sub-repositories:

  1. optima: This repository contains the core Rust code of the Optima Toolbox. This code can be compiled to Rust, Python, and Web-assembly.
  2. optima_assets: This repository contains asset files that are pre-packaged with the toolbox. For instance, these assets include pre-processed robot models (in the optima_robots repository), as well as 3D mesh files that can be used to build up scenes around robot models.
  3. optima_js: This repository will contain utility code when using Optima in Javascript. This folder is an empty place-holder right now; code will be added here over time.
  4. optima_py: This repository will contain utility code when using Optima in Python. This folder is an empty place-holder right now; code will be added here over time.

Getting Started

In this Chapter, I will overview how to set up the Optima Toolbox.

Optima Toolbox Installation

The Optima Toolbox code lives on github. A standard way to install this code is to ensure that git is installed on your system, then clone the code by running the following command in your terminal after navigating to a desired directory:

git clone https://github.com/djrakita/optima_toolbox.git

The Optima Toolbox repository has several sub-repositories. In order to pull all updates from the sub-repositories, navigate into the optima_toolbox directory:

cd optima_toolbox

then run the following command in your terminal:

git submodule update --init --recursive

And that's it! The Optima Toolbox files are now on your computer. In the following section, I will outline how to install the Rust toolchain in order to further set up the Optima Toolbox.

Note that if the optima_bevy feature is on (it is by default), you will also need to setup dependencies for the bevy game engine on your system.

Rust Installation

We will next ensure that the Rust programming language toolchain is installed on your computer. Rust is required in order to set up and compile all of the features offered by Optima, even if you are mostly interested in using Optima in Python, Webassembly, etc.

Fortunately, the Rust toolchain is very easy to install. Simply follow the one-step installation guide here. To verify that Rust has been successfully installed, run the following command:

cargo --version

Also, to verify that the newly installed Rust compiler works with the Optima Toolbox, navigate to the optima sub-directory in the Optima Toolbox (assuming you are already in the optima_toolbox directory.):

cd optima

Then run:

cargo test

Numerous tests should run, all passing with a result of ok.

Robot Test

We will now further verify that the Optima Toolbox is properly setup with the Rust toolchain.

In the optima_toolbox/optima directory, run the following command:

cargo run --bin print_robot_info ur5

If all is working, the console should contain configuration and diagnostics information for the given robot model (ur5 in this case):

  Link index:  0   Link name:  base_link   Present:  true 
  Link index:  1   Link name:  shoulder_link   Present:  true 
  Link index:  2   Link name:  upper_arm_link   Present:  true 
  Link index:  3   Link name:  forearm_link   Present:  true 
  Link index:  4   Link name:  wrist_1_link   Present:  true 
  Link index:  5   Link name:  wrist_2_link   Present:  true 
  Link index:  6   Link name:  wrist_3_link   Present:  true 
  Link index:  7   Link name:  ee_link   Present:  true 
  Link index:  8   Link name:  base   Present:  true 
  Link index:  9   Link name:  tool0   Present:  true 
  Link index:  10   Link name:  world   Present:  true 

>> Joint index:  0   Joint name:  shoulder_pan_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  1   Joint name:  shoulder_lift_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  2   Joint name:  elbow_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  3   Joint name:  wrist_1_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  4   Joint name:  wrist_2_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  5   Joint name:  wrist_3_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  6   Joint name:  ee_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  7   Joint name:  base_link-base_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  wrist_3_link-tool0_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  9   Joint name:  world_joint   Num dofs:  0   Present:  true 

DOF Joint Axis 0 ---> JointAxis { joint_idx: 0, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 1 ---> JointAxis { joint_idx: 1, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 2 ---> JointAxis { joint_idx: 2, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 3 ---> JointAxis { joint_idx: 3, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 4 ---> JointAxis { joint_idx: 4, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 5 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }

Feel free to run the command above with different robot models as an argument. Any robot in the optima_toolbox/optima_assets/optima_robots directory will work. For instance, the following command:

cargo run --bin print_robot_info yumi

will result in the following output:

  Link index:  0   Link name:  yumi_base_link   Present:  true 
  Link index:  1   Link name:  yumi_body   Present:  true 
  Link index:  2   Link name:  yumi_link_1_r   Present:  true 
  Link index:  3   Link name:  yumi_link_2_r   Present:  true 
  Link index:  4   Link name:  yumi_link_3_r   Present:  true 
  Link index:  5   Link name:  yumi_link_4_r   Present:  true 
  Link index:  6   Link name:  yumi_link_5_r   Present:  true 
  Link index:  7   Link name:  yumi_link_6_r   Present:  true 
  Link index:  8   Link name:  yumi_link_7_r   Present:  true 
  Link index:  9   Link name:  yumi_link_1_l   Present:  true 
  Link index:  10   Link name:  yumi_link_2_l   Present:  true 
  Link index:  11   Link name:  yumi_link_3_l   Present:  true 
  Link index:  12   Link name:  yumi_link_4_l   Present:  true 
  Link index:  13   Link name:  yumi_link_5_l   Present:  true 
  Link index:  14   Link name:  yumi_link_6_l   Present:  true 
  Link index:  15   Link name:  yumi_link_7_l   Present:  true 
  Link index:  16   Link name:  gripper_r_base   Present:  true 
  Link index:  17   Link name:  gripper_r_finger_r   Present:  true 
  Link index:  18   Link name:  gripper_r_finger_l   Present:  true 
  Link index:  19   Link name:  gripper_l_base   Present:  true 
  Link index:  20   Link name:  gripper_l_finger_r   Present:  true 
  Link index:  21   Link name:  gripper_l_finger_l   Present:  true 
  Link index:  22   Link name:  world   Present:  true 

>> Joint index:  0   Joint name:  yumi_base_link_to_body   Num dofs:  0   Present:  true 
>> Joint index:  1   Joint name:  yumi_joint_1_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  2   Joint name:  yumi_joint_2_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  3   Joint name:  yumi_joint_7_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  4   Joint name:  yumi_joint_3_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  5   Joint name:  yumi_joint_4_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  6   Joint name:  yumi_joint_5_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  7   Joint name:  yumi_joint_6_r   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  8   Joint name:  yumi_joint_1_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  9   Joint name:  yumi_joint_2_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  10   Joint name:  yumi_joint_7_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  11   Joint name:  yumi_joint_3_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  12   Joint name:  yumi_joint_4_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  13   Joint name:  yumi_joint_5_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  14   Joint name:  yumi_joint_6_l   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  15   Joint name:  yumi_link_7_r_joint   Num dofs:  0   Present:  true 
>> Joint index:  16   Joint name:  gripper_r_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Translation about axis [[-1.0, 0.0, 0.0]], Not fixed.
>> Joint index:  17   Joint name:  gripper_r_joint_m   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Translation about axis [[-1.0, 0.0, 0.0]], Not fixed.
>> Joint index:  18   Joint name:  yumi_link_7_l_joint   Num dofs:  0   Present:  true 
>> Joint index:  19   Joint name:  gripper_l_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Translation about axis [[-1.0, 0.0, 0.0]], Not fixed.
>> Joint index:  20   Joint name:  gripper_l_joint_m   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Translation about axis [[-1.0, 0.0, 0.0]], Not fixed.
>> Joint index:  21   Joint name:  world_joint   Num dofs:  0   Present:  true 

DOF Joint Axis 0 ---> JointAxis { joint_idx: 1, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.94087978961, 2.94087978961) }
DOF Joint Axis 1 ---> JointAxis { joint_idx: 2, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.50454747661, 0.759218224618) }
DOF Joint Axis 2 ---> JointAxis { joint_idx: 3, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.94087978961, 2.94087978961) }
DOF Joint Axis 3 ---> JointAxis { joint_idx: 4, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.15548162621, 1.3962634016) }
DOF Joint Axis 4 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-5.06145483078, 5.06145483078) }
DOF Joint Axis 5 ---> JointAxis { joint_idx: 6, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-1.53588974176, 2.40855436775) }
DOF Joint Axis 6 ---> JointAxis { joint_idx: 7, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.99680398707, 3.99680398707) }
DOF Joint Axis 7 ---> JointAxis { joint_idx: 8, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.94087978961, 2.94087978961) }
DOF Joint Axis 8 ---> JointAxis { joint_idx: 9, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.50454747661, 0.759218224618) }
DOF Joint Axis 9 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.94087978961, 2.94087978961) }
DOF Joint Axis 10 ---> JointAxis { joint_idx: 11, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.15548162621, 1.3962634016) }
DOF Joint Axis 11 ---> JointAxis { joint_idx: 12, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-5.06145483078, 5.06145483078) }
DOF Joint Axis 12 ---> JointAxis { joint_idx: 13, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-1.53588974176, 2.40855436775) }
DOF Joint Axis 13 ---> JointAxis { joint_idx: 14, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.99680398707, 3.99680398707) }
DOF Joint Axis 14 ---> JointAxis { joint_idx: 16, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[-1.0, 0.0, 0.0]], axis: [[-1.0, 0.0, 0.0]], axis_primitive_type: Translation, bounds: (0.0, 0.025) }
DOF Joint Axis 15 ---> JointAxis { joint_idx: 17, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[-1.0, 0.0, 0.0]], axis: [[-1.0, 0.0, 0.0]], axis_primitive_type: Translation, bounds: (0.0, 0.025) }
DOF Joint Axis 16 ---> JointAxis { joint_idx: 19, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[-1.0, 0.0, 0.0]], axis: [[-1.0, 0.0, 0.0]], axis_primitive_type: Translation, bounds: (0.0, 0.025) }
DOF Joint Axis 17 ---> JointAxis { joint_idx: 20, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[-1.0, 0.0, 0.0]], axis: [[-1.0, 0.0, 0.0]], axis_primitive_type: Translation, bounds: (0.0, 0.025) }

The outputs of these commands will make more sense as you move through the tutorials ahead, but for now, as long as the outputs in your console appear as they do here, the toolbox is correctly set up on your system!

Adding a Robot

The Optima Toolbox comes pre-packaged with models of several popular robots. These models can be found in the optima_toolbox/optima_assets/optima_robots directory. Each robot directory here (e.g., ur5, sawyer, etc.) contains all necessary information for using their respective robot models throughout the Optima Toolbox.

Guide on adding a robot is coming soon! If you need a new robot added to the Optima Toolbox before this guide is made available, please email me at daniel.rakita@yale.edu.

Python Setup

Coming Soon

WASM Setup

Coming Soon

Citing Optima

If you use any part of the the Optima Toolbox in your research, please cite the software as follows:

 @misc{rakita_2022, url={https://djrakita.github.io/optima_toolbox/}, 
 author={Rakita, Daniel}, 
 title={Optima: An Applied Planning and Optimization Toolbox}
 year={2022}} 

Robots and Robot Sets

In this Chapter, I will cover brief tutorial topics on how to load and save robot models in Optima.

Loading a Robot Model

In this tutorial, we will cover how to load a robot model into Optima. The code for this example is as follows:

extern crate optima;

use optima::robot_modules::robot::Robot;
use optima::utils::utils_robot::robot_module_utils::RobotNames;

fn main () {
    // load the given robot
    let robot = Robot::new_from_names(RobotNames::new("ur5", None));

    // prints a summary of the robot configuration
    robot.robot_configuration_module().print_summary();

    // prints a summary of the robot's degrees of freedom
    robot.robot_joint_state_module().print_dof_summary();
}

Let's take a look through the code. The important part of this code is line 8 where the ur5 robot model is loaded. The easiest way to load a robot is using the new_from_names function. This function takes a RobotNames parameter, which has two parameters of its own. The first parameter here is the name of the robot ("ur5" in this case). This string literal parameter could be any robot in the directory optima_toolbox/optima_assets/optima_robots. The second parameter is a Rust Option that can, as the name implies, serve as an optional string parameter specifying the name of a robot configuration. Robot configurations will be covered in a following section, so in this case, the configuration is specifies as None, meaning the loaded robot model will simply be its base configuration.

A Robot in Optima is a large struct that aggregates smaller structs that contain specialized code to perform particular tasks, referred to as modules. For instance, a Robot contains a RobotConfigurationModule, a RobotJointStateModule, a RobotKinematicsModule, and so on. For details on each module, refer to the Optima API documentation. Lines 11 and 14 here print a summary of the configuration module and joint state module, respectively, to the console.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/0_load_robot.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 0_robots

The output of this example should be the following:

  Link index:  0   Link name:  base_link   Present:  true 
  Link index:  1   Link name:  shoulder_link   Present:  true 
  Link index:  2   Link name:  upper_arm_link   Present:  true 
  Link index:  3   Link name:  forearm_link   Present:  true 
  Link index:  4   Link name:  wrist_1_link   Present:  true 
  Link index:  5   Link name:  wrist_2_link   Present:  true 
  Link index:  6   Link name:  wrist_3_link   Present:  true 
  Link index:  7   Link name:  ee_link   Present:  true 
  Link index:  8   Link name:  base   Present:  true 
  Link index:  9   Link name:  tool0   Present:  true 
  Link index:  10   Link name:  world   Present:  true 

>> Joint index:  0   Joint name:  shoulder_pan_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  1   Joint name:  shoulder_lift_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  2   Joint name:  elbow_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  3   Joint name:  wrist_1_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  4   Joint name:  wrist_2_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  5   Joint name:  wrist_3_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  6   Joint name:  ee_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  7   Joint name:  base_link-base_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  wrist_3_link-tool0_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  9   Joint name:  world_joint   Num dofs:  0   Present:  true 

DOF Joint Axis 0 ---> JointAxis { joint_idx: 0, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 1 ---> JointAxis { joint_idx: 1, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 2 ---> JointAxis { joint_idx: 2, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 3 ---> JointAxis { joint_idx: 3, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 4 ---> JointAxis { joint_idx: 4, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 5 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }

Robot Configurations

In this tutorial, we will cover how to create a new robot configuration, how to use this robot configuration module to instantiate a robot, and how to save this configuration for later use.

A RobotConfigurationModule affords extra specificity and functionality over a robot model. For example, a robot configuration can include a mobile base, a base offset, removed links, as well as fixed joint values that will not be considered degrees of freedom. Let's examine some code that creates, modifies, saves, and loads a robot configuration:

extern crate optima;

use optima::robot_modules::robot::Robot;
use optima::robot_modules::robot_configuration_module::{ContiguousChainMobilityMode, RobotConfigurationModule};
use optima::utils::utils_robot::robot_module_utils::RobotNames;

fn main() {
    // Initialize a new robot configuration for the ur5 robot.
    let mut robot_configuration = RobotConfigurationModule::new_from_names(RobotNames::new_base("ur5")).expect("error");

    // Sets the 0 joint idx (should_pan_joint, in the case of the ur5 robot) with joint_sub_idx 0 (the
    // default sub-idx for joints with only 1 degree of freedom) to a fixed value of 1.2.  From here on
    // out, this joint will no longer be considered a degree of freedom and, instead,
    // will remain locked at 1.2.
    robot_configuration.set_fixed_joint(0, 0, 1.2);

    // Sets link with idx 5 (wrist_2_link, in the case of the ur5 robot) as a "dead end link".  A
    // dead-end link means that this link, as well as all predecessor links in the kinematic chain,
    // will be removed from the model.  Note that this will also remove all joints that connect
    // these removed links, thus possibly reducing the number of degrees of freedom of the model.
    // Links and joints that are removed in this way are said to be not "present" in the model.
    robot_configuration.set_dead_end_link(5);

    // Sets the base of the robot to be mobile.  In this case, the base is floating (not
    // realistic for the ur5 robot, but is very useful, for example, for the hips
    // of humanoid robots).  This base will automatically add 6 degrees of freedom
    // to the model, 3 for translation (x, y, z) and 3 for rotation (rx, ry, rz).
    // All lower and upper bounds are set for each of these degrees of freedom.
    // All mobility modes are PlanarTranslation (x and y translation), PlanarRotation (rz rotation),
    // PlanarTranslationAndRotation (x and y translation + z rotation),
    // Static (i.e., 0 DOFs), and Floating (x, y, z translation + rx, ry, rz rotation).
    robot_configuration.set_mobile_base(ContiguousChainMobilityMode::Floating {
        x_bounds: (-2.0, 2.0),
        y_bounds: (-2.0, 2.0),
        z_bounds: (-2.0, 2.0),
        xr_bounds: (-3.14, 3.14),
        yr_bounds: (-3.14, 3.14),
        zr_bounds: (-3.14, 3.14)
    });

    // Saves the robot configuration for later use.  This will add a file to
    // `optima_toolbox/optima_assets/optima_robots/ur5/configurations/test_configuration.JSON`.
    // NOTE: if you run this example multiple times and the test_configuration file is already
    // present, a prompt will show up in the console asking if you would like to save over
    // the already saved file.
    robot_configuration.save("test_configuration");

    // For illustrative purposes, let's now load in our robot configuration from the file that
    // was just saved.  The second parameter in the `RobotNames` struct is now an option of
    // `Some` with the name of our recently saved configuration.
    let loaded_robot_configuration = RobotConfigurationModule::new_from_names(RobotNames::new("ur5", Some("test_configuration"))).expect("error");

    // This configuration can now be used to instantiate a `Robot`.
    let robot = Robot::new_from_robot_configuration_module(loaded_robot_configuration);

    // when we print information about the robot, we see that the information that we
    // inputted above is reflected in the printed output.
    robot.robot_configuration_module().print_summary();
    robot.robot_joint_state_module().print_dof_summary();
}

Explanations for the code above are written as comments line by line.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/1_robot_configurations.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 1_robot_configurations

Note that, if this example is run, a file called test_configuration.JSON will be saved in optima_toolbox/optima_assets/optima_robots/ur5/configurations. Because this configuration is only for educational purposes and is mostly nonsensical, I recommend deleting this file before moving on.

The output of this example should be the following:

  Link index:  0   Link name:  base_link   Present:  true 
  Link index:  1   Link name:  shoulder_link   Present:  true 
  Link index:  2   Link name:  upper_arm_link   Present:  true 
  Link index:  3   Link name:  forearm_link   Present:  true 
  Link index:  4   Link name:  wrist_1_link   Present:  true 
  Link index:  5   Link name:  wrist_2_link   Present:  false 
  Link index:  6   Link name:  wrist_3_link   Present:  false 
  Link index:  7   Link name:  ee_link   Present:  false 
  Link index:  8   Link name:  base   Present:  true 
  Link index:  9   Link name:  tool0   Present:  false 
  Link index:  10   Link name:  world   Present:  true 
  Link index:  11   Link name:  base_of_chain_link_with_child_link_10   Present:  true 

>> Joint index:  0   Joint name:  shoulder_pan_joint   Num dofs:  0   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Fixed at value 1.2
>> Joint index:  1   Joint name:  shoulder_lift_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  2   Joint name:  elbow_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  3   Joint name:  wrist_1_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  4   Joint name:  wrist_2_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  5   Joint name:  wrist_3_joint   Num dofs:  1   Present:  false 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  6   Joint name:  ee_fixed_joint   Num dofs:  0   Present:  false 
>> Joint index:  7   Joint name:  base_link-base_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  wrist_3_link-tool0_fixed_joint   Num dofs:  0   Present:  false 
>> Joint index:  9   Joint name:  world_joint   Num dofs:  0   Present:  true 
>> Joint index:  10   Joint name:  base_of_chain_connector_joint_with_child_link_11   Num dofs:  6   Present:  true 
      -- Joint sub idx 0:  Translation about axis [[1.0, 0.0, 0.0]], Not fixed.
      -- Joint sub idx 1:  Translation about axis [[0.0, 1.0, 0.0]], Not fixed.
      -- Joint sub idx 2:  Translation about axis [[0.0, 0.0, 1.0]], Not fixed.
      -- Joint sub idx 3:  Rotation about axis [[1.0, 0.0, 0.0]], Not fixed.
      -- Joint sub idx 4:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
      -- Joint sub idx 5:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.

DOF Joint Axis 0 ---> JointAxis { joint_idx: 1, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 1 ---> JointAxis { joint_idx: 2, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 2 ---> JointAxis { joint_idx: 3, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 3 ---> JointAxis { joint_idx: 4, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
DOF Joint Axis 4 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[1.0, 0.0, 0.0]], axis: [[1.0, 0.0, 0.0]], axis_primitive_type: Translation, bounds: (-2.0, 2.0) }
DOF Joint Axis 5 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 1, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Translation, bounds: (-2.0, 2.0) }
DOF Joint Axis 6 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 2, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Translation, bounds: (-2.0, 2.0) }
DOF Joint Axis 7 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 3, fixed_value: None, axis_as_unit: [[1.0, 0.0, 0.0]], axis: [[1.0, 0.0, 0.0]], axis_primitive_type: Rotation, bounds: (-3.14, 3.14) }
DOF Joint Axis 8 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 4, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-3.14, 3.14) }
DOF Joint Axis 9 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 5, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.14, 3.14) }

Robot Sets

Thus far, the previous tutorials have been building up an understanding of the Robot struct and its underlying modules in Optima. This struct only contains a model and configuration for a single robot (ur5, yumi, sawyer, etc.). However, what if we want to handle cases where our model may contain multiple robots.

Optima handles this case by providing the RobotSet struct. A RobotSet can be thought of as a superset abstraction of the Robot struct. A RobotSet initialized with a single robot is indistiguishable from a Robot; however, it can also incorporate more robots into a single model when needed.

IMPORTANT: Because the RobotSet struct is a superset of Robot, almost all downstream applications and algorithms in Optima will depend on a RobotSet instead of a Robot. Thus, it is highly recommended to always use a RobotSet in code, even when only one robot is present in the model, in order to maximimze compatibility throughout the library.

Code that loads a RobotSet can be seen here:

extern crate optima;

use optima::robot_set_modules::robot_set::RobotSet;
use optima::utils::utils_robot::robot_module_utils::RobotNames;

fn main() {
    // Loads a robot set with two robots (a ur5 and sawyer).
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new("ur5", None), RobotNames::new("sawyer", None)]);

    // prints a summary of the robot set configuration
    robot_set.robot_set_configuration_module().print_summary();

    // prints a summary of the robot set's degrees of freedom.
    robot_set.robot_set_joint_state_module().print_dof_summary();
}

The robot set loaded in line 8 has two robots, a ur5 and a sawyer. Both of these robots are their "base" configurations (i.e., the second parameter in both RobotNames structs are None). However, either of these could have been named configurations previously saved for either robot.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/0_load_robot.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 2_robot_sets

The output of this example should be the following:

Robot 0 ---> 
  Link index:  0   Link name:  base_link   Present:  true 
  Link index:  1   Link name:  shoulder_link   Present:  true 
  Link index:  2   Link name:  upper_arm_link   Present:  true 
  Link index:  3   Link name:  forearm_link   Present:  true 
  Link index:  4   Link name:  wrist_1_link   Present:  true 
  Link index:  5   Link name:  wrist_2_link   Present:  true 
  Link index:  6   Link name:  wrist_3_link   Present:  true 
  Link index:  7   Link name:  ee_link   Present:  true 
  Link index:  8   Link name:  base   Present:  true 
  Link index:  9   Link name:  tool0   Present:  true 
  Link index:  10   Link name:  world   Present:  true 

>> Joint index:  0   Joint name:  shoulder_pan_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  1   Joint name:  shoulder_lift_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  2   Joint name:  elbow_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  3   Joint name:  wrist_1_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  4   Joint name:  wrist_2_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  5   Joint name:  wrist_3_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  6   Joint name:  ee_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  7   Joint name:  base_link-base_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  wrist_3_link-tool0_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  9   Joint name:  world_joint   Num dofs:  0   Present:  true 


Robot 1 ---> 
  Link index:  0   Link name:  base   Present:  true 
  Link index:  1   Link name:  torso   Present:  true 
  Link index:  2   Link name:  pedestal   Present:  true 
  Link index:  3   Link name:  controller_box   Present:  true 
  Link index:  4   Link name:  pedestal_feet   Present:  true 
  Link index:  5   Link name:  right_arm_base_link   Present:  true 
  Link index:  6   Link name:  right_l0   Present:  true 
  Link index:  7   Link name:  head   Present:  true 
  Link index:  8   Link name:  right_torso_itb   Present:  true 
  Link index:  9   Link name:  right_l1   Present:  true 
  Link index:  10   Link name:  right_l2   Present:  true 
  Link index:  11   Link name:  right_l3   Present:  true 
  Link index:  12   Link name:  right_l4   Present:  true 
  Link index:  13   Link name:  right_arm_itb   Present:  true 
  Link index:  14   Link name:  right_l5   Present:  true 
  Link index:  15   Link name:  right_hand_camera   Present:  true 
  Link index:  16   Link name:  right_wrist   Present:  true 
  Link index:  17   Link name:  right_l6   Present:  true 
  Link index:  18   Link name:  right_hand   Present:  true 
  Link index:  19   Link name:  right_l1_2   Present:  true 
  Link index:  20   Link name:  right_l2_2   Present:  true 
  Link index:  21   Link name:  right_l4_2   Present:  true 
  Link index:  22   Link name:  screen   Present:  true 
  Link index:  23   Link name:  head_camera   Present:  true 

>> Joint index:  0   Joint name:  controller_box_fixed   Num dofs:  0   Present:  true 
>> Joint index:  1   Joint name:  pedestal_feet_fixed   Num dofs:  0   Present:  true 
>> Joint index:  2   Joint name:  pedestal_fixed   Num dofs:  0   Present:  true 
>> Joint index:  3   Joint name:  right_arm_mount   Num dofs:  0   Present:  true 
>> Joint index:  4   Joint name:  torso_t0   Num dofs:  0   Present:  true 
>> Joint index:  5   Joint name:  right_j0   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  6   Joint name:  head_pan   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  7   Joint name:  right_torso_itb   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  right_j1   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  9   Joint name:  right_j2   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  10   Joint name:  right_j3   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  11   Joint name:  right_j4   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  12   Joint name:  right_arm_itb   Num dofs:  0   Present:  true 
>> Joint index:  13   Joint name:  right_j5   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  14   Joint name:  right_hand_camera   Num dofs:  0   Present:  true 
>> Joint index:  15   Joint name:  right_wrist   Num dofs:  0   Present:  true 
>> Joint index:  16   Joint name:  right_j6   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  17   Joint name:  right_hand   Num dofs:  0   Present:  true 
>> Joint index:  18   Joint name:  right_j1_2   Num dofs:  0   Present:  true 
>> Joint index:  19   Joint name:  right_j2_2   Num dofs:  0   Present:  true 
>> Joint index:  20   Joint name:  right_j4_2   Num dofs:  0   Present:  true 
>> Joint index:  21   Joint name:  display_joint   Num dofs:  0   Present:  true 
>> Joint index:  22   Joint name:  head_camera   Num dofs:  0   Present:  true 


Robot 0, DOF Joint Axis 0 ---> JointAxis { joint_idx: 0, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 1 ---> JointAxis { joint_idx: 1, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 2 ---> JointAxis { joint_idx: 2, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 3 ---> JointAxis { joint_idx: 3, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 4 ---> JointAxis { joint_idx: 4, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 5 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
------------------------------------------------------------------------
Robot 1, DOF Joint Axis 6 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.0503, 3.0503) }
Robot 1, DOF Joint Axis 7 ---> JointAxis { joint_idx: 6, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-5.0952, 0.9064) }
Robot 1, DOF Joint Axis 8 ---> JointAxis { joint_idx: 8, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.8095, 2.2736) }
Robot 1, DOF Joint Axis 9 ---> JointAxis { joint_idx: 9, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.0426, 3.0426) }
Robot 1, DOF Joint Axis 10 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.0439, 3.0439) }
Robot 1, DOF Joint Axis 11 ---> JointAxis { joint_idx: 11, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.9761, 2.9761) }
Robot 1, DOF Joint Axis 12 ---> JointAxis { joint_idx: 13, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.9761, 2.9761) }
Robot 1, DOF Joint Axis 13 ---> JointAxis { joint_idx: 16, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-4.7124, 4.7124) }
------------------------------------------------------------------------

Robot Set Configurations

In the previous tutorial, we overviewed how to instantiate a robot set with multiple robots. In this case, both robot models were simply their "base" configurations; however, it is possible to build up unique configurations for all robots in the set using what is called a RobotSetConfiguration. In this section, we will show how to achieve this effect.

Example code that instantiates a RobotSet using a RobotSetConfiguration can be seen here:

extern crate optima;

use optima::robot_modules::robot_configuration_module::{ContiguousChainMobilityMode, RobotConfigurationModule};
use optima::robot_set_modules::robot_set::RobotSet;
use optima::robot_set_modules::robot_set_configuration_module::RobotSetConfigurationModule;
use optima::utils::utils_robot::robot_module_utils::RobotNames;
use optima::utils::utils_se3::optima_se3_pose::{OptimaSE3Pose, OptimaSE3PoseType};

fn main() {
    // Initialize `RobotSetConfigurationModule`
    let mut robot_set_configuration_module = RobotSetConfigurationModule::new_empty();

    // Load a base robot configuration module
    let mut ur5_configuration = RobotConfigurationModule::new_from_names(RobotNames::new_base("ur5")).expect("error");
    // Set the ur5 to have a planar translation mobile base.
    ur5_configuration.set_mobile_base(ContiguousChainMobilityMode::PlanarTranslation {
        x_bounds: (-2.0, 2.0),
        y_bounds: (-2.0, 2.0)
    }).expect("error");

    // Add the ur5 configuration to the robot_set_configuration
    robot_set_configuration_module.add_robot_configuration(ur5_configuration).expect("error");

    let mut sawyer_configuration = RobotConfigurationModule::new_from_names(RobotNames::new_base("sawyer")).expect("error");
    // Move the sawyer robot over 1 meter on the y axis so it is not overlapping with the ur5 robot.
    sawyer_configuration.set_base_offset(&OptimaSE3Pose::new_from_euler_angles(0.,0.,0., 0., 1., 0., &OptimaSE3PoseType::ImplicitDualQuaternion)).expect("error");
    // Remove the pedestal from the sawyer model.
    sawyer_configuration.set_dead_end_link(2).expect("error");

    // Add the sawyer configuration to the robot_set_configuration
    robot_set_configuration_module.add_robot_configuration(sawyer_configuration).expect("error");

    // Similar to a `RobotConfigurationModule`, a `RobotSetConfigurationModule` can be saved to a file
    // to allow for easy loading of a model at a later time.  This command will save a file
    // `optima_toolbox/optima_assets/optima_robot_sets/test_configuration.JSON`
    robot_set_configuration_module.save_robot_set_configuration_module("test_configuration").expect("error");

    // We will now load the just saved configuration
    let loaded_robot_set_configuration_module = RobotSetConfigurationModule::new_from_set_name("test_configuration").expect("error");

    // This loaded robot set configuration will now be used to initialize a `RobotSet`.
    let robot_set = RobotSet::new_from_robot_set_configuration_module(loaded_robot_set_configuration_module);

    // When we now print information about the robot set, the changes we made to the configurations
    // are reflected in the combined model.
    robot_set.print_summary();
    robot_set.robot_set_configuration_module().print_summary();
    robot_set.robot_set_joint_state_module().print_dof_summary();
}

Explanations for the code above are written as comments line by line.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/3_robot_set_configurations.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 3_robot_set_configurations

Note that, if this example is run, a directory called test_configuration will be saved in optima_toolbox/optima_assets/optima_robot_sets. Because this configuration will be used in future examples, please to leave this directory in place while you are going through tutorials. However, long-term, feel free to delete this directory.

The output of this example should be the following:

 2 robots.
 Robot 0 ---> "ur5"
   Base Offset: EulerAnglesAndTranslation { euler_angles: [[0.0, -0.0, 0.0]], translation: [[0.0, 0.0, 0.0]], phantom_data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.0, 0.0]], is_identity: true, rot_is_identity: true, translation_is_zeros: true }, pose_type: EulerAnglesAndTranslation }
 Robot 1 ---> "sawyer"
   Base Offset: EulerAnglesAndTranslation { euler_angles: [[0.0, -0.0, 0.0]], translation: [[0.0, 1.0, 0.0]], phantom_data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 1.0, 0.0]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: EulerAnglesAndTranslation }

Robot 0 ---> 
  Link index:  0   Link name:  base_link   Present:  true 
  Link index:  1   Link name:  shoulder_link   Present:  true 
  Link index:  2   Link name:  upper_arm_link   Present:  true 
  Link index:  3   Link name:  forearm_link   Present:  true 
  Link index:  4   Link name:  wrist_1_link   Present:  true 
  Link index:  5   Link name:  wrist_2_link   Present:  true 
  Link index:  6   Link name:  wrist_3_link   Present:  true 
  Link index:  7   Link name:  ee_link   Present:  true 
  Link index:  8   Link name:  base   Present:  true 
  Link index:  9   Link name:  tool0   Present:  true 
  Link index:  10   Link name:  world   Present:  true 
  Link index:  11   Link name:  base_of_chain_link_with_child_link_10   Present:  true 

>> Joint index:  0   Joint name:  shoulder_pan_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  1   Joint name:  shoulder_lift_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  2   Joint name:  elbow_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  3   Joint name:  wrist_1_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  4   Joint name:  wrist_2_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  5   Joint name:  wrist_3_joint   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 1.0, 0.0]], Not fixed.
>> Joint index:  6   Joint name:  ee_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  7   Joint name:  base_link-base_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  wrist_3_link-tool0_fixed_joint   Num dofs:  0   Present:  true 
>> Joint index:  9   Joint name:  world_joint   Num dofs:  0   Present:  true 
>> Joint index:  10   Joint name:  base_of_chain_connector_joint_with_child_link_11   Num dofs:  2   Present:  true 
      -- Joint sub idx 0:  Translation about axis [[1.0, 0.0, 0.0]], Not fixed.
      -- Joint sub idx 1:  Translation about axis [[0.0, 1.0, 0.0]], Not fixed.


Robot 1 ---> 
  Link index:  0   Link name:  base   Present:  true 
  Link index:  1   Link name:  torso   Present:  true 
  Link index:  2   Link name:  pedestal   Present:  false 
  Link index:  3   Link name:  controller_box   Present:  true 
  Link index:  4   Link name:  pedestal_feet   Present:  true 
  Link index:  5   Link name:  right_arm_base_link   Present:  true 
  Link index:  6   Link name:  right_l0   Present:  true 
  Link index:  7   Link name:  head   Present:  true 
  Link index:  8   Link name:  right_torso_itb   Present:  true 
  Link index:  9   Link name:  right_l1   Present:  true 
  Link index:  10   Link name:  right_l2   Present:  true 
  Link index:  11   Link name:  right_l3   Present:  true 
  Link index:  12   Link name:  right_l4   Present:  true 
  Link index:  13   Link name:  right_arm_itb   Present:  true 
  Link index:  14   Link name:  right_l5   Present:  true 
  Link index:  15   Link name:  right_hand_camera   Present:  true 
  Link index:  16   Link name:  right_wrist   Present:  true 
  Link index:  17   Link name:  right_l6   Present:  true 
  Link index:  18   Link name:  right_hand   Present:  true 
  Link index:  19   Link name:  right_l1_2   Present:  true 
  Link index:  20   Link name:  right_l2_2   Present:  true 
  Link index:  21   Link name:  right_l4_2   Present:  true 
  Link index:  22   Link name:  screen   Present:  true 
  Link index:  23   Link name:  head_camera   Present:  true 

>> Joint index:  0   Joint name:  controller_box_fixed   Num dofs:  0   Present:  true 
>> Joint index:  1   Joint name:  pedestal_feet_fixed   Num dofs:  0   Present:  true 
>> Joint index:  2   Joint name:  pedestal_fixed   Num dofs:  0   Present:  true 
>> Joint index:  3   Joint name:  right_arm_mount   Num dofs:  0   Present:  true 
>> Joint index:  4   Joint name:  torso_t0   Num dofs:  0   Present:  true 
>> Joint index:  5   Joint name:  right_j0   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  6   Joint name:  head_pan   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  7   Joint name:  right_torso_itb   Num dofs:  0   Present:  true 
>> Joint index:  8   Joint name:  right_j1   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  9   Joint name:  right_j2   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  10   Joint name:  right_j3   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  11   Joint name:  right_j4   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  12   Joint name:  right_arm_itb   Num dofs:  0   Present:  true 
>> Joint index:  13   Joint name:  right_j5   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  14   Joint name:  right_hand_camera   Num dofs:  0   Present:  true 
>> Joint index:  15   Joint name:  right_wrist   Num dofs:  0   Present:  true 
>> Joint index:  16   Joint name:  right_j6   Num dofs:  1   Present:  true 
      -- Joint sub idx 0:  Rotation about axis [[0.0, 0.0, 1.0]], Not fixed.
>> Joint index:  17   Joint name:  right_hand   Num dofs:  0   Present:  true 
>> Joint index:  18   Joint name:  right_j1_2   Num dofs:  0   Present:  true 
>> Joint index:  19   Joint name:  right_j2_2   Num dofs:  0   Present:  true 
>> Joint index:  20   Joint name:  right_j4_2   Num dofs:  0   Present:  true 
>> Joint index:  21   Joint name:  display_joint   Num dofs:  0   Present:  true 
>> Joint index:  22   Joint name:  head_camera   Num dofs:  0   Present:  true 


Robot 0, DOF Joint Axis 0 ---> JointAxis { joint_idx: 0, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 1 ---> JointAxis { joint_idx: 1, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 2 ---> JointAxis { joint_idx: 2, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 3 ---> JointAxis { joint_idx: 3, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 4 ---> JointAxis { joint_idx: 4, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 5 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Rotation, bounds: (-6.2831853, 6.2831853) }
Robot 0, DOF Joint Axis 6 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[1.0, 0.0, 0.0]], axis: [[1.0, 0.0, 0.0]], axis_primitive_type: Translation, bounds: (-2.0, 2.0) }
Robot 0, DOF Joint Axis 7 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 1, fixed_value: None, axis_as_unit: [[0.0, 1.0, 0.0]], axis: [[0.0, 1.0, 0.0]], axis_primitive_type: Translation, bounds: (-2.0, 2.0) }
------------------------------------------------------------------------
Robot 1, DOF Joint Axis 8 ---> JointAxis { joint_idx: 5, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.0503, 3.0503) }
Robot 1, DOF Joint Axis 9 ---> JointAxis { joint_idx: 6, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-5.0952, 0.9064) }
Robot 1, DOF Joint Axis 10 ---> JointAxis { joint_idx: 8, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.8095, 2.2736) }
Robot 1, DOF Joint Axis 11 ---> JointAxis { joint_idx: 9, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.0426, 3.0426) }
Robot 1, DOF Joint Axis 12 ---> JointAxis { joint_idx: 10, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-3.0439, 3.0439) }
Robot 1, DOF Joint Axis 13 ---> JointAxis { joint_idx: 11, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.9761, 2.9761) }
Robot 1, DOF Joint Axis 14 ---> JointAxis { joint_idx: 13, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-2.9761, 2.9761) }
Robot 1, DOF Joint Axis 15 ---> JointAxis { joint_idx: 16, joint_sub_dof_idx: 0, fixed_value: None, axis_as_unit: [[0.0, 0.0, 1.0]], axis: [[0.0, 0.0, 1.0]], axis_primitive_type: Rotation, bounds: (-4.7124, 4.7124) }
----------------------------------------------------------------------

Joint States

In this tutorial, we will cover how to spawn and interpret robot joint states.

"Robot states" are vectors that contain scalar joint values for each joint axis in the robot model. These objects are sometimes referred to as robot configurations or robot poses in the robotics literature, but in this library, we will stick to the convention of referring to them as robot states.

A RobotSetJointState object contains the vector of joint angles in the field joint_state, as well as a state type (either DOF or Full). A DOF joint state only contains values for joint values that are free (not fixed), while the Full joint state includes joint values for all present joint axes (even if they are fixed). A dof joint state is important for operations such as optimization where only the free values are decision variables, while a full joint state is important for operations such as forward kinematics where all present joint axes need to somehow contribute to the model. The library will ensure that mathematical operations (additions, scalar multiplication, etc) can only occur over robot states of the same type. Conversions between DOF and Full states can be done via the RobotSetJointStateModule.

The code for this example is as follows:

extern crate optima;

use nalgebra::DVector;
use optima::robot_modules::robot_configuration_module::RobotConfigurationModule;
use optima::robot_set_modules::robot_set::RobotSet;
use optima::utils::utils_robot::robot_module_utils::RobotNames;

fn main() {
    // Let's start this demonstration by initializing a base configuration of the ur5 robot
    // and fixing its 0th joint (should_pan_joint, in the case of the ur5 robot) to -1.0.
    let mut robot_configuration = RobotConfigurationModule::new_from_names(RobotNames::new_base("ur5")).expect("error");
    robot_configuration.set_fixed_joint(0, 0, -1.0).expect("error");
    let robot_set = RobotSet::new_from_robot_configuration_modules(vec![robot_configuration]);

    // Use the robot_set_joint_state_module to spawn a `RobotSetJointState`.  In this case, the
    // resulting `RobotSetJointState` will be of type DOF because the model only has five
    // degrees of freedom (one is fixed), and five values are provided.
    let dof_joint_state = robot_set.robot_set_joint_state_module().spawn_robot_set_joint_state_try_auto_type(DVector::from_vec(vec![0., 1., 2., 3., 4.])).expect("error");
    println!("DOF joint state 1: {:?}", dof_joint_state);

    // This converts the DOF `RobotSetJointState` to a Full `RobotSetJointState`, i.e., it places the
    // fixed value of -1.0 in the right place within the full state.
    let full_joint_state = robot_set.robot_set_joint_state_module().convert_state_to_full_state(&dof_joint_state).expect("error");
    println!("Full joint state 1: {:?}", full_joint_state);

    // We can use the robot_set_joint_state_module to print a summary of the joint state.
    robot_set.robot_set_joint_state_module().print_robot_joint_state_summary(&full_joint_state);

    println!(); ////////////////////////////////////////////////////////////////////////////////////

    // Let's now try a different configuration, where we set a dead end link at link index 4.
    // This will remove four links (wrist_2_link, wrist_3_link, ee_link, and tool0) from the model,
    // and will also remove connecting joints between these links as well.
    let mut robot_configuration = RobotConfigurationModule::new_from_names(RobotNames::new_base("ur5")).expect("error");
    robot_configuration.set_dead_end_link(5).expect("error");
    let robot_set = RobotSet::new_from_robot_configuration_modules(vec![robot_configuration]);

    // Use the robot_set_joint_state_module to spawn a `RobotSetJointState`.  In this case, the
    // resulting `RobotSetJointState` will automatically be of type Full because the model only has five
    // degrees of freedom, and five values are provided.
    let full_joint_state = robot_set.robot_set_joint_state_module().spawn_robot_set_joint_state_try_auto_type(DVector::from_vec(vec![0., 1., 2., 3., 4.])).expect("error");
    println!("Full joint state 2: {:?}", full_joint_state);

    let dof_joint_state = robot_set.robot_set_joint_state_module().convert_state_to_dof_state(&dof_joint_state).expect("error");
    println!("DOF joint state 2: {:?}", dof_joint_state);

    // Again, we can use the robot_set_joint_state_module to print a summary of the joint state.
    robot_set.robot_set_joint_state_module().print_robot_joint_state_summary(&dof_joint_state);

    println!(); ////////////////////////////////////////////////////////////////////////////////////

    // Lastly, let's show how joint states work for robot sets with multiple robots.  For simplicity,
    // we will use a ur5 base model (6DOF) and a sawyer base model (8DOF, including the screen).
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new_base("ur5"), RobotNames::new_base("sawyer")]);

    // The joint state here will have 14 degrees of freedom (6 from ur5 and 8 from sawyer) instantiated
    // using a concatenated vector of 14 values.
    // Because no joints are fixed or removed, the DOF and Full joint state types here will be
    // identical (by default, the underlying type will be Full in these situations).
    let joint_state = robot_set.robot_set_joint_state_module().spawn_robot_set_joint_state_try_auto_type(DVector::from_vec(vec![0.0; 14])).expect("error");
    println!("joint state 3: {:?}", joint_state);

    // For a last time, we can use the robot_set_joint_state_module to print a summary of the joint state.
    robot_set.robot_set_joint_state_module().print_robot_joint_state_summary(&joint_state);
}

Explanations for this code are in the comments line by line.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/4_joint_states.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 4_joint_states

The output of this example should be the following:

DOF joint state 1: RobotSetJointState { robot_set_joint_state_type: DOF, concatenated_state: VecStorage { data: [0.0, 1.0, 2.0, 3.0, 4.0], nrows: Dynamic { value: 5 }, ncols: Const } }
Full joint state 1: RobotSetJointState { robot_set_joint_state_type: Full, concatenated_state: VecStorage { data: [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], nrows: Dynamic { value: 6 }, ncols: Const } }
Joint state index 0 ---> 
   > Robot Index in Robot Set: 0
   > joint name: shoulder_pan_joint
   > joint index: 0
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: -1
Joint state index 1 ---> 
   > Robot Index in Robot Set: 0
   > joint name: shoulder_lift_joint
   > joint index: 1
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 0
Joint state index 2 ---> 
   > Robot Index in Robot Set: 0
   > joint name: elbow_joint
   > joint index: 2
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 1
Joint state index 3 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_1_joint
   > joint index: 3
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 2
Joint state index 4 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_2_joint
   > joint index: 4
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 3
Joint state index 5 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_3_joint
   > joint index: 5
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 4

Full joint state 2: RobotSetJointState { robot_set_joint_state_type: Full, concatenated_state: VecStorage { data: [0.0, 1.0, 2.0, 3.0, 4.0], nrows: Dynamic { value: 5 }, ncols: Const } }
DOF joint state 2: RobotSetJointState { robot_set_joint_state_type: DOF, concatenated_state: VecStorage { data: [0.0, 1.0, 2.0, 3.0, 4.0], nrows: Dynamic { value: 5 }, ncols: Const } }
Joint state index 0 ---> 
   > Robot Index in Robot Set: 0
   > joint name: shoulder_pan_joint
   > joint index: 0
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 1 ---> 
   > Robot Index in Robot Set: 0
   > joint name: shoulder_lift_joint
   > joint index: 1
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 1
Joint state index 2 ---> 
   > Robot Index in Robot Set: 0
   > joint name: elbow_joint
   > joint index: 2
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 2
Joint state index 3 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_1_joint
   > joint index: 3
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 3
Joint state index 4 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_2_joint
   > joint index: 4
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 4

joint state 3: RobotSetJointState { robot_set_joint_state_type: Full, concatenated_state: VecStorage { data: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], nrows: Dynamic { value: 14 }, ncols: Const } }
Joint state index 0 ---> 
   > Robot Index in Robot Set: 0
   > joint name: shoulder_pan_joint
   > joint index: 0
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 1 ---> 
   > Robot Index in Robot Set: 0
   > joint name: shoulder_lift_joint
   > joint index: 1
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 0
Joint state index 2 ---> 
   > Robot Index in Robot Set: 0
   > joint name: elbow_joint
   > joint index: 2
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 0
Joint state index 3 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_1_joint
   > joint index: 3
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 0
Joint state index 4 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_2_joint
   > joint index: 4
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 5 ---> 
   > Robot Index in Robot Set: 0
   > joint name: wrist_3_joint
   > joint index: 5
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 1.0, 0.0]]
   > joint value: 0
Joint state index 6 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j0
   > joint index: 5
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 7 ---> 
   > Robot Index in Robot Set: 1
   > joint name: head_pan
   > joint index: 6
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 8 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j1
   > joint index: 8
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 9 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j2
   > joint index: 9
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 10 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j3
   > joint index: 10
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 11 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j4
   > joint index: 11
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 12 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j5
   > joint index: 13
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0
Joint state index 13 ---> 
   > Robot Index in Robot Set: 1
   > joint name: right_j6
   > joint index: 16
   > joint sub dof index: 0
   > joint sub dof axis type: Rotation
   > axis: [[0.0, 0.0, 1.0]]
   > joint value: 0

Robot Kinematics

In this Chapter, I will cover how to perform standard kinematics subroutines, such as forward kinematics and jacobian computations, in Optima.

Forward Kinematics

In this tutorial, I will overview how to perform forward kinematics using a RobotSet.

Example code can be found here:

extern crate optima;

use nalgebra::DVector;
use optima::robot_set_modules::robot_set::RobotSet;
use optima::utils::utils_robot::robot_module_utils::RobotNames;
use optima::utils::utils_se3::optima_se3_pose::OptimaSE3PoseType;

fn main() {
    // Create a base configuration ur5.
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new_base("ur5")]);

    // Spawn joint state with zeros for all degrees of freedom.
    let joint_state = robot_set.spawn_robot_set_joint_state(DVector::from_vec(vec![0.0; 6])).expect("error");

    // Compute forward kinematics.  The second argument in `compute_fk` is the SE(3) representation that should
    // be used to compute FK.  All options are `ImplicitDualQuaternion`, `EulerAnglesAndTranslation`,
    // `RotationMatrixAndTranslation`, `UnitQuaternionAndTranslation`, and `HomogeneousMatrix`.
    // The result is an `RobotSetFKResult`.
    let fk_res = robot_set.robot_set_kinematics_module().compute_fk(&joint_state, &OptimaSE3PoseType::ImplicitDualQuaternion).expect("error");

    // Print summary of the fk result.
    fk_res.print_summary();

    println!("////////////////////////////////////////////////////////////////////////////////////");

    // Load the test configuration created in the previous tutorial.
    // Make sure that you ran example 3_robot_set_configurations prior to starting this tutorial.
    let robot_set = RobotSet::new_from_set_name("test_configuration");

    // Spawn joint state with zeros for all degrees of freedom.
    let joint_state = robot_set.spawn_robot_set_joint_state(DVector::from_vec(vec![0.0; 16])).expect("error");

    // Compute forward kinematics.  The second argument in `compute_fk` is the SE(3) representation that should
    // be used to compute FK.  All options are `ImplicitDualQuaternion`, `EulerAnglesAndTranslation`,
    // `RotationMatrixAndTranslation`, `UnitQuaternionAndTranslation`, and `HomogeneousMatrix`.
    // The result is an `RobotSetFKResult`.
    let fk_res = robot_set.robot_set_kinematics_module().compute_fk(&joint_state, &OptimaSE3PoseType::HomogeneousMatrix).expect("error");

    // Print summary of the fk result.
    fk_res.print_summary();

    println!("////////////////////////////////////////////////////////////////////////////////////");

    // Access the pose of the just computed fk result.
    // The arguments in `get_pose_from_idxs` here are `robot_idx_in_set` (in this case,
    // the sawyer robot, the "second" robot in the set) and `link_idx_in_robot` (in this case,
    // link 18 "right hand" on the sawyer robot).
    let pose = fk_res.get_pose_from_idxs(1, 18);
    println!("{:?}", pose);
}

Explanations are written in this code line by line.

Note that any link that is removed from the model (i.e., by setting a dead-end link in the configuration) will return a pose of None in the result. This can be seen in the result below on the sawyer robot link 2 (pedestal) as we removed that link in this test configuration.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/5_forward_kinematics.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 5_forward_kinematics

The output of this example should be the following:

Robot 0 ---> 
Link 0 base_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.0, 0.0]], is_identity: true, rot_is_identity: true, translation_is_zeros: true }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
Link 1 shoulder_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.0, 0.089159]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.089159]]
Link 2 upper_arm_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.13585, 0.089159]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.13585, 0.089159]]
Link 3 forearm_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.016149999999999998, 0.514159]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.016149999999999998, 0.514159]]
Link 4 wrist_1_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.016149999999999998, 0.906409]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.016149999999999998, 0.906409]]
Link 5 wrist_2_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.10915, 0.906409]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.10915, 0.906409]]
Link 6 wrist_3_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.10915, 1.001059]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.10915, 1.001059]]
Link 7 ee_link ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.7071067805519557, 0.7071067818211392], translation: [[0.0, 0.19145, 1.001059]], is_identity: false, rot_is_identity: false, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 1.5707963250000003]]
   > Pose Translation: [[0.0, 0.19145, 1.001059]]
Link 8 base ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 1.0, -1.7948964430104646e-9], translation: [[0.0, 0.0, 0.0]], is_identity: false, rot_is_identity: false, translation_is_zeros: true }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, -3.14159265]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
Link 9 tool0 ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [-0.7071067805519557, 0.0, 0.0, 0.7071067818211392], translation: [[0.0, 0.19145, 1.001059]], is_identity: false, rot_is_identity: false, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[-1.5707963250000003, 0.0, 0.0]]
   > Pose Translation: [[0.0, 0.19145, 1.001059]]
Link 10 world ---> 
   > Pose: Some(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.0, 0.0]], is_identity: true, rot_is_identity: true, translation_is_zeros: true }, pose_type: ImplicitDualQuaternion })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
////////////////////////////////////////////////////////////////////////////////////
Robot 0 ---> 
Link 0 base_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
Link 1 shoulder_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.089159, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.089159]]
Link 2 upper_arm_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.13585, 0.089159, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.13585, 0.089159]]
Link 3 forearm_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.016149999999999998, 0.514159, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.016149999999999998, 0.514159]]
Link 4 wrist_1_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.016149999999999998, 0.906409, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.016149999999999998, 0.906409]]
Link 5 wrist_2_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.10915, 0.906409, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.10915, 0.906409]]
Link 6 wrist_3_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.10915, 1.001059, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.10915, 1.001059]]
Link 7 ee_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.7948965149208059e-9, 1.0, 0.0, 0.0], [-1.0, 1.7948965149208059e-9, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.19145, 1.001059, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 1.5707963250000003]]
   > Pose Translation: [[0.0, 0.19145, 1.001059]]
Link 8 base ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[-1.0, -3.5897930298416118e-9, 0.0, 0.0], [3.5897930298416118e-9, -1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, -3.14159265]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
Link 9 tool0 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.7948965149208059e-9, -1.0, 0.0], [0.0, 1.0, 1.7948965149208059e-9, 0.0], [0.0, 0.19145, 1.001059, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-1.5707963250000003, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.19145, 1.001059]]
Link 10 world ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
Link 11 base_of_chain_link_with_child_link_10 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 0.0, 0.0]]
Robot 1 ---> 
Link 0 base ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.0]]
Link 1 torso ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.0]]
Link 2 pedestal ---> 
   > Pose: None
Link 3 controller_box ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.0]]
Link 4 pedestal_feet ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.0]]
Link 5 right_arm_base_link ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.0]]
Link 6 right_l0 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.08, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.08]]
Link 7 head ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 1.0, 0.3765, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.0, 1.0, 0.3765]]
Link 8 right_torso_itb ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, 1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 4.8965888601467475e-12, 0.0], [-0.055, 1.0, 0.3, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -1.5707963118937354, 0.0]]
   > Pose Translation: [[-0.055, 1.0, 0.3]]
Link 9 right_l1 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [-1.0, 4.8965888601467475e-12, -4.8965888601467475e-12, 0.0], [4.8965888601467475e-12, 1.0, 2.3976582465313225e-23, 0.0], [0.081, 1.05, 0.317, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-1.570789747253445, 1.5707963057214724, -4.509437977892592e-5]]
   > Pose Translation: [[0.081, 1.05, 0.317]]
Link 10 right_l2 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 4.8965888601467475e-12, 0.0], [0.2210000000006978, 1.1924999999993144, 0.3170000000006855, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, 1.5707963118937354, 0.0]]
   > Pose Translation: [[0.2210000000006978, 1.1924999999993144, 0.3170000000006855]]
Link 11 right_l3 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [-1.0, 4.8965888601467475e-12, -4.8965888601467475e-12, 0.0], [4.8965888601467475e-12, 1.0, 2.3976582465313225e-23, 0.0], [0.4810000000006978, 1.1504999999993144, 0.3170000000019586, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-1.570789747253445, 1.5707963057214724, -4.509437977892592e-5]]
   > Pose Translation: [[0.4810000000006978, 1.1504999999993144, 0.3170000000019586]]
Link 12 right_l4 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 4.8965888601467475e-12, 0.0], [0.6060000000000784, 1.0239999999987024, 0.31700000000257067, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, 1.5707963118937354, 0.0]]
   > Pose Translation: [[0.6060000000000784, 1.0239999999987024, 0.31700000000257067]]
Link 13 right_arm_itb ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.6809999999998091, 1.0239999999987024, 0.3720000000029379, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, -0.0, 0.0]]
   > Pose Translation: [[0.6809999999998091, 1.0239999999987024, 0.3720000000029379]]
Link 14 right_l5 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [-1.0, 4.8965888601467475e-12, -4.8965888601467475e-12, 0.0], [4.8965888601467475e-12, 1.0, 2.3976582465313225e-23, 0.0], [0.8810000000000784, 1.0549999999987023, 0.31700000000391726, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-1.570789747253445, 1.5707963057214724, -4.509437977892592e-5]]
   > Pose Translation: [[0.8810000000000784, 1.0549999999987023, 0.31700000000391726]]
Link 15 right_hand_camera ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[-4.8965888601227706e-12, -1.0, -4.8965888601707244e-12, 0.0], [-1.0, 4.8965888601467475e-12, -4.8965888601467475e-12, 0.0], [4.8965888601707244e-12, 4.8965888601467475e-12, -1.0, 0.0], [0.9140000000006123, 1.1244999999985408, 0.2774480000040789, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-3.1415926535848966, 4.896679426577553e-12, -1.5707963267997933]]
   > Pose Translation: [[0.9140000000006123, 1.1244999999985408, 0.2774480000040789]]
Link 16 right_wrist ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 4.8965888601467475e-12, 0.0], [0.8810000000005945, 1.1604099999987023, 0.31700000000391726, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, 1.5707963118937354, 0.0]]
   > Pose Translation: [[0.8810000000005945, 1.1604099999987023, 0.31700000000391726]]
Link 17 right_l6 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[7.234801487997977e-6, 0.1736452969071067, 0.9848082609369704, 0.0], [1.2756688056520986e-6, -0.9848082609635456, 0.1736452969024209, 0.0], [0.9999999999730153, -7.438770000002659e-14, -7.3464061603243535e-6, 0.0], [0.991000000000594, 1.1602999999981636, 0.3170000000044559, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[1.5708386337636842, -1.3962663266418527, 1.5707546625424669]]
   > Pose Translation: [[0.991000000000594, 1.1602999999981636, 0.3170000000044559]]
Link 18 right_hand ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.2756711699482342e-6, -0.9848082042170961, 0.17364561873272535, 0.0], [-7.234801071115536e-6, -0.1736456187374112, -0.984808204190521, 0.0], [0.9999999999730153, -7.438770000002659e-14, -7.3464061603243535e-6, 0.0], [1.0154999999999328, 1.1602999999981618, 0.31699982001750493, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-1.5708037865277962, -0.17453032679013836, -1.5707950314450365]]
   > Pose Translation: [[1.0154999999999328, 1.1602999999981618, 0.31699982001750493]]
Link 19 right_l1_2 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [-1.0, 4.8965888601467475e-12, -4.8965888601467475e-12, 0.0], [4.8965888601467475e-12, 1.0, 2.3976582465313225e-23, 0.0], [0.081, 1.05, 0.317, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-1.570789747253445, 1.5707963057214724, -4.509437977892592e-5]]
   > Pose Translation: [[0.081, 1.05, 0.317]]
Link 20 right_l2_2 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 4.8965888601467475e-12, 0.0], [0.2210000000006978, 1.1924999999993144, 0.3170000000006855, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, 1.5707963118937354, 0.0]]
   > Pose Translation: [[0.2210000000006978, 1.1924999999993144, 0.3170000000006855]]
Link 21 right_l4_2 ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 0.0, -1.0, 0.0], [0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 4.8965888601467475e-12, 0.0], [0.6060000000000784, 1.0239999999987024, 0.31700000000257067, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[0.0, 1.5707963118937354, 0.0]]
   > Pose Translation: [[0.6060000000000784, 1.0239999999987024, 0.31700000000257067]]
Link 22 screen ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, 1.0, 0.0, 0.0], [-4.8965888601467475e-12, 2.3976582465313225e-23, 1.0, 0.0], [1.0, -4.8965888601467475e-12, 4.8965888601467475e-12, 0.0], [0.03, 1.0, 0.4815, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[1.5707963267899998, -4.387820765954416e-16, 1.5707963267899998]]
   > Pose Translation: [[0.03, 1.0, 0.4815]]
Link 23 head_camera ---> 
   > Pose: Some(HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[4.8965888601467475e-12, -1.0, 0.0, 0.0], [-0.529917833208038, -2.594789758879581e-12, -0.8480489903585158, 0.0], [0.8480489903585158, 4.152547239048205e-12, -0.529917833208038, 0.0], [0.0228027, 1.0, 0.593072, 1.0]] }, pose_type: HomogeneousMatrix })
   > Pose Euler Angles: [[-2.1293, 1.4578619949671078e-17, -1.5707963267899998]]
   > Pose Translation: [[0.0228027, 1.0, 0.593072]]
////////////////////////////////////////////////////////////////////////////////////
HomogeneousMatrix { data: HomogeneousMatrix { matrix: [[1.2756711699482342e-6, -0.9848082042170961, 0.17364561873272535, 0.0], [-7.234801071115536e-6, -0.1736456187374112, -0.984808204190521, 0.0], [0.9999999999730153, -7.438770000002659e-14, -7.3464061603243535e-6, 0.0], [1.0154999999999328, 1.1602999999981618, 0.31699982001750493, 1.0]] }, pose_type: HomogeneousMatrix }

Robot Jacobians

In this tutorial, I will overview how to compute the jacobian matrix of a RobotSet.

Example code can be found here:

extern crate optima;

use nalgebra::DVector;
use optima::robot_modules::robot_kinematics_module::{JacobianEndPoint, JacobianMode};
use optima::robot_set_modules::robot_set::RobotSet;
use optima::utils::utils_robot::robot_module_utils::RobotNames;

fn main() {
    // Create a base configuration ur5.
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new_base("ur5")]);

    // Spawn joint state with zeros for all degrees of freedom.
    let joint_state = robot_set.spawn_robot_set_joint_state(DVector::from_vec(vec![0.0; 6])).expect("error");

    // Computes the full jacobian matrix with end link idx of 9.  The `robot_jacobian_end_point` in
    // this case is simply the link center.
    let full_jacobian_matrix = robot_set.robot_set_kinematics_module().compute_jacobian(&joint_state, 0, None, 9, &JacobianEndPoint::Link, None, JacobianMode::Full).expect("error");

    println!("{}", full_jacobian_matrix);

    println!("////////////////////////////////////////////////////////////////////////////////////");

    // Computes the 3 x N (N = 6 for the ur5 robot) translation jacobian matrix with end link idx of 9.
    // The `robot_jacobian_end_point` in this case is simply the link center.
    let translational_jacobian_matrix = robot_set.robot_set_kinematics_module().compute_jacobian(&joint_state, 0, None, 9, &JacobianEndPoint::Link, None, JacobianMode::Translational).expect("error");

    println!("{}", translational_jacobian_matrix);

    println!("////////////////////////////////////////////////////////////////////////////////////");

    // Load the test configuration created in the previous tutorial.
    // Make sure that you ran example 3_robot_set_configurations prior to starting this tutorial.
    let robot_set = RobotSet::new_from_set_name("test_configuration");

    // Spawn joint state with zeros for all degrees of freedom.
    let joint_state = robot_set.spawn_robot_set_joint_state(DVector::from_vec(vec![0.0; 16])).expect("error");

    // Computes the 6 x N (N = 16 for this configuration) full jacobian matrix with end link idx of 18 on robot 1
    // (sawyer, in this case of this `RobotSet`).
    // The `robot_jacobian_end_point` in this case is the inertial origin of the link.
    let full_jacobian_matrix = robot_set.robot_set_kinematics_module().compute_jacobian(&joint_state, 1, None, 18, &JacobianEndPoint::InertialOrigin, None, JacobianMode::Full).expect("error");

    println!("{}", full_jacobian_matrix);

    println!("////////////////////////////////////////////////////////////////////////////////////");
}

Jacobian matrices are outputted as nalgebra DMatrix objects. These objects are compatible and can multiply with DVector objects (that are often used throughout the rest of the library).

The code for this tutorial can be found in the file optima_toolbox/optima/examples/6_jacobians.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 6_jacobians

The output of this example should be the following:

  ┌                                                                                                                               ┐
  │             -0.19145   0.9118999999999999   0.4868999999999999   0.0946499999999999 -0.08230000000000001                    0 │
  │                    0                    0                    0                    0                    0                    0 │
  │                    0                    0                    0                    0                    0                    0 │
  │                    0                    0                    0                    0                    0                    0 │
  │                    0                    1                    1                    1                    0                    1 │
  │                    1                    0                    0                    0                    1                    0 │
  └                                                                                                                               ┘


////////////////////////////////////////////////////////////////////////////////////

  ┌                                                                                                                               ┐
  │             -0.19145   0.9118999999999999   0.4868999999999999   0.0946499999999999 -0.08230000000000001                    0 │
  │                    0                    0                    0                    0                    0                    0 │
  │                    0                    0                    0                    0                    0                    0 │
  └                                                                                                                               ┘


////////////////////////////////////////////////////////////////////////////////////

  ┌                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ┐
  │                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                           -0.16029998841362336                                              0                     -0.00000018809419351637482                0.00000000000015770399553929073                     -0.00000018809615244339527                -0.0000000000006676236440362641                     -0.00000018809811155149317               -0.00000000000008510472191613595 │
  │                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                             1.0155000099998726                                              0              0.0000000000000006234230649834017                       0.0000001880987702344505              0.0000000000000006536766262225693                      0.00000018809877014091413              0.0000000000000002398410089369438                     0.000000008111625825649074 │
  │                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                             -0.934500009999332                           -0.03220001158569105                            -0.5345000099991265                            0.13629998841492075                           -0.13450000999927902                    -0.000000011584538162702774 │
  │                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                 0.0000000000048965276278067904                             0.9999999999999992                  0.000000000004896361094353097                             0.9999999999999986                  0.000000000004896361094353097                             0.9999999999730148 │
  │                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                             0.9999999999999996 -0.0000000000000000000000000003844217798755143                             0.9999999999999989             0.00000000000000007850462293399649                             0.9999999999999984               -0.00000000000007473188734508085 │
  │                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              0                                              1                                              0              0.0000000000000006661338147750939                 0.0000000000048976378508314156              0.0000000000000012212453270876722                  0.000000000004898192962343728              0.0000000000000017763568394002505                       -0.000007346406160069208 │
  └                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ┘


////////////////////////////////////////////////////////////////////////////////////

Intersection, Contact, and Proximity Checks

In this Chapter, I will cover brief tutorial topics on how to perform collision and proximity checks in Optima.

Robot Geometric Shape Scenes

All tutorials up to this point have involved robot sets in empty environments. However, robots are commonly surrounded by other obstacles. Optima allows for the modeling of these obstacles as static or dynamic geometric shapes in a RobotGeometricShapeScene. This struct is another abstraction layer on top of the RobotSet struct that allows for easy intersection checks, proximity checks, contact checks, ray casting, etc.

Example code can be found here:

extern crate optima;

use nalgebra::Vector3;
use optima::robot_set_modules::robot_set::RobotSet;
use optima::scenes::robot_geometric_shape_scene::{EnvObjInfoBlock, EnvObjPoseConstraint, RobotGeometricShapeScene};
use optima::utils::utils_robot::robot_module_utils::RobotNames;
use optima::utils::utils_se3::optima_se3_pose::{OptimaSE3Pose, OptimaSE3PoseType};

fn main() {
    // Initialize a `RobotSet`.  In this case, it is just a base model ur5 robot.
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new_base("ur5")]);

    // We use the `RobotSet` to initialize a `RobotGeometricShapeScene`.  By default, the
    // environment is empty.
    let mut robot_geometric_shape_scene = RobotGeometricShapeScene::new(robot_set, None).expect("error");

    // This line adds a "sphere" object to the environment, scaled down to a size of 0.1.
    // Note that any mesh directory in `optima_toolbox/optima_assets/optima_scenes/mesh_files`
    // can be loaded in.
    let env_obj_idx = robot_geometric_shape_scene.add_environment_object(EnvObjInfoBlock {
        asset_name: "sphere".to_string(),
        scale: Vector3::new(0.1, 0.1, 0.1),
        ..Default::default()
    }, false).expect("error");

    // Adding the object to the scene above returns the index of the added environment object.
    // Indices are assigned in order, so the first returned index will be 0.
    assert_eq!(env_obj_idx, 0);

    // This line can update the rotation and translation of the given environment object in the scene.
    // In this case, we are moving the sphere such that its position will be at (0.5,0.5,0).
    // Note that we can also set the transform of an object such that it is relative to another
    // shape in the environment.
    robot_geometric_shape_scene.set_curr_single_env_obj_pose_constraint(env_obj_idx, EnvObjPoseConstraint::Absolute(OptimaSE3Pose::new_from_euler_angles(0., 0., 0., 0.5, 0.5, 0., &OptimaSE3PoseType::ImplicitDualQuaternion))).expect("error");

    // This prints a summary of the whole scene.
    robot_geometric_shape_scene.print_summary();
}

Explanations are written in this code line by line. I will show how to use this robot geometric shape scene for various queries in the following tutorials, we are just focused on initializing the scene here.

The code for this tutorial can be found in the file optima_toolbox/optima/examples/7_robot_geometric_shape_scene.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example 7_robot_geometric_shape_scene

The output of this example should be the following:

1 robots.
 Robot 0 ---> "ur5"
   Base Offset: EulerAnglesAndTranslation { euler_angles: [[0.0, -0.0, 0.0]], translation: [[0.0, 0.0, 0.0]], phantom_data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.0, 0.0, 0.0]], is_identity: true, rot_is_identity: true, translation_is_zeros: true }, pose_type: EulerAnglesAndTranslation }

1 objects.
 Object 0 ---> 
    Object Info: EnvObjInfoBlock { asset_name: "sphere", scale: [[0.1, 0.1, 0.1]], shape_representation: BestFitConvexShape, decomposition_resolution: Medium, pose_constraint: Absolute(ImplicitDualQuaternion { data: ImplicitDualQuaternion { rotation: [0.0, 0.0, 0.0, 1.0], translation: [[0.5, 0.5, 0.0]], is_identity: false, rot_is_identity: true, translation_is_zeros: false }, pose_type: ImplicitDualQuaternion }) }

Adding New Environment Mesh

Coming Soon

Intersection Checking

Coming Soon

Contact Checking

Coming Soon

Bounding Volume Hierarchies

Coming Soon

Standard Proximity Checking

Coming Soon

Standard Proximity Checking

Coming Soon

Inverse Kinematics

In this Chapter, I will cover brief tutorial topics on how to perform inverse kinematics in Optima.

The default IK solver in Optima is an nonlinear optimization-based solver called OptimaIK. It is built from the ground up in Rust is feature rich. The folowing sections will overview some options and features.

Static IK

The OptimaIK solver can be used to solve for "static" IK solutions. By static here, I mean that the user is looking to compute a single, indpenedent robot pose that matches some objectives (e.g., have the robot's end-effector exhibit X position and Y orientation). The static solver is focused more on the exactness of a single solution rather than the smoothness and continutity of a sequence of solutions. This static solver can optionally take as input an initial guess and, in this case, the output solution may be somewhat close to the initial guess if such a valid solution exists; however, it is more focused on precision and accuracy of result rather than keeping the solution close to the intial condition. Thus, we recommend this version of the solver be used only if indepenent, single solutions are desired; if you would like a high quality sequence of solutions from OptimaIK (e.g., to create a motion trajectory), please refer to the following "motion" IK section.

Note that the static IK version of OptimaIK is not optimized for speed or efficiency! While it may be fast on some occasions (e.g., if allowable error bounds are large, if the initial condition is close to a sufficient solution, etc.), its main focus is on accuracy and precision of the result. In fact, under the hood, the solver may be trying hundreds of individual optimizations from random initial conditions in order to find a sufficient solution. Thus, echoing the same message mentioned above: only use this static IK option if you are focused on getting independent, high quality solutions from the IK solver and do not use it for getting a sequence of fast solutions.

Example code can be found here:

extern crate optima;

use optima::inverse_kinematics::OptimaIK;
use optima::optima_tensor_function::robotics_functions::RobotCollisionProximityGenericParams;
use optima::optimization::{NonlinearOptimizerType, OptimizerParameters};
use optima::robot_modules::robot_geometric_shape_module::RobotLinkShapeRepresentation;
use optima::robot_set_modules::robot_set::RobotSet;
use optima::scenes::robot_geometric_shape_scene::RobotGeometricShapeScene;
use optima::utils::utils_console::OptimaDebug;
use optima::utils::utils_robot::robot_module_utils::RobotNames;
use optima::utils::utils_robot::robot_set_link_specification::{RobotLinkTFAllowableError, RobotLinkTFGoal, RobotLinkTFSpec, RobotLinkTFSpecAndAllowableError, RobotLinkTFSpecAndAllowableErrorCollection};
use optima::utils::utils_se3::optima_se3_pose::{OptimaSE3Pose, OptimaSE3PoseType};

fn main() {
    // Initialize `RobotSet` with base ur5 configuration.
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new_base("ur5")]);

    // Initialize a `RobotGeometricShapeScene`.
    let robot_geometric_shape_scene = RobotGeometricShapeScene::new(robot_set, RobotLinkShapeRepresentation::ConvexShapes, vec![]).expect("error");

    // Initialize "static" IK solver.
    // The `NonlinearOptimizerType` enum many options.  Feel free to check them all out; here we
    // will just use the OpEn (Optimization Engine) option.
    // This solver can optionally exhibit a collision avoidance objective.  This is controlled
    // by the `RobotCollisionProximityGenericParams` enum.  Here, it is None (i.e., no collision
    // avoidance objective will be present).
    let mut optima_ik = OptimaIK::new_static_ik(robot_geometric_shape_scene, NonlinearOptimizerType::OpEn, RobotCollisionProximityGenericParams::None);

    // Initialize a `RobotLinkTFSpecAndAllowableErrorCollection`.  This will be used to specify
    // which links should be at what location or orientation, as well as what errors will be
    // tolerable, in our final solution.
    let mut robot_link_tf_spec_and_allowable_error_collection = RobotLinkTFSpecAndAllowableErrorCollection::new_empty();

    // Creates a `RobotLinkTFSpecAndAllowableError` object.  This specifies that the ur5's link 9
    // (its end effector link) should exhibit an identity orientation (euler angles 0,0,0) and
    // a location of x: 0.5, y: 0.1, z: 0.4.  also, the translation errors along all axes whould
    // be within 1 millimenter, while rotation errors along all axes should be within 0.01 radians.
    let robot_link_tf_spec_and_allowable_error = RobotLinkTFSpecAndAllowableError::new(RobotLinkTFSpec::Absolute {
        goal: RobotLinkTFGoal::LinkSE3PoseGoal {
            robot_idx_in_set: 0,
            link_idx_in_robot: 9,
            goal: OptimaSE3Pose::new_from_euler_angles(0., 0., 0., 0.5, 0.1, 0.4, &OptimaSE3PoseType::ImplicitDualQuaternion),
            weight: None
        }
    }, Some(RobotLinkTFAllowableError {
        rx: 0.01,
        ry: 0.01,
        rz: 0.01,
        x: 0.001,
        y: 0.001,
        z: 0.001
    }));

    // This adds the robot_link_tf_spec_and_allowable_error to the collection.  Note that the collection
    // can accept numerous entries and the optimization will try its best to match all of them,
    // if possible.
    robot_link_tf_spec_and_allowable_error_collection.add(robot_link_tf_spec_and_allowable_error);

    // This calls the static ik solver with the `RobotLinkTFSpecAndAllowableErrorCollection` as
    // first input.  This function can optionally take an initial condition (in this case it is `None`).
    // Also, I set a maximum number of tries of 100 here; if an acceptable solution within the error
    // bounds cannot be found in this number of tries, the solver will return None.
    // However, if a valid solution is found, it will be returned as Some(RobotSetJointState).
    // We also specified here that the solver should reject any solution that is in a collision;
    // If a solution is found in a collision, it will throw away the result and try again (up to the
    // maximum number of tries).
    let res = optima_ik.solve_static_ik(robot_link_tf_spec_and_allowable_error_collection, &OptimizerParameters::default(), None, Some(100), true, OptimaDebug::False);

    // This prints the result.  Because, by default, the static ik solver in `OptimaIK` uses
    // random initial conditions, the result will be different each time this script is
    // run.
    println!("{:?}", res);
}

The code for this tutorial can be found in the file optima_toolbox/optima/examples/_static_ik.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example _static_ik

The output will be different each time the script is run as the static ik solver uses random initial conditions by default.

Motion IK

In contrast to the "static" IK solver explained in the previous section, the "motion" IK version of OptimaIK is intended to compute a sequence of smooth, temporally coherent motions that each try to achieve some objective as best as possible.

The motion IK version of OptimaIK is heavily influenced by our previously proposed solvers RelaxedIK and CollisionIK. If you use motion IK in your research, please cite the following papers:

@inproceedings{rakita2018relaxedik,
  title={RelaxedIK: Real-time Synthesis of Accurate and Feasible Robot Arm Motion.},
  author={Rakita, Daniel and Mutlu, Bilge and Gleicher, Michael},
  booktitle={Robotics: Science and Systems},
  pages={26--30},
  year={2018},
  organization={Pittsburgh, PA}
}
@inproceedings{rakita2021collisionik,
  title={Collisionik: A per-instant pose optimization method for generating robot motions with environment collision avoidance},
  author={Rakita, Daniel and Shi, Haochen and Mutlu, Bilge and Gleicher, Michael},
  booktitle={2021 IEEE International Conference on Robotics and Automation (ICRA)},
  pages={9995--10001},
  year={2021},
  organization={IEEE}
}

Also, if you use the Proxima collision avoidance options in OptimaIK, please cite the following paper:

@inproceedings{rakita2022proxima,
  title={PROXIMA: An Approach for Time or Accuracy Budgeted Collision Proximity Queries},
  author={Rakita, Daniel and Mutlu, Bilge and Gleicher, Michael},
  booktitle={Proceedings of Robotics: Science and Systems (RSS)},
  year={2022}
}

Example code can be found here:

extern crate optima;

use nalgebra::DVector;
use optima::inverse_kinematics::OptimaIK;
use optima::optima_tensor_function::robotics_functions::{RobotCollisionProximityGenericParams, RobotCollisionProximityGradientFDMode};
use optima::optimization::{NonlinearOptimizerType, OptimizerParameters};
use optima::robot_modules::robot_geometric_shape_module::RobotLinkShapeRepresentation;
use optima::robot_set_modules::robot_set::RobotSet;
use optima::scenes::robot_geometric_shape_scene::RobotGeometricShapeScene;
use optima::utils::utils_console::OptimaDebug;
use optima::utils::utils_robot::robot_module_utils::RobotNames;
use optima::utils::utils_robot::robot_set_link_specification::{RobotLinkTFGoal, RobotLinkTFSpec, RobotLinkTFSpecCollection};
use optima::utils::utils_se3::optima_se3_pose::{OptimaSE3Pose, OptimaSE3PoseType};

fn main() {
    // Initialize `RobotSet` with base ur5 configuration.
    let robot_set = RobotSet::new_from_robot_names(vec![RobotNames::new_base("ur5")]);

    // Initialize a `RobotGeometricShapeScene`.
    let robot_geometric_shape_scene = RobotGeometricShapeScene::new(robot_set, RobotLinkShapeRepresentation::ConvexShapes, vec![]).expect("error");

    // Spawn the robot joint state that the robot's motion will start at.
    let init_state = robot_geometric_shape_scene.robot_set().spawn_robot_set_joint_state(DVector::from_vec(vec![0.1; 6])).expect("error");

    // Initialize the "motion" version of `OptimaIK`.
    // This is using the OpEn Nonlinear optimizer (same as previous example).
    // This solver can optionally exhibit a collision avoidance objective.  This is controlled
    // by the `RobotCollisionProximityGenericParams` enum.  Here, it is None (i.e., no collision
    // avoidance objective will be present).
    // We also provide the initial joint state and initial time.  In most cases, the initial time
    // will be 0.0.
    let mut optima_ik = OptimaIK::new_motion_ik(robot_geometric_shape_scene, NonlinearOptimizerType::OpEn, RobotCollisionProximityGenericParams::None, &init_state, 0.0);

    // This specifies a robot link transform that the robot should try to achieve.  Note that, in this
    // example, we only provide this single goal, and the solver will exhibit a sequence of
    // state/ time tuples that will, over time, meet this goal.  Alternatively, we could also
    // provide new transform goal for links at each call of `solve_motion_ik` below.  This would
    // be useful for something like shared control or telemanipulation where an operator is
    // providing a new transform goal at each update.
    let mut robot_link_tf_spec_collection = RobotLinkTFSpecCollection::new();
    robot_link_tf_spec_collection.add(RobotLinkTFSpec::Absolute {
        goal: RobotLinkTFGoal::LinkSE3PoseGoal {
            robot_idx_in_set: 0,
            link_idx_in_robot: 9,
            goal: OptimaSE3Pose::new_from_euler_angles(0., 0., 0., 0.5, 0.1, 0.4, &OptimaSE3PoseType::ImplicitDualQuaternion),
            weight: None
        }
    });

    // Optimizer parameters.  We'll just use the default in this case.
    let mut params = OptimizerParameters::default();
    params.set_open_tolerance(0.00001);

    for i in 1..10000 {
        // Solves the motion ik problem at each given time.  The time here advances 1 millisecond
        // on each loop.  Thus, all 5000 loops will result in 5 seconds of outputted motion.
        // The output of each `solve_motion_ik` call will be a `TimedGenericRobotJointState` that
        // will contain a `RobotSetJointState` as well as a time value.
        let res = optima_ik.solve_motion_ik(robot_link_tf_spec_collection.clone(), i as f64 * 0.001, &params, OptimaDebug::False);
        println!("{:?}", res);
    }
} 

The code for this tutorial can be found in the file optima_toolbox/optima/examples/_motion_ik.rs and can be run from the optima_toolbox/optima directory using the following command:

cargo run --example _motion_ik

Optimization

In this Chapter, I will cover brief tutorial topics on how to perform optimization in Optima.

Optima Tensors

Coming Soon

Optima Tensor Functions

Coming Soon

Optima Tensor Function Derivatives

Coming Soon

Nonlinear Optimization

Coming Soon