Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parametric improvements #72

Merged
merged 32 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
b231b38
Added PyCharm conf files to gitignore
S-Dafarra Feb 29, 2024
71f3732
Workaround issue while loading stickBot model
S-Dafarra Feb 29, 2024
48ae25b
Fixed order of arguments in get_total_mass and removed leftover forwa…
S-Dafarra Feb 29, 2024
d3c8b12
Fixed typos
S-Dafarra Mar 5, 2024
d87d797
Updating the visual info after the parametric modifications
S-Dafarra Mar 5, 2024
430ac4d
Initial draft to convert an ADAM model to an iDynTree model
S-Dafarra Mar 5, 2024
bc5f5ee
Completed draft of conversion to iDynTree model
S-Dafarra Mar 6, 2024
515bc72
Added initial test for conversion to iDynTree
S-Dafarra Mar 6, 2024
7a92d12
Set visuals
S-Dafarra Mar 7, 2024
dc73658
Added handling of frames in the conversion to iDynTree
S-Dafarra Mar 7, 2024
9ddb136
Removed useless aliases in iDynTree conversion
S-Dafarra Mar 7, 2024
72e7cb6
Adding check when setting visuals to avoid CI failures
S-Dafarra Mar 7, 2024
af2d1c4
Removed useless origin field in Link and added Pose for type hinting
S-Dafarra Mar 7, 2024
603d997
Removed usages of link origin.
S-Dafarra Mar 7, 2024
8638677
Added Inertia type hint and fixed some warnings
S-Dafarra Mar 7, 2024
55e857d
Using List in Pose type hint to avoid warnings about presence of [] o…
S-Dafarra Mar 8, 2024
df57464
Avoiding to use iyx in the inertia.
S-Dafarra Mar 8, 2024
a5e21bb
Added missing self in method definitions of SpatialMath
S-Dafarra Mar 8, 2024
fbeb8e4
Properly using Pose objects in parametric joint and links
S-Dafarra Mar 8, 2024
7076329
Added test for comparison from parametric model to iDynTree
S-Dafarra Mar 8, 2024
80f54c8
Added conversion to idyntree to __init__.py
S-Dafarra Mar 8, 2024
576584d
Properly setting the transform of visuals and set joint limits in the…
S-Dafarra Mar 8, 2024
4539ebd
Removed check of attribute availability in the conversion to idyntree
S-Dafarra Mar 8, 2024
e7ee427
Added Limits type hint and fixed other type warnings
S-Dafarra Mar 8, 2024
66f2273
Removed unused method with empty start string
S-Dafarra Mar 8, 2024
8e25f02
Return an error if the input link is not supported in the parametric …
S-Dafarra Mar 8, 2024
fa390b8
Some type hinting fix in tree.py
S-Dafarra Mar 8, 2024
fa984b1
Removed useless member in parametric link
S-Dafarra Mar 8, 2024
bfb9ffc
Added method to retrieve the original densities
S-Dafarra Mar 8, 2024
045922e
Use Union in Tree type hint to be compatible with python 3.8
S-Dafarra Mar 8, 2024
67a9e7d
Set minimum idyntree version in CI
S-Dafarra Mar 12, 2024
5d2252d
improved the documentation of the get_original_densities methods
S-Dafarra Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,6 @@ dmypy.json

# Cython debug symbols
cython_debug/

# Pycharm
.idea/*
4 changes: 2 additions & 2 deletions ci_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies:
- pytest
- pytest-repeat
- icub-models
- idyntree
- idyntree >=11.0.0
- gitpython
- jax
- pytorch
- pytorch
2 changes: 1 addition & 1 deletion ci_env_win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ dependencies:
- pytest
- pytest-repeat
- icub-models
- idyntree
- idyntree >=11.0.0
- gitpython
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ test =
icub-models
black
gitpython
conversions =
idyntree
all =
jax
jaxlib
Expand Down
14 changes: 7 additions & 7 deletions src/adam/core/spatial_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def factory(self) -> ArrayLikeFactory:
return self._factory

@abc.abstractmethod
def vertcat(x: npt.ArrayLike) -> npt.ArrayLike:
def vertcat(self, x: npt.ArrayLike) -> npt.ArrayLike:
"""
Args:
x (npt.ArrayLike): elements
Expand All @@ -118,7 +118,7 @@ def vertcat(x: npt.ArrayLike) -> npt.ArrayLike:
pass

@abc.abstractmethod
def horzcat(x: npt.ArrayLike) -> npt.ArrayLike:
def horzcat(self, x: npt.ArrayLike) -> npt.ArrayLike:
"""
Args:
x (npt.ArrayLike): elements
Expand All @@ -129,11 +129,11 @@ def horzcat(x: npt.ArrayLike) -> npt.ArrayLike:
pass

@abc.abstractmethod
def mtimes(x: npt.ArrayLike, y: npt.ArrayLike) -> npt.ArrayLike:
def mtimes(self, x: npt.ArrayLike, y: npt.ArrayLike) -> npt.ArrayLike:
pass

@abc.abstractmethod
def sin(x: npt.ArrayLike) -> npt.ArrayLike:
def sin(self, x: npt.ArrayLike) -> npt.ArrayLike:
"""
Args:
x (npt.ArrayLike): angle value
Expand All @@ -144,7 +144,7 @@ def sin(x: npt.ArrayLike) -> npt.ArrayLike:
pass

@abc.abstractmethod
def cos(x: npt.ArrayLike) -> npt.ArrayLike:
def cos(self, x: npt.ArrayLike) -> npt.ArrayLike:
"""
Args:
x (npt.ArrayLike): angle value
Expand All @@ -155,7 +155,7 @@ def cos(x: npt.ArrayLike) -> npt.ArrayLike:
pass

@abc.abstractmethod
def skew(x):
def skew(self, x):
pass

def R_from_axis_angle(self, axis: npt.ArrayLike, q: npt.ArrayLike) -> npt.ArrayLike:
Expand Down Expand Up @@ -417,7 +417,7 @@ def spatial_inertial_with_parameters(self, I, mass, c, rpy):
R_temp = self.R_from_RPY(rpy)
inertia_matrix = self.vertcat(
self.horzcat(I.ixx, I.ixy, I.ixz),
self.horzcat(I.iyx, I.iyy, I.iyz),
self.horzcat(I.ixy, I.iyy, I.iyz),
self.horzcat(I.ixz, I.iyz, I.izz),
)

Expand Down
47 changes: 37 additions & 10 deletions src/adam/model/abc_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@
from adam.core.spatial_math import SpatialMath


@dataclasses.dataclass
class Pose:
"""Pose class"""

xyz: List
rpy: List


@dataclasses.dataclass
class Inertia:
"""Inertia class"""

ixx: npt.ArrayLike
ixy: npt.ArrayLike
ixz: npt.ArrayLike
iyy: npt.ArrayLike
iyz: npt.ArrayLike
izz: npt.ArrayLike


@dataclasses.dataclass
class Limits:
"""Limits class"""

lower: npt.ArrayLike
upper: npt.ArrayLike
effort: npt.ArrayLike
velocity: npt.ArrayLike


@dataclasses.dataclass
class Joint(abc.ABC):
"""Base Joint class. You need to fill at least these fields"""
Expand All @@ -17,8 +47,8 @@ class Joint(abc.ABC):
child: str
type: str
axis: List
origin: List
limit: List
origin: Pose
limit: Limits
idx: int
"""
Abstract base class for all joints.
Expand Down Expand Up @@ -58,8 +88,8 @@ class Inertial:
"""Inertial description"""

mass: npt.ArrayLike
inertia = npt.ArrayLike
origin = npt.ArrayLike
inertia = Inertia
origin = Pose


@dataclasses.dataclass
Expand All @@ -71,24 +101,21 @@ class Link(abc.ABC):
visuals: List
inertial: Inertial
collisions: List
origin: List

@abc.abstractmethod
def spatial_inertia(self) -> npt.ArrayLike:
"""
Args:
link (Link): Link

Returns:
npt.ArrayLike: the 6x6 inertia matrix expressed at the origin of the link (with rotation)
npt.ArrayLike: the 6x6 inertia matrix expressed at
the origin of the link (with rotation)
"""
pass

@abc.abstractmethod
def homogeneous(self) -> npt.ArrayLike:
"""
Returns:
npt.ArrayLike: the homogeneus transform of the link
npt.ArrayLike: the homogeneous transform of the link
"""
pass

Expand Down
1 change: 1 addition & 0 deletions src/adam/model/conversions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .idyntree import to_idyntree_model
205 changes: 205 additions & 0 deletions src/adam/model/conversions/idyntree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import idyntree.bindings
import numpy as np
import urdf_parser_py.urdf
from typing import List


from adam.model.model import Model
from adam.model.abc_factories import Link, Joint


def to_idyntree_solid_shape(
visual: urdf_parser_py.urdf.Visual,
) -> idyntree.bindings.SolidShape:
"""
Args:
visual (urdf_parser_py.urdf.Visual): the visual to convert

Returns:
iDynTree.SolidShape: the iDynTree solid shape
"""
visual_position = idyntree.bindings.Position.FromPython(visual.origin.xyz)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vaguely recall that due to a change by @GiulioRomualdi it is not necessary anymore to call FromPython, but for sure he remember better.

visual_rotation = idyntree.bindings.Rotation.RPY(*visual.origin.rpy)
visual_transform = idyntree.bindings.Transform()
visual_transform.setRotation(visual_rotation)
visual_transform.setPosition(visual_position)
material = idyntree.bindings.Material(visual.material.name)
if visual.material.color is not None:
color = idyntree.bindings.Vector4()
color[0] = visual.material.color.rgba[0]
color[1] = visual.material.color.rgba[1]
color[2] = visual.material.color.rgba[2]
color[3] = visual.material.color.rgba[3]
material.setColor(color)
if isinstance(visual.geometry, urdf_parser_py.urdf.Box):
output = idyntree.bindings.Box()
output.setX(visual.geometry.size[0])
output.setY(visual.geometry.size[1])
output.setZ(visual.geometry.size[2])
output.setLink_H_geometry(visual_transform)
return output
if isinstance(visual.geometry, urdf_parser_py.urdf.Cylinder):
output = idyntree.bindings.Cylinder()
output.setRadius(visual.geometry.radius)
output.setLength(visual.geometry.length)
output.setLink_H_geometry(visual_transform)
return output
if isinstance(visual.geometry, urdf_parser_py.urdf.Sphere):
output = idyntree.bindings.Sphere()
output.setRadius(visual.geometry.radius)
output.setLink_H_geometry(visual_transform)
return output
if isinstance(visual.geometry, urdf_parser_py.urdf.Mesh):
output = idyntree.bindings.ExternalMesh()
output.setFilename(visual.geometry.filename)
output.setScale(visual.geometry.scale)
output.setLink_H_geometry(visual_transform)
return output

raise NotImplementedError(
f"The visual type {visual.geometry.__class__} is not supported"
)


def to_idyntree_link(
link: Link,
) -> [idyntree.bindings.Link, List[idyntree.bindings.SolidShape]]:
"""
Args:
link (Link): the link to convert

Returns:
A tuple containing the iDynTree link and the iDynTree solid shapes
"""
output = idyntree.bindings.Link()
input_inertia = link.inertial.inertia
inertia_matrix = np.array(
[
[input_inertia.ixx, input_inertia.ixy, input_inertia.ixz],
[input_inertia.ixy, input_inertia.iyy, input_inertia.iyz],
[input_inertia.ixz, input_inertia.iyz, input_inertia.izz],
]
)
inertia_rotation = idyntree.bindings.Rotation.RPY(*link.inertial.origin.rpy)
idyn_spatial_rotational_inertia = idyntree.bindings.RotationalInertia()
for i in range(3):
for j in range(3):
idyn_spatial_rotational_inertia.setVal(i, j, inertia_matrix[i, j])
Comment on lines +85 to +87
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure about this. Is it possible to use idyntree.fromPython?
Maybe it just works with some specific types.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try that, but unfortunately FromPython is defined byMatrix3x3 from which RotationalInertia inherits, and it is a static method, so it returns a Matrix3x3 instead of a RotationalInertia. In the end, this was the only method I managed to use reliably

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was imagining it :)

rotated_inertia = inertia_rotation * idyn_spatial_rotational_inertia
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice that you avoided this, it is a common bug in URDF parsers, see ros/robot_model#66 and ros/robot_model#117 .

idyn_spatial_inertia = idyntree.bindings.SpatialInertia()
com_position = idyntree.bindings.Position.FromPython(link.inertial.origin.xyz)
idyn_spatial_inertia.fromRotationalInertiaWrtCenterOfMass(
link.inertial.mass,
com_position,
rotated_inertia,
)
output.setInertia(idyn_spatial_inertia)

return output, [to_idyntree_solid_shape(v) for v in link.visuals]


def to_idyntree_joint(
joint: Joint, parent_index: int, child_index: int
) -> idyntree.bindings.IJoint:
"""
Args:
joint (Joint): the joint to convert
parent_index (int): the parent link index
child_index (int): the child link index
Returns:
iDynTree.bindings.IJoint: the iDynTree joint
"""

rest_position = idyntree.bindings.Position.FromPython(joint.origin.xyz)
rest_rotation = idyntree.bindings.Rotation.RPY(*joint.origin.rpy)
rest_transform = idyntree.bindings.Transform()
rest_transform.setRotation(rest_rotation)
rest_transform.setPosition(rest_position)

if joint.type == "fixed":
return idyntree.bindings.FixedJoint(parent_index, child_index, rest_transform)

direction = idyntree.bindings.Direction(*joint.axis)
origin = idyntree.bindings.Position.Zero()
axis = idyntree.bindings.Axis()
axis.setDirection(direction)
axis.setOrigin(origin)

if joint.type in ["revolute", "continuous"]:
output = idyntree.bindings.RevoluteJoint()
output.setAttachedLinks(parent_index, child_index)
output.setRestTransform(rest_transform)
output.setAxis(axis, child_index, parent_index)
if joint.limit is not None and joint.type == "revolute":
output.setPosLimits(0, joint.limit.lower, joint.limit.upper)
return output
if joint.type in ["prismatic"]:
output = idyntree.bindings.PrismaticJoint()
output.setAttachedLinks(parent_index, child_index)
output.setRestTransform(rest_transform)
output.setAxis(axis, child_index, parent_index)
if joint.limit is not None:
output.setPosLimits(0, joint.limit.lower, joint.limit.upper)
return output

NotImplementedError(f"The joint type {joint.type} is not supported")


def to_idyntree_model(model: Model) -> idyntree.bindings.Model:
"""
Args:
model (Model): the model to convert

Returns:
iDynTree.Model: the iDynTree model
"""

output = idyntree.bindings.Model()
output_visuals = []
links_map = {}

for node in model.tree:
link, visuals = to_idyntree_link(node.link)
link_index = output.addLink(node.name, link)
assert output.isValidLinkIndex(link_index)
assert link_index == len(output_visuals)
output_visuals.append(visuals)
links_map[node.name] = link_index

for i, visuals in enumerate(output_visuals):
output.visualSolidShapes().clearSingleLinkSolidShapes(i)
for visual in visuals:
output.visualSolidShapes().addSingleLinkSolidShape(i, visual)

for node in model.tree:
for j in node.arcs:
assert j.name not in model.frames
joint = to_idyntree_joint(j, links_map[j.parent], links_map[j.child])
joint_index = output.addJoint(j.name, joint)
assert output.isValidJointIndex(joint_index)

frames_list = [f + "_fixed_joint" for f in model.frames]
for name in model.joints:
if name in frames_list:
joint = model.joints[name]
frame_position = idyntree.bindings.Position.FromPython(joint.origin.xyz)
frame_transform = idyntree.bindings.Transform()
frame_transform.setRotation(
idyntree.bindings.Rotation.RPY(*joint.origin.rpy)
)
frame_transform.setPosition(frame_position)
frame_name = joint.name.replace("_fixed_joint", "")

ok = output.addAdditionalFrameToLink(
joint.parent,
frame_name,
frame_transform,
)
assert ok

model_reducer = idyntree.bindings.ModelLoader()
model_reducer.loadReducedModelFromFullModel(output, model.actuated_joints)
output_reduced = model_reducer.model().copy()

assert output_reduced.isValid()
return output_reduced
Loading
Loading