Name | skidy JSON |
Version |
0.0.1
JSON |
| download |
home_page | None |
Summary | Symbolic kinematics and dynamics model generation using Equations of Motion in closed form. |
upload_time | 2024-06-07 15:40:20 |
maintainer | Hannah Isermann |
docs_url | None |
author | Underactuated Lab DFKI Robotics Innovation Center Bremen |
requires_python | >=3.8 |
license | BSD 3-Clause License Copyright (c) 2023, DFKI RIC Underactuated Robotics Lab All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
keywords |
symbolic
code generation
robot
kinematics
dynamics
equation of motion
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# skidy - symbolic kinematics and dynamics generator
- [skidy - symbolic kinematics and dynamics generator](#skidy---symbolic-kinematics-and-dynamics-generator)
- [1. Installation](#1-installation)
- [Install package from source](#install-package-from-source)
- [Dependencies](#dependencies)
- [2. Usage](#2-usage)
- [2.1. YAML and JSON](#21-yaml-and-json)
- [2.1.1. Create robot model as YAML file](#211-create-robot-model-as-yaml-file)
- [2.1.2. Code generation using YAML](#212-code-generation-using-yaml)
- [2.2. Python](#22-python)
- [2.3. URDF](#23-urdf)
- [3. Unit testing](#3-unit-testing)
- [4. Benchmarking](#4-benchmarking)
- [5. License](#5-license)
Symbolic kinematics and dynamics model generation using Equations of Motion in closed form.
## 1. Installation
```bash
python3 -m pip install skidy
```
### Install package from source
To install the package from source go to the root folder of the package:
```bash
cd /path/to/project
```
then use:
```bash
python3 -m pip install -e .
```
to install the package in editable mode or:
```bash
python3 -m pip install .
```
to install the package uneditable.
**Note:** The `.` is part of the command.
### Dependencies
The project has the following dependencies:
- sympy (Version >= 1.8)
```bash
python3 -m pip install --upgrade sympy
```
- numpy
```bash
python3 -m pip install numpy
```
- urdf_parser_py (Version >= 0.0.4)
```bash
python3 -m pip install --upgrade urdf_parser_py
```
- regex
```bash
python3 -m pip install regex
```
- PyYAML
```bash
python3 -m pip install PyYAML
```
- pylatex
```bash
python3 -m pip install pylatex
```
- pydot
```bash
python3 -m pip install pydot
```
For unit testing the following packages are additionally recommended:
- oct2py
```bash
python3 -m pip install oct2py
```
This requires a working octave installation on your system:
Install on Ubuntu with:
```bash
sudo apt install octave
```
- cython
```bash
python3 -m pip install cython
```
- kinpy
```bash
python3 -m pip install kinpy
```
- pinocchio
```bash
python3 -m pip install pin
```
## 2. Usage
There are four ways to load your robot into the library:
1. using a *YAML* file
2. using a *JSON* file
3. directly in *python*
4. using *URDF*
For defining a robot with one of the first three options (YAML, JSON, python) the following parameters are required:
- 6D joint screw coordinates for any joint
- 4x4 body reference coordinates for any link
- 4x4 end-effector configuration w.r.t. last link body fixed frame in the chain
For generating the inverse dynamics the following two parameters are required:
- 3D gravity vector
- 6x6 Mass-inertia matrix for any link
Tree-like robot structures require the following graph description parameters additionally:
- parent link for any joint
- support graph for any joint
- (child links for any joint) -> currently not used
Using URDF the following three parameters are required:
- path to URDF file
- 4x4 end-effector configuration w.r.t. last link body fixed frame in the chain
- 3D gravity vector
### 2.1. YAML and JSON
**NOTE:** As *JSON* and *YAML* files represent the same data structure this documentation covers only *YAML* files. Use *JSON* accordingly and just replace `yaml` with `json` in all commands.
#### 2.1.1. Create robot model as YAML file
There is a function to generate a **template YAML file** in which it is easy to modify the parameters for your robot.
To generate your robot template use
```bash
python3 -m skidy --template [options] new_filename.yaml
```
or the python function `skidy.generate_template_yaml(path, structure)`.
For [options] the option `--structure` is highly recommended. There you can define which joint types to use in the template. E.g. use `--structure 'rrp'` for a robot which has two revolute joints followed by one prismatic joint.
The command `python3 -m skidy --template --structure 'rp' my_robot_template.yaml` creates the following output file:
```yaml
---
parent:
- 0
- 1
child:
- [1]
- []
support:
- [1]
- [1,2]
ee_parent: 2
gravity: [0,0,-g]
representation: spatial
joint_screw_coord:
- type: revolute
axis: [0,0,1]
vec: [0,0,0]
- type: prismatic
axis: [0,0,1]
body_ref_config:
- rotation:
axis: [0,0,1]
angle: 0
translation: [0,0,0]
- rotation:
axis: [0,0,1]
angle: 0
translation: [0,0,0]
ee:
- rotation:
axis: [0,0,1]
angle: 0
translation: [0,0,0]
mass_inertia:
- mass: m1
frame: body
inertia:
Ixx: Ixx1
Ixy: Ixy1
Ixz: Ixz1
Iyy: Iyy1
Iyz: Iyz1
Izz: Izz1
com: [0,0,0]
- mass: m2
frame: body
inertia:
Ixx: Ixx2
Ixy: Ixy2
Ixz: Ixz2
Iyy: Iyy2
Iyz: Iyz2
Izz: Izz2
com: [0,0,0]
q: [q1,q2]
qd: [dq1,dq2]
q2d: [ddq1,ddq2]
WEE: [0,0,0,0,0,0]
assignments:
g: 9.81
```
The code explained:
```yaml
parent:
- 0
- 1
child:
- [1]
- []
support:
- [1]
- [1,2]
ee_parent: 2
```
The parameters are generated to represent a serial robot by default. Modify parameters for tree-like structures. For serial robots these parameters are optional.
- **parent**: list of parent links for any joint. Use 0 for World.
- **child**: list of lists with child links for any link. Use empty list if no child is present.
- **support**: list of lists with all support links beginning with first link including current link for any link.
- **ee_parent**: parent joint index for end-effector link. Defaults to last link. For robots with more than one end-effector you can use a list of indices instead e.g.: `ee_parent: [1,2]`. Note, that this requires a list of end_effector transforms `ee`.
---
```yaml
gravity: [0,0,-g]
```
Gravity vector.
**Note:** you can always use symbolic variables instead of numeric values.
---
```yaml
representation: spatial
```
Define whether the representation of the joint screw coordinates and the body reference configuration is w.r.t. world frame (`representation: spatial`) or in body fixed coordinates (`representation: body_fixed`)
---
```yaml
joint_screw_coord:
- type: revolute
axis: [0,0,1]
vec: [0,0,0]
- type: prismatic
axis: [0,0,1]
```
The joint screw coordinates can be defined either using the syntax which is used above, where `type` is the joint type (`revolute` or `prismatic`), `axis` is the joint axis and `vec` is a vector from the origin to the joint axis.
Alternatively, you can directly use the 6D joint screw vectors instead:
```yaml
joint_screw_coord:
- [0,0,1,0,0,0]
- [0,0,0,0,0,1]
```
---
```yaml
body_ref_config:
- rotation:
axis: [0,0,1]
angle: 0
translation: [0,0,0]
- rotation:
axis: [0,0,1]
angle: 0
translation: [0,0,0]
```
The body reference configuration is a list of SE(3) transformation matrices. To define them you have several options:
1. Write down the whole matrix e.g.:
```yaml
body_ref_config:
- [[cos(pi),-sin(pi),0, 0],
[sin(pi), cos(pi),0, 0],
[ 0, 0,1,L1],
[ 0, 0,0, 1]]
```
2. Write rotation and translation separately:
```yaml
body_ref_config:
- rotation:
[[1,0,0],
[0,1,0],
[0,0,1]]
translation: [0,0,L1]
```
3. Use axis angle representation for rotation:
-> See code above.
4. For zero rotations or translations it is possible to omit the option:
```yaml
body_ref_config:
- translation: [0,0,L1]
```
5. Use xyz_rpy coordinates to define Pose:
```yaml
body_ref_config:
- xyzrpy: [0, 0, L1, 0, pi/2, 0]
```
6. Use roll pitch yaw (rpy) euler angles to define rotation:
```yaml
body_ref_config:
- rotation:
rpy: [0, pi/2, 0]
translation: [0,0,L1]
```
7. Use quaternion [w,x,y,z] to define rotation:
```yaml
body_ref_config:
- rotation:
Q: [1, 0, 0, 0]
translation: [0,0,L1]
```
---
```yaml
ee:
- rotation:
axis: [0,0,1]
angle: 0
translation: [0,0,0]
```
End-effector representation w.r.t. last link body frame in the chain as SE(3) transformation matrix. Here you have the same syntax options as for the body reference configuration. For robots with more than one end-effector you can define more than one transform here. Note: in this case you have to define a list of parent joints in `ee_parent`.
If you have just one end-effector, you can omit the top level list (indicated by the trailing `-`).
---
```yaml
mass_inertia:
- mass: m1
frame: body
inertia:
Ixx: Ixx1
Ixy: Ixy1
Ixz: Ixz1
Iyy: Iyy1
Iyz: Iyz1
Izz: Izz1
com: [0,0,0]
- mass: m2
frame: body
inertia:
Ixx: Ixx2
Ixy: Ixy2
Ixz: Ixz2
Iyy: Iyy2
Iyz: Iyz2
Izz: Izz2
com: [0,0,0]
```
Mass-inertia matrices of all links. The keyword `frame` defines the frame where the `inertia` is defined in and accepts two values: `frame: body` and `frame: com`. When the inertia is w.r.t. the com frame, there is another keyword `rotation` available, which defines the rotation between the com frame and the body frame. The rotation can be defined like all other rotations above.
For the definition you have the following syntax options:
1. Write down whole matrix:
```yaml
mass_inertia:
- [[ Ixx1, Ixy1, Ixz1, 0, -cz1*m1, cy1*m1],
[ Ixy1, Iyy1, Iyz1, cz1*m1, 0, -cx1*m1],
[ Ixz1, Iyz1, Izz1, -cy1*m1, cx1*m1, 0],
[ 0, cz1*m1, -cy1*m1, m1, 0, 0],
[-cz1*m1, 0, cx1*m1, 0, m1, 0],
[ cy1*m1, -cx1*m1, 0, 0, 0, m1]]
```
2. Define mass, inertia and center of mass (com) separately:
```yaml
mass_inertia:
- mass: m1
frame: body
inertia:
[[Ixx1,Ixy1,Ixz1],
[Ixy1,Iyy1,Iyz1],
[Ixz1,Iyz1,Izz1]]
com: [cx1,cy1,cz1]
```
3. Only define the 6 independent inertia parameters:
```yaml
mass_inertia:
- mass: m1
frame: body
inertia: [Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1]
com: [cx1,cy1,cz1]
```
4. Define only necessary inertia parameters:
```yaml
mass_inertia:
- mass: m1
frame: body
inertia:
Ixx: Ixx1
Iyy: Iyy1
Izz: Izz1
com: [cx1,cy1,cz1]
```
Supports the parameters `Ixx`, `Ixy`, `Ixz`, `Iyy`, `Iyz` and `Izz`. All parameters default to 0.
5. Define only one value which is internally multiplied by an identity matrix:
```yaml
mass_inertia:
- mass: m1
frame: com
inertia: m1*L1**2
com: [cx1,cy1,cz1]
```
6. Automatically generate symbols in inertia matrix:
```yaml
mass_inertia:
- mass: m1
frame: body
inertia:
index: 1
pointmass: False
com: [0,0,0]
```
Here the parameter index is appended to the names `Ixx` etc. and generates an inertia matrix following the naming scheme used in the examples above.
With the parameter `pointmass: True` the resulting inertia matrix looks like this:
```yaml
[[I1, 0, 0],
[ 0,I1, 0],
[ 0, 0,I1]]
```
---
```yaml
q: [q1,q2]
qd: [dq1,dq2]
q2d: [ddq1,ddq2]
WEE: [0,0,0,0,0,0]
```
Define symbolic generalized vectors (q, qd, q2d) and the time varying wrench on the end-effector link (WEE). Where the generalized vectors are list of length n and the wrench is a list of length 6 with symbolic values e.g.: `[Mx,My,Mz,Fx,Fy,Fz]`.
If the robot has more than one end-effector, one wrench per end_effector has to be defined in a list instead, e.g.:
```YAML
WEE:
- [Mx1,My1,Mz1,Fx1,Fy1,Fz1]
- [Mx2,My2,Mz2,Fx2,Fy2,Fz2]
```
Add jerk and jounce here, to activate the calculation of the 1st and 2nd order derivatives of the Equation of Motion. Optionally, you can define the time derivatives of the external wrench `WDEE` and `W2DEE` here accordingly. E.g.:
```yaml
q: [q1,q2]
qd: [dq1,dq2]
q2d: [ddq1,ddq2]
q3d: [dddq1,dddq2]
q4d: [ddddq1,ddddq2]
WEE: [Mx,My,Mz,Fx,Fy,Fz]
WDEE: [dMx,dMy,dMz,dFx,dFy,dFz]
W2DEE: [ddMx,ddMy,ddMz,ddFx,ddFy,ddFz]
```
___
```yaml
assignments:
g: 9.81
```
Under the optional `assignments` keyword you can create a dictionary which maps the symbolic values to numeric values. These numeric values will be used as default values for the parameters in the generated code.
#### 2.1.2. Code generation using YAML
To start the code generation process use:
```bash
python3 -m skidy [options] path/to/robot.yaml
```
In the options you have to specify what kind of code (python `-p`, Matlab `-m`, C++ `-c`, C `-C`, julia `-j`, cython `--cython`, latex `-l`) you'd like to generate and whether the equations should be simplified `-s`.
Use
```bash
python3 -m skidy -h
```
to get a description of all available options.
### 2.2. Python
As for YAML and JSON there is a function to auto-generate a **python template file** which makes it easier to define your own robot.
To generate your robot template use
```bash
python3 -m skidy --template [options] new_filename.py
```
or the python function `skidy.generate_template_python(path, structure)`.
For [options] the option `--structure` is highly recommended. There you can define which joint types to use in the template. E.g. use `--structure 'rrp'` for a robot which has two revolute joints followed by one prismatic joint.
The command `python3 -m skidy --template --structure 'rp' my_robot_template.py` creates the following output file:
```python
from skidy import (SymbolicKinDyn,
transformation_matrix,
mass_matrix_mixed_data,
joint_screw,
SO3Exp,
inertia_matrix,
generalized_vectors)
from skidy.symbols import g, pi
import sympy
# Define symbols:
m1, m2 = sympy.symbols('m1 m2', real=True, const=True)
Ixx1, Ixx2 = sympy.symbols('Ixx1 Ixx2', real=True, const=True)
Ixy1, Ixy2 = sympy.symbols('Ixy1 Ixy2', real=True, const=True)
Ixz1, Ixz2 = sympy.symbols('Ixz1 Ixz2', real=True, const=True)
Iyy1, Iyy2 = sympy.symbols('Iyy1 Iyy2', real=True, const=True)
Iyz1, Iyz2 = sympy.symbols('Iyz1 Iyz2', real=True, const=True)
Izz1, Izz2 = sympy.symbols('Izz1 Izz2', real=True, const=True)
# Define connectivity graph
parent = [0,
1]
child = [[1],
[]]
support = [[1],
[1,2]]
ee_parent = 2
# gravity vector
gravity = sympy.Matrix([0,0,g])
# representation of joint screw coordinates and body reference configurations
representation = 'spatial' # alternative: 'body_fixed'
# joint screw coordinates (6x1 sympy.Matrix per joint)
joint_screw_coord = []
joint_screw_coord.append(joint_screw(axis=[0,0,1], vec=[0,0,0], revolute=True))
joint_screw_coord.append(joint_screw(axis=[0,0,1], revolute=False))
# body reference configurations (4x4 SE3 Pose (sympy.Matrix) per link)
body_ref_config = []
body_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))
body_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))
# end-effector configuration w.r.t. last link body fixed frame in the chain (4x4 SE3 Pose (sympy.Matrix))
ee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])
# mass_inertia parameters (6x6 sympy.Matrix per link)
Mb = []
Mb.append(mass_matrix_mixed_data(m1, inertia_matrix(Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1), sympy.Matrix([0,0,0]), frame="body"))
Mb.append(mass_matrix_mixed_data(m2, inertia_matrix(Ixx2,Ixy2,Ixz2,Iyy2,Iyz2,Izz2), sympy.Matrix([0,0,0]), frame="body"))
q, qd, q2d = generalized_vectors(len(body_ref_config), startindex=1)
WEE = sympy.zeros(6,1)
skd = SymbolicKinDyn(gravity_vector=gravity,
ee=ee,
body_ref_config=body_ref_config,
joint_screw_coord=joint_screw_coord,
config_representation=representation,
Mb=Mb,
parent=parent,
child=child,
support=support,
ee_parent=ee_parent,
)
# run Calculations
skd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)
skd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)
# Generate Code
skd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,
folder="./generated_code", use_global_vars=True,
name="plant", project="Project")
```
The code explained:
```python
from skidy import (SymbolicKinDyn,
transformation_matrix,
mass_matrix_mixed_data,
joint_screw,
SO3Exp,
inertia_matrix,
generalized_vectors)
```
The class `SymbolicKinDyn` is the main object for calculating the kinematic and dynamic equations of your robot and generate the code.
Additionally, we import several helper functions for defining the matrices which are useful for the robot definition:
- `transformation_matrix`: Create SE(3) transformation matrix from SO(3) rotation and translation vector.
- `mass_matrix_mixed_data`: Create 6x6 mass-inertia matrix from mass, 3x3 inertia matrix and 3x1 center of mass vector.
- `joint_screw`: create 6x1 joint screw vector from joint axis and vector from origin to joint axis.
- `SO3Exp`: Exponential mapping of SO(3) to generate rotation matrix from rotation angle and rotation axis.
- `inertia_matrix`: generate 3x3 inertia matrix from 6 independent parameters (Ixx, Ixy, ...).
- `generalized_vectors`: generate symbolic generalized vectors q, qd and q2d of predefined length n.
```python
from skidy.symbols import g, pi
```
The package `skidy.symbols` includes the most common used symbolic variables, which can be used for defining your robot.
```python
import sympy
```
The whole library used sympy objects for all symbolic equations etc. Hence, we need `sympy` to create additional symbolic variables and matrices later.
---
```python
# Define symbols:
m1, m2 = sympy.symbols('m1 m2', real=True, const=True)
Ixx1, Ixx2 = sympy.symbols('Ixx1 Ixx2', real=True, const=True)
Ixy1, Ixy2 = sympy.symbols('Ixy1 Ixy2', real=True, const=True)
Ixz1, Ixz2 = sympy.symbols('Ixz1 Ixz2', real=True, const=True)
Iyy1, Iyy2 = sympy.symbols('Iyy1 Iyy2', real=True, const=True)
Iyz1, Iyz2 = sympy.symbols('Iyz1 Iyz2', real=True, const=True)
Izz1, Izz2 = sympy.symbols('Izz1 Izz2', real=True, const=True)
```
Create symbolic variables which can be used in the equations for the robot definition later. The most common symbols are also already present in the `skidy.symbols` package and may be imported from there instead.
---
```python
# Define connectivity graph
parent = [0,
1]
child = [[1],
[]]
support = [[1],
[1,2]]
ee_parent = 2
```
Connectivity graph of the robot. The parameters are generated to represent a serial robot by default. Modify parameters for tree-like structures. For serial robots these parameters are optional.
- **parent**: list of parent links for any joint. Use 0 for World.
- **child**: list of lists with child links for any link. Use empty list if no child is present.
- **support**: list of lists with all support links beginning with first link including current link for any link.
- **ee_parent**: parent joint index for end-effector link. Defaults to last link. For robots with more than one end-effector you can use a list of indices instead e.g.: `ee_parent = [1,2]`. Note, that this requires a list of end_effector transforms in `ee`.
---
```python
# gravity vector
gravity = sympy.Matrix([0,0,g])
```
Gravity vector as `sympy.Matrix`. Note that we can use symbolic variables here.
---
```python
# representation of joint screw coordinates and body reference configurations
representation = 'spatial' # alternative: 'body_fixed'
```
Define whether the representation of the joint screw coordinates and the body reference configuration is w.r.t. world frame (`representation = 'spatial'`) or in body fixed coordinates (`representation = 'body_fixed'`).
---
```python
# joint screw coordinates (6x1 sympy.Matrix per joint)
joint_screw_coord = []
joint_screw_coord.append(joint_screw(axis=[0,0,1], vec=[0,0,0], revolute=True))
joint_screw_coord.append(joint_screw(axis=[0,0,1], revolute=False))
```
The joint screw coordinates can be defined either using the syntax which is used above, where `axis` is the joint axis, `vec` is a vector from the origin to the joint axis and `revolute` has to be `True` for revolute joints and `False` for prismatic joints. Note that prismatic joints don't need the parameter `vec`.
Alternatively, you can directly use the 6D joint screw vectors instead:
```python
joint_screw_coord = []
joint_screw_coord.append(sympy.Matrix([0,0,1,0,0,0]))
joint_screw_coord.append(sympy.Matrix([0,0,0,0,0,1]))
```
---
```python
# body reference configurations (4x4 SE3 Pose (sympy.Matrix) per link)
body_ref_config = []
body_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))
body_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))
```
The body reference configuration is a list of SE(3) transformation matrices. To define them you have several options:
1. Write down the whole matrix e.g.:
```python
body_ref_config.append(
sympy.Matrix([[sympy.cos(pi/2),-sympy.sin(pi/2),0, 0],
[sympy.sin(pi/2), sympy.cos(pi/2),0, 0],
[ 0, 0,1,L1],
[ 0, 0,0, 1]])
)
```
Note: this example assumes you have defined the symbolic variable `L1` before.
2. Write rotation and translation separately:
```python
body_ref_config.append(
transformation_matrix(
r=sympy.Matrix([[1,0,0],
[0,1,0],
[0,0,1]]),
t=sympy.Matrix([0,0,L1])
)
)
```
3. Use axis angle representation for rotation:
-> See code above.
4. For zero rotations or translations it is possible to omit the option:
```python
body_ref_config.append(transformation_matrix(t=[0,0,0]))
```
5. Use xyz_rpy coordinates to define Pose:
```python
body_ref_config.append(xyz_rpy_to_matrix([0, 0, L1, 0, pi/2, 0]))
```
Note that you have to import the function using `from skidy import xyz_rpy_to_matrix`.
6. Use roll pitch yaw (rpy) euler angles to define rotation:
```python
body_ref_config.append(
transformation_matrix(
r=rpy_to_matrix([0, pi/2, 0]),
t=sympy.Matrix([0,0,L1])
)
)
```
Note that you have to import the function using `from skidy import rpy_to_matrix`.
7. Use quaternion [w,x,y,z] to define rotation:
```python
body_ref_config.append(
transformation_matrix(
r=quaternion_to_matrix([1,0,0,0]),
t=sympy.Matrix([0,0,L1])
)
)
```
Note that you have to import the function using `from skidy import quaternion_to_matrix`.
---
```python
# end-effector configuration w.r.t. last link body fixed frame in the chain (4x4 SE3 Pose (sympy.Matrix))
ee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])
```
End-effector representation w.r.t. last link body frame in the chain as SE(3) transformation matrix. Here you have the same syntax options as for the body reference configuration.
For robots with more than one end-effector you can use a list of transforms instead. This requires a list of parent joint indices for the parameter `ee_parent`.
---
```python
# mass_inertia parameters (6x6 sympy.Matrix per link)
Mb = []
Mb.append(mass_matrix_mixed_data(m1, inertia_matrix(Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1), sympy.Matrix([0,0,0]), frame='body'))
Mb.append(mass_matrix_mixed_data(m2, inertia_matrix(Ixx2,Ixy2,Ixz2,Iyy2,Iyz2,Izz2), sympy.Matrix([0,0,0]), frame='body'))
```
Mass-inertia matrices of all links. The mass inertia matrix has to be w.r.t. the body-fixed frame. But with the argument `frame = 'com'` you can define the inertia matrix w.r.t. the center of mass frame instead. In this case, there is another argument `R` in the function `mass_matrix_mixed_data` which defines the rotation matrix of the COM frame to the body-fixed frame.
For the definition you have the following syntax options:
1. Write down whole matrix:
```python
Mb.append(
sympy.Matrix([[ Ixx1, Ixy1, Ixz1, 0, -cz1*m1, cy1*m1],
[ Ixy1, Iyy1, Iyz1, cz1*m1, 0, -cx1*m1],
[ Ixz1, Iyz1, Izz1, -cy1*m1, cx1*m1, 0],
[ 0, cz1*m1, -cy1*m1, m1, 0, 0],
[-cz1*m1, 0, cx1*m1, 0, m1, 0],
[ cy1*m1, -cx1*m1, 0, 0, 0, m1]])
)
```
2. Define mass, inertia matrix and center of mass separately:
```python
Mb.append(
mass_matrix_mixed_data(
m1,
sympy.Matrix([[Ixx1,Ixy1,Ixz1],
[Ixy1,Iyy1,Iyz1],
[Ixz1,Iyz1,Izz1]]),
sympy.Matrix([cx1,cy1,cz1])
)
)
```
3. Only define the 6 independent inertia parameters:
```python
Mb.append(
mass_matrix_mixed_data(
m1,
inertia_matrix(Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1),
sympy.Matrix([cx1,cy1,cz1])
)
)
```
4. Automatically generate symbols in inertia matrix:
```python
Mb.append(
mass_matrix_mixed_data(
m1,
symbolic_inertia_matrix(index=1, pointmass=False),
sympy.Matrix([cx1,cy1,cz1])
)
)
```
where `symbolic_inertia_matrix(index=1, pointmass=False)` auto generates the variables `Ixx1`, `Ixy1`, etc. and creates a `sympy.Matrix` from it.
With the parameter `pointmass=True` the resulting inertia matrix looks like this instead:
```python
sympy.Matrix([[I1, 0, 0],
[ 0,I1, 0],
[ 0, 0,I1]])
```
Note that you have to import the function using `from skidy import symbolic_inertia_matrix`.
---
```python
q, qd, q2d = generalized_vectors(len(body_ref_config), startindex=1)
```
Generate the generalized vectors (joint positions `q`, joint velocities `qd` and joint accelerations `q2d`). The symbols are auto generated starting at index `startindex`. The degrees of freedom in this case are taken from the length of `body_ref_config`.
The equivalent code would be:
```python
q1, q2 = sympy.symbols("q1 d2", real=True, constant=False)
dq1, dq2 = sympy.symbols("dq1 dd2", real=True, constant=False)
ddq1, ddq2 = sympy.symbols("ddq1 ddq2", real=True, constant=False)
q = sympy.Matrix([q1,q2])
qd = sympy.Matrix([dq1,dq2])
q2d = sympy.Matrix([ddq1,ddq2])
```
To generate jerk and jounce as well, use:
```python
q, qd, q2d, q3d, q4d = generalized_vectors(len(body_ref_config), startindex=1, derivatives=True)
```
---
```python
WEE = sympy.zeros(6,1)
```
Define the time varying wrench on the end-effector link. Should be a (6,1) sympy.Matrix with symbolic variables, e.g.: `sympy.Matrix([Mx,My,Mz,Fx,Fy,Fz])`.
Optionally, you can define the time derivatives `WDEE` and `W2DEE` here too. E.g.:
```python
WEE = sympy.Matrix([Mx,My,Mz,Fx,Fy,Fz])
WDEE = sympy.Matrix([dMx,dMy,dMz,dFx,dFy,dFz])
W2DEE = sympy.Matrix([ddMx,ddMy,ddMz,ddFx,ddFy,ddFz])
```
If your robot has more than one end-effector, you have to use one external wrench per end-effector in a list, e.g.:
```python
WEE = [sympy.Matrix([Mx1,My1,Mz1,Fx1,Fy1,Fz1]),
sympy.Matrix([Mx2,My2,Mz2,Fx2,Fy2,Fz2])]
```
---
```python
skd = SymbolicKinDyn(gravity_vector=gravity,
ee=ee,
body_ref_config=body_ref_config,
joint_screw_coord=joint_screw_coord,
config_representation=representation,
Mb=Mb,
parent=parent,
child=child,
support=support,
)
```
Initialize class with all defined parameters.
---
```python
# run Calculations
skd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)
skd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)
```
Generate forward kinematics and inverse dynamics equations. Both functions share the following arguments:
- simplify: generated expressions are simplified. Note that the simplification takes a lot of time for robots with more than 2 revolute joints in a chain.
- cse: Use common subexpression elimination to shorten equations. Note that the equations are not human-readable afterwards.
- parallel: use parallel computation.
`skd.closed_form_kinematics_body_fixed` generates the following equations and saves them as class parameters:
- body_acceleration
- body_acceleration_ee
- body_jacobian_matrix
- body_jacobian_matrix_dot
- body_jacobian_matrix_ee
- body_jacobian_matrix_ee_dot
- body_twist_ee
- forward_kinematics
- hybrid_acceleration
- hybrid_acceleration_ee
- hybrid_jacobian_matrix
- hybrid_jacobian_matrix_dot
- hybrid_jacobian_matrix_ee
- hybrid_jacobian_matrix_ee_dot
- hybrid_twist_ee
and `skd.closed_form_inv_dyn_body_fixed` generates the following equations and saves them as class parameters:
- coriolis_centrifugal_matrix
- generalized_mass_inertia_matrix
- gravity_vector
- inverse_dynamics
with provided generalized jerk and jounce vectors, `skd.closed_form_inv_dyn_body_fixed` additionally generates the following derivatives of the equation of motion:
- coriolis_centrifugal_matrix_dot
- generalized_mass_inertia_matrix_dot
- gravity_vector_dot
- inverse_dynamics_dot
- coriolis_centrifugal_matrix_ddot
- generalized_mass_inertia_matrix_ddot
- gravity_vector_ddot
- inverse_dynamics_ddot
`skd.closed_form_inv_dyn_body_fixed` takes the wrench `WEE` (6x1 sympy.Matrix) (tau,F) on the end-effector link and its time derivatives `WDEE` and `W2DEE` as optional additional arguments.
To calculate the 1st and 2nd order time derivative of the EOM use e.g.:
```python
skd.closed_form_inv_dyn_body_fixed(q, qd, q2d, q3d, q4d, WEE, WDEE, W2DEE, simplify=True)
```
---
```python
# Generate Code
skd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,
folder="./generated_code", use_global_vars=True,
name="plant", project="Project")
```
Generate Python, Matlab, C++, C (C99), Julia, Cython and/or LaTeX code from the generated equations.
Note that this can take some time, especially for non-simplified equations and complex robots.
### 2.3. URDF
There are two ways to use URDF files.
First, you can convert your URDF file to YAML or JSON using:
```bash
skidy --convert my_robot.urdf my_robot.yaml
```
Here, you can add end-effector transforms, and modify whatever you want.
Second, you can load your URDF using python. There is a function to generate a template python file, which loads your URDF. In the python file it is necessary to define:
1. the URDF path
2. the gravity vector
3. end-effector configuration w.r.t. last link body fixed frame in the chain
To generate the python template file use:
```bash
python -m skidy --template --urdf my_urdf_template.py
```
or the python function `skidy.generate_template_python(path, urdf=True)`.
This generates the following output:
```python
from skidy import (SymbolicKinDyn,
transformation_matrix,
SO3Exp,
generalized_vectors)
from skidy.symbols import g, pi
import sympy
# Define symbols:
lee = sympy.symbols('lee', real=True, const=True)
urdfpath = '/path/to/robot.urdf' # TODO: change me!
# gravity vector
gravity = sympy.Matrix([0,0,g])
# end-effector configuration w.r.t. last link body fixed frame in the chain
ee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])
skd = SymbolicKinDyn(gravity_vector=gravity,
ee=ee,
)
skd.load_from_urdf(path = urdfpath,
symbolic=False,
cse=False,
simplify_numbers=True,
tolerance=0.0001,
max_denominator=8,
)
q, qd, q2d = generalized_vectors(len(skd.body_ref_config), startindex=1)
WEE = sympy.zeros(6,1)
# run Calculations
skd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)
skd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)
# Generate Code
skd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,
folder="./generated_code", use_global_vars=True,
name="plant", project="Project")
```
The code explained:
```python
from skidy import (SymbolicKinDyn,
transformation_matrix,
SO3Exp,
generalized_vectors)
```
The class `SymbolicKinDyn` is the main object for calculating the kinematic and dynamic equations of your robot and generate the code.
Additionally, we import several helper functions for defining the matrices which are useful for the robot definition:
- `transformation_matrix`: Create SE(3) transformation matrix from SO(3) rotation and translation vector.
- `SO3Exp`: Exponential mapping of SO(3) to generate rotation matrix from rotation angle and rotation axis.
- `generalized_vectors`: generate symbolic generalized vectors q, qd and q2d of predefined length n.
```python
from skidy.symbols import g, pi
```
The package `skidy.symbols` includes the most common used symbolic variables, which can be used for defining your robot.
```python
import sympy
```
The whole library used sympy objects for all symbolic equations etc. Hence, we need `sympy` to create additional symbolic variables and matrices later.
---
```python
# Define symbols:
lee = sympy.symbols('lee', real=True, const=True)
```
Create symbolic variables which can be used in the equations for the robot definition later. The most common symbols are also already present in the `skidy.symbols` package and may be imported from there instead.
---
```python
urdfpath = '/path/to/robot.urdf' # TODO: change me!
```
Enter the path to your URDF file here.
---
```python
# gravity vector
gravity = sympy.Matrix([0,0,g])
```
Gravity vector as `sympy.Matrix`. Note that we can use symbolic variables here.
---
```python
# end-effector configuration w.r.t. last link body fixed frame in the chain
ee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])
```
End-effector representation w.r.t. last link body frame in the chain as SE(3) transformation matrix. Look up the chapter [Python](#22-python) for all available syntax options.
---
```python
skd = SymbolicKinDyn(gravity_vector=gravity,
ee=ee,
)
```
Initialize class with the two defined parameters.
---
```python
skd.load_from_urdf(path = urdfpath,
symbolic=False,
cse=False,
simplify_numbers=True,
tolerance=0.0001,
max_denominator=8,
)
```
Load the URDF file. Here you can specify the following options:
1. `symbolic`: symbolify values in urdf file (bool).
2. `cse`: use common subexpression elimination to shorten equations (bool).
3. `simplify_numbers`: round numbers if close to common fractions like 1/2 etc and replace eg 3.1416 by pi (bool).
4. `tolerance`: tolerance for simplify numbers.
5. `max_denominator`: define max denominator for simplify numbers to avoid simplification to something like 13/153. Use 0 to deactivate.
---
```python
q, qd, q2d = generalized_vectors(len(skd.body_ref_config), startindex=1)
```
Generate the generalized vectors (joint positions `q`, joint velocities `qd` and joint accelerations `q2d`). The symbols are auto generated starting at index `startindex`. The degrees of freedom in this case are taken from the length of the parameter `skd.body_ref_config`, which was generated by the function `load_from_urdf`.
---
```python
WEE = sympy.zeros(6,1)
```
Define the time varying wrench on the end-effector link. Should be a (6,1) sympy.Matrix with symbolic variables.
---
```python
# run Calculations
skd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)
skd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)
```
Generate forward kinematics and inverse dynamics equations. See chapter [Python](#22-python) for more information.
---
```python
# Generate Code
skd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,
folder="./generated_code", use_global_vars=True,
name="plant", project="Project")
```
Generate Python, Matlab, C++, C (C99), Julia, Cython and/or LaTeX code from the generated equations.
Note that this can take time, especially for non-simplified equations and complex robots.
## 3. Unit testing
To run the unit tests use:
```bash
python3 ./test/unit_testing.py
```
## 4. Benchmarking
For benchmarking the project the script `benchmarking/benchmarking.py` was used. This script loads 4 robots with increasing complexity (1 to 4 revolute joint in a chain with planar task space) and takes the execution time of the functions `closed_form_kinematics_body_fixed()`, `closed_form_inv_dyn_body_fixed()` and `generate_code()`. Additionally, the arguments `parallel`, `simplify` and `cse` have been altered.
The results are shown in the following table:
arguments | parallel | serial
:---------|:----------:|:-----------:
simplify|![1 dof: 0.65 s; 2 dof: 2.14 s; 3 dof: 8.77 s; 4 dof: 44.04 s](/benchmarking/parallel_with_simplification_without_cse.png) | ![1 dof: 0.16 s; 2 dof: 2.38 s; 3 dof: 13.06 s; 4 dof: 60.68 s](/benchmarking/serial_with_simplification_without_cse.png)
simplify + cse|![1 dof: 0.56 s; 2 dof: 2.22 s; 3 dof: 12.86 s; 4 dof: 84.28 s](/benchmarking/parallel_with_simplification_with_cse.png) |![1 dof: 0.22 s; 2 dof: 2.78 s; 3 dof: 18.49 s; 4 dof: 113.30 s](/benchmarking/serial_with_simplification_with_cse.png)
no simplify|![1 dof: 0.74 s; 2 dof: 2.65 s; 3 dof: 11.29 s; 4 dof: 47.14 s](/benchmarking/parallel_without_simplification_without_cse.png) | ![1 dof: 0.08 s; 2 dof: 1.57 s; 3 dof: 9.51 s; 4 dof: 44.57 s](/benchmarking/serial_without_simplification_without_cse.png)
no simplify + cse|![1 dof: 0.80 s; 2 dof: 4.50 s; 3 dof: 29.13 s; 4 dof: 161.01 s](/benchmarking/parallel_without_simplification_with_cse.png) |![1 dof: 0.08 s; 2 dof: 4.49 s; 3 dof: 37.74 s; 4 dof: 201.11 s](/benchmarking/serial_without_simplification_with_cse.png)
## 5. License
This work has been released under the BSD 3-Clause License. Details and terms of use are specified in the LICENSE file within this repository. Note that we do not publish third-party software, hence software packages from other developers are released under their very own terms and conditions. If you install third-party software packages along with this repo ensure that you follow each individual license agreement.
Raw data
{
"_id": null,
"home_page": null,
"name": "skidy",
"maintainer": "Hannah Isermann",
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Hannah Isermann <hannah.isermann@dfki.de>",
"keywords": "symbolic, code generation, robot, kinematics, dynamics, equation of motion",
"author": "Underactuated Lab DFKI Robotics Innovation Center Bremen",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/a6/8d/4ec961976eebe2e120844b932c5880f05071997491605091c10fcc6d8c51/skidy-0.0.1.tar.gz",
"platform": null,
"description": "# skidy - symbolic kinematics and dynamics generator\n\n- [skidy - symbolic kinematics and dynamics generator](#skidy---symbolic-kinematics-and-dynamics-generator)\n - [1. Installation](#1-installation)\n - [Install package from source](#install-package-from-source)\n - [Dependencies](#dependencies)\n - [2. Usage](#2-usage)\n - [2.1. YAML and JSON](#21-yaml-and-json)\n - [2.1.1. Create robot model as YAML file](#211-create-robot-model-as-yaml-file)\n - [2.1.2. Code generation using YAML](#212-code-generation-using-yaml)\n - [2.2. Python](#22-python)\n - [2.3. URDF](#23-urdf)\n - [3. Unit testing](#3-unit-testing)\n - [4. Benchmarking](#4-benchmarking)\n - [5. License](#5-license)\n\nSymbolic kinematics and dynamics model generation using Equations of Motion in closed form.\n\n## 1. Installation\n\n```bash\npython3 -m pip install skidy\n```\n\n### Install package from source\n\nTo install the package from source go to the root folder of the package:\n\n```bash\ncd /path/to/project\n```\n\nthen use:\n\n```bash\npython3 -m pip install -e .\n```\n\nto install the package in editable mode or:\n\n```bash\npython3 -m pip install .\n```\n\nto install the package uneditable.\n\n**Note:** The `.` is part of the command. \n\n### Dependencies\n\nThe project has the following dependencies:\n\n- sympy (Version >= 1.8)\n\n ```bash\n python3 -m pip install --upgrade sympy\n ```\n\n- numpy\n\n ```bash\n python3 -m pip install numpy\n ```\n\n- urdf_parser_py (Version >= 0.0.4)\n\n ```bash\n python3 -m pip install --upgrade urdf_parser_py\n ```\n\n- regex\n\n ```bash\n python3 -m pip install regex\n ```\n\n- PyYAML\n\n ```bash\n python3 -m pip install PyYAML\n ```\n\n- pylatex\n\n ```bash\n python3 -m pip install pylatex\n ```\n\n- pydot\n\n ```bash\n python3 -m pip install pydot\n ```\n\nFor unit testing the following packages are additionally recommended:\n\n- oct2py\n\n ```bash\n python3 -m pip install oct2py\n ```\n\n This requires a working octave installation on your system:\n Install on Ubuntu with:\n\n ```bash\n sudo apt install octave\n ```\n\n- cython\n\n ```bash\n python3 -m pip install cython\n ```\n\n- kinpy\n\n ```bash\n python3 -m pip install kinpy\n ```\n\n- pinocchio\n\n ```bash\n python3 -m pip install pin\n ```\n\n## 2. Usage\n\nThere are four ways to load your robot into the library:\n\n1. using a *YAML* file\n2. using a *JSON* file\n3. directly in *python*\n4. using *URDF*\n\nFor defining a robot with one of the first three options (YAML, JSON, python) the following parameters are required:\n\n- 6D joint screw coordinates for any joint\n- 4x4 body reference coordinates for any link\n- 4x4 end-effector configuration w.r.t. last link body fixed frame in the chain\n\nFor generating the inverse dynamics the following two parameters are required:\n\n- 3D gravity vector\n- 6x6 Mass-inertia matrix for any link\n\nTree-like robot structures require the following graph description parameters additionally:\n\n- parent link for any joint\n- support graph for any joint\n- (child links for any joint) -> currently not used\n\nUsing URDF the following three parameters are required:\n\n- path to URDF file\n- 4x4 end-effector configuration w.r.t. last link body fixed frame in the chain\n- 3D gravity vector\n\n### 2.1. YAML and JSON\n\n**NOTE:** As *JSON* and *YAML* files represent the same data structure this documentation covers only *YAML* files. Use *JSON* accordingly and just replace `yaml` with `json` in all commands.\n\n#### 2.1.1. Create robot model as YAML file\n\nThere is a function to generate a **template YAML file** in which it is easy to modify the parameters for your robot.\nTo generate your robot template use\n\n```bash\npython3 -m skidy --template [options] new_filename.yaml\n```\n\nor the python function `skidy.generate_template_yaml(path, structure)`.\n\nFor [options] the option `--structure` is highly recommended. There you can define which joint types to use in the template. E.g. use `--structure 'rrp'` for a robot which has two revolute joints followed by one prismatic joint.\n\nThe command `python3 -m skidy --template --structure 'rp' my_robot_template.yaml` creates the following output file:\n\n```yaml\n---\nparent:\n - 0\n - 1\n\nchild:\n - [1]\n - []\n\nsupport:\n - [1]\n - [1,2]\n\nee_parent: 2\n\ngravity: [0,0,-g]\n\nrepresentation: spatial\n\njoint_screw_coord:\n - type: revolute\n axis: [0,0,1]\n vec: [0,0,0]\n\n - type: prismatic\n axis: [0,0,1]\n\nbody_ref_config:\n - rotation:\n axis: [0,0,1]\n angle: 0\n translation: [0,0,0]\n\n - rotation:\n axis: [0,0,1]\n angle: 0\n translation: [0,0,0]\n\nee:\n - rotation:\n axis: [0,0,1]\n angle: 0\n translation: [0,0,0]\n\nmass_inertia:\n - mass: m1\n frame: body\n inertia:\n Ixx: Ixx1\n Ixy: Ixy1\n Ixz: Ixz1\n Iyy: Iyy1\n Iyz: Iyz1\n Izz: Izz1\n com: [0,0,0]\n\n - mass: m2\n frame: body\n inertia:\n Ixx: Ixx2\n Ixy: Ixy2\n Ixz: Ixz2\n Iyy: Iyy2\n Iyz: Iyz2\n Izz: Izz2\n com: [0,0,0]\n\nq: [q1,q2]\nqd: [dq1,dq2]\nq2d: [ddq1,ddq2]\nWEE: [0,0,0,0,0,0]\n\nassignments:\n g: 9.81\n\n```\n\nThe code explained:\n\n```yaml\nparent:\n - 0\n - 1\n\nchild:\n - [1]\n - []\n\nsupport:\n - [1]\n - [1,2]\n\nee_parent: 2\n```\n\nThe parameters are generated to represent a serial robot by default. Modify parameters for tree-like structures. For serial robots these parameters are optional.\n\n- **parent**: list of parent links for any joint. Use 0 for World.\n- **child**: list of lists with child links for any link. Use empty list if no child is present.\n- **support**: list of lists with all support links beginning with first link including current link for any link.\n- **ee_parent**: parent joint index for end-effector link. Defaults to last link. For robots with more than one end-effector you can use a list of indices instead e.g.: `ee_parent: [1,2]`. Note, that this requires a list of end_effector transforms `ee`.\n\n---\n\n```yaml\ngravity: [0,0,-g]\n```\n\nGravity vector.\n**Note:** you can always use symbolic variables instead of numeric values.\n\n---\n\n```yaml\nrepresentation: spatial\n```\n\nDefine whether the representation of the joint screw coordinates and the body reference configuration is w.r.t. world frame (`representation: spatial`) or in body fixed coordinates (`representation: body_fixed`)\n\n---\n\n```yaml\njoint_screw_coord:\n - type: revolute\n axis: [0,0,1]\n vec: [0,0,0]\n\n - type: prismatic\n axis: [0,0,1]\n```\n\nThe joint screw coordinates can be defined either using the syntax which is used above, where `type` is the joint type (`revolute` or `prismatic`), `axis` is the joint axis and `vec` is a vector from the origin to the joint axis.\nAlternatively, you can directly use the 6D joint screw vectors instead:\n\n```yaml\njoint_screw_coord:\n - [0,0,1,0,0,0]\n - [0,0,0,0,0,1]\n```\n\n---\n\n```yaml\nbody_ref_config:\n - rotation:\n axis: [0,0,1]\n angle: 0\n translation: [0,0,0]\n\n - rotation:\n axis: [0,0,1]\n angle: 0\n translation: [0,0,0]\n```\n\nThe body reference configuration is a list of SE(3) transformation matrices. To define them you have several options:\n\n1. Write down the whole matrix e.g.:\n\n ```yaml\n body_ref_config:\n - [[cos(pi),-sin(pi),0, 0],\n [sin(pi), cos(pi),0, 0],\n [ 0, 0,1,L1],\n [ 0, 0,0, 1]]\n ```\n\n2. Write rotation and translation separately:\n\n ```yaml\n body_ref_config:\n - rotation:\n [[1,0,0],\n [0,1,0],\n [0,0,1]]\n translation: [0,0,L1]\n ```\n\n3. Use axis angle representation for rotation:\n -> See code above.\n\n4. For zero rotations or translations it is possible to omit the option:\n\n ```yaml\n body_ref_config:\n - translation: [0,0,L1]\n ```\n\n5. Use xyz_rpy coordinates to define Pose:\n\n ```yaml\n body_ref_config:\n - xyzrpy: [0, 0, L1, 0, pi/2, 0]\n ```\n\n6. Use roll pitch yaw (rpy) euler angles to define rotation:\n\n ```yaml\n body_ref_config:\n - rotation:\n rpy: [0, pi/2, 0]\n translation: [0,0,L1]\n ```\n\n7. Use quaternion [w,x,y,z] to define rotation:\n\n ```yaml\n body_ref_config:\n - rotation:\n Q: [1, 0, 0, 0]\n translation: [0,0,L1]\n ```\n\n---\n\n```yaml\nee:\n - rotation:\n axis: [0,0,1]\n angle: 0\n translation: [0,0,0]\n```\n\nEnd-effector representation w.r.t. last link body frame in the chain as SE(3) transformation matrix. Here you have the same syntax options as for the body reference configuration. For robots with more than one end-effector you can define more than one transform here. Note: in this case you have to define a list of parent joints in `ee_parent`.\nIf you have just one end-effector, you can omit the top level list (indicated by the trailing `-`).\n\n---\n\n```yaml\nmass_inertia:\n - mass: m1\n frame: body\n inertia:\n Ixx: Ixx1\n Ixy: Ixy1\n Ixz: Ixz1\n Iyy: Iyy1\n Iyz: Iyz1\n Izz: Izz1\n com: [0,0,0]\n\n - mass: m2\n frame: body\n inertia:\n Ixx: Ixx2\n Ixy: Ixy2\n Ixz: Ixz2\n Iyy: Iyy2\n Iyz: Iyz2\n Izz: Izz2\n com: [0,0,0]\n```\n\nMass-inertia matrices of all links. The keyword `frame` defines the frame where the `inertia` is defined in and accepts two values: `frame: body` and `frame: com`. When the inertia is w.r.t. the com frame, there is another keyword `rotation` available, which defines the rotation between the com frame and the body frame. The rotation can be defined like all other rotations above.\n\nFor the definition you have the following syntax options:\n\n1. Write down whole matrix:\n\n ```yaml\n mass_inertia:\n - [[ Ixx1, Ixy1, Ixz1, 0, -cz1*m1, cy1*m1],\n [ Ixy1, Iyy1, Iyz1, cz1*m1, 0, -cx1*m1],\n [ Ixz1, Iyz1, Izz1, -cy1*m1, cx1*m1, 0],\n [ 0, cz1*m1, -cy1*m1, m1, 0, 0],\n [-cz1*m1, 0, cx1*m1, 0, m1, 0],\n [ cy1*m1, -cx1*m1, 0, 0, 0, m1]]\n ```\n\n2. Define mass, inertia and center of mass (com) separately:\n\n ```yaml\n mass_inertia:\n - mass: m1\n frame: body\n inertia:\n [[Ixx1,Ixy1,Ixz1],\n [Ixy1,Iyy1,Iyz1],\n [Ixz1,Iyz1,Izz1]]\n com: [cx1,cy1,cz1]\n\n ```\n\n3. Only define the 6 independent inertia parameters:\n\n ```yaml\n mass_inertia:\n - mass: m1\n frame: body\n inertia: [Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1]\n com: [cx1,cy1,cz1]\n\n ```\n\n4. Define only necessary inertia parameters:\n\n ```yaml\n mass_inertia:\n - mass: m1\n frame: body\n inertia: \n Ixx: Ixx1\n Iyy: Iyy1\n Izz: Izz1\n com: [cx1,cy1,cz1]\n ```\n\n Supports the parameters `Ixx`, `Ixy`, `Ixz`, `Iyy`, `Iyz` and `Izz`. All parameters default to 0.\n\n5. Define only one value which is internally multiplied by an identity matrix:\n\n ```yaml\n mass_inertia:\n - mass: m1\n frame: com\n inertia: m1*L1**2\n com: [cx1,cy1,cz1]\n ```\n\n6. Automatically generate symbols in inertia matrix:\n\n ```yaml\n mass_inertia:\n - mass: m1\n frame: body\n inertia:\n index: 1\n pointmass: False\n com: [0,0,0]\n\n ```\n\n Here the parameter index is appended to the names `Ixx` etc. and generates an inertia matrix following the naming scheme used in the examples above.\n With the parameter `pointmass: True` the resulting inertia matrix looks like this:\n\n ```yaml\n [[I1, 0, 0],\n [ 0,I1, 0],\n [ 0, 0,I1]]\n ```\n\n---\n\n```yaml\nq: [q1,q2]\nqd: [dq1,dq2]\nq2d: [ddq1,ddq2]\nWEE: [0,0,0,0,0,0]\n```\n\nDefine symbolic generalized vectors (q, qd, q2d) and the time varying wrench on the end-effector link (WEE). Where the generalized vectors are list of length n and the wrench is a list of length 6 with symbolic values e.g.: `[Mx,My,Mz,Fx,Fy,Fz]`.\nIf the robot has more than one end-effector, one wrench per end_effector has to be defined in a list instead, e.g.:\n\n```YAML\nWEE: \n - [Mx1,My1,Mz1,Fx1,Fy1,Fz1]\n - [Mx2,My2,Mz2,Fx2,Fy2,Fz2]\n```\n\nAdd jerk and jounce here, to activate the calculation of the 1st and 2nd order derivatives of the Equation of Motion. Optionally, you can define the time derivatives of the external wrench `WDEE` and `W2DEE` here accordingly. E.g.:\n\n```yaml\nq: [q1,q2]\nqd: [dq1,dq2]\nq2d: [ddq1,ddq2]\nq3d: [dddq1,dddq2]\nq4d: [ddddq1,ddddq2]\nWEE: [Mx,My,Mz,Fx,Fy,Fz]\nWDEE: [dMx,dMy,dMz,dFx,dFy,dFz]\nW2DEE: [ddMx,ddMy,ddMz,ddFx,ddFy,ddFz]\n```\n\n___\n\n```yaml\nassignments:\n g: 9.81\n```\n\nUnder the optional `assignments` keyword you can create a dictionary which maps the symbolic values to numeric values. These numeric values will be used as default values for the parameters in the generated code.\n#### 2.1.2. Code generation using YAML\n\nTo start the code generation process use:\n\n```bash\npython3 -m skidy [options] path/to/robot.yaml\n```\n\nIn the options you have to specify what kind of code (python `-p`, Matlab `-m`, C++ `-c`, C `-C`, julia `-j`, cython `--cython`, latex `-l`) you'd like to generate and whether the equations should be simplified `-s`.\n\nUse\n\n```bash\npython3 -m skidy -h\n```\n\nto get a description of all available options.\n\n### 2.2. Python\n\nAs for YAML and JSON there is a function to auto-generate a **python template file** which makes it easier to define your own robot.\nTo generate your robot template use\n\n```bash\npython3 -m skidy --template [options] new_filename.py\n```\n\nor the python function `skidy.generate_template_python(path, structure)`.\n\nFor [options] the option `--structure` is highly recommended. There you can define which joint types to use in the template. E.g. use `--structure 'rrp'` for a robot which has two revolute joints followed by one prismatic joint.\n\nThe command `python3 -m skidy --template --structure 'rp' my_robot_template.py` creates the following output file:\n\n```python\nfrom skidy import (SymbolicKinDyn,\n transformation_matrix,\n mass_matrix_mixed_data,\n joint_screw,\n SO3Exp,\n inertia_matrix,\n generalized_vectors)\nfrom skidy.symbols import g, pi\nimport sympy\n\n# Define symbols:\nm1, m2 = sympy.symbols('m1 m2', real=True, const=True)\nIxx1, Ixx2 = sympy.symbols('Ixx1 Ixx2', real=True, const=True)\nIxy1, Ixy2 = sympy.symbols('Ixy1 Ixy2', real=True, const=True)\nIxz1, Ixz2 = sympy.symbols('Ixz1 Ixz2', real=True, const=True)\nIyy1, Iyy2 = sympy.symbols('Iyy1 Iyy2', real=True, const=True)\nIyz1, Iyz2 = sympy.symbols('Iyz1 Iyz2', real=True, const=True)\nIzz1, Izz2 = sympy.symbols('Izz1 Izz2', real=True, const=True)\n\n# Define connectivity graph\nparent = [0,\n 1]\n\nchild = [[1],\n []]\n\nsupport = [[1],\n [1,2]]\n\nee_parent = 2\n\n# gravity vector\ngravity = sympy.Matrix([0,0,g])\n\n# representation of joint screw coordinates and body reference configurations\nrepresentation = 'spatial' # alternative: 'body_fixed'\n\n# joint screw coordinates (6x1 sympy.Matrix per joint)\njoint_screw_coord = []\njoint_screw_coord.append(joint_screw(axis=[0,0,1], vec=[0,0,0], revolute=True))\njoint_screw_coord.append(joint_screw(axis=[0,0,1], revolute=False))\n\n# body reference configurations (4x4 SE3 Pose (sympy.Matrix) per link)\nbody_ref_config = []\nbody_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))\nbody_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))\n\n# end-effector configuration w.r.t. last link body fixed frame in the chain (4x4 SE3 Pose (sympy.Matrix))\nee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])\n\n# mass_inertia parameters (6x6 sympy.Matrix per link)\nMb = []\nMb.append(mass_matrix_mixed_data(m1, inertia_matrix(Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1), sympy.Matrix([0,0,0]), frame=\"body\"))\nMb.append(mass_matrix_mixed_data(m2, inertia_matrix(Ixx2,Ixy2,Ixz2,Iyy2,Iyz2,Izz2), sympy.Matrix([0,0,0]), frame=\"body\"))\n\nq, qd, q2d = generalized_vectors(len(body_ref_config), startindex=1)\nWEE = sympy.zeros(6,1)\n\nskd = SymbolicKinDyn(gravity_vector=gravity,\n ee=ee,\n body_ref_config=body_ref_config,\n joint_screw_coord=joint_screw_coord,\n config_representation=representation,\n Mb=Mb,\n parent=parent,\n child=child,\n support=support,\n ee_parent=ee_parent,\n )\n\n# run Calculations\nskd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)\nskd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)\n\n# Generate Code\nskd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,\n folder=\"./generated_code\", use_global_vars=True,\n name=\"plant\", project=\"Project\")\n```\n\nThe code explained:\n\n```python\nfrom skidy import (SymbolicKinDyn,\n transformation_matrix,\n mass_matrix_mixed_data,\n joint_screw,\n SO3Exp,\n inertia_matrix,\n generalized_vectors)\n```\n\nThe class `SymbolicKinDyn` is the main object for calculating the kinematic and dynamic equations of your robot and generate the code.\nAdditionally, we import several helper functions for defining the matrices which are useful for the robot definition:\n\n- `transformation_matrix`: Create SE(3) transformation matrix from SO(3) rotation and translation vector.\n- `mass_matrix_mixed_data`: Create 6x6 mass-inertia matrix from mass, 3x3 inertia matrix and 3x1 center of mass vector.\n- `joint_screw`: create 6x1 joint screw vector from joint axis and vector from origin to joint axis.\n- `SO3Exp`: Exponential mapping of SO(3) to generate rotation matrix from rotation angle and rotation axis.\n- `inertia_matrix`: generate 3x3 inertia matrix from 6 independent parameters (Ixx, Ixy, ...).\n- `generalized_vectors`: generate symbolic generalized vectors q, qd and q2d of predefined length n.\n\n```python\nfrom skidy.symbols import g, pi\n```\n\nThe package `skidy.symbols` includes the most common used symbolic variables, which can be used for defining your robot.\n\n```python\nimport sympy\n```\n\nThe whole library used sympy objects for all symbolic equations etc. Hence, we need `sympy` to create additional symbolic variables and matrices later.\n\n---\n\n```python\n# Define symbols:\nm1, m2 = sympy.symbols('m1 m2', real=True, const=True)\nIxx1, Ixx2 = sympy.symbols('Ixx1 Ixx2', real=True, const=True)\nIxy1, Ixy2 = sympy.symbols('Ixy1 Ixy2', real=True, const=True)\nIxz1, Ixz2 = sympy.symbols('Ixz1 Ixz2', real=True, const=True)\nIyy1, Iyy2 = sympy.symbols('Iyy1 Iyy2', real=True, const=True)\nIyz1, Iyz2 = sympy.symbols('Iyz1 Iyz2', real=True, const=True)\nIzz1, Izz2 = sympy.symbols('Izz1 Izz2', real=True, const=True)\n```\n\nCreate symbolic variables which can be used in the equations for the robot definition later. The most common symbols are also already present in the `skidy.symbols` package and may be imported from there instead.\n\n---\n\n```python\n# Define connectivity graph\nparent = [0,\n 1]\n\nchild = [[1],\n []]\n\nsupport = [[1],\n [1,2]]\n\nee_parent = 2\n```\n\nConnectivity graph of the robot. The parameters are generated to represent a serial robot by default. Modify parameters for tree-like structures. For serial robots these parameters are optional.\n\n- **parent**: list of parent links for any joint. Use 0 for World.\n- **child**: list of lists with child links for any link. Use empty list if no child is present.\n- **support**: list of lists with all support links beginning with first link including current link for any link.\n- **ee_parent**: parent joint index for end-effector link. Defaults to last link. For robots with more than one end-effector you can use a list of indices instead e.g.: `ee_parent = [1,2]`. Note, that this requires a list of end_effector transforms in `ee`.\n\n---\n\n```python\n# gravity vector\ngravity = sympy.Matrix([0,0,g])\n```\n\nGravity vector as `sympy.Matrix`. Note that we can use symbolic variables here.\n\n---\n\n```python\n# representation of joint screw coordinates and body reference configurations\nrepresentation = 'spatial' # alternative: 'body_fixed'\n```\n\nDefine whether the representation of the joint screw coordinates and the body reference configuration is w.r.t. world frame (`representation = 'spatial'`) or in body fixed coordinates (`representation = 'body_fixed'`).\n\n---\n\n```python\n# joint screw coordinates (6x1 sympy.Matrix per joint)\njoint_screw_coord = []\njoint_screw_coord.append(joint_screw(axis=[0,0,1], vec=[0,0,0], revolute=True))\njoint_screw_coord.append(joint_screw(axis=[0,0,1], revolute=False))\n```\n\nThe joint screw coordinates can be defined either using the syntax which is used above, where `axis` is the joint axis, `vec` is a vector from the origin to the joint axis and `revolute` has to be `True` for revolute joints and `False` for prismatic joints. Note that prismatic joints don't need the parameter `vec`.\nAlternatively, you can directly use the 6D joint screw vectors instead:\n\n```python\njoint_screw_coord = []\njoint_screw_coord.append(sympy.Matrix([0,0,1,0,0,0]))\njoint_screw_coord.append(sympy.Matrix([0,0,0,0,0,1]))\n```\n\n---\n\n```python\n# body reference configurations (4x4 SE3 Pose (sympy.Matrix) per link)\nbody_ref_config = []\nbody_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))\nbody_ref_config.append(transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0]))\n```\n\nThe body reference configuration is a list of SE(3) transformation matrices. To define them you have several options:\n\n1. Write down the whole matrix e.g.:\n\n ```python\n body_ref_config.append(\n sympy.Matrix([[sympy.cos(pi/2),-sympy.sin(pi/2),0, 0],\n [sympy.sin(pi/2), sympy.cos(pi/2),0, 0],\n [ 0, 0,1,L1],\n [ 0, 0,0, 1]])\n )\n ```\n\n Note: this example assumes you have defined the symbolic variable `L1` before.\n\n2. Write rotation and translation separately:\n\n ```python\n body_ref_config.append(\n transformation_matrix(\n r=sympy.Matrix([[1,0,0],\n [0,1,0],\n [0,0,1]]),\n t=sympy.Matrix([0,0,L1])\n )\n )\n ```\n\n3. Use axis angle representation for rotation:\n -> See code above.\n\n4. For zero rotations or translations it is possible to omit the option:\n\n ```python\n body_ref_config.append(transformation_matrix(t=[0,0,0]))\n ```\n\n5. Use xyz_rpy coordinates to define Pose:\n\n ```python\n body_ref_config.append(xyz_rpy_to_matrix([0, 0, L1, 0, pi/2, 0]))\n ```\n\n Note that you have to import the function using `from skidy import xyz_rpy_to_matrix`.\n\n6. Use roll pitch yaw (rpy) euler angles to define rotation:\n\n ```python\n body_ref_config.append(\n transformation_matrix(\n r=rpy_to_matrix([0, pi/2, 0]),\n t=sympy.Matrix([0,0,L1])\n )\n )\n ```\n\n Note that you have to import the function using `from skidy import rpy_to_matrix`.\n\n7. Use quaternion [w,x,y,z] to define rotation:\n\n ```python\n body_ref_config.append(\n transformation_matrix(\n r=quaternion_to_matrix([1,0,0,0]),\n t=sympy.Matrix([0,0,L1])\n )\n )\n ```\n\n Note that you have to import the function using `from skidy import quaternion_to_matrix`.\n\n---\n\n```python\n# end-effector configuration w.r.t. last link body fixed frame in the chain (4x4 SE3 Pose (sympy.Matrix))\nee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])\n```\n\nEnd-effector representation w.r.t. last link body frame in the chain as SE(3) transformation matrix. Here you have the same syntax options as for the body reference configuration.\nFor robots with more than one end-effector you can use a list of transforms instead. This requires a list of parent joint indices for the parameter `ee_parent`.\n\n---\n\n```python\n# mass_inertia parameters (6x6 sympy.Matrix per link)\nMb = []\nMb.append(mass_matrix_mixed_data(m1, inertia_matrix(Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1), sympy.Matrix([0,0,0]), frame='body'))\nMb.append(mass_matrix_mixed_data(m2, inertia_matrix(Ixx2,Ixy2,Ixz2,Iyy2,Iyz2,Izz2), sympy.Matrix([0,0,0]), frame='body'))\n```\n\nMass-inertia matrices of all links. The mass inertia matrix has to be w.r.t. the body-fixed frame. But with the argument `frame = 'com'` you can define the inertia matrix w.r.t. the center of mass frame instead. In this case, there is another argument `R` in the function `mass_matrix_mixed_data` which defines the rotation matrix of the COM frame to the body-fixed frame.\n\nFor the definition you have the following syntax options:\n\n1. Write down whole matrix:\n\n ```python\n Mb.append(\n sympy.Matrix([[ Ixx1, Ixy1, Ixz1, 0, -cz1*m1, cy1*m1],\n [ Ixy1, Iyy1, Iyz1, cz1*m1, 0, -cx1*m1],\n [ Ixz1, Iyz1, Izz1, -cy1*m1, cx1*m1, 0],\n [ 0, cz1*m1, -cy1*m1, m1, 0, 0],\n [-cz1*m1, 0, cx1*m1, 0, m1, 0],\n [ cy1*m1, -cx1*m1, 0, 0, 0, m1]])\n )\n ```\n\n2. Define mass, inertia matrix and center of mass separately:\n\n ```python\n Mb.append(\n mass_matrix_mixed_data(\n m1,\n sympy.Matrix([[Ixx1,Ixy1,Ixz1],\n [Ixy1,Iyy1,Iyz1],\n [Ixz1,Iyz1,Izz1]]),\n sympy.Matrix([cx1,cy1,cz1])\n )\n )\n ```\n\n3. Only define the 6 independent inertia parameters:\n\n ```python\n Mb.append(\n mass_matrix_mixed_data(\n m1,\n inertia_matrix(Ixx1,Ixy1,Ixz1,Iyy1,Iyz1,Izz1),\n sympy.Matrix([cx1,cy1,cz1])\n )\n )\n ```\n\n4. Automatically generate symbols in inertia matrix:\n\n ```python\n Mb.append(\n mass_matrix_mixed_data(\n m1,\n symbolic_inertia_matrix(index=1, pointmass=False),\n sympy.Matrix([cx1,cy1,cz1])\n )\n )\n ```\n\n where `symbolic_inertia_matrix(index=1, pointmass=False)` auto generates the variables `Ixx1`, `Ixy1`, etc. and creates a `sympy.Matrix` from it.\n With the parameter `pointmass=True` the resulting inertia matrix looks like this instead:\n\n ```python\n sympy.Matrix([[I1, 0, 0],\n [ 0,I1, 0],\n [ 0, 0,I1]])\n ```\n\n Note that you have to import the function using `from skidy import symbolic_inertia_matrix`.\n\n---\n\n```python\nq, qd, q2d = generalized_vectors(len(body_ref_config), startindex=1)\n```\n\nGenerate the generalized vectors (joint positions `q`, joint velocities `qd` and joint accelerations `q2d`). The symbols are auto generated starting at index `startindex`. The degrees of freedom in this case are taken from the length of `body_ref_config`.\n\nThe equivalent code would be:\n\n```python\nq1, q2 = sympy.symbols(\"q1 d2\", real=True, constant=False)\ndq1, dq2 = sympy.symbols(\"dq1 dd2\", real=True, constant=False)\nddq1, ddq2 = sympy.symbols(\"ddq1 ddq2\", real=True, constant=False)\nq = sympy.Matrix([q1,q2])\nqd = sympy.Matrix([dq1,dq2])\nq2d = sympy.Matrix([ddq1,ddq2])\n```\n\nTo generate jerk and jounce as well, use:\n\n```python\nq, qd, q2d, q3d, q4d = generalized_vectors(len(body_ref_config), startindex=1, derivatives=True)\n```\n\n---\n\n```python\nWEE = sympy.zeros(6,1)\n```\n\nDefine the time varying wrench on the end-effector link. Should be a (6,1) sympy.Matrix with symbolic variables, e.g.: `sympy.Matrix([Mx,My,Mz,Fx,Fy,Fz])`.\n\nOptionally, you can define the time derivatives `WDEE` and `W2DEE` here too. E.g.:\n\n```python\nWEE = sympy.Matrix([Mx,My,Mz,Fx,Fy,Fz])\nWDEE = sympy.Matrix([dMx,dMy,dMz,dFx,dFy,dFz])\nW2DEE = sympy.Matrix([ddMx,ddMy,ddMz,ddFx,ddFy,ddFz])\n```\n\nIf your robot has more than one end-effector, you have to use one external wrench per end-effector in a list, e.g.:\n\n```python\nWEE = [sympy.Matrix([Mx1,My1,Mz1,Fx1,Fy1,Fz1]),\n sympy.Matrix([Mx2,My2,Mz2,Fx2,Fy2,Fz2])]\n```\n\n---\n\n```python\nskd = SymbolicKinDyn(gravity_vector=gravity,\n ee=ee,\n body_ref_config=body_ref_config,\n joint_screw_coord=joint_screw_coord,\n config_representation=representation,\n Mb=Mb,\n parent=parent,\n child=child,\n support=support,\n )\n```\n\nInitialize class with all defined parameters.\n\n---\n\n```python\n# run Calculations\nskd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)\nskd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)\n```\n\nGenerate forward kinematics and inverse dynamics equations. Both functions share the following arguments:\n\n- simplify: generated expressions are simplified. Note that the simplification takes a lot of time for robots with more than 2 revolute joints in a chain.\n- cse: Use common subexpression elimination to shorten equations. Note that the equations are not human-readable afterwards.\n- parallel: use parallel computation.\n\n`skd.closed_form_kinematics_body_fixed` generates the following equations and saves them as class parameters:\n\n- body_acceleration\n- body_acceleration_ee\n- body_jacobian_matrix\n- body_jacobian_matrix_dot\n- body_jacobian_matrix_ee\n- body_jacobian_matrix_ee_dot\n- body_twist_ee\n- forward_kinematics\n- hybrid_acceleration\n- hybrid_acceleration_ee\n- hybrid_jacobian_matrix\n- hybrid_jacobian_matrix_dot\n- hybrid_jacobian_matrix_ee\n- hybrid_jacobian_matrix_ee_dot\n- hybrid_twist_ee\n\nand `skd.closed_form_inv_dyn_body_fixed` generates the following equations and saves them as class parameters:\n\n- coriolis_centrifugal_matrix\n- generalized_mass_inertia_matrix\n- gravity_vector\n- inverse_dynamics\n\nwith provided generalized jerk and jounce vectors, `skd.closed_form_inv_dyn_body_fixed` additionally generates the following derivatives of the equation of motion:\n\n- coriolis_centrifugal_matrix_dot\n- generalized_mass_inertia_matrix_dot\n- gravity_vector_dot\n- inverse_dynamics_dot\n- coriolis_centrifugal_matrix_ddot\n- generalized_mass_inertia_matrix_ddot\n- gravity_vector_ddot\n- inverse_dynamics_ddot\n\n`skd.closed_form_inv_dyn_body_fixed` takes the wrench `WEE` (6x1 sympy.Matrix) (tau,F) on the end-effector link and its time derivatives `WDEE` and `W2DEE` as optional additional arguments.\n\nTo calculate the 1st and 2nd order time derivative of the EOM use e.g.:\n\n```python\nskd.closed_form_inv_dyn_body_fixed(q, qd, q2d, q3d, q4d, WEE, WDEE, W2DEE, simplify=True)\n```\n\n---\n\n```python\n# Generate Code\nskd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,\n folder=\"./generated_code\", use_global_vars=True,\n name=\"plant\", project=\"Project\")\n```\n\nGenerate Python, Matlab, C++, C (C99), Julia, Cython and/or LaTeX code from the generated equations.\nNote that this can take some time, especially for non-simplified equations and complex robots.\n\n### 2.3. URDF\n\nThere are two ways to use URDF files. \nFirst, you can convert your URDF file to YAML or JSON using:\n\n```bash\nskidy --convert my_robot.urdf my_robot.yaml\n```\n\nHere, you can add end-effector transforms, and modify whatever you want.\n\nSecond, you can load your URDF using python. There is a function to generate a template python file, which loads your URDF. In the python file it is necessary to define:\n\n1. the URDF path\n2. the gravity vector\n3. end-effector configuration w.r.t. last link body fixed frame in the chain\n\nTo generate the python template file use:\n\n```bash\npython -m skidy --template --urdf my_urdf_template.py\n```\n\nor the python function `skidy.generate_template_python(path, urdf=True)`.\n\nThis generates the following output:\n\n```python\nfrom skidy import (SymbolicKinDyn,\n transformation_matrix,\n SO3Exp,\n generalized_vectors)\nfrom skidy.symbols import g, pi\nimport sympy\n\n# Define symbols:\nlee = sympy.symbols('lee', real=True, const=True)\n\nurdfpath = '/path/to/robot.urdf' # TODO: change me!\n\n# gravity vector\ngravity = sympy.Matrix([0,0,g])\n\n# end-effector configuration w.r.t. last link body fixed frame in the chain\nee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])\n\nskd = SymbolicKinDyn(gravity_vector=gravity,\n ee=ee,\n )\n\nskd.load_from_urdf(path = urdfpath,\n symbolic=False, \n cse=False, \n simplify_numbers=True, \n tolerance=0.0001, \n max_denominator=8, \n )\n\nq, qd, q2d = generalized_vectors(len(skd.body_ref_config), startindex=1)\nWEE = sympy.zeros(6,1)\n\n# run Calculations\nskd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)\nskd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)\n\n# Generate Code\nskd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,\n folder=\"./generated_code\", use_global_vars=True,\n name=\"plant\", project=\"Project\")\n```\n\nThe code explained:\n\n```python\nfrom skidy import (SymbolicKinDyn,\n transformation_matrix,\n SO3Exp,\n generalized_vectors)\n```\n\nThe class `SymbolicKinDyn` is the main object for calculating the kinematic and dynamic equations of your robot and generate the code.\nAdditionally, we import several helper functions for defining the matrices which are useful for the robot definition:\n\n- `transformation_matrix`: Create SE(3) transformation matrix from SO(3) rotation and translation vector.\n- `SO3Exp`: Exponential mapping of SO(3) to generate rotation matrix from rotation angle and rotation axis.\n- `generalized_vectors`: generate symbolic generalized vectors q, qd and q2d of predefined length n.\n\n```python\nfrom skidy.symbols import g, pi\n```\n\nThe package `skidy.symbols` includes the most common used symbolic variables, which can be used for defining your robot.\n\n```python\nimport sympy\n```\n\nThe whole library used sympy objects for all symbolic equations etc. Hence, we need `sympy` to create additional symbolic variables and matrices later.\n\n---\n\n```python\n# Define symbols:\nlee = sympy.symbols('lee', real=True, const=True)\n```\n\nCreate symbolic variables which can be used in the equations for the robot definition later. The most common symbols are also already present in the `skidy.symbols` package and may be imported from there instead.\n\n---\n\n```python\nurdfpath = '/path/to/robot.urdf' # TODO: change me!\n```\n\nEnter the path to your URDF file here.\n\n---\n\n```python\n# gravity vector\ngravity = sympy.Matrix([0,0,g])\n```\n\nGravity vector as `sympy.Matrix`. Note that we can use symbolic variables here.\n\n---\n\n```python\n# end-effector configuration w.r.t. last link body fixed frame in the chain\nee = transformation_matrix(r=SO3Exp(axis=[0,0,1],angle=0),t=[0,0,0])\n```\n\nEnd-effector representation w.r.t. last link body frame in the chain as SE(3) transformation matrix. Look up the chapter [Python](#22-python) for all available syntax options.\n\n---\n\n```python\nskd = SymbolicKinDyn(gravity_vector=gravity,\n ee=ee,\n )\n```\n\nInitialize class with the two defined parameters.\n\n---\n\n```python\nskd.load_from_urdf(path = urdfpath,\n symbolic=False, \n cse=False, \n simplify_numbers=True, \n tolerance=0.0001, \n max_denominator=8, \n )\n```\n\nLoad the URDF file. Here you can specify the following options:\n\n1. `symbolic`: symbolify values in urdf file (bool).\n2. `cse`: use common subexpression elimination to shorten equations (bool).\n3. `simplify_numbers`: round numbers if close to common fractions like 1/2 etc and replace eg 3.1416 by pi (bool).\n4. `tolerance`: tolerance for simplify numbers.\n5. `max_denominator`: define max denominator for simplify numbers to avoid simplification to something like 13/153. Use 0 to deactivate.\n\n---\n\n```python\nq, qd, q2d = generalized_vectors(len(skd.body_ref_config), startindex=1)\n```\n\nGenerate the generalized vectors (joint positions `q`, joint velocities `qd` and joint accelerations `q2d`). The symbols are auto generated starting at index `startindex`. The degrees of freedom in this case are taken from the length of the parameter `skd.body_ref_config`, which was generated by the function `load_from_urdf`.\n\n---\n\n```python\nWEE = sympy.zeros(6,1)\n```\n\nDefine the time varying wrench on the end-effector link. Should be a (6,1) sympy.Matrix with symbolic variables.\n\n---\n\n```python\n# run Calculations\nskd.closed_form_kinematics_body_fixed(q, qd, q2d, simplify=True)\nskd.closed_form_inv_dyn_body_fixed(q, qd, q2d, WEE=WEE, simplify=True)\n```\n\nGenerate forward kinematics and inverse dynamics equations. See chapter [Python](#22-python) for more information.\n\n---\n\n```python\n# Generate Code\nskd.generate_code(python=True, cpp=False, C=False, Matlab=False, latex=False,\n folder=\"./generated_code\", use_global_vars=True,\n name=\"plant\", project=\"Project\")\n```\n\nGenerate Python, Matlab, C++, C (C99), Julia, Cython and/or LaTeX code from the generated equations.\nNote that this can take time, especially for non-simplified equations and complex robots.\n\n## 3. Unit testing\n\nTo run the unit tests use:\n\n```bash\npython3 ./test/unit_testing.py\n```\n\n## 4. Benchmarking\n\nFor benchmarking the project the script `benchmarking/benchmarking.py` was used. This script loads 4 robots with increasing complexity (1 to 4 revolute joint in a chain with planar task space) and takes the execution time of the functions `closed_form_kinematics_body_fixed()`, `closed_form_inv_dyn_body_fixed()` and `generate_code()`. Additionally, the arguments `parallel`, `simplify` and `cse` have been altered.\nThe results are shown in the following table:\n\narguments | parallel | serial\n:---------|:----------:|:-----------:\nsimplify|![1 dof: 0.65 s; 2 dof: 2.14 s; 3 dof: 8.77 s; 4 dof: 44.04 s](/benchmarking/parallel_with_simplification_without_cse.png) | ![1 dof: 0.16 s; 2 dof: 2.38 s; 3 dof: 13.06 s; 4 dof: 60.68 s](/benchmarking/serial_with_simplification_without_cse.png)\nsimplify + cse|![1 dof: 0.56 s; 2 dof: 2.22 s; 3 dof: 12.86 s; 4 dof: 84.28 s](/benchmarking/parallel_with_simplification_with_cse.png) |![1 dof: 0.22 s; 2 dof: 2.78 s; 3 dof: 18.49 s; 4 dof: 113.30 s](/benchmarking/serial_with_simplification_with_cse.png)\nno simplify|![1 dof: 0.74 s; 2 dof: 2.65 s; 3 dof: 11.29 s; 4 dof: 47.14 s](/benchmarking/parallel_without_simplification_without_cse.png) | ![1 dof: 0.08 s; 2 dof: 1.57 s; 3 dof: 9.51 s; 4 dof: 44.57 s](/benchmarking/serial_without_simplification_without_cse.png)\nno simplify + cse|![1 dof: 0.80 s; 2 dof: 4.50 s; 3 dof: 29.13 s; 4 dof: 161.01 s](/benchmarking/parallel_without_simplification_with_cse.png) |![1 dof: 0.08 s; 2 dof: 4.49 s; 3 dof: 37.74 s; 4 dof: 201.11 s](/benchmarking/serial_without_simplification_with_cse.png)\n\n## 5. License\n\nThis work has been released under the BSD 3-Clause License. Details and terms of use are specified in the LICENSE file within this repository. Note that we do not publish third-party software, hence software packages from other developers are released under their very own terms and conditions. If you install third-party software packages along with this repo ensure that you follow each individual license agreement.\n",
"bugtrack_url": null,
"license": "BSD 3-Clause License Copyright (c) 2023, DFKI RIC Underactuated Robotics Lab All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"summary": "Symbolic kinematics and dynamics model generation using Equations of Motion in closed form.",
"version": "0.0.1",
"project_urls": {
"Repository": "https://github.com/dfki-ric-underactuated-lab/skidy"
},
"split_keywords": [
"symbolic",
" code generation",
" robot",
" kinematics",
" dynamics",
" equation of motion"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "fa84126aa098a8cd906e87c74de3e91aab9992567cf8dac7b17e9d9db53d1183",
"md5": "fe082e976e8feee0f11097e1a5f2a9ba",
"sha256": "c4fab2d5945039220be89c7402e7e8042b58d7c939450ffe7615c92754e6a7dc"
},
"downloads": -1,
"filename": "skidy-0.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fe082e976e8feee0f11097e1a5f2a9ba",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 59923,
"upload_time": "2024-06-07T15:40:13",
"upload_time_iso_8601": "2024-06-07T15:40:13.818059Z",
"url": "https://files.pythonhosted.org/packages/fa/84/126aa098a8cd906e87c74de3e91aab9992567cf8dac7b17e9d9db53d1183/skidy-0.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a68d4ec961976eebe2e120844b932c5880f05071997491605091c10fcc6d8c51",
"md5": "c7a99e17a675c4416690613e541ea753",
"sha256": "38765b30008c85a0df0c68e2030f61c0e83d205fd2755756e005305b487c3939"
},
"downloads": -1,
"filename": "skidy-0.0.1.tar.gz",
"has_sig": false,
"md5_digest": "c7a99e17a675c4416690613e541ea753",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 2501362,
"upload_time": "2024-06-07T15:40:20",
"upload_time_iso_8601": "2024-06-07T15:40:20.984324Z",
"url": "https://files.pythonhosted.org/packages/a6/8d/4ec961976eebe2e120844b932c5880f05071997491605091c10fcc6d8c51/skidy-0.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-07 15:40:20",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dfki-ric-underactuated-lab",
"github_project": "skidy",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "skidy"
}