diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..078dfc9 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,16 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.12" + +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: docs/requirements.txt diff --git a/README.md b/README.md index b80583e..73f5e19 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OnShape URDF Exporter +[Read the docs here](https://onshape-urdf-exporter.readthedocs.io/en/latest/) + Exports OnShape assemblies into URDF files with STL meshes. This is based off of the URDF exporting functionality provided by onshape-to-robot, but with some improvements: diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +build diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..ed88099 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/img/after.png b/docs/_static/img/after.png new file mode 100644 index 0000000..fdbb076 Binary files /dev/null and b/docs/_static/img/after.png differ diff --git a/docs/_static/img/before.png b/docs/_static/img/before.png new file mode 100644 index 0000000..189eb71 Binary files /dev/null and b/docs/_static/img/before.png differ diff --git a/docs/_static/img/design.png b/docs/_static/img/design.png new file mode 100644 index 0000000..20dfcbf Binary files /dev/null and b/docs/_static/img/design.png differ diff --git a/docs/_static/img/design.xcf b/docs/_static/img/design.xcf new file mode 100644 index 0000000..5bad493 Binary files /dev/null and b/docs/_static/img/design.xcf differ diff --git a/docs/_static/img/frame.png b/docs/_static/img/frame.png new file mode 100644 index 0000000..24bd489 Binary files /dev/null and b/docs/_static/img/frame.png differ diff --git a/docs/_static/img/main.png b/docs/_static/img/main.png new file mode 100644 index 0000000..1bd8a1d Binary files /dev/null and b/docs/_static/img/main.png differ diff --git a/docs/_static/img/opened_chain.png b/docs/_static/img/opened_chain.png new file mode 100644 index 0000000..abf02a5 Binary files /dev/null and b/docs/_static/img/opened_chain.png differ diff --git a/docs/_static/img/pure-shape.png b/docs/_static/img/pure-shape.png new file mode 100644 index 0000000..4cc75a8 Binary files /dev/null and b/docs/_static/img/pure-shape.png differ diff --git a/docs/_static/img/shape-approx.png b/docs/_static/img/shape-approx.png new file mode 100644 index 0000000..4dc73d3 Binary files /dev/null and b/docs/_static/img/shape-approx.png differ diff --git a/docs/_static/img/shape-approx.xcf b/docs/_static/img/shape-approx.xcf new file mode 100644 index 0000000..f042550 Binary files /dev/null and b/docs/_static/img/shape-approx.xcf differ diff --git a/docs/_static/img/smalls/after.png b/docs/_static/img/smalls/after.png new file mode 100644 index 0000000..c573f51 Binary files /dev/null and b/docs/_static/img/smalls/after.png differ diff --git a/docs/_static/img/smalls/before.png b/docs/_static/img/smalls/before.png new file mode 100644 index 0000000..23992bf Binary files /dev/null and b/docs/_static/img/smalls/before.png differ diff --git a/docs/_static/img/smalls/design.png b/docs/_static/img/smalls/design.png new file mode 100644 index 0000000..b2ec3e1 Binary files /dev/null and b/docs/_static/img/smalls/design.png differ diff --git a/docs/_static/img/smalls/frame.png b/docs/_static/img/smalls/frame.png new file mode 100644 index 0000000..febc7ba Binary files /dev/null and b/docs/_static/img/smalls/frame.png differ diff --git a/docs/_static/img/smalls/main.png b/docs/_static/img/smalls/main.png new file mode 100644 index 0000000..03f209a Binary files /dev/null and b/docs/_static/img/smalls/main.png differ diff --git a/docs/_static/img/smalls/pure-shape.png b/docs/_static/img/smalls/pure-shape.png new file mode 100644 index 0000000..fa124b8 Binary files /dev/null and b/docs/_static/img/smalls/pure-shape.png differ diff --git a/docs/_static/img/smalls/shape-approx.png b/docs/_static/img/smalls/shape-approx.png new file mode 100644 index 0000000..82f263b Binary files /dev/null and b/docs/_static/img/smalls/shape-approx.png differ diff --git a/docs/_static/img/zaxis.png b/docs/_static/img/zaxis.png new file mode 100644 index 0000000..b8bca60 Binary files /dev/null and b/docs/_static/img/zaxis.png differ diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..04eab9f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,57 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'Onshape URDF Exporter' +copyright = '2020, Rhoban & 2023, Urban Machine' +author = 'Rhoban' + +# The full version, including alpha/beta/rc tags +release = '1' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +master_doc = 'index' diff --git a/docs/config.rst b/docs/config.rst new file mode 100644 index 0000000..c0bef39 --- /dev/null +++ b/docs/config.rst @@ -0,0 +1,252 @@ +Export your own robot (writing config.yaml) +=========================================== + +To export your own robot, first create a directory: + +.. code-block:: bash + + mkdir my-robot + +Then edit ``my-robot/config.yaml``, here is the minimum example: + +.. code-block:: yaml + + document_id: document-id + +The ``document_id`` is the number (below XXXXXXXXX) you can find in Onshape URL: + +.. code-block:: bash + + https://cad.onshape.com/documents/XXXXXXXXX/w/YYYYYYYY/e/ZZZZZZZZ + +Once this is done, if you properly :doc:`installed and setup your API key `, just run: + +.. code-block:: bash + + onshape-urdf-exporter my-robot + +``config.yaml`` entries +----------------------- + +Here is the full list of possible entries for this configuration. + +``document_id`` +~~~~~~~~~~~~~~~ + +This is the Onshape ID of the document to be imported. It can be found in the Onshape URL, +just after ``document/``. + + +``assembly_name`` +~~~~~~~~~~~~~~~~~ + +*optional* + +This can be used to specify the name of the assembly (in the Onshape document) to be used for robot export. If none +is used, the first assembly found will be used. + +``workspace_id`` +~~~~~~~~~~~~~~~~ + +*optional, no default* + +This argument can be used to use a specific workspace of the document. This can be used for specific branches +ofr your robot without making a version. +The workspace ID can be found in URL, after the ``/w/`` part when selecting a specific version in the tree. + +``version_id`` +~~~~~~~~~~~~~~ + +*optional, no default* + +This argument can be used to use a specific version of the document instead of the last one. The version ID +can be found in URL, after the ``/v/`` part when selecting a specific version in the tree. + +If it is not specified, the very last version will be used for import. + +``configuration`` +~~~~~~~~~~~~~~~~~ + +*optional, default: "default"* + +This is the robot configuration string that will be passed to Onshape. An example of format: + +.. code-block:: js + + left_motor_angle=3+radian;enable_yaw=true + +``draw_frames`` +~~~~~~~~~~~~~~~ + +*optional, default: false* + +When :ref:`adding custom frames to your model `, the part that is used for positioning the frame is +by default excluded from the output description (a dummy link is kept instead). Passing this option to ``true`` will +keep it instead. + +``draw_collisions`` +~~~~~~~~~~~~~~~~~~~ + +*optional, default: false* + +Controls if collision shapes are shown visually. + +``joint_max_effort`` and ``joint_max_velocity`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*optional, default: 1 and 20* + +Those parameters can be used to specify the values that will be included in the ``joint`` entries. + +Alternatively, they can be dictionaries associating named joints to the values. + + +``dynamics`` +~~~~~~~~~~~~ + +*optional, default: {}* + +This ``dict`` can be used to override the mass and inertia computed by Onshape for a specific part. +See :ref:`example ` below. + + +``no_dynamics`` +~~~~~~~~~~~~~~~ + +*optional, default: false* + +This flag can be set if there is no dynamics. In that case all masses and inertia will be set to 0. + +``ignore`` +~~~~~~~~~~ + +*optional, default: []* + +This can be a list of parts that you want to be ignored during the export. + +Note: the dynamics of the part will not be ignored, but the visual and collision aspect will. + +``whitelist`` +~~~~~~~~~~~~~ + +*optional, default: None* + +This can be used as the opposed of ``ignore``, to import only some items listed in the configuration +(all items not listed in ``whitelist`` will be ignored if it is not ``None``) + +``color`` +~~~~~~~~~ + +*optional, default: None* + +Can override the color for parts (should be an array: ``[r, g, b]`` with numbers from 0 to 1) + +``package_name`` +~~~~~~~~~~~~~~~~ + +*optional* + +Prepends a string to the paths of STL files. This is helpful for ROS users as they often need to specify their +``robot_description`` package. + +``add_dummy_base_link`` +~~~~~~~~~~~~~~~~~~~~~~~ + +*optional* + +Adds a ``base_link`` without inertia as root. This is often necessary for ROS users. + +``robot_name`` +~~~~~~~~~~~~~~ + +*optional* + +Specifies the robot name. + +``additional_urdf_file`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +*optional* + +Specifies a file with XML content that is inserted into the URDF at the end of the file. Useful to add things that can't be modelled in Onshape, e.g. simulated sensors. + +``use_fixed_links`` +~~~~~~~~~~~~~~~~~~~ + +*optional, default: false* + +With this option, visual parts will be added through fixed links to each part of the robot. + +``simplify_stls`` +~~~~~~~~~~~~~~~~~ + +*optional, default: "no"* + +Can be "no", "visual", "collision" or "all". + +If this is set, the complexity of the STL files will be reduced. This can be +good for file size and visualization performance. + +``use_collisions_configurations`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*optional, default: true* + +With this option (enabled by default), the collisions=true configuration will be passed when exporting STL +meshes (and NOT dynamics), in order to retrieve simplified mesh parts from Onshape. + +This is a way to approximate your robot with simpler meshes. + +``post_import_commands`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +*optional, default: []* + +This is an array of commands that will be executed after the import is done. It can be used to be sure that +some processing scripts are run everytime you run the tool. + +.. _example-config: + +Example ``config.yaml`` file +---------------------------- + +Here is an example of configuration: + +.. code-block:: yaml + + # Can be found in the URL when editing the assembly + document_id: 483c803918afc4d52e2647f0 + # If not specified, the first assembly will be used + assembly_name: robot + # The frames parts are kept in the final file + draw_frames: false + # Collisions (pure shapes) are also used in the visual section + draw_collisions: false + # Masses, com and inertias will be zero (can be used if you import a static + # field for example) + no_dynamics: false + # Should we simplify STLs files? + simplify_stls: false + + # Those can be used to configure the joint max efforts and velocity, and + # overriden for specific joints + joint_max_effort: + default: 1.5 + head_pitch: 0.5 + joint_max_velocity: 22 + + # This can be used to override the dynamics of some part (suppose it's a compound + # which dynamics is well specified) + dynamics: + motorcase: + mass: 0.5 + com: [0, 0.1, 0] + inertia: [0.1, 0, 0, 0, 0.1, 0, 0, 0, 0.1] + + # "fixed" can be used to assign a null mass to the object, which makes it fixed (non-dynamics) + base: fixed + + # Some parts can be totally ignored during import + ignore: + - small_screw + - small_nut diff --git a/docs/design.rst b/docs/design.rst new file mode 100644 index 0000000..4cda958 --- /dev/null +++ b/docs/design.rst @@ -0,0 +1,65 @@ +Design-time considerations +========================== + +.. note:: + Try to make your robot assembly mostly based on sub pre-assembled components (avoid to have a lot of + constraints that are not relevant for the export). In this main assembly, do not use features + such as sub-assemblies network. + +Specifying degrees of freedom +----------------------------- + +* Degree of freedoms should be slider, cylindrical or revolute mate connectors named ``dof_something``, where + ``something`` will be used to name the joint in the final document + + * If the mate connector is **cylindrical** or **revolute**, a ``revolute`` joint will be issued by default + + * To make a ``continuous`` joint, add ``continuous`` or ``wheel`` in the name of the joint. For instance, a **revolute** mate named + ``dof_front_left_wheel`` will result in a ``continuous`` joint named ``front_left_wheel`` in the resulting URDF. + * If the mate connector is a **slider**, a ``prismatic`` joint will be issued + * If the mate connector is **fastened**, a ``fixed`` joint will be issued +* When doing this connection, click the children joint first. This will be used to find the trunk of the robot (part with children but no parent) + +.. image:: _static/img/smalls/design.png + :align: center + +Inverting axis orientation +-------------------------- + +It is possible to invert the axis for convenience by adding ``_inv`` at the end of the name. For instance +``dof_head_pitch_inv`` will result in a joint named ``head_pitch`` having the axis inverted with the one +from the OnShape assembly. + +Naming links +------------ + +If you create a mate connector and name it ``link_something``, the link corresponding to the part +on which it is attached will be named ``something`` in the resulting URDF. + +.. _custom-frames: + +Adding custom frames in your model +---------------------------------- + +If you want to track some frames on your robot, you can do the following: + +* Connect any part to your robot using mate relations in OnShape +* Name one of these relations ``frame_something``, when ``something`` will be the name of + the frame (a link) in the resulting ``sdf`` or ``urdf`` + +.. image:: _static/img/smalls/frame.png + :align: center + + +Here is a `link `_ of a document that can be used as a frame (note: the center cube is 5mm side, so +you might need 2.5mm offset to center it). + +Joint frames +------------ + +Joint frames are the ones you see in OnShape when you click on the joint in the tree on the left. +Thus, they are always revolving around the z axis, or translating along the z axis, even if the +``_inv`` suffix is added. + +.. image:: _static/img/zaxis.png + :align: center diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..de89eb1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,38 @@ +Onshape URDF Exporter Documentation +=================================== + +.. image:: _static/img/main.png + +Introduction +~~~~~~~~~~~~ + +Onshape URDF Exporter is a tool that that exports Onshape robot designs as +URDF files. These files can be used with tools like RViz or Gazebo to +calculate inverse kinematics, run physics simulations, and provide +visualizations. + +This project is based on onshape-to-robot_, but with some +different design decisions and a number of bug fixes: + +- This tool focuses on doing one thing well: Exporting URDFs +- XML document creation is done using Python's XML library, fixing a number of + bugs related to characters not being properly escaped +- Files created by this tool always have valid filenames, even on Windows +- Uses Open3D for STL simplification instead of MeshLab, which makes + installation easier +- Uses YAML files for configuration, instead of the non-standard commentjson + format + +.. _onshape-to-robot: https://github.com/Rhoban/onshape-to-robot + +* `Onshape URDF Exporter GitHub repository `_ +* `Onshape URDF Exporter on PyPI `_ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + installation + design + kinematic_loops + config diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..32e67ef --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,40 @@ + +Installation & requirements +=========================== + +Requirements +------------- + +You will need an Onshape account and Python 3. + +Installation +------------ + +Pipx_ is the recommended way to install Onshape URDF Exporter. Just run the +following command: + +.. code-block:: bash + + pipx install onshape-urdf-exporter + +.. _Pipx: https://pipx.pypa.io/stable/installation/ + +.. _api-key: + +Setting up your API key +----------------------- + +To authenticate the tool with Onshape, you will need to obtain an API key and +secret from the `Onshape developer portal`_. + +These keys are provided to the tool using environment variables. Declare them +in your ``.bashrc`` (or shell equivalent) by adding these lines: + +.. code-block:: bash + + # Obtained at https://dev-portal.onshape.com/keys + export ONSHAPE_API=https://cad.onshape.com + export ONSHAPE_ACCESS_KEY=Your_Access_Key + export ONSHAPE_SECRET_KEY=Your_Secret_Key + +.. _Onshape developer portal: https://dev-portal.onshape.com/keys diff --git a/docs/kinematic_loops.rst b/docs/kinematic_loops.rst new file mode 100644 index 0000000..c848f18 --- /dev/null +++ b/docs/kinematic_loops.rst @@ -0,0 +1,57 @@ +Handling Kinematic Loops +======================== + +Some robots have *kinematic loops*, meaning that the kinematic chain is not a tree but a graph. +Here is an example: + +Introduction +------------ + +Here is a 2D planar robot with kinematic loop, we assume the two first joints to be actuated and the others to +be passive: + +.. raw:: html + +
+ +
+
+ + +Here, the two branches are connected together, thus this can't be represented as a tree. +URDF doesn't allow to directly represent this type of structure. We need to break it down in a tree, and enforce the +closing constraints in software during execution. +To enforce the constraints, frames can be placed at relevant positions, here is an example for the above robot: + +.. image:: _static/img/opened_chain.png + :width: 200px + :align: center + +Using Onshape URDF Exporter +--------------------------- + +The above mentioned example can be achieved by adding :ref:`frames `. However, this would require to +manually place two frames at each closing position. Onshape URDF Exporter comes with a more convenient way to achieve this, +by using mate connectors. + +If you add a relation with ``closing_something`` as name, two frames will be added to your URDF +(``closing_something_1`` and ``closing_something_2``) that will be attached to the two parts mated. + +This way, the closure is handled in Onshape exactly the way it should appear in the final mechanism, and will result +in the two frames being placed at the correct position in the URDF. + +Here is the complete `Onshape assembly `_ for the above example robot. + +Handling constraints on execution time +-------------------------------------- + +Here are some resources on how to handle kinematic loops in software: + +* In MuJoCo, you can add an `equality `_ constraint in your XML model. +* In `pyBullet `_, you can use `createConstraint` method to add the relevant constraint. +* In the `PlaCo `_ solver, you can create a + ``RelativePositionTask``. See the + `kinematics loop documentation section `_ + for more details. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..6247f7e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..6c5d5d4 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +sphinx-rtd-theme