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

Added usage section in ReadMe and basic example for simulator #314

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

CarlottaSartore
Copy link
Contributor

@CarlottaSartore CarlottaSartore commented Dec 9, 2024

This PR:

  • Adds the usage section in the main readme
  • Simplifies the main readme entry in favour of the usage section
  • Added an example that shows the basic usage of jaxsim as simulator
  • Renamed the example_physics_engine in example_physics_engine_advanced

📚 Documentation preview 📚: https://jaxsim--314.org.readthedocs.build//314/

@CarlottaSartore
Copy link
Contributor Author

When loading the icub model, I encountered in the following warning:

jaxsim[141861] DEBUG Found model 'iCub' in SDF resource
/home/carlotta/miniforge3/envs/jaxsim/lib/python3.13/site-packages/rod/sdf/model.py:166: UserWarning: Gimbal lock detected. Setting third angle to zero since it is not possible to uniquely determine all angles.
  switch_frame_convention(

I am having issues in backtracking the source, @flferretti do you perhaps know which part of rod raises this warning ? thanks !

@flferretti
Copy link
Collaborator

flferretti commented Dec 9, 2024

When loading the icub model, I encountered in the following warning:

jaxsim[141861] DEBUG Found model 'iCub' in SDF resource
/home/carlotta/miniforge3/envs/jaxsim/lib/python3.13/site-packages/rod/sdf/model.py:166: UserWarning: Gimbal lock detected. Setting third angle to zero since it is not possible to uniquely determine all angles.
  switch_frame_convention(

I am having issues in backtracking the source, @flferretti do you perhaps know which part of rod raises this warning ? thanks !

Hi @CarlottaSartore, thanks for the contribution! The warning is not raised from rod, but rather from scipy.spatial.transform.Rotation in https://github.com/ami-iit/rod/blob/c67a77da59bfe6d5d7df8abf2c4878b78877079a/src/rod/sdf/common.py#L101

@traversaro
Copy link
Contributor

Related: scipy/scipy#19512 and https://mail.python.org/archives/list/scipy-dev@python.org/message/LXT4ZGNP4OVAARSKEOM5AGQCANH6ZV5K/ . I totally agree that this should not be a warning, or there should be a way to provide this conversion without triggering a warning, but unfortunately there was not a follow up in the mailing list.

@flferretti
Copy link
Collaborator

Early comment: can we use robot_description.icub_description instead of adding an additional dependency?

- import icub_models
- model_path = icub_models.get_model_file("iCubGazeboV2_5")

+ import robot_description.icub_description
+ model_path = robot_description.icub_description.URDF_PATH

@traversaro
Copy link
Contributor

traversaro commented Dec 9, 2024

Early comment: can we use robot_description.icub_description instead of adding an additional dependency?

- import icub_models
- model_path = icub_models.get_model_file("iCubGazeboV2_5")

+ import robot_description.icub_description
+ model_path = robot_description.icub_description.URDF_PATH

I strongly prefer use any other robot in that case. There is nothing ensuring that the robot_description.icub_description model is updated or it even matches the real iCub robot, so I would prefer to avoid IIT users getting used to get IIT's model from robot_description. Just to give you an example, the model used is the iCubGazeboV2_5, see https://github.com/robot-descriptions/robot_descriptions.py/blob/b3cf619db307d98aad0f02f79e4818bcc89fe55d/robot_descriptions/icub_description.py#L22 . The Gazebo in the name indicates the the inertia used in the model are completely wrong for compatibility with the default settings of Gazebo Classic with the ODE physics engines (see robotology/icub-models#33 for more details). Most users are (unfortunately) already not aware of this, and it would be even more confusing if there is no Gazebo in the name, as it happens if one calls robot_description.icub_description.

@flferretti
Copy link
Collaborator

Early comment: can we use robot_description.icub_description instead of adding an additional dependency?

- import icub_models
- model_path = icub_models.get_model_file("iCubGazeboV2_5")

+ import robot_description.icub_description
+ model_path = robot_description.icub_description.URDF_PATH

I strongly prefer use any other robot in that case. There is nothing ensuring that the robot_description.icub_description model is updated or it even matches the real iCub robot, so I would prefer to avoid IIT users getting used to get IIT's model from robot_description.

I see, thanks for the explanation! I was wondering though if we need the actual IIT model since this is just an example

@traversaro
Copy link
Contributor

I was wondering though if we need the actual IIT model since this is just an example

The problem is that lab users starts from examples, that they copy and they start using assuming that that is the "best way" to do, especially if the info come from an official repo of the lab. I really do not look forward to having to explain each new student in the lab why they should not be using robot_description.icub_description if that is what is used in the jaxsim README.

@traversaro
Copy link
Contributor

(I also edited my previous comments for more details).

@CarlottaSartore
Copy link
Contributor Author

CarlottaSartore commented Dec 9, 2024

I see, thanks for the explanation! I was wondering though if we need the actual IIT model since this is just an example

I opted to use the icub models since it is the model of a humanoid robots, and I wanted to use a robot since this is the main goal of the simulator, and the icub is the model I am the more confident with.
A different solution could be as we do in the example, thus downloading ergoCub from the ergoCub software repository, this would require more lines but we will still use a humanoid robot without adding any dependencies (even if I liked the simplicity of the include, i took inspiration from https://github.com/ami-iit/adam)

@traversaro
Copy link
Contributor

If it helps reach consensus, feel free to use robot_description with iCub, even if I am not super-enthusiastic about it.

A different solution could be as we do in the example, thus downloading ergoCub from the ergoCub software repository,

The tricky aspect there is that you also need to download the meshes if you want the visualizer to work.

@flferretti
Copy link
Collaborator

Thanks @traversaro, I understood the problem. It seems to me that the available options are:

  1. Use a different robot, e.g. stickbot --> not the most exciting model, but it does its job
  2. Still use iCub/ErgoCub from the official repos --> definitely more interesting, but requires more lines of code and downloading the meshes from source for visualization

Copy link
Collaborator

@flferretti flferretti left a comment

Choose a reason for hiding this comment

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

Thanks a lot for working on this @CarlottaSartore! I added some comments

examples/jaxsim_as_physics_engine.ipynb Outdated Show resolved Hide resolved
examples/jaxsim_as_physics_engine_advanced.ipynb Outdated Show resolved Hide resolved
examples/jaxsim_as_physics_engine.ipynb Outdated Show resolved Hide resolved
examples/jaxsim_as_physics_engine.ipynb Outdated Show resolved Hide resolved
examples/jaxsim_as_physics_engine.ipynb Show resolved Hide resolved
examples/jaxsim_as_physics_engine.ipynb Outdated Show resolved Hide resolved
Comment on lines +243 to +245
"data_batch_t0 = jax.vmap(\n",
" lambda key: data_zero.reset_base_position(base_position=jnp.array([0.0, 0.0, 1.0]))\n",
")(jnp.vstack(subkeys))\n",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here we could think of using js.data.random_model_data instead, since key is not really used here, or generating a vector of equal base positions like:

data_batch_v0 = jax.vmap(
        data_zero.reset_base_position
    )(base_position=jnp.array([0.0, 0.0, 1.0] * batch_size))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem with random data is that is is difficult to ensure that all the simulations succed. this is the reason why I decided to set all of them to 1.0 to show how to handle batched initial data while still being sure that all simulations somehow work. Changing the initial base position leads the simulator to encounter "not normalize quaternion" error. I would prefer to leave it like this until we solve such an error.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, I didn't mean to use random data. My comment was about which argument to pass to vmap, the result will be the same

Comment on lines +30 to +63
```python
import jax
import jax.numpy as jnp
import jaxsim.api as js
import icub_models
import pathlib

# Load the iCub model
model_path = icub_models.get_model_file("iCubGazeboV2_5")
joints = ('torso_pitch', 'torso_roll', 'torso_yaw', 'l_shoulder_pitch',
'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow', 'r_shoulder_pitch',
'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow', 'l_hip_pitch',
'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch',
'r_ankle_roll')
ndof = len(joints)

# Build and reduce the model
model_description = pathlib.Path(model_path)
full_model = js.model.JaxSimModel.build_from_model_description(
model_description=model_description, time_step=0.0001, is_urdf=True
)
model = js.model.reduce(model=full_model, considered_joints=joints)

# Initialize data and simulation
data = js.data.JaxSimModelData.zero(model=model).reset_base_position(
base_position=jnp.array([0.0, 0.0, 1.0])
)
T = jnp.arange(start=0, stop=1.0, step=model.time_step)
tau = jnp.zeros(ndof)

# Simulate
for t in T:
data, _ = js.model.step(model=model, data=data, link_forces=None, joint_force_references=tau)
Copy link
Collaborator

Choose a reason for hiding this comment

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

What do you think about compressing the example code in a collapsible section?
I also removed an unused import and reformatted

Suggested change
```python
import jax
import jax.numpy as jnp
import jaxsim.api as js
import icub_models
import pathlib
# Load the iCub model
model_path = icub_models.get_model_file("iCubGazeboV2_5")
joints = ('torso_pitch', 'torso_roll', 'torso_yaw', 'l_shoulder_pitch',
'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow', 'r_shoulder_pitch',
'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow', 'l_hip_pitch',
'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch',
'r_ankle_roll')
ndof = len(joints)
# Build and reduce the model
model_description = pathlib.Path(model_path)
full_model = js.model.JaxSimModel.build_from_model_description(
model_description=model_description, time_step=0.0001, is_urdf=True
)
model = js.model.reduce(model=full_model, considered_joints=joints)
# Initialize data and simulation
data = js.data.JaxSimModelData.zero(model=model).reset_base_position(
base_position=jnp.array([0.0, 0.0, 1.0])
)
T = jnp.arange(start=0, stop=1.0, step=model.time_step)
tau = jnp.zeros(ndof)
# Simulate
for t in T:
data, _ = js.model.step(model=model, data=data, link_forces=None, joint_force_references=tau)
<details>
<summary><b>Using JaxSim as a simulator</b></summary>
```python
import jax.numpy as jnp
import jaxsim.api as js
import icub_models
import pathlib
# Load the iCub model.
model_path = icub_models.get_model_file("iCubGazeboV2_5")
joints = ('torso_pitch', 'torso_roll', 'torso_yaw', 'l_shoulder_pitch',
'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow', 'r_shoulder_pitch',
'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow', 'l_hip_pitch',
'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch',
'r_ankle_roll')
# Build and reduce the model.
model_description = pathlib.Path(model_path)
full_model = js.model.JaxSimModel.build_from_model_description(
model_description=model_description, time_step=0.001, is_urdf=True
)
model = js.model.reduce(model=full_model, considered_joints=joints)
# Initialize data and simulation.
data = js.data.JaxSimModelData.zero(model=model).reset_base_position(
base_position=jnp.array([0.0, 0.0, 1.0])
)
T = jnp.arange(start=0, stop=1.0, step=model.time_step)
tau = jnp.zeros(model.dofs())
# Simulate.
for _t in T:
data, _ = js.model.step(
model=model, data=data, link_forces=None, joint_force_references=tau
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SInce the example are quite short I would prefer to have them directly in the readme, so that it is directly visible that it is possible to use jaxsim within few lines of code, what do you think ?

Comment on lines +68 to +107
``` python
import jax.numpy as jnp
import jaxsim.api as js
import icub_models
import pathlib

# Load the iCub model
model_path = icub_models.get_model_file("iCubGazeboV2_5")
joints = ('torso_pitch', 'torso_roll', 'torso_yaw', 'l_shoulder_pitch',
'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow', 'r_shoulder_pitch',
'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow', 'l_hip_pitch',
'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch',
'r_ankle_roll')

# Build and reduce the model
model_description = pathlib.Path(model_path)
full_model = js.model.JaxSimModel.build_from_model_description(
model_description=model_description, time_step=0.0001, is_urdf=True
)
model = js.model.reduce(model=full_model, considered_joints=joints)

# Initialize model data
data = js.data.JaxSimModelData.zero(model=model).reset_base_position(
base_position=jnp.array([0.0, 0.0, 1.0])
)

# Frame and dynamics computations
frame_index = js.frame.name_to_idx(model=model, frame_name="l_foot")
W_H_F = js.frame.transform(model=model, data=data, frame_index=frame_index) # Frame transformation
W_J_F = js.frame.jacobian(model=model, data=data, frame_index=frame_index) # Frame Jacobian

# Dynamics properties
M = js.model.free_floating_mass_matrix(model=model, data=data) # Mass matrix
h = js.model.free_floating_bias_forces(model=model, data=data) # Bias forces
g = js.model.free_floating_gravity_forces(model=model, data=data) # Gravity forces
C = js.model.free_floating_coriolis_matrix(model=model, data=data) # Coriolis matrix

# Print dynamics results
print(f"M: shape={M.shape}, h: shape={h.shape}, g: shape={g.shape}, C: shape={C.shape}")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Collapsible section as above. Reformatted and used f-string syntax to print the shapes

Suggested change
``` python
import jax.numpy as jnp
import jaxsim.api as js
import icub_models
import pathlib
# Load the iCub model
model_path = icub_models.get_model_file("iCubGazeboV2_5")
joints = ('torso_pitch', 'torso_roll', 'torso_yaw', 'l_shoulder_pitch',
'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow', 'r_shoulder_pitch',
'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow', 'l_hip_pitch',
'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch',
'r_ankle_roll')
# Build and reduce the model
model_description = pathlib.Path(model_path)
full_model = js.model.JaxSimModel.build_from_model_description(
model_description=model_description, time_step=0.0001, is_urdf=True
)
model = js.model.reduce(model=full_model, considered_joints=joints)
# Initialize model data
data = js.data.JaxSimModelData.zero(model=model).reset_base_position(
base_position=jnp.array([0.0, 0.0, 1.0])
)
# Frame and dynamics computations
frame_index = js.frame.name_to_idx(model=model, frame_name="l_foot")
W_H_F = js.frame.transform(model=model, data=data, frame_index=frame_index) # Frame transformation
W_J_F = js.frame.jacobian(model=model, data=data, frame_index=frame_index) # Frame Jacobian
# Dynamics properties
M = js.model.free_floating_mass_matrix(model=model, data=data) # Mass matrix
h = js.model.free_floating_bias_forces(model=model, data=data) # Bias forces
g = js.model.free_floating_gravity_forces(model=model, data=data) # Gravity forces
C = js.model.free_floating_coriolis_matrix(model=model, data=data) # Coriolis matrix
# Print dynamics results
print(f"M: shape={M.shape}, h: shape={h.shape}, g: shape={g.shape}, C: shape={C.shape}")
<details>
<summary><b>Using JaxSim as a multibody dynamics library</b></summary>
``` python
import jax.numpy as jnp
import jaxsim.api as js
import icub_models
import pathlib
# Load the iCub model.
model_path = icub_models.get_model_file("iCubGazeboV2_5")
joints = ('torso_pitch', 'torso_roll', 'torso_yaw', 'l_shoulder_pitch',
'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow', 'r_shoulder_pitch',
'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow', 'l_hip_pitch',
'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch',
'r_ankle_roll')
# Build and reduce the model.
model_description = pathlib.Path(model_path)
full_model = js.model.JaxSimModel.build_from_model_description(
model_description=model_description, time_step=0.0001, is_urdf=True
)
model = js.model.reduce(model=full_model, considered_joints=joints)
# Initialize model data.
data = js.data.JaxSimModelData.zero(model=model).reset_base_position(
base_position=jnp.array([0.0, 0.0, 1.0])
)
# Frame and dynamics computations.
frame_index = js.frame.name_to_idx(model=model, frame_name="l_foot")
W_H_F = js.frame.transform(model=model, data=data, frame_index=frame_index) # Frame transformation
W_J_F = js.frame.jacobian(model=model, data=data, frame_index=frame_index) # Frame Jacobian
# Dynamics properties.
M = js.model.free_floating_mass_matrix(model=model, data=data) # Mass matrix
h = js.model.free_floating_bias_forces(model=model, data=data) # Bias forces
g = js.model.free_floating_gravity_forces(model=model, data=data) # Gravity forces
C = js.model.free_floating_coriolis_matrix(model=model, data=data) # Coriolis matrix
# Print dynamics results.
print(f"{M.shape=} \t {h.shape=} \t {g.shape=} \t {C.shape=}")
```

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same comments above about the collapse

examples/jaxsim_as_physics_engine_advanced.ipynb Outdated Show resolved Hide resolved
@traversaro
Copy link
Contributor

Use a different robot, e.g. stickbot --> not the most exciting model, but it does its job

Stickbot is nice as it is a self-contained URDF. I think there is also some value in showing to users how to load a physical URDF. My assumption is that most users will want to use the simulator with their own model for which they either have the absolute file path or the relocatable package://package/name/of/the/mode.urdf . In that case, use any Python helper such as icub-models or robot_descriptions obfuscate a bit that workflow for users.

CarlottaSartore and others added 6 commits December 16, 2024 15:51
Co-authored-by: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
Co-authored-by: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
Co-authored-by: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
Co-authored-by: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
Co-authored-by: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
Co-authored-by: Filippo Luca Ferretti <102977828+flferretti@users.noreply.github.com>
@CarlottaSartore
Copy link
Contributor Author

Concerning the usage of model descriptor, I would stay with the icub models dependency. even though it introduces a new dependencies it shows how:

  • simply load a humanoid robot
  • simply load ergoCub robot
  • does not lead to strange models (as ergoCUb gazebo)
  • is a real humanoid robots with meshes (unlike stickbot )

Copy link
Member

@xela-95 xela-95 left a comment

Choose a reason for hiding this comment

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

Thanks @CarlottaSartore ! It LGTM! I will update the title of the examples/jaxsim_as_physics_engine_advanced.ipynb notebook since up to now it's the same of the non advanced one :)

README.md Outdated Show resolved Hide resolved
CarlottaSartore and others added 3 commits December 17, 2024 09:35
Co-authored-by: Alessandro Croci <57228872+xela-95@users.noreply.github.com>
@CarlottaSartore
Copy link
Contributor Author

Thanks @CarlottaSartore ! It LGTM! I will update the title of the examples/jaxsim_as_physics_engine_advanced.ipynb notebook since up to now it's the same of the non advanced one :)

Thanks @xela-95 you're right! I have also updated the example readme with the new example !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants