diff --git a/README.md b/README.md index 4454b239b..745ceba8d 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Its design facilitates research and accelerates prototyping in the intersection - Provides all the quantities included in the Euler-Poincarè formulation of the equations of motion. - Supports body-fixed, inertial-fixed, and mixed [velocity representations][notation]. - Exposes all the necessary quantities to develop controllers in centroidal coordinates. +- Supports running open-loop and full closed-loop control architectures on hardware accelerators. ### JaxSim for robot learning @@ -82,7 +83,7 @@ You can install the project using [`pypa/pip`][pip], preferably in a [virtual en pip install jaxsim ``` -Check [`setup.cfg`](setup.cfg) for the complete list of optional dependencies. +Check [`pyproject.toml`](pyproject.toml) for the complete list of optional dependencies. You can obtain a full installation using `jaxsim[all]`. If you need GPU support, follow the official [installation instructions][jax_gpu] of JAX. @@ -112,6 +113,71 @@ pip install --no-deps -e . [venv]: https://docs.python.org/3/tutorial/venv.html [jax_gpu]: https://github.com/google/jax/#installation +## Overview + +
+Structure of the Python package + +``` +# tree -L 2 -I "__pycache__" -I "__init__*" -I "__main__*" src/jaxsim + +src/jaxsim +|-- api..........................# Package containing the main functional APIs. +| |-- com.py...................# |-- APIs for computing quantities related to the center of mass. +| |-- common.py................# |-- Common utilities used in the current package. +| |-- contact.py...............# |-- APIs for computing quantities related to the collidable points. +| |-- data.py..................# |-- Class storing the data of a simulated model. +| |-- frame.py.................# |-- APIs for computing quantities related to additional frames. +| |-- joint.py.................# |-- APIs for computing quantities related to the joints. +| |-- kin_dyn_parameters.py....# |-- Class storing kinematic and dynamic parameters of a model. +| |-- link.py..................# |-- APIs for computing quantities related to the links. +| |-- model.py.................# |-- Class defining a simulated model and APIs for computing related quantities. +| |-- ode.py...................# |-- APIs for computing quantities related to the system dynamics. +| |-- ode_data.py..............# |-- Set of classes to store the data of the system dynamics. +| `-- references.py............# `-- Helper class to create references (link forces and joint torques). +|-- exceptions.py................# Module containing functions to raise exceptions from JIT-compiled functions. +|-- integrators..................# Package containing the integrators used to simulate the system dynamics. +| |-- common.py................# |-- Common utilities used in the current package. +| |-- fixed_step.py............# |-- Fixed-step integrators (explicit Runge-Kutta schemes). +| `-- variable_step.py.........# `-- Variable-step integrators (embedded Runge-Kutta schemes). +|-- logging.py...................# Module containing logging utilities. +|-- math.........................# Package containing mathematical utilities. +| |-- adjoint.py...............# |-- APIs for creating and manipulating 6D transformations. +| |-- cross.py.................# |-- APIs for computing cross products of 6D quantities. +| |-- inertia.py...............# |-- APIs for creating and manipulating 6D inertia matrices. +| |-- joint_model.py...........# |-- APIs defining the supported joint model and the corresponding transformations. +| |-- quaternion.py............# |-- APIs for creating and manipulating quaternions. +| |-- rotation.py..............# |-- APIs for creating and manipulating rotation matrices. +| |-- skew.py..................# |-- APIs for creating and manipulating skew-symmetric matrices. +| `-- transform.py.............# `-- APIs for creating and manipulating homogeneous transformations. +|-- mujoco.......................# Package containing utilities to interact with the Mujoco passive viewer. +| |-- loaders.py...............# |-- Utilities for converting JaxSim models to Mujoco models. +| |-- model.py.................# |-- Class providing high-level methods to compute quantities using Mujoco. +| `-- visualizer.py............# `-- Class that simplifies opening the passive viewer and recording videos. +|-- parsers......................# Package containing utilities to parse model descriptions (SDF and URDF models). +| |-- descriptions/............# |-- Package containing the intermediate representation of a model description. +| |-- kinematic_graph.py.......# |-- Definition of the kinematic graph associated with a parsed model description. +| `-- rod/.....................# `-- Package to create the intermediate representation from model descriptions using ROD. +|-- rbda.........................# Package containing the low-level rigid body dynamics algorithms. +| |-- aba.py...................# |-- The Articulated Body Algorithm. +| |-- collidable_points.py.....# |-- Kinematics of collidable points. +| |-- contacts/................# |-- Package containing the supported contact models. +| |-- crba.py..................# |-- The Composite Rigid Body Algorithm. +| |-- forward_kinematics.py....# |-- Forward kinematics of the model. +| |-- jacobian.py..............# |-- Full Jacobian and full Jacobian derivative. +| |-- rnea.py..................# |-- The Recursive Newton-Euler Algorithm. +| `-- utils.py.................# `-- Common utilities used in the current package. +|-- terrain......................# Package containing resources to specify the terrain. +| `-- terrain.py...............# `-- Classes defining the supported terrains. +|-- typing.py....................# Module containing type hints. +`-- utils........................# Package of common utilities. + |-- jaxsim_dataclass.py......# |-- Utilities to operate on pytree dataclasses. + |-- tracing.py...............# |-- Utilities to use when JAX is tracing functions. + `-- wrappers.py..............# `-- Utilities to wrap objects for specific use cases on pytree dataclass attributes. +``` + +
+ ## Credits The RBDAs are based on the theory of the [Rigid Body Dynamics Algorithms][RBDA] @@ -157,6 +223,19 @@ Please read the [contributing guide](./CONTRIBUTING.md) to get started. } ``` +Theoretical aspects of JaxSim are based on Chapters 7 and 8 of the following Ph.D. thesis: + +```bibtex +@phdthesis{ferigo_phd_thesis_2022, + title = {Simulation Architectures for Reinforcement Learning applied to Robotics}, + author = {Diego Ferigo}, + school = {University of Manchester}, + type = {PhD Thesis}, + month = {July}, + year = {2022}, +} +``` + ## People | Author | Maintainers | diff --git a/pyproject.toml b/pyproject.toml index 266a1e053..e3abd4ba4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,112 @@ [project] -dynamic = ["version", "dependencies", "optional-dependencies", "urls", "classifiers", "keywords", "authors", "requires-python", "license", "readme"] name = "jaxsim" +dynamic = ["version"] +requires-python = ">= 3.10" +description = "A differentiable physics engine and multibody dynamics library for control and robot learning." +authors = [ + { name = "Diego Ferigo", email = "dgferigo@gmail.com" }, +] +maintainers = [ + { name = "Diego Ferigo", email = "dgferigo@gmail.com" }, + { name = "Filippo Luca Ferretti", email = "filippo.ferretti@iit.it" }, +] +license.file = "LICENSE" +keywords = [ + "physics", + "physics engine", + "jax", + "rigid body dynamics", + "featherstone", + "reinforcement learning", + "robot", + "robotics", + "sdf", + "urdf", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Framework :: Robot Framework", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Games/Entertainment :: Simulation", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Software Development", +] +dependencies = [ + "coloredlogs", + "jax >= 0.4.13", + "jaxlib >= 0.4.13", + "jaxlie >= 1.3.0", + "jax_dataclasses >= 1.4.0", + "pptree", + "rod >= 0.3.0", + "typing_extensions ; python_version < '3.12'", +] + +[project.optional-dependencies] +style = [ + "black[jupyter] ~= 24.0", + "isort", + "pre-commit", +] +testing = [ + "idyntree >= 12.2.1", + "pytest >=6.0", + "pytest-icdiff", + "robot-descriptions", +] +viz = [ + "lxml", + "mediapy", + "mujoco >= 3.0.0", +] +all = [ + "jaxsim[style,testing,viz]", +] + +[project.readme] +file = "README.md" +content-type = "text/markdown" + +[project.urls] +Changelog = "https://github.com/ami-iit/jaxsim/releases" +Documentation = "https://jaxsim.readthedocs.io" +Source = "https://github.com/ami-iit/jaxsim" +Tracker = "https://github.com/ami-iit/jaxsim/issues" + +# =========== +# Build tools +# =========== [build-system] build-backend = "setuptools.build_meta" requires = [ - "wheel", - "setuptools>=64", - "setuptools_scm[toml]>=8", + "setuptools>=64", + "setuptools-scm[toml]>=8", + "wheel", ] +[tool.setuptools] +package-dir = { "" = "src" } + [tool.setuptools_scm] local_scheme = "dirty-tag" version_file = "src/jaxsim/_version.py" +# ================= +# Style and testing +# ================= + [tool.black] line-length = 88 @@ -25,42 +118,46 @@ profile = "black" addopts = "-rsxX -v --strict-markers" minversion = "6.0" testpaths = [ - "tests", + "tests", ] +# ================== +# Ruff configuration +# ================== + [tool.ruff] exclude = [ - ".git", - ".pytest_cache", - ".ruff_cache", - ".vscode", - ".devcontainer", - "__pycache__", + ".git", + ".pytest_cache", + ".ruff_cache", + ".vscode", + ".devcontainer", + "__pycache__", ] preview = true [tool.ruff.lint] # https://docs.astral.sh/ruff/rules/ select = [ - "B", - "E", - "F", - "I", - "W", - "RUF", - "YTT", + "B", + "E", + "F", + "I", + "W", + "RUF", + "YTT", ] ignore = [ - "B008", # Function call in default argument - "B024", # Abstract base class without abstract methods - "E402", # Module level import not at top of file - "E501", # Line too long - "E731", # Do not assign a `lambda` expression, use a `def` - "E741", # Ambiguous variable name - "F841", # Local variable is assigned to but never used - "I001", # Import block is unsorted or unformatted - "RUF003", # Ambigous unicode character in comment + "B008", # Function call in default argument + "B024", # Abstract base class without abstract methods + "E402", # Module level import not at top of file + "E501", # Line too long + "E731", # Do not assign a `lambda` expression, use a `def` + "E741", # Ambiguous variable name + "F841", # Local variable is assigned to but never used + "I001", # Import block is unsorted or unformatted + "RUF003", # Ambigous unicode character in comment ] [tool.ruff.lint.per-file-ignores] @@ -70,6 +167,10 @@ ignore = [ "__init__.py" = ["F401"] "docs/conf.py" = ["F401"] +# ================== +# Pixi configuration +# ================== + [tool.pixi.project] channels = ["conda-forge"] platforms = ["linux-64", "osx-arm64", "osx-64"] @@ -90,9 +191,9 @@ sdformat14 = "*" typing_extensions = "*" [tool.pixi.feature.test.tasks] -examples = {cmd = "jupyter notebook ./examples"} +examples = { cmd = "jupyter notebook ./examples" } pipcheck = "pip check" -test = {cmd = "pytest", depends_on = ["pipcheck"]} +test = { cmd = "pytest", depends_on = ["pipcheck"] } [tool.pixi.feature.test.dependencies] black = "24.*" @@ -105,15 +206,15 @@ pytest-icdiff = "*" robot_descriptions = "*" [tool.pixi.feature.gpu] -dependencies = {cuda-version = "12.*", cuda-cupti = "*", jaxlib = "**cuda*"} +dependencies = { cuda-version = "12.*", cuda-cupti = "*", jaxlib = "**cuda*" } platforms = ["linux-64"] -system-requirements = {cuda = "12.1"} +system-requirements = { cuda = "12.1" } [tool.pixi.pypi-dependencies] -jaxsim = {path = "./", editable = true} +jaxsim = { path = "./", editable = true } [tool.pixi.environments] -default = {solve-group = "cpugroup"} -gpu = {features = ["gpu"], solve-group = "gpugroup"} -test-cpu = {features = ["test"], solve-group = "cpugroup"} -test-gpu = {features = ["test", "gpu"], solve-group = "gpugroup"} +default = { solve-group = "cpugroup" } +gpu = { features = ["gpu"], solve-group = "gpugroup" } +test-cpu = { features = ["test"], solve-group = "cpugroup" } +test-gpu = { features = ["test", "gpu"], solve-group = "gpugroup" } diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 0061982ed..000000000 --- a/setup.cfg +++ /dev/null @@ -1,87 +0,0 @@ -[metadata] -name = jaxsim -description = A differentiable physics engine and multibody dynamics library for control and robot learning. -long_description = file: README.md -long_description_content_type = text/markdown -author = Diego Ferigo -author_email = diego.ferigo@iit.it -license = BSD -license_files = LICENSE -platforms = any -url = https://github.com/ami-iit/jaxsim - -project_urls = - Changelog = https://github.com/ami-iit/jaxsim/releases - Documentation = https://jaxsim.readthedocs.io - Source = https://github.com/ami-iit/jaxsim - Tracker = https://github.com/ami-iit/jaxsim/issues - -keywords = - physics - physics engine - jax - rigid body dynamics - featherstone - reinforcement learning - robot - robotics - sdf - urdf - -classifiers = - Development Status :: 4 - Beta - Framework :: Robot Framework - Intended Audience :: Developers - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Operating System :: POSIX :: Linux - Operating System :: MacOS - Operating System :: Microsoft - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: Implementation :: CPython - Topic :: Games/Entertainment :: Simulation - Topic :: Scientific/Engineering :: Artificial Intelligence - Topic :: Scientific/Engineering :: Physics - Topic :: Software Development - -[options] -zip_safe = False -packages = find: -package_dir = - =src -python_requires = >=3.10 -install_requires = - coloredlogs - jax >= 0.4.13 - jaxlib >= 0.4.13 - jaxlie >= 1.3.0 - jax_dataclasses >= 1.4.0 - pptree - rod >= 0.3.0 - typing_extensions ; python_version < '3.12' - -[options.packages.find] -where = src - -[options.extras_require] -style = - # Note: keep the black version in sync with style.yml and environment.yml. - black[jupyter] ~= 24.0 - isort - pre-commit -testing = - idyntree >= 12.2.1 - pytest >=6.0 - pytest-icdiff - robot-descriptions -viz = - lxml - mediapy - mujoco >= 3.0.0 -all = - %(style)s - %(testing)s - %(viz)s diff --git a/src/jaxsim/mujoco/model.py b/src/jaxsim/mujoco/model.py index 6b61177ff..bb11d6399 100644 --- a/src/jaxsim/mujoco/model.py +++ b/src/jaxsim/mujoco/model.py @@ -378,7 +378,7 @@ def _mask_qpos(self, joint_names: tuple[str, ...]) -> npt.NDArray: for i in range(self.joint_dofs(joint_name=joint_name)) ] ) - for idx, joint_name in zip(idxs, joint_names) + for idx, joint_name in zip(idxs, joint_names, strict=True) ] ).squeeze() ) diff --git a/src/jaxsim/parsers/kinematic_graph.py b/src/jaxsim/parsers/kinematic_graph.py index 9edf811b4..5073894f1 100644 --- a/src/jaxsim/parsers/kinematic_graph.py +++ b/src/jaxsim/parsers/kinematic_graph.py @@ -797,7 +797,7 @@ def initial_joint_positions( self._transform_cache.clear() # Update initial joint positions. - for joint_name, position in zip(joint_names, s): + for joint_name, position in zip(joint_names, s, strict=True): self._initial_joint_positions[joint_name] = position def transform(self, name: str) -> npt.NDArray: diff --git a/tests/test_api_frame.py b/tests/test_api_frame.py index f721c80fa..7d2e6322d 100644 --- a/tests/test_api_frame.py +++ b/tests/test_api_frame.py @@ -163,7 +163,7 @@ def test_frame_jacobians( assert len(frame_indices) == len(frame_names) - for frame_name, frame_index in zip(frame_names, frame_indices): + for frame_name, frame_index in zip(frame_names, frame_indices, strict=True): J_WL_js = js.frame.jacobian(model=model, data=data, frame_index=frame_index) J_WL_idt = kin_dyn.jacobian_frame(frame_name=frame_name) diff --git a/tests/test_api_link.py b/tests/test_api_link.py index 1e808687d..418f78a99 100644 --- a/tests/test_api_link.py +++ b/tests/test_api_link.py @@ -75,6 +75,7 @@ def test_link_inertial_properties( for link_name, link_idx in zip( model.link_names(), js.link.names_to_idxs(model=model, link_names=model.link_names()), + strict=True, ): if link_name == model.base_link(): continue @@ -118,7 +119,7 @@ def test_link_transforms( assert W_H_LL_model == pytest.approx(W_H_LL_links) - for W_H_L, link_name in zip(W_H_LL_links, model.link_names()): + for W_H_L, link_name in zip(W_H_LL_links, model.link_names(), strict=True): assert W_H_L == pytest.approx( kin_dyn.frame_transform(frame_name=link_name) @@ -152,7 +153,7 @@ def test_link_jacobians( lambda idx: js.link.jacobian(model=model, data=data, link_index=idx) )(jnp.arange(model.number_of_links())) - for J_WL, link_name in zip(J_WL_links, model.link_names()): + for J_WL, link_name in zip(J_WL_links, model.link_names(), strict=True): assert J_WL == pytest.approx( kin_dyn.jacobian_frame(frame_name=link_name), abs=1e-9 ), link_name @@ -164,6 +165,7 @@ def test_link_jacobians( for link_name, link_idx in zip( model.link_names(), js.link.names_to_idxs(model=model, link_names=model.link_names()), + strict=True, ): v_WL_idt = kin_dyn.frame_velocity(frame_name=link_name) v_WL_js = js.link.velocity(model=model, data=data, link_index=link_idx) @@ -184,6 +186,7 @@ def test_link_jacobians( for link_name, link_idx in zip( model.link_names(), js.link.names_to_idxs(model=model, link_names=model.link_names()), + strict=True, ): v_WL_idt = kin_dyn_other_repr.frame_velocity(frame_name=link_name) v_WL_js = js.link.velocity( @@ -218,6 +221,7 @@ def test_link_bias_acceleration( for name, index in zip( model.link_names(), js.link.names_to_idxs(model=model, link_names=model.link_names()), + strict=True, ): Jν_idt = kin_dyn.frame_bias_acc(frame_name=name) Jν_js = js.link.bias_acceleration(model=model, data=data, link_index=index) diff --git a/tests/test_api_model.py b/tests/test_api_model.py index 24654f726..a0fe49db5 100644 --- a/tests/test_api_model.py +++ b/tests/test_api_model.py @@ -84,6 +84,7 @@ def test_model_creation_and_reduction( data_full.joint_positions( model=model_full, joint_names=model_full.joint_names() ).tolist(), + strict=True, ) ), ) diff --git a/tests/utils_idyntree.py b/tests/utils_idyntree.py index 3dc0df833..5376a82ef 100644 --- a/tests/utils_idyntree.py +++ b/tests/utils_idyntree.py @@ -64,6 +64,7 @@ def build_kindyncomputations_from_jaxsim_model( zip( model.joint_names(), data.joint_positions(model=model, joint_names=model.joint_names()), + strict=True, ) ) )