diff --git a/.github/workflows/models.yml b/.github/workflows/models.yml index 2a24f54..02493ed 100644 --- a/.github/workflows/models.yml +++ b/.github/workflows/models.yml @@ -81,14 +81,14 @@ jobs: libgl1-mesa-dri python3-pyqt5 python3-pil \ ros-${ROS_DISTRO}-launch-ros ros-${ROS_DISTRO}-rviz2 \ ros-${ROS_DISTRO}-joint-state-publisher-gui - - name: Build four packages and relocate the install + - name: Build the description and MoveIt packages and relocate the install shell: bash run: | source /opt/ros/${ROS_DISTRO}/setup.bash task_ws=${RUNNER_TEMP}/description_ros2_ws mkdir -p "${task_ws}/src" colcon list --names-only --base-paths "${GITHUB_WORKSPACE}" > "${task_ws}/packages.txt" - python3 -c 'import pathlib,sys; assert set(pathlib.Path(sys.argv[1]).read_text().split()) == {"par6_description","parol6_description","msg_description","ssg48_description","par6_moveit_config","parol6_moveit_config"}' "${task_ws}/packages.txt" + python3 -c 'import pathlib,sys; assert set(pathlib.Path(sys.argv[1]).read_text().split()) == {"par6_description","parol6_description","msg_description","ssg48_description","raft_description","par6_moveit_config","parol6_moveit_config"}' "${task_ws}/packages.txt" cp -a urdf/. "${task_ws}/src/" cd "${task_ws}" colcon build --merge-install --event-handlers console_cohesion+ diff --git a/CHANGELOG.md b/CHANGELOG.md index a732b60..68a9786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog - Source Robotics robot descriptions +## 2.19.0 - 2026-11-06 + +- Added RAFT, the teleoperation leader arm for PAR6: `mujoco/raft` and `urdf/raft_description`, with PAR6's joint names, + torque actuators, the trigger, the optional J2 counterbalance tendon, `home`/`parked` keyframes, ROS 2 display support + and two downloadable ZIPs. Joint ranges come from the measured mechanical stops and a CAD sweep. +- Built from RAFT's model reference (`source/raft/`, imported from the RAFT repository by `tools/import_raft_reference.py`), + the same source as RAFT's firmware model; both formats are checked against its kinematics and gravity torques. +- RAFT's printed parts, servos, horns and screws are separate visual pieces coloured by the shared palette: new keys + `raft_structure`, `raft_trigger`, `raft_horns`, `raft_fasteners`; the servos follow `motors` or the `raft_motors` override. +- Linux ROS 2 checks and the release now include `raft_description` and the two RAFT ZIPs. + ## 2.18.1 - 2026-11-03 - Organized reports by appearance, dynamics, grippers, kinematics, package checks and historical milestones; moved generated JSON out of the tools folder. diff --git a/CITATION.cff b/CITATION.cff index 6816731..1971ad7 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,6 +1,6 @@ cff-version: 1.2.0 message: "If you use Source Robotics robot descriptions in your work, please cite it as below." -title: "Source Robotics robot descriptions: URDF and MuJoCo models of PAR6, PAROL6, MSG and SSG-48" +title: "Source Robotics robot descriptions: URDF and MuJoCo models of PAR6, PAROL6, RAFT, MSG and SSG-48" type: software authors: - name: Source Robotics @@ -8,8 +8,8 @@ authors: - given-names: Petar family-names: Crnjak abstract: "URDF and MuJoCo models, meshes and generation tools for the Source Robotics arms and grippers, each robot with its end-effector configurations, ready for simulation." -version: v2.18.1 -date-released: 2026-11-03 +version: v2.19.0 +date-released: 2026-11-06 repository-code: https://github.com/Source-Robotics/robot_descriptions url: https://github.com/Source-Robotics/robot_descriptions license: diff --git a/README.md b/README.md index 577310a..0f89a8c 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,17 @@ collision geometry, joints and mass properties, ready to open without a CAD buil ## Meet the models -| **[PAR6](guides/par6.md)** | **[PAROL6](guides/parol6.md)** | -| :---: | :---: | -| ![PAR6 with MSG Soft TPU jaws, isometric view](docs/images/robots/par6_msg_soft_tpu_isometric.png) | ![PAROL6 with pneumatic gripper, isometric view](docs/images/robots/parol6_pneumatic_isometric.png) | -| Six end-effector configurations | Six end-effector configurations | +| **[PAR6](guides/par6.md)** | **[PAROL6](guides/parol6.md)** | **[RAFT leader arm](guides/raft.md)** | +| :---: | :---: | :---: | +| ![PAR6 with MSG Soft TPU jaws, isometric view](docs/images/robots/par6_msg_soft_tpu_isometric.png) | ![PAROL6 with pneumatic gripper, isometric view](docs/images/robots/parol6_pneumatic_isometric.png) | ![RAFT teleoperation leader arm, isometric view](docs/images/robots/raft_isometric.png) | +| Six end-effector configurations | Six end-effector configurations | Teleoperation leader for PAR6, with a trigger | + | **[MSG gripper](guides/msg.md)** | **[SSG48 gripper](guides/ssg48.md)** | +| :---: | :---: | | ![Standalone MSG with Soft TPU jaws](source/grippers/previews/msg_soft_tpu.png) | ![Standalone SSG48 with Fin-ray jaws](source/grippers/previews/ssg48_fin_ray.png) | | Solid jaws · Soft TPU jaws | Solid jaws · Fin-ray jaws | -Both arms support **Flange only**, **MSG / Solid jaws**, **MSG / Soft TPU jaws**, +PAR6 and PAROL6 support **Flange only**, **MSG / Solid jaws**, **MSG / Soft TPU jaws**, **SSG48 / Solid jaws**, **SSG48 / Fin-ray jaws** and **Pneumatic gripper**. Standalone gripper downloads include both of that gripper's jaw options. MSG and SSG48 default to Solid jaws. @@ -36,6 +38,7 @@ needed.** The product guides list every scene and URDF filename. | **[PAROL6](guides/parol6.md)** | [parol6_mujoco.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/parol6_mujoco.zip) | [parol6_urdf.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/parol6_urdf.zip) | | **[MSG — gripper only](guides/msg.md)** | [msg_mujoco.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/msg_mujoco.zip) | [msg_urdf.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/msg_urdf.zip) | | **[SSG48 — gripper only](guides/ssg48.md)** | [ssg48_mujoco.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/ssg48_mujoco.zip) | [ssg48_urdf.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/ssg48_urdf.zip) | +| **[RAFT — leader arm](guides/raft.md)** | [raft_mujoco.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/raft_mujoco.zip) | [raft_urdf.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/raft_urdf.zip) | The [latest release](https://github.com/Source-Robotics/robot_descriptions/releases/latest) also includes the dimensioned review PDF, checksums and optional MoveIt packages. @@ -69,7 +72,7 @@ The [PAR6](guides/par6.md), [PAROL6](guides/parol6.md), [MSG](guides/msg.md) and
Using ROS 2, RViz or MoveIt? -All four URDF packages install with colcon and provide RViz views and joint +All five URDF packages install with colcon and provide RViz views and joint sliders. Follow the [ROS 2 setup guide](docs/ROS2.md) for Humble or Jazzy, then run: ```bash diff --git a/REPOSITORY_GUIDE.md b/REPOSITORY_GUIDE.md index e04fd06..6f768d5 100644 --- a/REPOSITORY_GUIDE.md +++ b/REPOSITORY_GUIDE.md @@ -18,8 +18,8 @@ ```text robot_descriptions/ guides/ Product-specific getting-started pages - mujoco/ Four portable MuJoCo model packages - urdf/ Four portable URDF/ROS description packages + mujoco/ Five portable MuJoCo model packages + urdf/ Five portable URDF/ROS description packages moveit/ Two optional MoveIt configuration packages docs/ Usage guides, images, PDF, maintenance notes and releases source/ Editable inputs, original exports and checked caches @@ -56,8 +56,8 @@ See [the move list](docs/maintenance/FOLDER_CHANGES.md) for older paths. | Format | Packages | | --- | --- | -| MuJoCo | `mujoco/par6`, `mujoco/parol6`, `mujoco/msg`, `mujoco/ssg48` | -| URDF | `urdf/par6_description`, `urdf/parol6_description`, `urdf/msg_description`, `urdf/ssg48_description` | +| MuJoCo | `mujoco/par6`, `mujoco/parol6`, `mujoco/msg`, `mujoco/ssg48`, `mujoco/raft` | +| URDF | `urdf/par6_description`, `urdf/parol6_description`, `urdf/msg_description`, `urdf/ssg48_description`, `urdf/raft_description` | | MoveIt | `moveit/par6_moveit_config`, `moveit/parol6_moveit_config`; each uses its matching URDF package. | The arm packages include all six tool configurations. Standalone gripper @@ -65,8 +65,8 @@ packages contain both jaw options. Runtime meshes are repeated where necessary so each model package can be downloaded or moved independently. Hardware repositories contain ordinary product-guide folders named -`par6_description`, `parol6_description`, `msg_description` and -`ssg48_description`. Those are guide pages and previews. The actual model +`par6_description`, `parol6_description`, `msg_description`, +`ssg48_description` and `raft_description`. Those are guide pages and previews. The actual model packages with those names live under this repository's `urdf/` folder. ## Inside a MuJoCo package @@ -106,11 +106,11 @@ SSG48. Detailed notes are grouped in `docs/`, which has its own clickable index. Only the README, license, package metadata and model/scene entry files stay at -the root. This layout is shared by all eight runtime packages. +the root. This layout is shared by all ten runtime packages. ## Inside a URDF package -The four folders are `urdf/par6_description/`, `urdf/parol6_description/`, +The five folders are `urdf/par6_description/`, `urdf/parol6_description/`, `urdf/msg_description/` and `urdf/ssg48_description/`. | File or folder | Purpose | diff --git a/docs/COLORS.md b/docs/COLORS.md index 523554e..12fbd22 100644 --- a/docs/COLORS.md +++ b/docs/COLORS.md @@ -3,8 +3,8 @@ [Back to the repository](../README.md) **Edit one file: [`source/appearance/colors.json`](../source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. diff --git a/docs/ROS2.md b/docs/ROS2.md index f1dd375..79efeec 100644 --- a/docs/ROS2.md +++ b/docs/ROS2.md @@ -2,7 +2,7 @@ [Back to the model instructions](../README.md) -The four URDF packages provide native `ament_cmake` installation and a ROS 2 +The five URDF packages provide native `ament_cmake` installation and a ROS 2 `display.launch.py` launcher. Choose a tool or jaw option, then view it in RViz and move its joints with sliders. Linux CI checks ROS 2 **Humble / Ubuntu 22.04** and **Jazzy / Ubuntu 24.04**, including installed assets, TF, mimic joints and @@ -55,7 +55,7 @@ colcon build --base-paths src/robot_descriptions/urdf source install/setup.bash ``` -This builds all four packages. Run colcon against the `urdf/` packages; there +This builds all five packages. Run colcon against the `urdf/` packages; there is no need to run `tools/build.py` to use the committed models. After opening a new terminal, source both `/opt/ros//setup.bash` and your workspace's `install/setup.bash` again. @@ -93,6 +93,7 @@ ros2 launch msg_description display.launch.py jaws:=solid ros2 launch msg_description display.launch.py jaws:=soft_tpu ros2 launch ssg48_description display.launch.py jaws:=solid ros2 launch ssg48_description display.launch.py jaws:=fin_ray +ros2 launch raft_description display.launch.py ``` Each launcher opens RViz and a joint-slider window. The two fingers retain diff --git a/docs/images/raft.png b/docs/images/raft.png new file mode 100644 index 0000000..47752c5 Binary files /dev/null and b/docs/images/raft.png differ diff --git a/docs/images/robots/raft_isometric.png b/docs/images/robots/raft_isometric.png new file mode 100644 index 0000000..a8f75ea Binary files /dev/null and b/docs/images/robots/raft_isometric.png differ diff --git a/docs/maintenance/BUILDING.md b/docs/maintenance/BUILDING.md index d54215f..5ea3032 100644 --- a/docs/maintenance/BUILDING.md +++ b/docs/maintenance/BUILDING.md @@ -76,13 +76,17 @@ extracting each ZIP, so the organized downloads remain portable. 10. Exports independent MSG/SSG48 packages with both jaw options, compares their tool-relative FK, inertials, controls and contacts against the mounted models, and runs finite close/open cycles. -11. Generates native ROS 2 support for all four URDF packages, then separate +11. Builds RAFT's two packages from `source/raft` (RAFT's model reference, CAD + meshes and visual pieces) and checks both formats against the kinematics and + gravity torques in that reference. Generates native ROS 2 support for all + five URDF packages, then separate `par6_moveit_config` / `parol6_moveit_config` packages. MoveIt adds planning limits, SRDF groups/TCP/collision rules and mock-control launch support; it does not rewrite the saved physical models. -12. Writes and extracts all eight model ZIPs: `par6_mujoco.zip`, `par6_urdf.zip`, +12. Writes and extracts all ten model ZIPs: `par6_mujoco.zip`, `par6_urdf.zip`, `parol6_mujoco.zip`, `parol6_urdf.zip`, `msg_mujoco.zip`, `msg_urdf.zip`, - `ssg48_mujoco.zip` and `ssg48_urdf.zip`. Each uses only its own assets. + `ssg48_mujoco.zip`, `ssg48_urdf.zip`, `raft_mujoco.zip` and `raft_urdf.zip`. + Each uses only its own assets. Also writes `par6_moveit_config.zip` and `parol6_moveit_config.zip`, which require the matching URDF package and contain no duplicate geometry. diff --git a/docs/maintenance/RELEASING.md b/docs/maintenance/RELEASING.md index 9f531ae..b5f98aa 100644 --- a/docs/maintenance/RELEASING.md +++ b/docs/maintenance/RELEASING.md @@ -1,6 +1,6 @@ # Publishing a description release -This repository is the source for eight model distributions and two MoveIt +This repository is the source for ten model distributions and two MoveIt configuration distributions. Model changes and generation tools belong here. Hardware repositories contain product guides, previews and release links. Software consumers can pin this repository as a @@ -34,7 +34,7 @@ git push origin v2.13.0 ``` The GitHub Actions workflow tests and builds that tag, checks every ZIP's -version against it, and publishes eight model ZIPs, two MoveIt ZIPs, the review PDF, a source +version against it, and publishes ten model ZIPs, two MoveIt ZIPs, the review PDF, a source manifest and SHA-256 checksums. Only the tag's release job has repository write permission. Release publishing waits for the portable build, ROS display tests and MoveIt planning/mock-execution tests on both Humble and Jazzy. @@ -50,6 +50,7 @@ Hardware repositories contain guide folders, without model copies or submodules: | PAROL6 | `parol6_description/` | | MSG | `msg_description/` | | SSG48 | `ssg48_description/` | +| RAFT | `raft_description/` | Keep the canonical guide in `guides/` here. After publishing the model release, export its guide and local previews to the corresponding hardware checkout: @@ -58,7 +59,7 @@ export its guide and local previews to the corresponding hardware checkout: python tools/export_hardware_guide.py --product par6 --revision CENTRAL_COMMIT --output /path/to/PAR6-Collaborative-Robot-Arm/par6_description ``` -Use `parol6`, `msg` or `ssg48` for the other products. `--revision` selects the +Use `parol6`, `msg`, `ssg48` or `raft` for the other products. `--revision` selects the central commit/tag for documentation and model-file links. The tool exports only README and preview images; it does not copy model packages. Review and commit those documentation files in the hardware repository. Model ZIP links @@ -77,7 +78,7 @@ git commit -m "Pin tested robot description models" ``` The application can select `external/robot_descriptions/mujoco/par6`, -`mujoco/parol6`, `mujoco/msg` or `mujoco/ssg48`, or the corresponding +`mujoco/parol6`, `mujoco/msg`, `mujoco/ssg48` or `mujoco/raft`, or the corresponding `urdf/PRODUCT_description` package. Every path is relative to that submodule's root. Clone these software consumers with `git clone --recurse-submodules URL`, or initialize an existing clone with `git submodule update --init --recursive`. diff --git a/docs/releases/README.md b/docs/releases/README.md index f293e67..8f7631e 100644 --- a/docs/releases/README.md +++ b/docs/releases/README.md @@ -2,6 +2,7 @@ [Changelog](../../CHANGELOG.md) · [Publishing instructions](../maintenance/RELEASING.md) +- [v2.19.0](v2.19.0.md) - [v2.18.1](v2.18.1.md) - [v2.18.0](v2.18.0.md) - [v2.17.0](v2.17.0.md) diff --git a/docs/releases/v2.19.0.md b/docs/releases/v2.19.0.md new file mode 100644 index 0000000..a493ff8 --- /dev/null +++ b/docs/releases/v2.19.0.md @@ -0,0 +1,23 @@ +# RAFT, the teleoperation leader arm for PAR6 + +This release adds RAFT, Source Robotics' teleoperation leader arm: PAR6's kinematic structure at 5/9 scale, with a +trigger on its handle that drives the follower's gripper. + +- **Two new packages:** `mujoco/raft` and `urdf/raft_description`, downloadable as `raft_mujoco.zip` and + `raft_urdf.zip`. They use PAR6's joint names, so RAFT's joint angles map one to one onto PAR6's. The MuJoCo model + has torque actuators (RAFT's servos run in current mode), the trigger, the optional J2 counterbalance tendon and + `home` / `parked` keyframes. The URDF package has native ROS 2 display support: + `ros2 launch raft_description display.launch.py`. +- **Built from RAFT's own model reference** (`source/raft/`), the same source as RAFT's firmware model. Both formats + are checked against its forward kinematics and gravity torques. One end of every joint range is a mechanical stop + measured on the first arm; the other comes from a sweep of the CAD. +- **Colours from the shared palette:** RAFT's printed parts, servos, horns and screws are separate visual pieces. + New keys `raft_structure`, `raft_trigger`, `raft_horns` and `raft_fasteners`; RAFT's servos follow `motors` or the + new `raft_motors` override. +- **Guides and checks:** a RAFT product guide and a RAFT column in "Meet the models"; the Linux ROS 2 checks, the + packaging and the release include `raft_description`. + +PAR6, PAROL6, MSG and SSG48 are unchanged: geometry, colors, kinematics, inertia, collision shapes, joint limits and +actuation settings stay as in v2.18.1. + +Models/data/docs remain CC BY 4.0; support code remains Apache-2.0. diff --git a/guides/README.md b/guides/README.md index e7ed4a1..d5397ed 100644 --- a/guides/README.md +++ b/guides/README.md @@ -8,6 +8,7 @@ | PAROL6 | [Get started](parol6.md) | Flange, both MSG jaws, both SSG48 jaws, pneumatic. | | MSG | [Get started](msg.md) | Standalone or mounted; Solid and Soft TPU jaws. | | SSG48 | [Get started](ssg48.md) | Standalone or mounted; Solid and Fin-ray jaws. | +| RAFT | [Get started](raft.md) | The teleoperation leader arm for PAR6, with its trigger. | Download one complete model package for your simulator. You do not need the authoring inputs or build tools to use it. diff --git a/guides/raft.md b/guides/raft.md new file mode 100644 index 0000000..463232e --- /dev/null +++ b/guides/raft.md @@ -0,0 +1,81 @@ +# RAFT models: getting started + +[All models](../README.md) | [PAR6](par6.md) | [Change colors](../docs/COLORS.md) | [License and attribution](../ATTRIBUTION.md) + +RAFT is the teleoperation leader arm for PAR6: PAR6's kinematic structure at 5/9 scale, with a trigger on its handle +that drives the follower's gripper. It is available in **MuJoCo and URDF**, in one configuration: the arm with its +handle and trigger. + +To customize appearance, edit the [shared color file](../source/appearance/colors.json) (`raft_structure`, +`raft_trigger`, `raft_horns`, `raft_fasteners`; the servos follow `motors`, or the `raft_motors` override) and +follow the [illustrated color guide](../docs/COLORS.md). One palette covers both formats and all tools. + +![RAFT, the handle held in front](../docs/images/raft.png) + +## 1. Download + +- **MuJoCo:** [download raft_mujoco.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/raft_mujoco.zip), then extract the `raft/` folder. +- **URDF:** [download raft_urdf.zip](https://github.com/Source-Robotics/robot_descriptions/releases/latest/download/raft_urdf.zip), then extract the `raft_description/` folder. + +Keep each extracted folder intact; the model files use relative paths to their meshes. No CAD tools or source build +are needed to use these downloads. + +| Format | Entry file | +| --- | --- | +| MuJoCo | [`scene.xml`](../mujoco/raft/scene.xml) to look at it, [`raft.xml`](../mujoco/raft/raft.xml) to put it into another MuJoCo world | +| URDF | [`raft.urdf`](../urdf/raft_description/raft.urdf) | + +## 2. Open the model + +**MuJoCo:** with the Python MuJoCo package installed, run this from the directory where you extracted the ZIP: + +```bash +python -m mujoco.viewer --mjcf=raft/scene.xml +``` + +For a checkout of the central `robot_descriptions` repository, run this from its root: + +```bash +python -m mujoco.viewer --mjcf=mujoco/raft/scene.xml +``` + +The hardware repository's `raft_description/` folder contains this guide and previews. Extract the model download +into a separate working directory or ROS workspace to use the actual model package. + +**URDF viewer:** open `raft_description/raft.urdf`, keeping the `meshes/` directory alongside it. + +**ROS 2 / RViz:** install the extracted `raft_description` package with colcon using +[the ROS 2 guide](../docs/ROS2.md), source your workspace, then run: + +```bash +ros2 launch raft_description display.launch.py +``` + +This opens RViz and joint sliders. ROS 1 retains `roslaunch raft_description display.launch`. + +## 3. Joints, actuators and the trigger + +The joints are named like PAR6's: `base_joint`, `shoulder_joint`, `elbow_joint`, `wrist_1_joint`, `wrist_2_joint`, +`wrist_3_joint`, plus `trigger_joint`. `q = 0` is the standby pose, PAR6's standby pose, so RAFT's joint angles map +one to one onto PAR6's. `tcp` is the tool point on the handle. + +The MuJoCo actuators (`_actuator`) take joint torque in Nm, as RAFT's servos run in current mode. The tendon +`j2_spring` models the optional J2 counterbalance; its stiffness 0 means no spring is fitted. + +## 4. Check dimensions and frames + +- [RAFT DH convention](../mujoco/raft/docs/DH.md) +- [RAFT and PAR6: the 5/9 scale](../mujoco/raft/docs/KINEMATICS_COMPARISON.md) +- [Joint ranges and where they come from](../mujoco/raft/docs/JOINT_LIMITS.md) +- [Masses, centres of mass and inertias](../mujoco/raft/docs/DYNAMICS.md) +- [Actuation and the J2 counterbalance](../mujoco/raft/docs/ACTUATION.md) + +One end of every joint range is a mechanical stop measured on the first arm; the masses come from the CAD; the +servos' friction, damping and rotor inertia are estimates. + +## The arm itself + +RAFT's parts, BOM, assembly manual and ESP32 firmware are in the +[RAFT repository](https://github.com/Source-Robotics/RAFT-teleoperation-arm). Its firmware computes gravity +compensation from the same model reference these packages are built from +([`source/raft/`](../source/raft/README.md)). diff --git a/moveit/par6_moveit_config/package.xml b/moveit/par6_moveit_config/package.xml index a3fee4e..0c82372 100644 --- a/moveit/par6_moveit_config/package.xml +++ b/moveit/par6_moveit_config/package.xml @@ -1,6 +1,6 @@ par6_moveit_config - 2.18.1 + 2.19.0 MoveIt 2 planning and mock execution for Source Robotics PAR6. Source Robotics CC-BY-4.0 diff --git a/moveit/parol6_moveit_config/package.xml b/moveit/parol6_moveit_config/package.xml index 387edf9..77229b3 100644 --- a/moveit/parol6_moveit_config/package.xml +++ b/moveit/parol6_moveit_config/package.xml @@ -1,6 +1,6 @@ parol6_moveit_config - 2.18.1 + 2.19.0 MoveIt 2 planning and mock execution for Source Robotics PAROL6. Source Robotics CC-BY-4.0 diff --git a/mujoco/msg/docs/COLORS.md b/mujoco/msg/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/mujoco/msg/docs/COLORS.md +++ b/mujoco/msg/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/mujoco/msg/docs/colors.json b/mujoco/msg/docs/colors.json index 6971ca3..09b8a15 100644 --- a/mujoco/msg/docs/colors.json +++ b/mujoco/msg/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/mujoco/msg/sim2bot.robot.json b/mujoco/msg/sim2bot.robot.json index 2816916..9506812 100644 --- a/mujoco/msg/sim2bot.robot.json +++ b/mujoco/msg/sim2bot.robot.json @@ -5,7 +5,7 @@ "category": "gripper", "license": "CC-BY-4.0", "sourceUrl": "https://github.com/Source-Robotics/robot_descriptions", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "scene_solid.xml", "dof": 1, "rootFrame": "flange", diff --git a/mujoco/par6/docs/COLORS.md b/mujoco/par6/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/mujoco/par6/docs/COLORS.md +++ b/mujoco/par6/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/mujoco/par6/docs/colors.json b/mujoco/par6/docs/colors.json index 6971ca3..09b8a15 100644 --- a/mujoco/par6/docs/colors.json +++ b/mujoco/par6/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/mujoco/par6/sim2bot.robot.json b/mujoco/par6/sim2bot.robot.json index 56db50a..a9c539e 100644 --- a/mujoco/par6/sim2bot.robot.json +++ b/mujoco/par6/sim2bot.robot.json @@ -6,7 +6,7 @@ "license": "CC-BY-4.0", "sourceUrl": "https://source-robotics.com/", "sourceRevision": "2025-02-10", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "scene_msg.xml", "dof": 6, "tcpSites": [ diff --git a/mujoco/parol6/docs/CHANGELOG.md b/mujoco/parol6/docs/CHANGELOG.md index 7e4db8a..dc83703 100644 --- a/mujoco/parol6/docs/CHANGELOG.md +++ b/mujoco/parol6/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # PAROL6 description changelog -## 2.18.1 - 2026-11-03 +## 2.19.0 - 2026-11-03 - Added native ROS 2 installation, RViz display and joint sliders for all six tool profiles. - Retained ROS 1 compatibility and portable model/physical definitions. diff --git a/mujoco/parol6/docs/COLORS.md b/mujoco/parol6/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/mujoco/parol6/docs/COLORS.md +++ b/mujoco/parol6/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/mujoco/parol6/docs/colors.json b/mujoco/parol6/docs/colors.json index 6971ca3..09b8a15 100644 --- a/mujoco/parol6/docs/colors.json +++ b/mujoco/parol6/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/mujoco/parol6/sim2bot.robot.json b/mujoco/parol6/sim2bot.robot.json index 2535901..2437d56 100644 --- a/mujoco/parol6/sim2bot.robot.json +++ b/mujoco/parol6/sim2bot.robot.json @@ -6,7 +6,7 @@ "license": "CC-BY-4.0", "sourceUrl": "https://github.com/Source-Robotics/robot_descriptions", "sourceRevision": "PAROL6 included export; see source/parol6/model.json in the authoring repository", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "scene_msg.xml", "dof": 6, "tcpSites": [ diff --git a/mujoco/raft/LICENSE b/mujoco/raft/LICENSE new file mode 100644 index 0000000..4ea99c2 --- /dev/null +++ b/mujoco/raft/LICENSE @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/mujoco/raft/README.md b/mujoco/raft/README.md new file mode 100644 index 0000000..e7b2ce7 --- /dev/null +++ b/mujoco/raft/README.md @@ -0,0 +1,55 @@ +# Source Robotics RAFT: MuJoCo leader arm + +This self-contained package contains the **RAFT teleoperation leader arm**: six joints like PAR6's at 5/9 scale, +the trigger on its handle, torque actuators and the optional J2 counterbalance spring. All required meshes and +materials are included. Keep the whole folder together when moving or uploading it. + +![RAFT, the handle held in front](previews/raft.png) + +| File | Use it for | +| --- | --- | +| `scene.xml` | Viewer scene: the arm with a floor and lighting. Open this for a quick look. | +| `raft.xml` | Native MJCF robot definition: bodies, joints, inertials, visuals, collisions, actuators and keyframes. Use this when integrating into another MuJoCo world. | + +Detailed notes are in [docs/](docs/), model images in [previews/](previews/), and geometry in `assets/` +(`assets/visual/` holds the separately coloured printed parts, servos, horns and screws). The model entry files +remain at the package root. + +## Open the model + +After extracting the ZIP, run this from its parent directory: + +```bash +python -m mujoco.viewer --mjcf=raft/scene.xml +``` + +## Joints, actuators and frames + +| Bodies | Joints | Actuators | +| --- | --- | --- | +| `base_link`, `shoulder_link`, `upper_arm_link`, `forearm_link`, `wrist_1_link`, `wrist_2_link`, `wrist_3_link`, `trigger_link` | `base_joint`, `shoulder_joint`, `elbow_joint`, `wrist_1_joint`, `wrist_2_joint`, `wrist_3_joint`, `trigger_joint` | `_actuator` | + +The actuators are `motor` actuators: `ctrl` is the joint torque in Nm, as RAFT's servos run in current mode +([actuation](docs/ACTUATION.md)). The site `tcp` is the tool point; the sensors `tcp_pos` and `tcp_quat` report it. +`qpos = 0` is the standby pose, PAR6's standby pose, so RAFT's joint angles map one to one onto PAR6's +([DH](docs/DH.md), [comparison](docs/KINEMATICS_COMPARISON.md)). Keyframes: `home` (all zero), `parked` (RAFT's +firmware rest pose), `handle_in_front`, `reach_forward` and `stretched`. + +Visual geoms are group 2 and collision geoms, the complete link meshes, group 3. The tendon `j2_spring` models the +optional J2 counterbalance; its stiffness 0 means no spring is fitted. + +## Physical properties and current limits + +Complete arm mass: **371.3 g**; moving links and trigger: **283.5 g**. Masses, centres of mass +and inertias come from the CAD ([dynamics](docs/DYNAMICS.md)); the joint ranges from the measured mechanical stops +and a CAD sweep ([joint ranges](docs/JOINT_LIMITS.md)). The servos' friction, damping and rotor inertia are +estimates, not measurements. + +The model is built from RAFT's reference export, the same source as the firmware's own model in the +[RAFT repository](https://github.com/Source-Robotics/RAFT-teleoperation-arm). + +## License and attribution + +Models and assets: **CC BY 4.0**. See [attribution and license scope](docs/ATTRIBUTION.md). + +[Changing colors](docs/COLORS.md): shared palette, part overrides and illustrated examples. diff --git a/mujoco/raft/assets/base_link.stl b/mujoco/raft/assets/base_link.stl new file mode 100644 index 0000000..2618394 Binary files /dev/null and b/mujoco/raft/assets/base_link.stl differ diff --git a/mujoco/raft/assets/forearm_link.stl b/mujoco/raft/assets/forearm_link.stl new file mode 100644 index 0000000..58ba361 Binary files /dev/null and b/mujoco/raft/assets/forearm_link.stl differ diff --git a/mujoco/raft/assets/shoulder_link.stl b/mujoco/raft/assets/shoulder_link.stl new file mode 100644 index 0000000..d792b47 Binary files /dev/null and b/mujoco/raft/assets/shoulder_link.stl differ diff --git a/mujoco/raft/assets/trigger_link.stl b/mujoco/raft/assets/trigger_link.stl new file mode 100644 index 0000000..faa1adf Binary files /dev/null and b/mujoco/raft/assets/trigger_link.stl differ diff --git a/mujoco/raft/assets/upper_arm_link.stl b/mujoco/raft/assets/upper_arm_link.stl new file mode 100644 index 0000000..77beb41 Binary files /dev/null and b/mujoco/raft/assets/upper_arm_link.stl differ diff --git a/mujoco/raft/assets/visual/base_link_fasteners.stl b/mujoco/raft/assets/visual/base_link_fasteners.stl new file mode 100644 index 0000000..b7915ef Binary files /dev/null and b/mujoco/raft/assets/visual/base_link_fasteners.stl differ diff --git a/mujoco/raft/assets/visual/base_link_motors.stl b/mujoco/raft/assets/visual/base_link_motors.stl new file mode 100644 index 0000000..698e6a0 Binary files /dev/null and b/mujoco/raft/assets/visual/base_link_motors.stl differ diff --git a/mujoco/raft/assets/visual/base_link_structure.stl b/mujoco/raft/assets/visual/base_link_structure.stl new file mode 100644 index 0000000..df8feae Binary files /dev/null and b/mujoco/raft/assets/visual/base_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/forearm_link_fasteners.stl b/mujoco/raft/assets/visual/forearm_link_fasteners.stl new file mode 100644 index 0000000..3220128 Binary files /dev/null and b/mujoco/raft/assets/visual/forearm_link_fasteners.stl differ diff --git a/mujoco/raft/assets/visual/forearm_link_motors.stl b/mujoco/raft/assets/visual/forearm_link_motors.stl new file mode 100644 index 0000000..e759a6a Binary files /dev/null and b/mujoco/raft/assets/visual/forearm_link_motors.stl differ diff --git a/mujoco/raft/assets/visual/forearm_link_structure.stl b/mujoco/raft/assets/visual/forearm_link_structure.stl new file mode 100644 index 0000000..b159145 Binary files /dev/null and b/mujoco/raft/assets/visual/forearm_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/shoulder_link_fasteners.stl b/mujoco/raft/assets/visual/shoulder_link_fasteners.stl new file mode 100644 index 0000000..74824c6 Binary files /dev/null and b/mujoco/raft/assets/visual/shoulder_link_fasteners.stl differ diff --git a/mujoco/raft/assets/visual/shoulder_link_horns.stl b/mujoco/raft/assets/visual/shoulder_link_horns.stl new file mode 100644 index 0000000..c382a4e Binary files /dev/null and b/mujoco/raft/assets/visual/shoulder_link_horns.stl differ diff --git a/mujoco/raft/assets/visual/shoulder_link_motors.stl b/mujoco/raft/assets/visual/shoulder_link_motors.stl new file mode 100644 index 0000000..befbcb0 Binary files /dev/null and b/mujoco/raft/assets/visual/shoulder_link_motors.stl differ diff --git a/mujoco/raft/assets/visual/shoulder_link_structure.stl b/mujoco/raft/assets/visual/shoulder_link_structure.stl new file mode 100644 index 0000000..4b8e3cd Binary files /dev/null and b/mujoco/raft/assets/visual/shoulder_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/trigger_link_horns.stl b/mujoco/raft/assets/visual/trigger_link_horns.stl new file mode 100644 index 0000000..b2846aa Binary files /dev/null and b/mujoco/raft/assets/visual/trigger_link_horns.stl differ diff --git a/mujoco/raft/assets/visual/trigger_link_structure.stl b/mujoco/raft/assets/visual/trigger_link_structure.stl new file mode 100644 index 0000000..f7d2f14 Binary files /dev/null and b/mujoco/raft/assets/visual/trigger_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/upper_arm_link_horns.stl b/mujoco/raft/assets/visual/upper_arm_link_horns.stl new file mode 100644 index 0000000..838eb32 Binary files /dev/null and b/mujoco/raft/assets/visual/upper_arm_link_horns.stl differ diff --git a/mujoco/raft/assets/visual/upper_arm_link_structure.stl b/mujoco/raft/assets/visual/upper_arm_link_structure.stl new file mode 100644 index 0000000..ee5a8b0 Binary files /dev/null and b/mujoco/raft/assets/visual/upper_arm_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/wrist_1_link_fasteners.stl b/mujoco/raft/assets/visual/wrist_1_link_fasteners.stl new file mode 100644 index 0000000..031cafc Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_1_link_fasteners.stl differ diff --git a/mujoco/raft/assets/visual/wrist_1_link_horns.stl b/mujoco/raft/assets/visual/wrist_1_link_horns.stl new file mode 100644 index 0000000..1df7e76 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_1_link_horns.stl differ diff --git a/mujoco/raft/assets/visual/wrist_1_link_motors.stl b/mujoco/raft/assets/visual/wrist_1_link_motors.stl new file mode 100644 index 0000000..b14979f Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_1_link_motors.stl differ diff --git a/mujoco/raft/assets/visual/wrist_1_link_structure.stl b/mujoco/raft/assets/visual/wrist_1_link_structure.stl new file mode 100644 index 0000000..204f434 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_1_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/wrist_2_link_fasteners.stl b/mujoco/raft/assets/visual/wrist_2_link_fasteners.stl new file mode 100644 index 0000000..0925626 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_2_link_fasteners.stl differ diff --git a/mujoco/raft/assets/visual/wrist_2_link_horns.stl b/mujoco/raft/assets/visual/wrist_2_link_horns.stl new file mode 100644 index 0000000..965edcf Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_2_link_horns.stl differ diff --git a/mujoco/raft/assets/visual/wrist_2_link_motors.stl b/mujoco/raft/assets/visual/wrist_2_link_motors.stl new file mode 100644 index 0000000..e83f7be Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_2_link_motors.stl differ diff --git a/mujoco/raft/assets/visual/wrist_2_link_structure.stl b/mujoco/raft/assets/visual/wrist_2_link_structure.stl new file mode 100644 index 0000000..22f1ff1 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_2_link_structure.stl differ diff --git a/mujoco/raft/assets/visual/wrist_3_link_fasteners.stl b/mujoco/raft/assets/visual/wrist_3_link_fasteners.stl new file mode 100644 index 0000000..e98196f Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_3_link_fasteners.stl differ diff --git a/mujoco/raft/assets/visual/wrist_3_link_horns.stl b/mujoco/raft/assets/visual/wrist_3_link_horns.stl new file mode 100644 index 0000000..35f87d8 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_3_link_horns.stl differ diff --git a/mujoco/raft/assets/visual/wrist_3_link_motors.stl b/mujoco/raft/assets/visual/wrist_3_link_motors.stl new file mode 100644 index 0000000..a595cd4 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_3_link_motors.stl differ diff --git a/mujoco/raft/assets/visual/wrist_3_link_structure.stl b/mujoco/raft/assets/visual/wrist_3_link_structure.stl new file mode 100644 index 0000000..2073df9 Binary files /dev/null and b/mujoco/raft/assets/visual/wrist_3_link_structure.stl differ diff --git a/mujoco/raft/assets/wrist_1_link.stl b/mujoco/raft/assets/wrist_1_link.stl new file mode 100644 index 0000000..620f9e3 Binary files /dev/null and b/mujoco/raft/assets/wrist_1_link.stl differ diff --git a/mujoco/raft/assets/wrist_2_link.stl b/mujoco/raft/assets/wrist_2_link.stl new file mode 100644 index 0000000..4aa9ed9 Binary files /dev/null and b/mujoco/raft/assets/wrist_2_link.stl differ diff --git a/mujoco/raft/assets/wrist_3_link.stl b/mujoco/raft/assets/wrist_3_link.stl new file mode 100644 index 0000000..c907022 Binary files /dev/null and b/mujoco/raft/assets/wrist_3_link.stl differ diff --git a/mujoco/raft/docs/ACTUATION.md b/mujoco/raft/docs/ACTUATION.md new file mode 100644 index 0000000..c5716eb --- /dev/null +++ b/mujoco/raft/docs/ACTUATION.md @@ -0,0 +1,43 @@ +# Actuation + +[Documentation index](README.md) + +RAFT's seven Feetech HLS3606 servos run in current (torque) mode: the firmware computes gravity, friction and +force-feedback torques itself and sends a current to each servo. The MuJoCo model works the same way. + +| Actuator | Joint | ctrlrange (Nm) | +|---|---|---| +| `base_actuator` | `base_joint` | +-0.5884 | +| `shoulder_actuator` | `shoulder_joint` | +-0.5884 | +| `elbow_actuator` | `elbow_joint` | +-0.5884 | +| `wrist_1_actuator` | `wrist_1_joint` | +-0.5884 | +| `wrist_2_actuator` | `wrist_2_joint` | +-0.5884 | +| `wrist_3_actuator` | `wrist_3_joint` | +-0.5884 | +| `trigger_actuator` | `trigger_joint` | +-0.5884 | + +- **Actuators** are `motor` actuators: `ctrl` is the joint output torque in Nm, limited to the servo's stall torque + (6 kg·cm at 6 V). A simulation sends what the firmware sends; there are no position servos to tune. +- **The URDF** carries the same limits as `effort` (Nm) and `velocity` = 6 rad/s, a conservative value + below the servo's no-load speed. + +## Joint dynamics (estimates) + +| Value | Model | What it is | +|---|---:|---| +| armature | 0.003 kg·m² | the motor rotor reflected through the 1:205 gear (about 0.7 g·cm² × 205²) | +| frictionloss (URDF `friction`) | 0.02 Nm | gearbox Coulomb friction at the output | +| damping | 0.01 Nm·s/rad | viscous friction at the output | + +These are estimates, not measurements. On the real arm the gears turn so easily from the joint side that the +firmware adds damping on the light wrist joints in gravity mode. + +## J2 counterbalance + +The MuJoCo model has the optional J2 counterbalance as a tendon, `j2_spring`, between the sites `j2_spring_anchor` +(13 mm above the J2 axis, on `shoulder_link`) and `j2_spring_pin` (on `upper_arm_link`). Its stiffness is 0, so the +spring is not fitted. Set it to the spring's rate in N/m to simulate one; the cable geometry gives a J2 torque of +-k · 13 mm · 20 mm · sin(q2). The mount is described in the RAFT repository's [J2 spring guide](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/docs/spring_mount.md). + +## Not modelled + +Servo current limits and derating with temperature, gear backlash, gear efficiency, and cable forces. diff --git a/mujoco/raft/docs/ATTRIBUTION.md b/mujoco/raft/docs/ATTRIBUTION.md new file mode 100644 index 0000000..0d15136 --- /dev/null +++ b/mujoco/raft/docs/ATTRIBUTION.md @@ -0,0 +1,18 @@ +# Model license and attribution + +Copyright 2026 Source Robotics. + +The robot/gripper descriptions, geometry, textures, physical data and +documentation in this package are licensed under **CC-BY-4.0**. +See the [full model license](../LICENSE). + +Source: [Source Robotics robot_descriptions](https://github.com/Source-Robotics/robot_descriptions). +License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0/). + +When sharing the models or adaptations, credit Source Robotics, link to the +source and license, retain supplied notices, and indicate your changes. +For example: + +> Robot models by Source Robotics, https://github.com/Source-Robotics/robot_descriptions, +> CC BY 4.0, https://creativecommons.org/licenses/by/4.0/. +> Changes: describe your modifications, or state that none were made. diff --git a/mujoco/raft/docs/COLORS.md b/mujoco/raft/docs/COLORS.md new file mode 100644 index 0000000..3f1f4f4 --- /dev/null +++ b/mujoco/raft/docs/COLORS.md @@ -0,0 +1,195 @@ +# Changing model colors + +[Back to the repository](../README.md) + +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +The starting palette keeps the existing appearance. + +## Quick example + +In that file, change these entries inside `colors`: + +```json +"structure": "#CAD1EE", +"motors": "#2060E0", +"camera_lens": "#080808", +"msg_soft_inserts": "#FFD21A" +``` + +This gives both robots blue J1/J2/J3 motors and makes the MSG motor blue too. +The camera lens stays black, the structures stay light, and the Soft TPU inserts +stay yellow. Keep the other settings in the file. + +![Starting palette and blue-motor example on PAROL6 with MSG Soft TPU jaws](../previews/color_examples/motors.png) + +The images in this guide are renders of the actual MuJoCo models. Each pair +uses the same model, pose, lighting and camera; only its example colors change. +The shared build applies the same settings to URDF. Examples are independent: +start from the default palette for each one, then change only the listed entries. + +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the +repository root: + +```bash +python tools/build.py --models-only --render +``` + +This uses the existing CAD meshes, applies the palette, rebakes the badge, +refreshes the dimensioned review snapshots, validates the models and exports +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds +reuse the checked snapshots without OpenGL; refresh them after changing colors. +If you also changed CAD/geometry, omit `--models-only`. + +Open a rebuilt model to see the result. Colors are baked into the model files +and badge texture: no extra process runs while viewing or simulating. This is +an editing convenience, not a simulation-speed optimization. It does not add +bodies, joints, geoms, triangles or higher-resolution textures, or change +kinematics, collisions, masses, COM, inertia or controller settings. + +## Common colors + +Each entry accepts `"#RRGGBB"` or `[red, green, blue, 1]`, with channels from +0 to 1. The alpha channel must be 1. Existing precise gray/black values use +arrays; replacing one with a HEX color is fine. + +| Key inside `colors` | Parts controlled | +| --- | --- | +| `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | +| `camera_lens` | MSG lens | +| `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | +| `msg_soft_holders` | MSG Soft TPU black holders and carriages | +| `msg_soft_inserts` | MSG Soft TPU honeycomb inserts | +| `ssg48_solid_jaws` | SSG48 Solid jaws and carriage geometry | +| `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | +| `pneumatic_body` | Pneumatic gripper housing | +| `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | + +A color applies to an existing visual partition. For example, a jaw and its +carriage cannot get separate colors if they share one visual mesh. + +### Example: teal MSG inserts and orange SSG48 jaws + +Change these three entries inside `colors`; keep the other entries as they are: + +```json +"msg_soft_holders": "#26364A", +"msg_soft_inserts": "#20B7A8", +"ssg48_solid_jaws": "#F08020" +``` + +![MSG Soft TPU and SSG48 Solid jaws, starting colors on the left and example colors on the right](../previews/color_examples/jaws.png) + +The dark blue holders and teal honeycomb inserts affect the MSG Soft TPU option. +The orange setting affects SSG48 Solid jaws. Each applies to both fingers, +standalone and arm-mounted, while the other jaw options keep their own colors. + +## Override an individual part + +The optional `overrides` section takes precedence over common colors. For +example, keep the common motor color black but make J1 orange and the MSG motor +blue: + +```json +"overrides": { + "j1_motor": "#F08020", + "msg_motor": "#2060E0" +} +``` + +Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). +Remove an override to return that part to its common color. Tool colors stay +the same whether the tool is standalone or mounted on either robot. + +`par6_panel` affects the textured cover surrounding the badge. Without that +override it follows `par6_structure`, which in turn follows `structure`. +Badge colors are baked into RGB pixels, so array values there are rounded to +the nearest 8-bit channel. PAROL6 retains its existing unbadged cover. + +Unknown keys, duplicate keys, invalid HEX values, non-finite/out-of-range +channels and transparency are rejected before the build changes models. + +## Badge artwork + +The same file has a `badge` section for `background`, `emblem`, `symbol`, +`wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run +the same build command. Badge size, resolution, placement and SVG shapes stay +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). + +### Example: graphite cover and teal logo + +Set `overrides` to the following, or add this entry to your existing overrides: + +```json +"overrides": { + "par6_panel": "#303A48" +} +``` + +Then set the badge section to: + +```json +"badge": { + "background": "#15364A", + "emblem": "#20B7A8", + "symbol": "#15364A", + "wordmark": "#20B7A8", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#20B7A8" +} +``` + +![Close-up of the PAR6 side badge with starting colors and a graphite-and-teal example](../previews/color_examples/badge.png) + +This changes the existing PAR6 cover and badge artwork. The logo shapes, +placement and texture resolution stay the same. PAROL6's unbadged cover is +unaffected by these PAR6-specific settings. + +The color fields retained in the historical badge/jaw/pneumatic geometry +configs are baseline settings for their low-level geometry tools. The complete +build always applies `source/appearance/colors.json` afterwards; use the shared +file for maintained colors. It never rewrites those geometry configurations. + +## Downloaded packages and previews + +Every package includes `docs/colors.json`, a copy of the palette used when it +was built. It is a record, not a runtime configuration. To regenerate a shared +theme, use the central source checkout and the command above. + +For a one-off edit without the build environment, you can still change visual +RGBA values in a downloaded URDF or MJCF. Those changes apply only to the files +you edit. For example, the retained material `j1_actuator_black` controls J1, +`msg_camera_lens_black` controls the lens, and `msg_jaw_yellow` controls the TPU +inserts. Historical material names stay unchanged when recolored. Leave hidden +collision geoms and collision/inertial/joint definitions alone. The PAR6 cover +uses an embedded texture; change its palette through the build instead. + +The `--render` flag refreshes the twelve dimensioned review sheets. Other guide +screenshots are separate presentation assets. To refresh those after a theme +change, run the optional renderers and rebuild to copy them into packages: + +```bash +python tools/render_parol6_previews.py +python tools/render_gripper_previews.py +python tools/render_msg_camera_previews.py +python tools/build.py --models-only +``` + +To regenerate the optional review PDF too, use +`python tools/build_review_sheets.py --pdf` with the review dependencies installed. + +To refresh the three illustrated comparisons in this guide, run +`python tools/render_color_examples.py`, then rebuild the packages. This optional +OpenGL renderer uses temporary model copies and leaves the active palette, +runtime models and checked badge texture unchanged. The exact settings and +viewing poses are recorded in the [render manifest](../previews/color_examples/render_manifest.json). diff --git a/mujoco/raft/docs/DH.md b/mujoco/raft/docs/DH.md new file mode 100644 index 0000000..4cfb4eb --- /dev/null +++ b/mujoco/raft/docs/DH.md @@ -0,0 +1,35 @@ +# RAFT DH convention + +[Documentation index](README.md) + +RAFT uses PAR6's kinematic structure: the standard DH (CM6) table, the same joint directions and the same zero +pose, with every length scaled to about 5/9 ([comparison](KINEMATICS_COMPARISON.md)). + +- **Frames.** Link *i*'s frame is DH frame *i - 1*, and joint *i* turns about its z axis. `wrist_3_link` is DH frame + 5, and the `tcp` frame is DH frame 6. +- **Zero pose.** `q = 0` is the standby pose: upper arm vertical, forearm level and forward, wrist straight. It is + PAR6's standby pose, and the `home` keyframe. +- **PAR6 / RCB angles.** The RCB runtime works in DH angles: theta = q + [0, -90, 180, 0, 0, 180] deg for joints 1 + to 6. They are continuous and never wrapped, as in the runtime. +- **Units.** Metres and radians in the model files; millimetres and degrees in these tables. + +## DH table (model version 2.19.0) + +| Joint | d (mm) | a (mm) | alpha | theta offset | +|---|---:|---:|---:|---:| +| `base_joint` | 51.220 | 13.000 | -90 deg | 0 deg | +| `shoulder_joint` | 0.000 | 100.000 | 180 deg | -90 deg | +| `elbow_joint` | 0.000 | -24.160 | 90 deg | 180 deg | +| `wrist_1_joint` | -97.920 | 0.000 | -90 deg | 0 deg | +| `wrist_2_joint` | 0.000 | 0.000 | 90 deg | 0 deg | +| `wrist_3_joint` | -78.277 | -0.000 | 180 deg | 180 deg | + +The trigger (`trigger_joint`) is not part of the DH chain. Its hinge frame is fixed to `wrist_3_link` (from the CAD +assembly), and it turns about the hinge frame's z axis. + +## Tool point + +`tcp` sits on the trigger loop, 78.277 mm from the J6 axis along the tool axis. That is exactly 5/9 of the +140.9 mm of PAR6's SSG48 and MSG tool points in the +[robot_descriptions kinematic review](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/kinematics/robot_kinematics_review.pdf). +RAFT has no separate flange: the handle is fixed to the J6 horn. diff --git a/mujoco/raft/docs/DYNAMICS.md b/mujoco/raft/docs/DYNAMICS.md new file mode 100644 index 0000000..b0ce7fa --- /dev/null +++ b/mujoco/raft/docs/DYNAMICS.md @@ -0,0 +1,33 @@ +# Masses, centres of mass and inertias + +[Documentation index](README.md) + +The mass properties are SolidWorks' (RAFT inertials.pdf, with the masses corrected to the printed parts at the +print table's settings). Each link includes its printed parts, the servo it carries, the horn it is fixed to and +the screws. They are given in each link's own frame; the inertias are about the centre of mass, and both formats +carry the full tensor, off-diagonal terms included. + +| Link | Mass (g) | COM in the link frame (mm) | +|---|---:|---| +| `base_link` | 87.76 | -7.30, -0.42, 10.52 | +| `shoulder_link` | 43.14 | 6.83, -0.10, 48.72 | +| `upper_arm_link` | 18.20 | 0.00, -50.00, -23.15 | +| `forearm_link` | 71.40 | 14.84, 7.44, 2.50 | +| `wrist_1_link` | 46.10 | -0.02, 21.35, -71.39 | +| `wrist_2_link` | 35.10 | 4.64, 0.03, 0.48 | +| `wrist_3_link` | 55.30 | 1.62, 26.17, -45.71 | +| `trigger_link` | 14.30 | -13.10, 15.20, 12.78 | +| total | 371.30 | | + +## Status + +| Item | Status | +|---|---| +| Link masses | From CAD. Not yet weighed: printed parts vary with filament and infill. | +| Centres of mass and inertias | From CAD. | +| Base | From CAD. The ESP32-PoE board and the cables are not included. | + +RAFT's firmware compensates gravity with the same numbers: one generator in the RAFT repository +([`firmware/tools/gen_model.py`](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/tools/gen_model.py)) writes the firmware's model and the +reference these packages are built from, so a mass that is off shows the same way in both. Weighing each printed +link with its servo and horn is the next improvement. diff --git a/mujoco/raft/docs/JOINT_LIMITS.md b/mujoco/raft/docs/JOINT_LIMITS.md new file mode 100644 index 0000000..5b960de --- /dev/null +++ b/mujoco/raft/docs/JOINT_LIMITS.md @@ -0,0 +1,46 @@ +# Joint ranges + +Angles in the model's convention: 0 is the standby pose (PAR6's standby pose), positive about each joint's z axis. +The URDF `` and the MuJoCo `range` carry the same numbers. + +| Joint | Lower | Upper | Where the numbers come from | +|---|---:|---:|---| +| `base_joint` | -136.28 deg | 136.28 deg | lower: measured stop; upper: the same angle mirrored (the CAD has no J1 stop; not measured) | +| `shoulder_joint` | -83.96 deg | 131.50 deg | lower: measured stop (CAD sweep -83.0); upper: CAD sweep | +| `elbow_joint` | -80.33 deg | 92.81 deg | lower: measured, the forearm lying limp on the upper arm (the CAD parts meet at about -77); upper: measured stop (CAD sweep 92.5) | +| `wrist_1_joint` | -158.56 deg | 153.50 deg | lower: measured stop (CAD sweep -154.0); upper: CAD sweep | +| `wrist_2_joint` | -126.00 deg | 119.72 deg | lower: CAD sweep; upper: measured stop (CAD sweep 117.5) | +| `wrist_3_joint` | -147.50 deg | 149.37 deg | lower: CAD sweep; upper: measured stop (CAD sweep 147.5) | +| `trigger_joint` | -39.63 deg | 0.00 deg | lower: measured stop, trigger released; upper: trigger flush against the handle (CAD) | + +## Where the numbers come from + +- **Measured stops.** Every joint has one mechanical stop. Its angle was measured on the first arm (2026-11-02), + and RAFT's firmware calibrates against it (`kStopDeg` in + [`firmware/src/main/arm_design.h`](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/src/main/arm_design.h)). These are the most trustworthy + numbers in the table. +- **CAD sweep.** The other end of a range comes from the CAD meshes: one joint turned, the others at 0, until its + moving parts come within 0.5 mm of the parts it turns against. Where a sweep can be compared with a measured stop + it ends 0.3 to 2.2 deg early (J2, J3, J5, J6) or 4.6 deg early (J4), so these ends are slightly conservative. + They hold with the other joints at 0; some wrist poses reach further or less far. +- **Forearm lying limp.** J3's lower end was read on the first arm with the arm limp and the forearm lying folded + on the upper arm (2026-11-06). The CAD parts meet at about -77 deg; the printed parts settle 3 deg further. +- **Trigger.** 0 is the trigger flush against the handle, as in the CAD; turning it positive would push it into + the handle. It opens toward negative angles, to its stop 39.63 deg from flush. +- **Not measured.** The CAD has no J1 stop. J1's upper end mirrors its measured stop until the real end is + measured. + +The ranges of one arm can be measured with the firmware's `calib_range` command (move every joint gently to both +ends). Report measured ranges in the [RAFT repository's issues](https://github.com/Source-Robotics/RAFT-teleoperation-arm/issues). + +## Keyframes + +| Keyframe | qpos (deg), base_joint to trigger_joint | +|---|---| +| `home` | 0, 0, 0, 0, 0, 0, 0 | +| `parked` | 0, -82, -80, 0, 0, 0, 0 | +| `handle_in_front` | 0, 20, 0, 0, 0, 0, 0 | +| `reach_forward` | 0, 45, 45, 0, 0, 0, 0 | +| `stretched` | 0, 90, 90, 0, 0, 0, 0 | + +`home` is the standby pose. `parked` is RAFT's firmware rest pose: the arm lying back the way it rests limp. diff --git a/mujoco/raft/docs/KINEMATICS_COMPARISON.md b/mujoco/raft/docs/KINEMATICS_COMPARISON.md new file mode 100644 index 0000000..14ca91a --- /dev/null +++ b/mujoco/raft/docs/KINEMATICS_COMPARISON.md @@ -0,0 +1,22 @@ +# RAFT and PAR6 + +[Documentation index](README.md) + +RAFT is a teleoperation leader for PAR6: the same kinematic structure, joint directions and zero pose, scaled to +5/9. Joint angles therefore map one to one, which is how the firmware teleoperates PAR6. + +| Length | RAFT (mm) | PAR6 (mm) | RAFT / PAR6 | +|---|---:|---:|---:| +| a2 | 13.000 | 23.4207 | 0.5551 | +| a3 | 100.000 | 180 | 0.5556 | +| a4 | 24.160 | 43.5 | 0.5554 | +| a5 | 97.920 | 177 | 0.5532 | +| a6 (TCP) | 78.277 | 140.9 | 0.5556 | + +PAR6's lengths are those of the +[robot_descriptions kinematic review](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/kinematics/robot_kinematics_review.pdf), +with the SSG48 or MSG tool point for a6. RAFT's base (d1 = 51.22 mm against PAR6's 110.5 mm) is lower than a pure +scale would make it; the base height does not change any joint angle. + +Not scaled: the joint ranges (RAFT's mechanical stops, [joint ranges](JOINT_LIMITS.md)), the masses, and the +actuation (RAFT's servos run in current mode, [actuation](ACTUATION.md)). diff --git a/mujoco/raft/docs/README.md b/mujoco/raft/docs/README.md new file mode 100644 index 0000000..4c4b2d1 --- /dev/null +++ b/mujoco/raft/docs/README.md @@ -0,0 +1,11 @@ +# Package documentation + +[Back to the model instructions](../README.md) + +- [Actuation](ACTUATION.md) +- [Model license and attribution](ATTRIBUTION.md) +- [Changing model colors](COLORS.md) +- [RAFT DH convention](DH.md) +- [Masses, centres of mass and inertias](DYNAMICS.md) +- [Joint ranges](JOINT_LIMITS.md) +- [RAFT and PAR6](KINEMATICS_COMPARISON.md) diff --git a/mujoco/raft/docs/colors.json b/mujoco/raft/docs/colors.json new file mode 100644 index 0000000..09b8a15 --- /dev/null +++ b/mujoco/raft/docs/colors.json @@ -0,0 +1,29 @@ +{ + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [0.06, 0.06, 0.065, 1], + "camera_lens": [0.06, 0.06, 0.065, 1], + "msg_solid_jaws": [0.29804, 0.29804, 0.29804, 1], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], + "pneumatic_body": [0.72, 0.75, 0.8, 1], + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } +} diff --git a/mujoco/raft/previews/color_examples/badge.png b/mujoco/raft/previews/color_examples/badge.png new file mode 100644 index 0000000..6d07e56 Binary files /dev/null and b/mujoco/raft/previews/color_examples/badge.png differ diff --git a/mujoco/raft/previews/color_examples/jaws.png b/mujoco/raft/previews/color_examples/jaws.png new file mode 100644 index 0000000..25860e5 Binary files /dev/null and b/mujoco/raft/previews/color_examples/jaws.png differ diff --git a/mujoco/raft/previews/color_examples/motors.png b/mujoco/raft/previews/color_examples/motors.png new file mode 100644 index 0000000..b5f0240 Binary files /dev/null and b/mujoco/raft/previews/color_examples/motors.png differ diff --git a/mujoco/raft/previews/color_examples/render_manifest.json b/mujoco/raft/previews/color_examples/render_manifest.json new file mode 100644 index 0000000..185c9ea --- /dev/null +++ b/mujoco/raft/previews/color_examples/render_manifest.json @@ -0,0 +1,666 @@ +{ + "mujoco_version": "3.3.0", + "examples": { + "motors": { + "settings_to_change": { + "colors": { + "structure": "#CAD1EE", + "motors": "#2060E0", + "camera_lens": "#080808", + "msg_soft_inserts": "#FFD21A" + } + }, + "views": [ + { + "source_model": "mujoco/parol6/parol6_msg_tpu_glue.xml", + "model_sha256": "a49d2ea730af7134d6ef91c7411f7002dc4b6b772074e668c95eb2a6980f56ba", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + -0.5, + -1.3, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.2 + ], + "distance": 0.68, + "azimuth": 135.0, + "elevation": -18.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/parol6/parol6_msg_tpu_glue.xml", + "model_sha256": "a49d2ea730af7134d6ef91c7411f7002dc4b6b772074e668c95eb2a6980f56ba", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": "#2060E0", + "camera_lens": "#080808", + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + -0.5, + -1.3, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.2 + ], + "distance": 0.68, + "azimuth": 135.0, + "elevation": -18.0, + "orthographic": false + } + } + ], + "image": "motors.png", + "image_sha256": "ab73b8d2ae868818ba63434d3716817f47287dc460e5214e23b240396a046eb1" + }, + "jaws": { + "settings_to_change": { + "colors": { + "msg_soft_holders": "#26364A", + "msg_soft_inserts": "#20B7A8", + "ssg48_solid_jaws": "#F08020" + } + }, + "views": [ + { + "source_model": "mujoco/msg/msg_soft_tpu.xml", + "model_sha256": "a1238295e5f42186e2b46fbb7df3637d5b357df8eddbfa1228b81ad09ef09965", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": true, + "camera": { + "lookat": [ + -0.035, + 0.0, + 0.022 + ], + "distance": 0.27, + "azimuth": 15.0, + "elevation": -18.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/msg/msg_soft_tpu.xml", + "model_sha256": "a1238295e5f42186e2b46fbb7df3637d5b357df8eddbfa1228b81ad09ef09965", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#26364A", + "msg_soft_inserts": "#20B7A8", + "ssg48_solid_jaws": "#F08020", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": true, + "camera": { + "lookat": [ + -0.035, + 0.0, + 0.022 + ], + "distance": 0.27, + "azimuth": 15.0, + "elevation": -18.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/ssg48/ssg48_solid.xml", + "model_sha256": "9c70383c5bf97558bb095066055dd83eb82849cb9e622efe07ba1b698a60b953", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.045 + ], + "distance": 0.3, + "azimuth": 135.0, + "elevation": -24.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/ssg48/ssg48_solid.xml", + "model_sha256": "9c70383c5bf97558bb095066055dd83eb82849cb9e622efe07ba1b698a60b953", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#26364A", + "msg_soft_inserts": "#20B7A8", + "ssg48_solid_jaws": "#F08020", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.045 + ], + "distance": 0.3, + "azimuth": 135.0, + "elevation": -24.0, + "orthographic": false + } + } + ], + "image": "jaws.png", + "image_sha256": "9ba48709e99132b177d53aa4c78c80f18f3eb75c868ea8c9d228b4fa8cad62b0" + }, + "badge": { + "settings_to_change": { + "overrides": { + "par6_panel": "#303A48" + }, + "badge": { + "background": "#15364A", + "emblem": "#20B7A8", + "symbol": "#15364A", + "wordmark": "#20B7A8", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#20B7A8" + } + }, + "views": [ + { + "source_model": "mujoco/par6/par6_flange.xml", + "model_sha256": "b6e0819df0137110772556c89f45f905211c6217a57e5c57f9b31b52992b9bbb", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.023421, + 0.081700445476714, + 0.21549965337517302 + ], + "distance": 0.55, + "azimuth": 270.0, + "elevation": 0.0, + "orthographic": true + } + }, + { + "source_model": "mujoco/par6/par6_flange.xml", + "model_sha256": "b6e0819df0137110772556c89f45f905211c6217a57e5c57f9b31b52992b9bbb", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": { + "par6_panel": "#303A48" + }, + "badge": { + "background": "#15364A", + "emblem": "#20B7A8", + "symbol": "#15364A", + "wordmark": "#20B7A8", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#20B7A8" + } + }, + "qpos": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.023421, + 0.081700445476714, + 0.21549965337517302 + ], + "distance": 0.55, + "azimuth": 270.0, + "elevation": 0.0, + "orthographic": true + } + } + ], + "image": "badge.png", + "image_sha256": "76acabf39402dc542280a4c8f026c366c1c9d8e3b3aecdc2b393c19a26223fbb" + } + }, + "description": "Native model renders; only example palettes and documented viewing poses differ.", + "runtime_models_palette_and_checked_texture_unchanged": true +} diff --git a/mujoco/raft/previews/raft.png b/mujoco/raft/previews/raft.png new file mode 100644 index 0000000..47752c5 Binary files /dev/null and b/mujoco/raft/previews/raft.png differ diff --git a/mujoco/raft/previews/raft_isometric.png b/mujoco/raft/previews/raft_isometric.png new file mode 100644 index 0000000..a8f75ea Binary files /dev/null and b/mujoco/raft/previews/raft_isometric.png differ diff --git a/mujoco/raft/raft.xml b/mujoco/raft/raft.xml new file mode 100644 index 0000000..6e590c7 --- /dev/null +++ b/mujoco/raft/raft.xml @@ -0,0 +1,167 @@ + + + + diff --git a/mujoco/raft/scene.xml b/mujoco/raft/scene.xml new file mode 100644 index 0000000..a5e370d --- /dev/null +++ b/mujoco/raft/scene.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mujoco/raft/sim2bot.robot.json b/mujoco/raft/sim2bot.robot.json new file mode 100644 index 0000000..76252f8 --- /dev/null +++ b/mujoco/raft/sim2bot.robot.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "name": "Source Robotics RAFT", + "manufacturer": "Source Robotics", + "category": "arm", + "license": "CC-BY-4.0", + "sourceUrl": "https://github.com/Source-Robotics/RAFT-teleoperation-arm", + "packageVersion": "2.19.0", + "entry": "scene.xml", + "dof": 6, + "rootFrame": "base_link", + "tcpSites": [ + "tcp" + ], + "armActuators": [ + "base_actuator", + "shoulder_actuator", + "elbow_actuator", + "wrist_1_actuator", + "wrist_2_actuator", + "wrist_3_actuator" + ], + "gripperActuators": [ + "trigger_actuator" + ], + "endEffectorBodies": [ + "wrist_3_link" + ], + "preview": "previews/raft.png", + "variants": [ + { + "id": "leader", + "label": "RAFT leader arm", + "entry": "scene.xml", + "tcpSites": [ + "tcp" + ], + "endEffectorBodies": [ + "wrist_3_link" + ], + "gripperActuators": [ + "trigger_actuator" + ], + "preview": "previews/raft.png" + } + ] +} diff --git a/mujoco/ssg48/docs/COLORS.md b/mujoco/ssg48/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/mujoco/ssg48/docs/COLORS.md +++ b/mujoco/ssg48/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/mujoco/ssg48/docs/colors.json b/mujoco/ssg48/docs/colors.json index 6971ca3..09b8a15 100644 --- a/mujoco/ssg48/docs/colors.json +++ b/mujoco/ssg48/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/mujoco/ssg48/sim2bot.robot.json b/mujoco/ssg48/sim2bot.robot.json index d66d64d..5da6c00 100644 --- a/mujoco/ssg48/sim2bot.robot.json +++ b/mujoco/ssg48/sim2bot.robot.json @@ -5,7 +5,7 @@ "category": "gripper", "license": "CC-BY-4.0", "sourceUrl": "https://github.com/Source-Robotics/robot_descriptions", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "scene_solid.xml", "dof": 1, "rootFrame": "flange", diff --git a/reports/kinematics/parol6_validation.json b/reports/kinematics/parol6_validation.json index 247f2f7..475216b 100644 --- a/reports/kinematics/parol6_validation.json +++ b/reports/kinematics/parol6_validation.json @@ -1,5 +1,5 @@ { - "package_version": "2.18.1", + "package_version": "2.19.0", "joint_convention": "PAR6 normalized", "poses_per_profile": 160, "reference": "PAR6 arm chain with the user-corrected 177 mm forearm; PAROL6 flange reach corrected to match PAR6 housing-to-tool spacing. Original export retained unchanged.", diff --git a/reports/packages/package_validation.json b/reports/packages/package_validation.json index 82e557a..f7dd702 100644 --- a/reports/packages/package_validation.json +++ b/reports/packages/package_validation.json @@ -1,60 +1,74 @@ { "par6_mujoco.zip": { - "bytes": 8973294, + "bytes": 8973474, "variants_checked": 6, "grasp_scenes_compiled": 8, "crc_and_assets_valid": true }, "par6_urdf.zip": { - "bytes": 8956041, + "bytes": 8956298, "variants_checked": 6, "grasp_scenes_compiled": 0, "crc_and_assets_valid": true }, "parol6_mujoco.zip": { - "bytes": 9118189, + "bytes": 9118370, "variants_checked": 6, "grasp_scenes_compiled": 8, "crc_and_assets_valid": true }, "parol6_urdf.zip": { - "bytes": 9117844, + "bytes": 9118103, "variants_checked": 6, "grasp_scenes_compiled": 0, "crc_and_assets_valid": true }, "msg_mujoco.zip": { - "bytes": 2863378, + "bytes": 2863556, "variants_checked": 2, "standalone_gripper": true, "crc_and_assets_valid": true }, "msg_urdf.zip": { - "bytes": 2873019, + "bytes": 2873278, "variants_checked": 2, "standalone_gripper": true, "crc_and_assets_valid": true }, "ssg48_mujoco.zip": { - "bytes": 702882, + "bytes": 703061, "variants_checked": 2, "standalone_gripper": true, "crc_and_assets_valid": true }, "ssg48_urdf.zip": { - "bytes": 717250, + "bytes": 717507, "variants_checked": 2, "standalone_gripper": true, "crc_and_assets_valid": true }, + "raft_mujoco.zip": { + "bytes": 3413820, + "variants_checked": 1, + "bodies": 8, + "geoms": 34, + "crc_and_assets_valid": true + }, + "raft_urdf.zip": { + "bytes": 3426755, + "variants_checked": 1, + "links": 9, + "meshes": 34, + "crc_and_assets_valid": true + }, "par6_moveit_config.zip": { - "bytes": 30217, + "bytes": 30216, "variants_checked": 6, "crc_and_assets_valid": true, "dependency": "par6_description" }, "parol6_moveit_config.zip": { - "bytes": 30019, + "bytes": 30018, "variants_checked": 6, "crc_and_assets_valid": true, "dependency": "parol6_description" diff --git a/source/README.md b/source/README.md index e5fe7fa..99056d3 100644 --- a/source/README.md +++ b/source/README.md @@ -12,6 +12,7 @@ package from `mujoco/` or `urdf/`. | [dynamics/](dynamics/) | Measured masses, captured RCB data and frame/property mappings. | | [jaws/](jaws/) | STEP jaw sources, build settings and checked tessellation caches. | | [parol6/](parol6/) | Supplied original export and normalization/material policy. | +| [raft/](raft/README.md) | RAFT's model reference, CAD meshes, visual pieces, documents and previews, imported from the RAFT repository. | | [pneumatic/](pneumatic/) | Pneumatic configuration and checked convex contact pieces. | | [grippers/](grippers/) | Canonical standalone-gripper preview images. | | [reviews/](reviews/) | Checked robot snapshots used by dimensioned review sheets. | diff --git a/source/appearance/colors.json b/source/appearance/colors.json index 6971ca3..09b8a15 100644 --- a/source/appearance/colors.json +++ b/source/appearance/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/source/raft/README.md b/source/raft/README.md new file mode 100644 index 0000000..a8fb94d --- /dev/null +++ b/source/raft/README.md @@ -0,0 +1,32 @@ +# RAFT model inputs + +[Source index](../README.md) · [Build instructions](../../docs/maintenance/BUILDING.md) + +RAFT's packages (`mujoco/raft`, `urdf/raft_description`) are built from this folder by +[`tools/build_raft.py`](../../tools/build_raft.py) and checked by [`tools/verify_raft.py`](../../tools/verify_raft.py). + +| File or folder | What it is | +| --- | --- | +| `reference.json` | RAFT's model reference: frames, mass properties, joint ranges and their sources, keyframes, actuation estimates, the J2 spring sites, and 40 sample poses with RAFT's own forward kinematics and gravity torques for the checks. | +| `meshes/` | The CAD mesh of every link, in its link's mesh frame (the complete meshes are the collision geometry). | +| `visual/` | Every link mesh split into printed parts, servos, horns and screws, and `partitions.json` with their triangle counts. Each CAD triangle is in exactly one piece. | +| `docs/` | The package documents; the build fills in the tables from `reference.json`. | +| `README.mujoco.md`, `README.urdf.md` | The two packages' front pages. | +| `previews/` | The package preview (`raft.png`) and the overview picture (`raft_isometric.png`). | + +## Where the numbers come from + +RAFT's firmware generator, `firmware/tools/gen_model.py` in the +[RAFT repository](https://github.com/Source-Robotics/RAFT-teleoperation-arm), computes the arm's model from its DH +table, the CAD mass properties and the measured joint stops; the same run writes the firmware's own model. It exports +`firmware/tools/sim/model/raft_reference.json` next to the CAD meshes. After a change there, import it again: + +```bash +python tools/import_raft_reference.py --raft ../RAFT-teleoperation-arm +python tools/build.py --models-only +``` + +The import checks the meshes against the reference's hashes, records the RAFT commit in `reference.json`, and splits +the meshes into their visual pieces again. The normal build needs no RAFT checkout. + +Previews need OpenGL: `python tools/render_raft_previews.py` renders both pictures after an appearance change. diff --git a/source/raft/README.mujoco.md b/source/raft/README.mujoco.md new file mode 100644 index 0000000..f63a550 --- /dev/null +++ b/source/raft/README.mujoco.md @@ -0,0 +1,49 @@ +# Source Robotics RAFT: MuJoCo leader arm + +This self-contained package contains the **RAFT teleoperation leader arm**: six joints like PAR6's at 5/9 scale, +the trigger on its handle, torque actuators and the optional J2 counterbalance spring. All required meshes and +materials are included. Keep the whole folder together when moving or uploading it. + +![RAFT, the handle held in front](previews/raft.png) + +| File | Use it for | +| --- | --- | +| `scene.xml` | Viewer scene: the arm with a floor and lighting. Open this for a quick look. | +| `raft.xml` | Native MJCF robot definition: bodies, joints, inertials, visuals, collisions, actuators and keyframes. Use this when integrating into another MuJoCo world. | + +Detailed notes are in [docs/](docs/), model images in [previews/](previews/), and geometry in `assets/` +(`assets/visual/` holds the separately coloured printed parts, servos, horns and screws). The model entry files +remain at the package root. + +## Open the model + +After extracting the ZIP, run this from its parent directory: + +```bash +python -m mujoco.viewer --mjcf=raft/scene.xml +``` + +## Joints, actuators and frames + +| Bodies | Joints | Actuators | +| --- | --- | --- | +| `base_link`, `shoulder_link`, `upper_arm_link`, `forearm_link`, `wrist_1_link`, `wrist_2_link`, `wrist_3_link`, `trigger_link` | `base_joint`, `shoulder_joint`, `elbow_joint`, `wrist_1_joint`, `wrist_2_joint`, `wrist_3_joint`, `trigger_joint` | `_actuator` | + +The actuators are `motor` actuators: `ctrl` is the joint torque in Nm, as RAFT's servos run in current mode +([actuation](docs/ACTUATION.md)). The site `tcp` is the tool point; the sensors `tcp_pos` and `tcp_quat` report it. +`qpos = 0` is the standby pose, PAR6's standby pose, so RAFT's joint angles map one to one onto PAR6's +([DH](docs/DH.md), [comparison](docs/KINEMATICS_COMPARISON.md)). Keyframes: `home` (all zero), `parked` (RAFT's +firmware rest pose), `handle_in_front`, `reach_forward` and `stretched`. + +Visual geoms are group 2 and collision geoms, the complete link meshes, group 3. The tendon `j2_spring` models the +optional J2 counterbalance; its stiffness 0 means no spring is fitted. + +## Physical properties and current limits + +Complete arm mass: **{{MASS_TOTAL}} g**; moving links and trigger: **{{MASS_MOVING}} g**. Masses, centres of mass +and inertias come from the CAD ([dynamics](docs/DYNAMICS.md)); the joint ranges from the measured mechanical stops +and a CAD sweep ([joint ranges](docs/JOINT_LIMITS.md)). The servos' friction, damping and rotor inertia are +estimates, not measurements. + +The model is built from RAFT's reference export, the same source as the firmware's own model in the +[RAFT repository](https://github.com/Source-Robotics/RAFT-teleoperation-arm). diff --git a/source/raft/README.urdf.md b/source/raft/README.urdf.md new file mode 100644 index 0000000..9531e9d --- /dev/null +++ b/source/raft/README.urdf.md @@ -0,0 +1,46 @@ +# Source Robotics RAFT: URDF leader arm + +This self-contained package contains the **RAFT teleoperation leader arm**: six joints like PAR6's at 5/9 scale and +the trigger on its handle, with all meshes, materials, collision geometry and mass properties. Keep the whole folder +together when moving or uploading it. + +![RAFT, the handle held in front](previews/raft.png) + +| File | Use it for | +| --- | --- | +| `raft.urdf` | The arm: links, joints, ranges, inertials, coloured visual meshes and collision meshes. | + +The URDF is at the package root and uses relative mesh paths. Detailed notes are in [docs/](docs/), model images in +[previews/](previews/), and geometry in `meshes/` (`meshes/visual/` holds the separately coloured printed parts, +servos, horns and screws). + +## Open the model + +Open `raft.urdf` in a viewer with `meshes/` alongside it, or install the package in a ROS 2 workspace using +[the ROS 2 guide](docs/ROS2.md), source the workspace and run: + +```bash +ros2 launch raft_description display.launch.py +``` + +## Links, joints and frames + +| Links | Joints | +| --- | --- | +| `base_link`, `shoulder_link`, `upper_arm_link`, `forearm_link`, `wrist_1_link`, `wrist_2_link`, `wrist_3_link`, `trigger_link`, `tcp` | `base_joint`, `shoulder_joint`, `elbow_joint`, `wrist_1_joint`, `wrist_2_joint`, `wrist_3_joint`, `trigger_joint`, `tcp_fixed_joint` | + +`base_link` is the root link. `tcp` is the tool point, fixed to `wrist_3_link`. `q = 0` is the standby pose, PAR6's +standby pose, so RAFT's joint angles map one to one onto PAR6's ([DH](docs/DH.md), +[comparison](docs/KINEMATICS_COMPARISON.md)). Each joint's `effort` is the servo's stall torque and `` +carries the estimated damping and friction ([actuation](docs/ACTUATION.md)). URDF itself does not implement the +servos' current control; the native MuJoCo package has the torque actuators. + +## Physical properties and current limits + +Complete arm mass: **{{MASS_TOTAL}} g**; moving links and trigger: **{{MASS_MOVING}} g**. Masses, centres of mass +and inertias come from the CAD ([dynamics](docs/DYNAMICS.md)); the joint ranges from the measured mechanical stops +and a CAD sweep ([joint ranges](docs/JOINT_LIMITS.md)). The servos' friction, damping and rotor inertia are +estimates, not measurements. + +The model is built from RAFT's reference export, the same source as the firmware's own model in the +[RAFT repository](https://github.com/Source-Robotics/RAFT-teleoperation-arm). diff --git a/source/raft/docs/ACTUATION.md b/source/raft/docs/ACTUATION.md new file mode 100644 index 0000000..8ec629a --- /dev/null +++ b/source/raft/docs/ACTUATION.md @@ -0,0 +1,35 @@ +# Actuation + +[Documentation index](README.md) + +RAFT's seven Feetech HLS3606 servos run in current (torque) mode: the firmware computes gravity, friction and +force-feedback torques itself and sends a current to each servo. The MuJoCo model works the same way. + +{{ACTUATOR_TABLE}} + +- **Actuators** are `motor` actuators: `ctrl` is the joint output torque in Nm, limited to the servo's stall torque + (6 kg·cm at 6 V). A simulation sends what the firmware sends; there are no position servos to tune. +- **The URDF** carries the same limits as `effort` (Nm) and `velocity` = {{SPEED}} rad/s, a conservative value + below the servo's no-load speed. + +## Joint dynamics (estimates) + +| Value | Model | What it is | +|---|---:|---| +| armature | {{ARMATURE}} kg·m² | the motor rotor reflected through the 1:205 gear (about 0.7 g·cm² × 205²) | +| frictionloss (URDF `friction`) | {{FRICTION}} Nm | gearbox Coulomb friction at the output | +| damping | {{DAMPING}} Nm·s/rad | viscous friction at the output | + +These are estimates, not measurements. On the real arm the gears turn so easily from the joint side that the +firmware adds damping on the light wrist joints in gravity mode. + +## J2 counterbalance + +The MuJoCo model has the optional J2 counterbalance as a tendon, `j2_spring`, between the sites `j2_spring_anchor` +(13 mm above the J2 axis, on `shoulder_link`) and `j2_spring_pin` (on `upper_arm_link`). Its stiffness is 0, so the +spring is not fitted. Set it to the spring's rate in N/m to simulate one; the cable geometry gives a J2 torque of +-k · 13 mm · 20 mm · sin(q2). The mount is described in the RAFT repository's [J2 spring guide](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/docs/spring_mount.md). + +## Not modelled + +Servo current limits and derating with temperature, gear backlash, gear efficiency, and cable forces. diff --git a/source/raft/docs/DH.md b/source/raft/docs/DH.md new file mode 100644 index 0000000..fc9b419 --- /dev/null +++ b/source/raft/docs/DH.md @@ -0,0 +1,28 @@ +# RAFT DH convention + +[Documentation index](README.md) + +RAFT uses PAR6's kinematic structure: the standard DH (CM6) table, the same joint directions and the same zero +pose, with every length scaled to about 5/9 ([comparison](KINEMATICS_COMPARISON.md)). + +- **Frames.** Link *i*'s frame is DH frame *i - 1*, and joint *i* turns about its z axis. `wrist_3_link` is DH frame + 5, and the `tcp` frame is DH frame 6. +- **Zero pose.** `q = 0` is the standby pose: upper arm vertical, forearm level and forward, wrist straight. It is + PAR6's standby pose, and the `home` keyframe. +- **PAR6 / RCB angles.** The RCB runtime works in DH angles: theta = q + [0, -90, 180, 0, 0, 180] deg for joints 1 + to 6. They are continuous and never wrapped, as in the runtime. +- **Units.** Metres and radians in the model files; millimetres and degrees in these tables. + +## DH table (model version {{VERSION}}) + +{{DH_TABLE}} + +The trigger (`trigger_joint`) is not part of the DH chain. Its hinge frame is fixed to `wrist_3_link` (from the CAD +assembly), and it turns about the hinge frame's z axis. + +## Tool point + +`tcp` sits on the trigger loop, 78.277 mm from the J6 axis along the tool axis. That is exactly 5/9 of the +140.9 mm of PAR6's SSG48 and MSG tool points in the +[robot_descriptions kinematic review](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/kinematics/robot_kinematics_review.pdf). +RAFT has no separate flange: the handle is fixed to the J6 horn. diff --git a/source/raft/docs/DYNAMICS.md b/source/raft/docs/DYNAMICS.md new file mode 100644 index 0000000..c6ffa97 --- /dev/null +++ b/source/raft/docs/DYNAMICS.md @@ -0,0 +1,23 @@ +# Masses, centres of mass and inertias + +[Documentation index](README.md) + +The mass properties are SolidWorks' (RAFT inertials.pdf, with the masses corrected to the printed parts at the +print table's settings). Each link includes its printed parts, the servo it carries, the horn it is fixed to and +the screws. They are given in each link's own frame; the inertias are about the centre of mass, and both formats +carry the full tensor, off-diagonal terms included. + +{{MASS_TABLE}} + +## Status + +| Item | Status | +|---|---| +| Link masses | From CAD. Not yet weighed: printed parts vary with filament and infill. | +| Centres of mass and inertias | From CAD. | +| Base | From CAD. The ESP32-PoE board and the cables are not included. | + +RAFT's firmware compensates gravity with the same numbers: one generator in the RAFT repository +([`firmware/tools/gen_model.py`](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/tools/gen_model.py)) writes the firmware's model and the +reference these packages are built from, so a mass that is off shows the same way in both. Weighing each printed +link with its servo and horn is the next improvement. diff --git a/source/raft/docs/JOINT_LIMITS.md b/source/raft/docs/JOINT_LIMITS.md new file mode 100644 index 0000000..7b761cc --- /dev/null +++ b/source/raft/docs/JOINT_LIMITS.md @@ -0,0 +1,32 @@ +# Joint ranges + +Angles in the model's convention: 0 is the standby pose (PAR6's standby pose), positive about each joint's z axis. +The URDF `` and the MuJoCo `range` carry the same numbers. + +{{LIMITS_TABLE}} + +## Where the numbers come from + +- **Measured stops.** Every joint has one mechanical stop. Its angle was measured on the first arm (2026-11-02), + and RAFT's firmware calibrates against it (`kStopDeg` in + [`firmware/src/main/arm_design.h`](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/src/main/arm_design.h)). These are the most trustworthy + numbers in the table. +- **CAD sweep.** The other end of a range comes from the CAD meshes: one joint turned, the others at 0, until its + moving parts come within 0.5 mm of the parts it turns against. Where a sweep can be compared with a measured stop + it ends 0.3 to 2.2 deg early (J2, J3, J5, J6) or 4.6 deg early (J4), so these ends are slightly conservative. + They hold with the other joints at 0; some wrist poses reach further or less far. +- **Forearm lying limp.** J3's lower end was read on the first arm with the arm limp and the forearm lying folded + on the upper arm (2026-11-06). The CAD parts meet at about -77 deg; the printed parts settle 3 deg further. +- **Trigger.** 0 is the trigger flush against the handle, as in the CAD; turning it positive would push it into + the handle. It opens toward negative angles, to its stop 39.63 deg from flush. +- **Not measured.** The CAD has no J1 stop. J1's upper end mirrors its measured stop until the real end is + measured. + +The ranges of one arm can be measured with the firmware's `calib_range` command (move every joint gently to both +ends). Report measured ranges in the [RAFT repository's issues](https://github.com/Source-Robotics/RAFT-teleoperation-arm/issues). + +## Keyframes + +{{KEYS_TABLE}} + +`home` is the standby pose. `parked` is RAFT's firmware rest pose: the arm lying back the way it rests limp. diff --git a/source/raft/docs/KINEMATICS_COMPARISON.md b/source/raft/docs/KINEMATICS_COMPARISON.md new file mode 100644 index 0000000..8e0ce8b --- /dev/null +++ b/source/raft/docs/KINEMATICS_COMPARISON.md @@ -0,0 +1,16 @@ +# RAFT and PAR6 + +[Documentation index](README.md) + +RAFT is a teleoperation leader for PAR6: the same kinematic structure, joint directions and zero pose, scaled to +5/9. Joint angles therefore map one to one, which is how the firmware teleoperates PAR6. + +{{SCALE_TABLE}} + +PAR6's lengths are those of the +[robot_descriptions kinematic review](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/kinematics/robot_kinematics_review.pdf), +with the SSG48 or MSG tool point for a6. RAFT's base (d1 = 51.22 mm against PAR6's 110.5 mm) is lower than a pure +scale would make it; the base height does not change any joint angle. + +Not scaled: the joint ranges (RAFT's mechanical stops, [joint ranges](JOINT_LIMITS.md)), the masses, and the +actuation (RAFT's servos run in current mode, [actuation](ACTUATION.md)). diff --git a/source/raft/meshes/base_link.stl b/source/raft/meshes/base_link.stl new file mode 100644 index 0000000..2618394 Binary files /dev/null and b/source/raft/meshes/base_link.stl differ diff --git a/source/raft/meshes/forearm_link.stl b/source/raft/meshes/forearm_link.stl new file mode 100644 index 0000000..58ba361 Binary files /dev/null and b/source/raft/meshes/forearm_link.stl differ diff --git a/source/raft/meshes/shoulder_link.stl b/source/raft/meshes/shoulder_link.stl new file mode 100644 index 0000000..d792b47 Binary files /dev/null and b/source/raft/meshes/shoulder_link.stl differ diff --git a/source/raft/meshes/trigger_link.stl b/source/raft/meshes/trigger_link.stl new file mode 100644 index 0000000..faa1adf Binary files /dev/null and b/source/raft/meshes/trigger_link.stl differ diff --git a/source/raft/meshes/upper_arm_link.stl b/source/raft/meshes/upper_arm_link.stl new file mode 100644 index 0000000..77beb41 Binary files /dev/null and b/source/raft/meshes/upper_arm_link.stl differ diff --git a/source/raft/meshes/wrist_1_link.stl b/source/raft/meshes/wrist_1_link.stl new file mode 100644 index 0000000..620f9e3 Binary files /dev/null and b/source/raft/meshes/wrist_1_link.stl differ diff --git a/source/raft/meshes/wrist_2_link.stl b/source/raft/meshes/wrist_2_link.stl new file mode 100644 index 0000000..4aa9ed9 Binary files /dev/null and b/source/raft/meshes/wrist_2_link.stl differ diff --git a/source/raft/meshes/wrist_3_link.stl b/source/raft/meshes/wrist_3_link.stl new file mode 100644 index 0000000..c907022 Binary files /dev/null and b/source/raft/meshes/wrist_3_link.stl differ diff --git a/source/raft/previews/raft.png b/source/raft/previews/raft.png new file mode 100644 index 0000000..47752c5 Binary files /dev/null and b/source/raft/previews/raft.png differ diff --git a/source/raft/previews/raft_isometric.png b/source/raft/previews/raft_isometric.png new file mode 100644 index 0000000..a8f75ea Binary files /dev/null and b/source/raft/previews/raft_isometric.png differ diff --git a/source/raft/reference.json b/source/raft/reference.json new file mode 100644 index 0000000..0fa7282 --- /dev/null +++ b/source/raft/reference.json @@ -0,0 +1,3649 @@ +{ + "schema_version": 1, + "robot": "raft", + "generator": "RAFT-teleoperation-arm: firmware/tools/gen_model.py", + "units": "metres, kilograms, radians (range_deg: degrees), newton metres", + "conventions": "link i frame = DH frame i-1, joint i turns about its z axis; q = 0 is the standby pose (PAR6's standby pose); PAR6/RCB DH angles theta = q + theta_offset, not wrapped", + "dh": { + "d": [ + 0.05122, + 0.0, + 0.0, + -0.09792, + 0.0, + -0.078277 + ], + "a": [ + 0.013, + 0.1, + -0.02416, + 0.0, + 0.0, + -0.0 + ], + "alpha": [ + -1.5707963267948966, + 3.141592653589793, + 1.5707963267948966, + -1.5707963267948966, + 1.5707963267948966, + 3.141592653589793 + ], + "theta_offset": [ + 0.0, + -1.5707963267948966, + 3.141592653589793, + 0.0, + 0.0, + 3.141592653589793 + ] + }, + "links": [ + { + "name": "base_link", + "mass": 0.08775999999999999, + "com": [ + -0.00730483363719237, + -0.00042041932543300043, + 0.010516627164995428 + ], + "inertia": { + "ixx": 5.189305835020962e-05, + "iyy": 6.095660624521156e-05, + "izz": 0.00010243280059213989, + "ixy": 2.2504778518778257e-06, + "ixz": -3.402514469243121e-06, + "iyz": 3.0948154641203437e-06 + }, + "mesh": { + "cad_file": "base_link.stl", + "sha256": "b9b40046eb706bbd7b11695a1374ca8d604d41479d3b96c9e3a49271b0d4b165", + "origin": { + "pos": [ + 0.0, + 0.0, + 0.0 + ], + "quat": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "rpy": [ + 0.0, + 0.0, + 0.0 + ] + } + } + }, + { + "name": "shoulder_link", + "mass": 0.043140000000000005, + "com": [ + 0.00683, + -9.999999999999965e-05, + 0.04872 + ], + "inertia": { + "ixx": 4.90853e-06, + "iyy": 8.812030000000002e-06, + "izz": 8.881160000000001e-06, + "ixy": 7.971e-08, + "ixz": 1.499e-07, + "iyz": 1.294e-08 + }, + "mesh": { + "cad_file": "link1.stl", + "sha256": "a30a974c151ae9fd526345aa48df048c3622e363354843093d15bdb01758b622", + "origin": { + "pos": [ + 0.013, + 0.0, + 0.05122 + ], + "quat": [ + 0.7071067811865476, + -0.7071067811865475, + 0.0, + 0.0 + ], + "rpy": [ + -1.5707963267948966, + 0.0, + 0.0 + ] + } + } + }, + { + "name": "upper_arm_link", + "mass": 0.0182, + "com": [ + 9.190635678063458e-19, + -0.04999999999999999, + -0.023149999999999997 + ], + "inertia": { + "ixx": 2.3590990000000004e-05, + "iyy": 7.822300000000001e-07, + "izz": 2.404505e-05, + "ixy": -8.420353511795656e-38, + "ixz": 0.0, + "iyz": 1.4244369026070515e-21 + }, + "mesh": { + "cad_file": "link2.stl", + "sha256": "5ad85b4e7ba097350650d5da6f1a337fe9514ae4c4b8abe7a5a6989d62b5fbaa", + "origin": { + "pos": [ + 0.0, + -0.1, + 0.0 + ], + "quat": [ + 0.0, + 0.7071067811865476, + -0.7071067811865475, + 0.0 + ], + "rpy": [ + 3.141592653589793, + 0.0, + -1.5707963267948966 + ] + } + } + }, + { + "name": "forearm_link", + "mass": 0.0714, + "com": [ + 0.014839999999999978, + 0.007440000000000008, + 0.0024999999999999935 + ], + "inertia": { + "ixx": 1.3758010000000002e-05, + "iyy": 1.738344e-05, + "izz": 1.81406e-05, + "ixy": -7.469000000000002e-08, + "ixz": -1.5376899999999998e-06, + "iyz": 2.9758999999999985e-07 + }, + "mesh": { + "cad_file": "link3.stl", + "sha256": "2fd24cde28d402638e3a15dbf32cb852fdb88e477ce77758a6ca4c157dc82cb8", + "origin": { + "pos": [ + 0.02416, + 0.0, + 0.0 + ], + "quat": [ + 0.0, + 0.0, + 0.7071067811865475, + 0.7071067811865476 + ], + "rpy": [ + 1.5707963267948966, + 0.0, + 3.141592653589793 + ] + } + } + }, + { + "name": "wrist_1_link", + "mass": 0.0461, + "com": [ + -1.999999999998868e-05, + 0.02134999999999999, + -0.07139 + ], + "inertia": { + "ixx": 4.667237000000001e-05, + "iyy": 3.838821e-05, + "izz": 1.075663e-05, + "ixy": -1.5769999999996663e-08, + "ixz": 4.1159999999992455e-08, + "iyz": 1.538609e-05 + }, + "mesh": { + "cad_file": "link4.stl", + "sha256": "426db78b28c2a055fad7ee1d68a98f1cd578b987f3e99fefbaf2aa2aa6bc700f", + "origin": { + "pos": [ + 0.0, + 0.0, + -0.09792 + ], + "quat": [ + 0.7071067811865476, + -0.7071067811865475, + 0.0, + 0.0 + ], + "rpy": [ + -1.5707963267948966, + 0.0, + 0.0 + ] + } + } + }, + { + "name": "wrist_2_link", + "mass": 0.0351, + "com": [ + 0.004640000000000005, + 3.0000000000003162e-05, + 0.0004799999999999871 + ], + "inertia": { + "ixx": 3.42008e-06, + "iyy": 4.47832e-06, + "izz": 5.27248e-06, + "ixy": 3.2149999999999815e-08, + "ixz": 1.369399999999999e-07, + "iyz": 4.161000000000013e-08 + }, + "mesh": { + "cad_file": "link5.stl", + "sha256": "e756969df480522897c6d31b6c9f9882c49214fd996851aeb3c70aebaf0c77f6", + "origin": { + "pos": [ + 0.0, + 0.0, + 0.0 + ], + "quat": [ + 0.7071067811865476, + 0.7071067811865475, + 0.0, + 0.0 + ], + "rpy": [ + 1.5707963267948966, + 0.0, + 0.0 + ] + } + } + }, + { + "name": "wrist_3_link", + "mass": 0.055299999999999995, + "com": [ + 0.001619999999999987, + 0.02616999999999999, + -0.045709999999999994 + ], + "inertia": { + "ixx": 9.016535000000001e-05, + "iyy": 1.875133000000001e-05, + "izz": 7.691496000000002e-05, + "ixy": 2.77967000000001e-06, + "ixz": -1.0080200000000043e-06, + "iyz": 3.053183000000001e-05 + }, + "mesh": { + "cad_file": "link6.stl", + "sha256": "ce8f29969dfb092a59ab5a8d886dab2f572ebf9a1200ef01d9cd45467c5955b8", + "origin": { + "pos": [ + 0.0, + 0.0, + -0.078277 + ], + "quat": [ + 0.0, + 0.0, + 1.0, + 0.0 + ], + "rpy": [ + 3.141592653589793, + 0.0, + 3.141592653589793 + ] + } + } + }, + { + "name": "trigger_link", + "mass": 0.0143, + "com": [ + -0.01309999999999999, + 0.015200000000000023, + 0.01277999999999998 + ], + "inertia": { + "ixx": 4.941750000000001e-06, + "iyy": 4.41935e-06, + "izz": 4.5468599999999985e-06, + "ixy": 1.3764199999999996e-06, + "ixz": -1.4238599999999998e-06, + "iyz": 1.7194800000000007e-06 + }, + "mesh": { + "cad_file": "gripper_trigger.stl", + "sha256": "493c61dfbbfe826dbbb06eea0ac9fea0435a9a8ba6342eab1a10997a33dc63c7", + "origin": { + "pos": [ + 0.0, + 0.0, + 0.0 + ], + "quat": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "rpy": [ + 0.0, + 0.0, + 0.0 + ] + } + } + } + ], + "joints": [ + { + "name": "base_joint", + "parent": "base_link", + "child": "shoulder_link", + "origin": { + "pos": [ + 0.0, + 0.0, + 0.0 + ], + "quat": [ + 1.0, + 0.0, + 0.0, + 0.0 + ], + "rpy": [ + 0.0, + 0.0, + 0.0 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -2.378534704617872, + 2.378534704617872 + ], + "range_deg": [ + -136.28, + 136.28 + ], + "range_source": "lower: measured stop; upper: the same angle mirrored (the CAD has no J1 stop; not measured)", + "measured_stop_deg": -136.28 + }, + { + "name": "shoulder_joint", + "parent": "shoulder_link", + "child": "upper_arm_link", + "origin": { + "pos": [ + 0.013, + 0.0, + 0.05122 + ], + "quat": [ + 0.7071067811865476, + -0.7071067811865475, + 0.0, + 0.0 + ], + "rpy": [ + -1.5707963267948966, + 0.0, + 0.0 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -1.465378439974439, + 2.2951079663725436 + ], + "range_deg": [ + -83.96, + 131.5 + ], + "range_source": "lower: measured stop (CAD sweep -83.0); upper: CAD sweep", + "measured_stop_deg": -83.96 + }, + { + "name": "elbow_joint", + "parent": "upper_arm_link", + "child": "forearm_link", + "origin": { + "pos": [ + 0.0, + -0.1, + 0.0 + ], + "quat": [ + 0.0, + 0.7071067811865476, + -0.7071067811865475, + 0.0 + ], + "rpy": [ + 3.141592653589793, + 0.0, + -1.5707963267948966 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -1.402022988127045, + 1.6198400787759373 + ], + "range_deg": [ + -80.33, + 92.81 + ], + "range_source": "lower: measured, the forearm lying limp on the upper arm (the CAD parts meet at about -77); upper: measured stop (CAD sweep 92.5)", + "measured_stop_deg": 92.81 + }, + { + "name": "wrist_1_joint", + "parent": "forearm_link", + "child": "wrist_1_link", + "origin": { + "pos": [ + 0.02416, + 0.0, + 0.0 + ], + "quat": [ + 0.0, + 0.0, + 0.7071067811865475, + 0.7071067811865476 + ], + "rpy": [ + 1.5707963267948966, + 0.0, + 3.141592653589793 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -2.767394061962209, + 2.6790804018112957 + ], + "range_deg": [ + -158.56, + 153.5 + ], + "range_source": "lower: measured stop (CAD sweep -154.0); upper: CAD sweep", + "measured_stop_deg": -158.56 + }, + { + "name": "wrist_2_joint", + "parent": "wrist_1_link", + "child": "wrist_2_link", + "origin": { + "pos": [ + 0.0, + 0.0, + -0.09792 + ], + "quat": [ + 0.7071067811865476, + -0.7071067811865475, + 0.0, + 0.0 + ], + "rpy": [ + -1.5707963267948966, + 0.0, + 0.0 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -2.199114857512855, + 2.0895081804876114 + ], + "range_deg": [ + -126.0, + 119.72 + ], + "range_source": "lower: CAD sweep; upper: measured stop (CAD sweep 117.5)", + "measured_stop_deg": 119.72 + }, + { + "name": "wrist_3_joint", + "parent": "wrist_2_link", + "child": "wrist_3_link", + "origin": { + "pos": [ + 0.0, + 0.0, + 0.0 + ], + "quat": [ + 0.7071067811865476, + 0.7071067811865475, + 0.0, + 0.0 + ], + "rpy": [ + 1.5707963267948966, + 0.0, + 0.0 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -2.5743606466916362, + 2.60699830370393 + ], + "range_deg": [ + -147.5, + 149.37 + ], + "range_source": "lower: CAD sweep; upper: measured stop (CAD sweep 147.5)", + "measured_stop_deg": 149.37 + }, + { + "name": "trigger_joint", + "parent": "wrist_3_link", + "child": "trigger_link", + "origin": { + "pos": [ + 0.0, + -0.011290000000000005, + -0.03571 + ], + "quat": [ + 0.5, + -0.4999999999999999, + 0.5000000000000001, + -0.5 + ], + "rpy": [ + -1.5707963267948966, + 0.0, + -1.5707963267948966 + ] + }, + "axis": [ + 0.0, + 0.0, + 1.0 + ], + "range": [ + -0.6916739825653528, + 0.0 + ], + "range_deg": [ + -39.63, + 0.0 + ], + "range_source": "lower: measured stop, trigger released; upper: trigger flush against the handle (CAD)", + "measured_stop_deg": 39.63 + } + ], + "tcp": { + "parent": "wrist_3_link", + "origin": { + "pos": [ + 0.0, + 0.0, + -0.078277 + ], + "quat": [ + 0.0, + 0.0, + 1.0, + 0.0 + ], + "rpy": [ + 3.141592653589793, + 0.0, + 3.141592653589793 + ] + } + }, + "actuation": { + "stall_torque_nm": 0.588399, + "speed_rad_s": 6.0, + "armature": 0.003, + "frictionloss": 0.02, + "damping": 0.01, + "servo": "Feetech HLS3606M, current mode", + "note": "armature, friction and damping are estimates, not measured" + }, + "j2_spring": { + "anchor": { + "body": "shoulder_link", + "pos": [ + 0.013, + -0.033, + 0.06422 + ] + }, + "pin": { + "body": "upper_arm_link", + "pos": [ + -2.8166876380389123e-18, + -0.020000000000000004, + -0.033 + ] + }, + "note": "zero-free-length cable spring; the model's tendon stiffness 0 = not fitted" + }, + "contact_excludes": [ + [ + "base_link", + "shoulder_link" + ], + [ + "wrist_1_link", + "wrist_3_link" + ], + [ + "wrist_1_link", + "trigger_link" + ], + [ + "wrist_2_link", + "trigger_link" + ] + ], + "keyframes": [ + { + "name": "home", + "qpos": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + { + "name": "parked", + "qpos": [ + 0.0, + -1.4311699866353502, + -1.3962634015954636, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + { + "name": "handle_in_front", + "qpos": [ + 0.0, + 0.3490658503988659, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + { + "name": "reach_forward", + "qpos": [ + 0.0, + 0.7853981633974483, + 0.7853981633974483, + 0.0, + 0.0, + 0.0, + 0.0 + ] + }, + { + "name": "stretched", + "qpos": [ + 0.0, + 1.5707963267948966, + 1.5707963267948966, + 0.0, + 0.0, + 0.0, + 0.0 + ] + } + ], + "checks": { + "mass_total": 0.3713, + "mass_moving": 0.28354, + "tcp_home": [ + 0.189197, + -1.839156193257508e-17, + 0.17537999999999998 + ], + "samples": [ + { + "q": [ + -1.766917326085276, + 0.41214917472159196, + 0.415622683577781, + -2.611140110473429, + -1.5647156432727722, + 2.2350338449798985, + -0.6429659022022328 + ], + "tcp": [ + [ + -0.5439069131239412, + -0.6771492970011636, + 0.49561487006224475, + 0.009391219851536924 + ], + [ + 0.7379749073483892, + -0.6671343550659224, + -0.10161096601702117, + -0.1559545858022288 + ], + [ + 0.39944750090609155, + 0.3104844309488094, + 0.8625781774182977, + 0.23486621398046886 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.24478013448264016, + 0.8026505812734743, + 0.5439069131239412, + -0.004060602759259513 + ], + [ + 0.5948447050191509, + 0.318673521075749, + -0.7379749073483892, + -0.14409736494308797 + ], + [ + -0.7656647194910708, + 0.5041817443606402, + -0.3994475009060915, + 0.19464347947689212 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.22862101523024592, + 0.13772741302986446, + -0.019946203272391827, + -0.008797597511600586, + 0.005944646219724348, + 0.0007060896312083287 + ] + }, + { + "q": [ + -1.7611910198147642, + 2.1007978173813404, + 0.47722407285727786, + -0.7576824362598851, + -0.005955828603822155, + 0.8600666180379051, + -0.5012500375325015 + ], + "tcp": [ + [ + -0.0875866215717945, + -0.9960466634355185, + 0.014819850897027684, + -0.02121671893535537 + ], + [ + -0.9945614869995723, + 0.08827831597462668, + 0.05526651342253888, + -0.11177866063953106 + ], + [ + -0.05635629777441884, + -0.00989864574852738, + -0.9983616501616567, + -0.17654038389261884 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.8663943022061836, + 0.4916192600237499, + 0.08758662157179456, + -0.010602188698302142 + ], + [ + -0.10397533021856636, + 0.006048080946860965, + 0.9945614869995723, + -0.1151278525037418 + ], + [ + 0.48841585130972176, + -0.8707892534307947, + 0.05635629777441878, + -0.13393136781968673 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.2240528300528899, + 0.028277503782980773, + -0.0010188183768855552, + 0.005210905887342854, + -0.0007703638699174675, + 0.0005588058626937453 + ] + }, + { + "q": [ + -1.7222110057312707, + 1.4980337428210109, + 0.6237149024797615, + 0.02328312411301514, + 1.303559837761013, + 0.2705954121089267, + -0.01320153911991162 + ], + "tcp": [ + [ + 0.3312506879792804, + -0.9365175853910063, + -0.11492516681150683, + -0.03827134714541788 + ], + [ + 0.360017761027916, + 0.23803755980218422, + -0.9020672546256561, + -0.26247680280647345 + ], + [ + 0.8721583534299834, + 0.2574352974570908, + 0.41601306970512275, + 0.03143811805580551 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.9379531231528941, + -0.10255203791726927, + -0.33125068797928037, + -0.022806044030688012 + ], + [ + -0.2261084871897432, + -0.9051310202203946, + -0.36001776102791605, + -0.2267659500289898 + ], + [ + -0.2629047180795684, + 0.41257837528620284, + -0.8721583534299834, + 0.01082324520937699 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.37322654863891636, + 0.1469216466890022, + 0.015210276961552527, + 0.02920334592204643, + 0.013266608769879618, + -0.00019760613978458963 + ] + }, + { + "q": [ + -1.4056690022254352, + 0.6169170623618321, + 0.05942462174788887, + -0.8432915851821923, + 0.3380143921759009, + -1.355180503951717, + -0.13681125745167477 + ], + "tcp": [ + [ + -0.8511349822448607, + 0.5165495495378325, + -0.09351900807484663, + 0.020085982902638733 + ], + [ + 0.2385001194373393, + 0.22181060738445657, + -0.9454722351714584, + -0.23846860726053304 + ], + [ + -0.46763974919527074, + -0.8270287886911805, + -0.31198789663796794, + 0.07706135730239398 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.4989682960277402, + -0.16309470119600544, + 0.8511349822448607, + 0.018234962105078602 + ], + [ + -0.09078988841245271, + -0.9668892848669022, + -0.23850011943733934, + -0.2007269323833601 + ], + [ + 0.8618514000227813, + -0.19627844826000748, + 0.4676397491952708, + 0.09967890112290578 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.2886668870882847, + 0.15739689165905624, + 0.002988335162656952, + 0.017790982353993456, + -0.008823350272635366, + -0.0014770247802609049 + ] + }, + { + "q": [ + 1.7474312024554632, + -0.9811794466363984, + 0.009408284855277893, + -1.2579314820121361, + -1.842657386176062, + 2.06784841391737, + -0.3942896584378621 + ], + "tcp": [ + [ + 0.026543820601722074, + -0.39112227277204625, + -0.9199558648812955, + -0.06557273950723935 + ], + [ + -0.995035047245841, + 0.07796692892008082, + -0.06185800471432518, + -0.0409138956576766 + ], + [ + 0.09592017691982217, + 0.9170302752561661, + -0.3871108289925121, + 0.1716620895275533 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.7145146327693732, + -0.6991167750428472, + -0.026543820601722105, + -0.021997207747240843 + ], + [ + -0.048221597260629585, + -0.08706280669780908, + 0.995035047245841, + -0.03916103259850963 + ], + [ + -0.6979566728071885, + -0.7096871159487036, + -0.09592017691982226, + 0.17778696437763544 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.14247756535690215, + 0.04611563039769173, + -0.016967844768123186, + 0.029169484883073693, + 0.003744030474539528, + 0.0027924513257282513 + ] + }, + { + "q": [ + -1.6759569395579343, + 1.0667915511073836, + -0.7909538422185453, + 2.142227288964752, + -1.2678478380860998, + -2.402988819217061, + -0.5528073204651816 + ], + "tcp": [ + [ + 0.0875995488990922, + -0.5331161323751528, + -0.8414948059459632, + -0.07594899406923238 + ], + [ + -0.8713174031361832, + 0.3684416089718546, + -0.3241246114849178, + -0.12086469691235585 + ], + [ + 0.4828377595266276, + 0.7616022388225953, + -0.43223804528953363, + -0.03507484884845215 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.8955613277704513, + -0.43622955795624613, + -0.08759954889909223, + -0.034110203530015086 + ], + [ + -0.14337277964301548, + -0.4693082452391533, + 0.8713174031361832, + -0.1112273903405696 + ], + [ + -0.4212055961872557, + -0.7677587796410046, + -0.48283775952662766, + -0.025274261250919666 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.2224509349710302, + 0.02376005248799773, + -0.008252039239431408, + -0.031988265813964745, + 0.008339445349558883, + 0.002309053281912554 + ] + }, + { + "q": [ + -0.7337880710666174, + 0.29794433440094403, + 1.336190904234, + 1.030765299869326, + -0.743896454583264, + -2.4869137371884644, + -0.581128091555454 + ], + "tcp": [ + [ + -0.8895946092760278, + 0.4429918270146164, + 0.11126397595486064, + 0.0616289905079194 + ], + [ + -0.2934896122785618, + -0.36774389064344964, + -0.8823991604591416, + -0.11679245834659871 + ], + [ + -0.3499789688418592, + -0.8176323575377085, + 0.4571564822636973, + 0.27922540679502406 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.43135228757973765, + -0.1501886651740372, + 0.8895946092760278, + 0.05189143911645383 + ], + [ + 0.7917846779226618, + -0.5356686207829296, + 0.29348961227856174, + -0.07507954475796988 + ], + [ + 0.4324491042962563, + 0.8309647968246038, + 0.3499789688418593, + 0.268996696131106 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.11505470584594057, + 0.04844535461678845, + 0.011428508722710617, + 0.028661416490161074, + -0.006726710799487279, + -0.002449181996100336 + ] + }, + { + "q": [ + 2.3615799171769303, + 0.26337725319436434, + 0.6862050127523414, + -2.4696458615530963, + -2.0530860467702476, + 1.8084996273885205, + -0.285051339386057 + ], + "tcp": [ + [ + 0.8695368907052446, + -0.4802738643825261, + 0.11507654362957864, + -0.07517523692879824 + ], + [ + 0.30532459026840114, + 0.3396320378545437, + -0.889621814840555, + 0.013644281300096844 + ], + [ + 0.3881784258302985, + 0.8086946853132179, + 0.4419620070391423, + 0.24457993988845222 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.4285331800146431, + 0.24548912262907494, + -0.8695368907052446, + -0.07465140823259979 + ], + [ + -0.07575930788468899, + -0.9492299098986852, + -0.3053245902684012, + 0.04767836738503695 + ], + [ + -0.9003442901997937, + 0.19671723062441965, + -0.38817842583029855, + 0.21663678013763082 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.14417195451366105, + 0.08509878815510241, + -0.02609716115431482, + 0.01700000720750161, + 0.007508557525737203, + 0.00155829688042175 + ] + }, + { + "q": [ + -0.9099810288864281, + -0.27188790602536606, + -1.132360224810225, + -1.8269534888543957, + -2.093674310622902, + 1.7734463969239989, + -0.3691441930728825 + ], + "tcp": [ + [ + 0.08659412243321495, + 0.821242940208432, + 0.5639694061895832, + 0.08607397199138034 + ], + [ + -0.6741608636693335, + -0.36848659652212745, + 0.6400974598294835, + -0.003828053462167816 + ], + [ + 0.7334906869597184, + -0.4356347797655148, + 0.5217410763990237, + 0.12990301696759196 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.9694111016464856, + 0.229659691640846, + -0.08659412243321497, + 0.05279565348315515 + ], + [ + 0.11270573529062121, + 0.7299346183934687, + 0.6741608636693335, + -0.026914868359994622 + ], + [ + 0.2180356237800316, + 0.6437793712959533, + -0.7334906869597184, + 0.11261238123206738 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.05306356731793471, + 0.11399909208287262, + -0.023192710002506445, + -0.02502429624628988, + 0.008780645266437404, + -0.0016479957398729539 + ] + }, + { + "q": [ + -1.773421603870255, + 1.31454938076376, + -0.8107869273802297, + -2.4301470824353113, + 0.36716331971466243, + 2.066881797679263, + -0.6730379259089988 + ], + "tcp": [ + [ + -0.5060520605371002, + -0.8583862321283664, + -0.08416880965429765, + -0.022429062890598078 + ], + [ + -0.5945922676383308, + 0.27650526525485053, + 0.7549866711082072, + -0.01800579472316255 + ], + [ + -0.6247970448800656, + 0.4321086839891221, + -0.6503158755023027, + -0.07030707360368481 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.7236671024357179, + 0.46927309413437207, + 0.5060520605371002, + -0.009155068609314332 + ], + [ + -0.686839933832202, + 0.4179843783660866, + 0.5945922676383308, + -0.05326505679695287 + ], + [ + 0.0675042972385352, + -0.7778636272274653, + 0.6247970448800656, + -0.04750358477341547 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.16005093158785733, + -0.059445458146234476, + -0.005076569910972273, + 0.029167471025243197, + -0.006793602291832457, + 0.00128554614522991 + ] + }, + { + "q": [ + 1.451553083170527, + -0.7502466771727352, + -1.1212876132207814, + -2.6695642934631727, + -0.9426552355650928, + 1.1930662959268048, + -0.35055493340416555 + ], + "tcp": [ + [ + 0.9628154651534747, + -0.024388608527079966, + -0.2690568264055216, + -0.015727382796718974 + ], + [ + 0.24858771466763807, + 0.46994418139732147, + 0.846969075283816, + 0.11081455789210712 + ], + [ + 0.10578529282286421, + -0.8823591457818719, + 0.4585322340659014, + 0.14727612548578847 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.1153045871165507, + -0.24431789179505434, + -0.9628154651534747, + -0.003999093476844403 + ], + [ + -0.7322285591294891, + 0.6340705680848727, + -0.24858771466763804, + 0.06945595545652516 + ], + [ + 0.6712273753245128, + 0.7336642845589165, + -0.10578529282286413, + 0.13771961863418258 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.010053509719165632, + 0.16476190667358281, + -0.022994482318239307, + -0.02996373082986281, + -0.0009179479953935754, + -0.002779519420181842 + ] + }, + { + "q": [ + 1.6788649095272823, + -0.6485582857769662, + -0.4495838852092874, + -1.361436504279454, + 1.996449938074953, + 2.301329186071458, + -0.45603021850572106 + ], + "tcp": [ + [ + 0.06653955837191022, + -0.3526731745793871, + 0.9333778008415288, + 0.06833688511638554 + ], + [ + 0.4502103755056004, + -0.8242009785056205, + -0.3435161783926403, + 0.0166636677277659 + ], + [ + 0.8904398379221234, + 0.4430737850093086, + 0.10393515323867779, + 0.1820898642649967 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.09441517250332221, + 0.9933067312632283, + -0.06653955837191014, + 0.032587472408965465 + ], + [ + 0.8912543010423124, + 0.05455628891966351, + -0.45021037550560034, + 0.04059134994073388 + ], + [ + -0.4435668451031336, + -0.10181035785456757, + -0.8904398379221234, + 0.17266335356433082 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.0437275296527595, + 0.09333190573168615, + 0.02501914314612108, + -0.009646396903484344, + 0.013048063962628476, + 0.0011329159277373435 + ] + }, + { + "q": [ + -0.30444528949800587, + -0.2833837489855693, + 0.8538247381919082, + -2.549463720689908, + -1.9098946953962201, + -0.480896876565978, + -0.5221488041282121 + ], + "tcp": [ + [ + 0.5111755146798416, + 0.558848630990169, + -0.6529837676636483, + -0.04705511770729404 + ], + [ + 0.4142380090716604, + 0.5054879892036225, + 0.7568941568087482, + 0.05797217014423277 + ], + [ + 0.7530647150359299, + -0.6573964560379777, + 0.02689673877184932, + 0.24834610085412098 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.1587101784185977, + -0.8446956093517571, + -0.5111755146798416, + -0.025568958713034533 + ], + [ + -0.8156276909669542, + 0.40392863425147524, + -0.4142380090716604, + 0.020046497173245886 + ], + [ + 0.5563834549968681, + 0.3511851163896356, + -0.7530647150359299, + 0.25462319336348843 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.07057874501503747, + -0.007134654755880376, + -0.013473889670656547, + 0.03193157771530508, + 0.008140896401442888, + -0.0018317528049927034 + ] + }, + { + "q": [ + 1.6422591997548754, + 1.324177062452614, + 0.24729168979696992, + 0.8353154351577179, + 0.7698092681424917, + 1.4725647532729922, + -0.05014456737864059 + ], + "tcp": [ + [ + -0.6723567703022981, + -0.4741499604340622, + -0.5684348585801541, + -0.05718124358197222 + ], + [ + -0.6902673038385398, + 0.12427332498002261, + 0.7128023498487378, + 0.23301064384855283 + ], + [ + -0.26733391606778095, + 0.8716294830481509, + -0.4108462262222666, + -0.03129065586673257 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.5020459417547929, + -0.5439542681110292, + 0.6723567703022981, + -0.02763152390349024 + ], + [ + -0.15984530457880378, + 0.7056773539342056, + 0.6902673038385398, + 0.20126574038351716 + ], + [ + -0.8499407926271407, + -0.4540189713527534, + 0.2673339160677809, + -0.02364286141874297 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.3554968063049224, + 0.1354569140882278, + -0.006558327797501207, + 0.032051231960150695, + -0.0015053467292136524, + 0.0026466840871708364 + ] + }, + { + "q": [ + -1.6662340899056498, + 0.8891755068309601, + -0.9680186535674895, + -0.35389270614719104, + 1.1729642014082087, + 2.061389774523239, + -0.16653562152629786 + ], + "tcp": [ + [ + 0.8859994063300826, + 0.25593677889191735, + -0.38665413122475645, + -0.0384774136434632 + ], + [ + -0.0911187142484207, + -0.721510740582538, + -0.6863815492404736, + -0.13950465362480008 + ], + [ + -0.4546453913727226, + 0.6433650724390219, + -0.6159374575953928, + -0.034745146385137524 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.18830142660530977, + -0.4237306039468313, + -0.8859994063300826, + -0.02490823347330874 + ], + [ + 0.8253079557758564, + -0.5572828348754266, + 0.0911187142484207, + -0.10214159395710398 + ], + [ + -0.5323620486769155, + -0.7140645749734834, + 0.4546453913727225, + -0.015790128295510998 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.21312199881273752, + 0.03691811228945241, + -0.004837181197259177, + 0.0326260478494822, + -0.004678949394598675, + 0.0024473981313777315 + ] + }, + { + "q": [ + -2.2100954201453464, + -0.11381421226827904, + -0.9092875684262379, + 2.6725581812811194, + -1.5814872769548007, + -1.3084798215381057, + -0.4445944011762859 + ], + "tcp": [ + [ + 0.3949867621128936, + 0.5466566691850171, + -0.7383440551606787, + -0.10996309110188107 + ], + [ + -0.910535624475183, + 0.33977177232839634, + -0.23554196927598534, + -0.08860684674956235 + ], + [ + 0.1221078798323892, + 0.7653245251292834, + 0.6319557238592555, + 0.14701735212452355 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.17595785724034885, + -0.901678595859388, + -0.3949867621128937, + -0.0847057535009553 + ], + [ + -0.20543626504430995, + -0.3587768353255724, + 0.910535624475183, + -0.08241655505297907 + ], + [ + -0.9627225838630924, + 0.24136050257407435, + -0.1221078798323892, + 0.11147637893829701 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.1097238749303564, + 0.1354932041008894, + 0.003555407412925149, + -0.013417441685481535, + 0.0036754605747812974, + 0.0016092649712806661 + ] + }, + { + "q": [ + -2.088892432779349, + 1.8076922088564635, + 0.5209740898029984, + -1.8973293523959127, + -0.06222884179554766, + -2.1667443886629396, + -0.26907515977520136 + ], + "tcp": [ + [ + 0.983664664346808, + 0.15174255355971217, + -0.09684020629190802, + -0.08723420581237355 + ], + [ + 0.11812528885752865, + -0.950066625015597, + -0.2888249022570568, + -0.1623429773210397 + ], + [ + -0.13583167615768735, + 0.2726675731924331, + -0.9524716007742919, + -0.13402927124979006 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.12053842742452432, + -0.13369486014617044, + -0.983664664346808, + -0.08482518218083505 + ], + [ + 0.9926617461129636, + -0.025866463544279412, + -0.11812528885752861, + -0.13932231551023747 + ], + [ + -0.00965118220658927, + -0.9906849198580789, + 0.13583167615768726, + -0.09656382952097334 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.31377684570413344, + 0.09320874958321235, + -0.0035746339603626165, + -0.015821420361328276, + -0.0011484451965804165, + 0.0018411681380892109 + ] + }, + { + "q": [ + -1.2764966489906537, + -1.320077258180761, + -1.053791031792345, + 0.2568296328267299, + 0.5326548654020171, + -0.8914519065685922, + -0.24661467801948372 + ], + "tcp": [ + [ + -0.7328726118898818, + -0.5966126350220653, + 0.3270337879695776, + 0.026828438458826882 + ], + [ + 0.2977903598756451, + -0.7134705466925482, + -0.6342560055430065, + -0.05370311420992026 + ], + [ + 0.6117341222351722, + -0.36744134597819067, + 0.7005485143506474, + 0.17994305875059585 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.49872547259683214, + 0.4627857363021997, + 0.7328726118898818, + 0.01964334785572499 + ], + [ + 0.8467202154261647, + -0.44089202573170116, + -0.2977903598756451, + -0.01864965634981223 + ], + [ + 0.18530455950066543, + 0.7690536937830986, + -0.6117341222351722, + 0.1542712229363256 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.07723249093039009, + 0.14257844935250397, + 0.014302878631466953, + 0.031527343596904345, + 0.007506018306902007, + -0.001808420801996391 + ] + }, + { + "q": [ + -0.7037318307136753, + -0.9740370210729705, + -0.4495006788747449, + -0.6145757613297329, + 1.7148490743687366, + -1.9750827341126804, + -0.6320766612233754 + ], + "tcp": [ + [ + -0.06540823493456184, + 0.6313759876845606, + -0.7727134818146314, + -0.05824448834723046 + ], + [ + -0.9724007023723235, + -0.21413138961972447, + -0.09265323526643755, + -0.009154700756324774 + ], + [ + -0.22396123956737973, + 0.7453268478693568, + 0.6279564101245972, + 0.22652235319220038 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.0528598797018771, + -0.9964575233900581, + 0.06540823493456176, + -0.03248062846778573 + ], + [ + 0.2275032225108329, + 0.05176058126604171, + 0.9724007023723235, + -0.0027931871019316367 + ], + [ + -0.9723415638884771, + 0.06628156837578023, + 0.2239612395673797, + 0.1913773925699816 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.10705253475351562, + 0.08063470546826323, + 0.01863971751947524, + -0.011726726451000147, + -0.0008835331632983667, + 0.0019515193350194693 + ] + }, + { + "q": [ + 0.29256941158883354, + 2.1574470450390555, + 1.3395777701667597, + 1.0464673171706167, + -1.9128460676445656, + 1.6038945906178816, + -0.2190014563756838 + ], + "tcp": [ + [ + -0.7681005128463924, + 0.5580994655553998, + -0.31392130974479265, + 0.14860627323325165 + ], + [ + 0.30123697256484744, + -0.11766947192824356, + -0.9462611593720275, + -0.021906663678079655 + ], + [ + -0.565046802066054, + -0.8213883867713891, + -0.07773821165952, + -0.06515383241522993 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.47656820365299846, + -0.427673180632201, + 0.7681005128463924, + 0.1556680186590378 + ], + [ + 0.32043892622372416, + -0.8980953072589524, + -0.3012369725648475, + 0.019701323430979308 + ], + [ + 0.8186584402713629, + 0.1025693406792928, + 0.565046802066054, + -0.052571275072870155 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.30547592541965707, + 0.11650932206161424, + 0.024589982740991223, + -0.024944549471134225, + -0.010073370210889054, + -0.0019341191287436388 + ] + }, + { + "q": [ + -1.693848566413299, + 0.28303612146780655, + -1.2529766774740472, + 1.6000029060743648, + 0.8828364022162454, + 1.5947063605585448, + -0.1657142680126108 + ], + "tcp": [ + [ + 0.07841445960476717, + 0.6375216391595973, + 0.7664315573670963, + 0.051588849719268144 + ], + [ + 0.9955006138970844, + -0.009051370990056935, + -0.09432179182844622, + -0.07534336673953605 + ], + [ + -0.05319492697072456, + 0.7703792782042238, + -0.6353629415209221, + 0.0004863221152151126 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.7552162323569193, + 0.6507684802673774, + -0.07841445960476717, + 0.011766538310711103 + ], + [ + 0.024486400542319285, + -0.09153657148380359, + -0.9955006138970844, + -0.07122618104829684 + ], + [ + -0.6550182123980225, + -0.7537383108025482, + 0.05319492697072447, + 0.018834234396010518 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.1254732607258724, + 0.062104906745075183, + -0.0011547186817839616, + 0.03282317766195586, + 0.0013095203671431577, + 0.0027818467807083636 + ] + }, + { + "q": [ + -1.107171860862157, + 1.5044300293198818, + -0.6490634158638086, + -2.0163498627118015, + -0.5248791655708875, + 0.005128921628379413, + -0.49397823940390706 + ], + "tcp": [ + [ + -0.9612709599055118, + 0.043591006841657585, + 0.2721359325132687, + 0.05666143291424185 + ], + [ + 0.17150944817187513, + 0.8675388009937172, + 0.46686286847227665, + -0.03417836411441234 + ], + [ + -0.2157374581061774, + 0.4954556013336756, + -0.8414161255151763, + -0.10306787786351473 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.16740829154588072, + 0.2189351629227821, + 0.9612709599055118, + 0.044585280207707224 + ], + [ + -0.9851824361150014, + -0.0002770528642742527, + -0.17150944817187516, + -0.06384582889989081 + ], + [ + -0.03728312610552063, + -0.9757393697487484, + 0.2157374581061773, + -0.07284501138776742 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.17014316857581063, + -0.056262614234192654, + 0.015371533341329569, + -0.01047805631756299, + -0.0015284207597471289, + 0.0018726222317591579 + ] + }, + { + "q": [ + 0.5032166192735459, + 0.8003286434908521, + -0.6773268639780394, + 0.6234038624939351, + -0.6669917648971331, + 1.2323298810554992, + -0.4907994413606199 + ], + "tcp": [ + [ + -0.7216088315097525, + 0.6628098401723774, + -0.19990950467098095, + 0.08765466792054094 + ], + [ + 0.6508208500790831, + 0.5510235077202451, + -0.5223076823501801, + 0.01598221608232752 + ], + [ + -0.23603583496890723, + -0.5070071101181368, + -0.8289938931621792, + -0.03927260182870765 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.4903457710702415, + -0.48871435325828083, + 0.7216088315097525, + 0.08868109271032444 + ], + [ + -0.23979881526401617, + -0.7203670934324484, + -0.6508208500790831, + 0.03199423179476607 + ], + [ + 0.8378887473832696, + -0.49216819444070786, + 0.2360358349689072, + 0.0017392914947605975 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.2233825427550018, + 0.06055869844479034, + 0.001930537257832654, + -0.009840922035597721, + -0.004477067355734832, + -0.0008821704200467906 + ] + }, + { + "q": [ + 1.4213810800171842, + 0.0956388805273023, + 0.2697932434107584, + 0.8999694843874932, + 0.024010297440865802, + -1.2395484594147685, + -0.014261220182894774 + ], + "tcp": [ + [ + 0.3039875459392655, + 0.9440941047177588, + 0.1275848476544008, + 0.027076344015597446 + ], + [ + -0.21874901546866327, + -0.0611729575025964, + 0.9738617650888016, + 0.18975367708232438 + ], + [ + 0.9272218936939062, + -0.3239509078665457, + 0.1879238386877175, + 0.20623478752253377 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.945817554210752, + 0.11410839610043728, + -0.3039875459392656, + 0.010986617363229069 + ], + [ + 0.047278750570074586, + 0.9746351050398417, + 0.21874901546866332, + 0.14898994601799367 + ], + [ + 0.3212380330709985, + 0.19252450743570942, + -0.9272218936939062, + 0.201892839230927 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.1712787087715384, + 0.14961080718745023, + 0.01915703126686985, + 0.02246385036319687, + 0.012505387392280909, + -0.0010387800619682253 + ] + }, + { + "q": [ + -1.921592466922395, + -0.24350737283785073, + 0.2573191850304941, + -2.6110776726837033, + -1.5347510276108443, + 1.5669152021500259, + -0.14952996737882507 + ], + "tcp": [ + [ + -0.7277799343840273, + -0.3206757023057726, + 0.6062206372754729, + 0.02574064006420955 + ], + [ + 0.5213312469959139, + -0.8330093712739557, + 0.18522720716853208, + -0.044835909991959855 + ], + [ + 0.44558960716523244, + 0.45084640546473076, + 0.7734257693313983, + 0.2770204526719804 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.22678662328973725, + 0.6472280854573755, + 0.7277799343840273, + 0.0035560748763366658 + ], + [ + 0.7961200745599873, + 0.3072565016208535, + -0.5213312469959139, + -0.0433158007178198 + ], + [ + -0.561035341470962, + 0.6976311687467059, + -0.4455896071652324, + 0.23900798203115395 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.03700229458947213, + 0.09171097817284313, + -0.023571250590270765, + 0.0049011948666244365, + 0.00749301934388491, + -8.574535526051363e-05 + ] + }, + { + "q": [ + 0.6515586578475654, + 1.9258269230588358, + 0.881065280598049, + -1.1400720925527306, + 0.566474583238016, + -0.8122718442473809, + -0.1730260973402291 + ], + "tcp": [ + [ + 0.06933777693831554, + -0.6134499161842015, + 0.7866838456602807, + 0.20217876431437365 + ], + [ + -0.9915380417515783, + -0.1291329968655632, + -0.013303416101274305, + 0.10618922090604502 + ], + [ + 0.10974782206812851, + -0.7791045305054479, + -0.6172127235379937, + -0.10440622863807947 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.46885138839090457, + 0.88055133200352, + -0.06933777693831546, + 0.17561784260987212 + ], + [ + 0.12949519070020754, + 0.009127285739039297, + 0.9915380417515783, + 0.10821341895384017 + ], + [ + 0.87373300909914, + -0.47386289616498195, + -0.10974782206812851, + -0.06933724448583117 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.3223809979316372, + 0.10962645336301535, + 0.006325451065714027, + 0.012244455629972784, + -0.0005388386334348461, + -0.0009922409907603402 + ] + }, + { + "q": [ + -0.5480443234074985, + -0.8891088725016755, + 1.2465088660132861, + 0.9928815764269774, + 0.9945315391334688, + 0.3251170162437309, + -0.15013363566755122 + ], + "tcp": [ + [ + 0.6688021116213559, + -0.712194541187145, + -0.21326666639221248, + -0.13403223192245395 + ], + [ + 0.3028903234259929, + -0.0009474462762406671, + 0.9530249494742785, + 0.14622533473310662 + ], + [ + -0.6789412253396553, + -0.701981508195754, + 0.2150831808522034, + 0.20084596648280223 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.7360814872569803, + -0.10434452361464443, + -0.6688021116213559, + -0.11691343336413378 + ], + [ + -0.1416074068622452, + 0.9424461758088087, + -0.30289032342599287, + 0.10566861837729376 + ], + [ + 0.6619149389757737, + 0.31765929247384667, + 0.6789412253396553, + 0.19961589195099655 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.32228294072345065, + -0.1460885807325063, + 0.005170476630178555, + -0.02801837134525087, + -0.011456231963080654, + -0.0019951670870987925 + ] + }, + { + "q": [ + -0.24796987149425753, + 0.6622785982454125, + -1.2129638047893896, + 0.2557735152323368, + 1.2944129079327622, + 1.0813239675411737, + -0.13615450501589565 + ], + "tcp": [ + [ + 0.2572674738486035, + 0.47554940933521356, + 0.8412289855803161, + 0.13195073273923585 + ], + [ + 0.8491576847414928, + -0.5267641231634766, + 0.0380891715882764, + -0.013754196806097569 + ], + [ + 0.4612425320197886, + 0.704536952777314, + -0.5393357106916168, + -0.012797552027038472 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.5853318969601131, + 0.7688953227199615, + -0.2572674738486035, + 0.09077318567864398 + ], + [ + 0.5167190676283291, + 0.10923658541797199, + -0.8491576847414928, + -0.009428371622580079 + ], + [ + -0.6248103516671166, + -0.6299740876461417, + -0.4612425320197887, + 0.002206128973115706 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.18097349213652691, + 0.04144594381583211, + -0.0029128010003987015, + 0.0321814324633074, + 0.008406334295329998, + 0.002489992101395444 + ] + }, + { + "q": [ + -0.018556758552747965, + 1.848870358813481, + -1.0747481523490354, + 2.0020865114092636, + -0.606871371827215, + -2.1030511188345833, + -0.2632974471320141 + ], + "tcp": [ + [ + 0.4890980568084148, + 0.4281295168321451, + -0.7599264488378508, + -0.0407077261758089 + ], + [ + -0.027554454159439495, + -0.8632277583380739, + -0.5040620887257828, + -0.039804949052213406 + ], + [ + -0.8717934634316823, + 0.2674751466056194, + -0.4104061440403815, + -0.0531195046973601 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.21559205517848537, + -0.8451645736601537, + -0.48909805680841484, + -0.013193519273163024 + ], + [ + 0.9646684609800891, + -0.2620219693962347, + 0.027554454159439516, + -0.008602696729786155 + ], + [ + -0.1514424845749133, + -0.46587694832817084, + 0.8717934634316823, + -0.03866954076917062 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.08702926737515909, + -0.13115967146051788, + -0.02501375617487006, + 0.0018662701262855963, + -0.011344617708783367, + 0.0011790665215005713 + ] + }, + { + "q": [ + -0.21457900850701872, + 0.14551090870138284, + 1.1726921034479794, + -2.0169168978717145, + 0.44668597865116455, + -0.43057261194058993, + -0.3262911457131282 + ], + "tcp": [ + [ + 0.29309885004153013, + 0.7966228736278567, + 0.5286634669780282, + 0.09753426344998348 + ], + [ + -0.6362651130150608, + 0.5752458121478652, + -0.5140612430112538, + -0.052476628295189634 + ], + [ + -0.7136243900430148, + -0.18569936138619075, + 0.6754672287509559, + 0.2993373862991865 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.9240447864841551, + 0.245426764383563, + -0.29309885004153013, + 0.06603677340787124 + ], + [ + -0.380121381780501, + -0.6713229037305481, + 0.6362651130150608, + -0.03708910858307899 + ], + [ + -0.04060748311253626, + 0.6993506003793807, + 0.7136243900430148, + 0.27268131856299466 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.09498830444432116, + 0.062087499308471905, + -0.0022702678625918793, + -0.011941348243560412, + -0.010251742010858322, + -0.0011986142633979568 + ] + }, + { + "q": [ + -0.002809617327331093, + -0.9609878762600025, + 0.14521738450378097, + 1.928041476983954, + -1.4647557284074653, + -2.5149362018777417, + -0.6449408246698912 + ], + "tcp": [ + [ + -0.8785116089440228, + 0.396769886422966, + -0.26606580046764927, + -0.06752781351209472 + ], + [ + 0.1272251417539715, + -0.34251735741404643, + -0.9308574666272921, + -0.07273351768492707 + ], + [ + -0.46046836618411274, + -0.8516193498846799, + 0.25042636970919985, + 0.2264596892425792 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.15712707669539722, + -0.451141258055327, + 0.8785116089440228, + -0.06068172260130358 + ], + [ + 0.8333037624271269, + -0.5379763961648096, + -0.12722514175397154, + -0.029242686937798544 + ], + [ + 0.5300150198758079, + 0.7120765144630039, + 0.4604683661841128, + 0.2254145724233657 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.18020241090987987, + 0.005804963926214768, + 0.014254192909346486, + 0.030200828143867042, + -0.008169222992515657, + -0.00243874007099798 + ] + }, + { + "q": [ + -0.19125463073584426, + 2.1990352615697124, + -1.2683451368823317, + 2.629678228203865, + 0.09981474058740547, + -1.951870773354604, + -0.402181186186425 + ], + "tcp": [ + [ + -0.159349836630551, + -0.4294553357931811, + -0.8889183000279512, + -0.07605705553209346 + ], + [ + 0.6705101149264209, + -0.7079593190592129, + 0.2218327936500222, + 0.018618141070903073 + ], + [ + -0.7245852712739713, + -0.5606796920845105, + 0.40076734839161227, + 0.03226942731188487 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.7431349488233666, + -0.6498908196021153, + 0.15934983663055097, + -0.03336991951369865 + ], + [ + 0.564639610893497, + 0.48124660579727546, + -0.6705101149264209, + 0.01716824525578109 + ], + [ + 0.3590718001282372, + 0.5882547296924121, + 0.7245852712739713, + 0.02154003731653323 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.012908978114754832, + -0.17067188093746258, + -0.0026202650466045085, + 0.03092526260854017, + -0.011257876913247505, + -0.0018466893851772082 + ] + }, + { + "q": [ + -1.3921084568104323, + 1.2201784478687716, + 0.2343572333132613, + -1.1990275358732567, + -1.1037034533196541, + 1.917757734202835, + -0.16168431018525786 + ], + "tcp": [ + [ + 0.5683592255660331, + -0.11454120410274153, + 0.8147687422064455, + 0.09597110790805302 + ], + [ + -0.4221612145442495, + -0.8905733773626767, + 0.16928959940693264, + -0.16499276455934223 + ], + [ + 0.7062207159581934, + -0.4401810673978717, + -0.5545204489069516, + -0.026136178490692476 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.018114799795701096, + 0.8225810870317531, + -0.5683592255660331, + 0.06258201705287121 + ], + [ + 0.851705740177481, + 0.3104468409941603, + 0.42216121454424954, + -0.1621443415068725 + ], + [ + 0.5237071568893162, + -0.476427449014439, + -0.7062207159581934, + 0.0024377377088517053 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.3233931779797472, + 0.11029264574552444, + -0.010372703057592858, + -0.014111182070064565, + 0.008770207116093331, + -0.0002411674124291885 + ] + }, + { + "q": [ + -0.30357218419744125, + 0.05954104085785783, + 0.8265191504608771, + 2.519687305746155, + -1.8577720638633173, + -1.7500913268315377, + -0.44106203110851705 + ], + "tcp": [ + [ + -0.22211470541115666, + -0.42453860814142463, + -0.8777425749258475, + 0.0006572004344554544 + ], + [ + 0.9384570948707281, + 0.15113107735286577, + -0.31057636507782627, + -0.04603965829811663 + ], + [ + 0.2645058387387852, + -0.8927073247461924, + 0.3648428341321614, + 0.26494988403177566 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.7586181427519009, + -0.6125059764016771, + 0.22211470541115663, + 0.04281310950724069 + ], + [ + -0.004082569673852605, + -0.3453717036927768, + -0.9384570948707281, + -0.034525624029162656 + ], + [ + 0.651522713427947, + 0.7110237796027479, + -0.26450583873878514, + 0.25949828480765647 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.03965105657699475, + 0.026148859910701816, + 0.008575040733038719, + 0.03224670383494832, + 0.0011500621843740788, + -0.0026958965060330797 + ] + }, + { + "q": [ + 0.0366998093078692, + -1.1968468178115554, + -1.2403847040818787, + -1.5720985129641554, + -0.5438799504769425, + 1.2561243522124457, + -0.2698121197034027 + ], + "tcp": [ + [ + 0.21100384167592434, + 0.5075871878593193, + 0.8353637683782255, + 0.0841666817706458 + ], + [ + -0.2585153859947578, + -0.7951979874185141, + 0.5484796769339096, + 0.043622762312106926 + ], + [ + 0.9426808441895819, + -0.3316857059425573, + -0.03657073244282426, + 0.10476205881415196 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.7118896992772431, + 0.6698435898483749, + -0.21100384167592431, + 0.04287709289115817 + ], + [ + 0.620231059342165, + 0.7405965353897337, + 0.25851538599475793, + 0.029253413182016217 + ], + [ + 0.32943358828488045, + 0.0531633041464236, + -0.9426808441895819, + 0.11006349680213713 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.04920769472449571, + 0.16201660529846507, + -0.00758426352276409, + -0.00037310128597756176, + 0.012138024640950817, + -0.0008001503626342554 + ] + }, + { + "q": [ + -2.2396087955287483, + -1.2961082162914241, + -0.0360510535368348, + 1.9974576810611664, + 1.7249628607630814, + -0.6826183678816713, + -0.07790140137210055 + ], + "tcp": [ + [ + 0.6332159231572668, + 0.5962725429781338, + 0.4934537963684946, + 0.08594472104923728 + ], + [ + 0.5551193051650808, + 0.09438465515084073, + -0.8263982659136541, + -0.004815452653066071 + ], + [ + -0.5393330619321681, + 0.797214269417229, + -0.2712365332038176, + 0.15773071271140074 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.6328660531701371, + 0.4455537603981628, + -0.6332159231572668, + 0.05820795628899644 + ], + [ + -0.029785918435923868, + -0.8312372441703789, + -0.5551193051650808, + 0.02929623957542745 + ], + [ + -0.7736881528159658, + -0.3324552458588831, + 0.5393330619321681, + 0.16027588911856713 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + 0.2243618645036971, + -0.00596327967561372, + -0.011801080251894192, + -0.03300249659665688, + -0.0055714919622307235, + 0.002260691911448731 + ] + }, + { + "q": [ + 2.0413741200808233, + 0.063688031490303, + 0.8525079444248174, + -0.7775663016991758, + -1.540480944692816, + 0.40594642673722614, + -0.631897917650688 + ], + "tcp": [ + [ + -0.4313047244530942, + 0.2603179679063911, + -0.8638349322929282, + -0.09991192383397568 + ], + [ + 0.27029988716270736, + 0.9507723184707135, + 0.1515584686833834, + 0.0753468561101919 + ], + [ + 0.8607637339388019, + -0.1681266011519639, + -0.4804365101871633, + 0.1999116871523753 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + 0.3001950295144608, + -0.8507991413484458, + 0.43130472445309415, + -0.06608005212872575 + ], + [ + -0.8567078419240882, + -0.43930586677803346, + -0.2702998871627074, + 0.05816124729821197 + ], + [ + 0.41944560772594064, + -0.28835945709330935, + -0.8607637339388019, + 0.22226057740851796 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.10082312162928986, + 0.08638173709663022, + -0.004717024326694741, + 0.02412136293483691, + 0.010600107902547506, + -0.00036446426432412693 + ] + }, + { + "q": [ + 0.7770823105465126, + 1.57760680746985, + 1.364208422129656, + -0.32723742191954, + -1.6956957031847306, + 2.101771836626274, + -0.08966175348143912 + ], + "tcp": [ + [ + -0.8575697265332565, + 0.24494741848077253, + -0.45229959795834535, + 0.11703613595948067 + ], + [ + 0.27343365057033975, + 0.9618876591655772, + 0.0024839244642187244, + 0.1501207275957747 + ], + [ + 0.43566983240689605, + -0.12154379179773805, + -0.8918626036593166, + -0.016399131079084674 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.20346382383019482, + -0.4724157454256484, + 0.8575697265332565, + 0.13352371659112564 + ], + [ + -0.9582462453785507, + -0.08365507727381445, + -0.2734336505703398, + 0.13915528271112693 + ], + [ + 0.20091442359946687, + -0.8773968267096942, + -0.4356698324068961, + 0.022937013780277918 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.36217391870263516, + 0.13527388097178683, + -0.005934065697155155, + 0.0002950507199672714, + 0.005884723146276638, + 0.00118398840731615 + ] + }, + { + "q": [ + 2.22445426361906, + 0.7682709484862245, + 0.6329103412501522, + -0.7343369174689118, + -1.413713685077726, + -1.062880454380651, + -0.19321460394235884 + ], + "tcp": [ + [ + 0.30932609656753046, + 0.7689367213381105, + -0.5595120057336498, + -0.1549407160969774 + ], + [ + 0.7465801737473329, + -0.560793331466835, + -0.35795095131837673, + 0.11707844749344959 + ], + [ + -0.5890122325976747, + -0.30699699992039925, + -0.7475409232210484, + 0.07534099435039521 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.6471938774446271, + -0.696747767115535, + -0.3093260965675306, + -0.1398052641328204 + ], + [ + 0.6190899536541796, + -0.24360967438069464, + -0.7465801737473329, + 0.1386467023504795 + ], + [ + 0.44482323936891477, + -0.6746827962588404, + 0.5890122325976747, + 0.11062756495824688 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.2758484140830131, + 0.11817353348653192, + -0.012207175159595351, + -0.011902971802646434, + -0.00907067593258759, + 0.00029137217638641744 + ] + }, + { + "q": [ + -0.8325649992389785, + 1.1353231154457468, + 0.11543337973603052, + -0.3219483139352999, + 1.9506764571320634, + -1.7137860619468166, + -0.35471753617059076 + ], + "tcp": [ + [ + -0.8110830640877588, + 0.5634141718722749, + 0.15719012082026984, + 0.1304190754527968 + ], + [ + -0.528594035642969, + -0.5909321231217113, + -0.6094157623048045, + -0.17752080240922183 + ], + [ + -0.25046478521479226, + -0.5773765641211507, + 0.7771124079378496, + 0.08344800262272772 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "trigger_link": [ + [ + -0.5829347532293108, + -0.04828391686156826, + 0.8110830640877588, + 0.11736701757940238 + ], + [ + 0.7658090614394341, + -0.36623056521805, + 0.528594035642969, + -0.1449081779851491 + ], + [ + 0.27152081852914195, + 0.9292705937844868, + 0.2504647852147924, + 0.056887240162965064 + ], + [ + 0.0, + 0.0, + 0.0, + 1.0 + ] + ], + "gravity_torque": [ + 0.0, + -0.34004646497221014, + 0.1343181176699515, + 0.008496460840445863, + 0.031109392173434003, + -0.004947856457881983, + -0.0022866916415953398 + ] + } + ] + }, + "imported_from": { + "repository": "https://github.com/Source-Robotics/RAFT-teleoperation-arm", + "commit": "5d6eadfbca8d834946998128c8bb6621197d2ef3", + "uncommitted_changes": false, + "file": "firmware/tools/sim/model/raft_reference.json" + } +} diff --git a/source/raft/visual/base_link_fasteners.stl b/source/raft/visual/base_link_fasteners.stl new file mode 100644 index 0000000..b7915ef Binary files /dev/null and b/source/raft/visual/base_link_fasteners.stl differ diff --git a/source/raft/visual/base_link_motors.stl b/source/raft/visual/base_link_motors.stl new file mode 100644 index 0000000..698e6a0 Binary files /dev/null and b/source/raft/visual/base_link_motors.stl differ diff --git a/source/raft/visual/base_link_structure.stl b/source/raft/visual/base_link_structure.stl new file mode 100644 index 0000000..df8feae Binary files /dev/null and b/source/raft/visual/base_link_structure.stl differ diff --git a/source/raft/visual/forearm_link_fasteners.stl b/source/raft/visual/forearm_link_fasteners.stl new file mode 100644 index 0000000..3220128 Binary files /dev/null and b/source/raft/visual/forearm_link_fasteners.stl differ diff --git a/source/raft/visual/forearm_link_motors.stl b/source/raft/visual/forearm_link_motors.stl new file mode 100644 index 0000000..e759a6a Binary files /dev/null and b/source/raft/visual/forearm_link_motors.stl differ diff --git a/source/raft/visual/forearm_link_structure.stl b/source/raft/visual/forearm_link_structure.stl new file mode 100644 index 0000000..b159145 Binary files /dev/null and b/source/raft/visual/forearm_link_structure.stl differ diff --git a/source/raft/visual/partitions.json b/source/raft/visual/partitions.json new file mode 100644 index 0000000..59de562 --- /dev/null +++ b/source/raft/visual/partitions.json @@ -0,0 +1,162 @@ +{ + "base_link": { + "source": "meshes/base_link.stl", + "triangles": 10668, + "servos": 1, + "parts": { + "structure": { + "file": "visual/base_link_structure.stl", + "triangles": 1862 + }, + "motors": { + "file": "visual/base_link_motors.stl", + "triangles": 1638 + }, + "fasteners": { + "file": "visual/base_link_fasteners.stl", + "triangles": 7168 + } + } + }, + "shoulder_link": { + "source": "meshes/shoulder_link.stl", + "triangles": 13060, + "servos": 1, + "parts": { + "structure": { + "file": "visual/shoulder_link_structure.stl", + "triangles": 2348 + }, + "motors": { + "file": "visual/shoulder_link_motors.stl", + "triangles": 1638 + }, + "horns": { + "file": "visual/shoulder_link_horns.stl", + "triangles": 896 + }, + "fasteners": { + "file": "visual/shoulder_link_fasteners.stl", + "triangles": 8178 + } + } + }, + "upper_arm_link": { + "source": "meshes/upper_arm_link.stl", + "triangles": 2954, + "servos": 0, + "parts": { + "structure": { + "file": "visual/upper_arm_link_structure.stl", + "triangles": 1162 + }, + "horns": { + "file": "visual/upper_arm_link_horns.stl", + "triangles": 1792 + } + } + }, + "forearm_link": { + "source": "meshes/forearm_link.stl", + "triangles": 20008, + "servos": 2, + "parts": { + "structure": { + "file": "visual/forearm_link_structure.stl", + "triangles": 1458 + }, + "motors": { + "file": "visual/forearm_link_motors.stl", + "triangles": 3276 + }, + "fasteners": { + "file": "visual/forearm_link_fasteners.stl", + "triangles": 15274 + } + } + }, + "wrist_1_link": { + "source": "meshes/wrist_1_link.stl", + "triangles": 12456, + "servos": 1, + "parts": { + "structure": { + "file": "visual/wrist_1_link_structure.stl", + "triangles": 1744 + }, + "motors": { + "file": "visual/wrist_1_link_motors.stl", + "triangles": 1638 + }, + "horns": { + "file": "visual/wrist_1_link_horns.stl", + "triangles": 896 + }, + "fasteners": { + "file": "visual/wrist_1_link_fasteners.stl", + "triangles": 8178 + } + } + }, + "wrist_2_link": { + "source": "meshes/wrist_2_link.stl", + "triangles": 12528, + "servos": 1, + "parts": { + "structure": { + "file": "visual/wrist_2_link_structure.stl", + "triangles": 1816 + }, + "motors": { + "file": "visual/wrist_2_link_motors.stl", + "triangles": 1638 + }, + "horns": { + "file": "visual/wrist_2_link_horns.stl", + "triangles": 896 + }, + "fasteners": { + "file": "visual/wrist_2_link_fasteners.stl", + "triangles": 8178 + } + } + }, + "wrist_3_link": { + "source": "meshes/wrist_3_link.stl", + "triangles": 11026, + "servos": 1, + "parts": { + "structure": { + "file": "visual/wrist_3_link_structure.stl", + "triangles": 1396 + }, + "motors": { + "file": "visual/wrist_3_link_motors.stl", + "triangles": 1638 + }, + "horns": { + "file": "visual/wrist_3_link_horns.stl", + "triangles": 896 + }, + "fasteners": { + "file": "visual/wrist_3_link_fasteners.stl", + "triangles": 7096 + } + } + }, + "trigger_link": { + "source": "meshes/trigger_link.stl", + "triangles": 1532, + "servos": 0, + "parts": { + "structure": { + "file": "visual/trigger_link_structure.stl", + "triangles": 636 + }, + "horns": { + "file": "visual/trigger_link_horns.stl", + "triangles": 896 + } + } + } +} diff --git a/source/raft/visual/shoulder_link_fasteners.stl b/source/raft/visual/shoulder_link_fasteners.stl new file mode 100644 index 0000000..74824c6 Binary files /dev/null and b/source/raft/visual/shoulder_link_fasteners.stl differ diff --git a/source/raft/visual/shoulder_link_horns.stl b/source/raft/visual/shoulder_link_horns.stl new file mode 100644 index 0000000..c382a4e Binary files /dev/null and b/source/raft/visual/shoulder_link_horns.stl differ diff --git a/source/raft/visual/shoulder_link_motors.stl b/source/raft/visual/shoulder_link_motors.stl new file mode 100644 index 0000000..befbcb0 Binary files /dev/null and b/source/raft/visual/shoulder_link_motors.stl differ diff --git a/source/raft/visual/shoulder_link_structure.stl b/source/raft/visual/shoulder_link_structure.stl new file mode 100644 index 0000000..4b8e3cd Binary files /dev/null and b/source/raft/visual/shoulder_link_structure.stl differ diff --git a/source/raft/visual/trigger_link_horns.stl b/source/raft/visual/trigger_link_horns.stl new file mode 100644 index 0000000..b2846aa Binary files /dev/null and b/source/raft/visual/trigger_link_horns.stl differ diff --git a/source/raft/visual/trigger_link_structure.stl b/source/raft/visual/trigger_link_structure.stl new file mode 100644 index 0000000..f7d2f14 Binary files /dev/null and b/source/raft/visual/trigger_link_structure.stl differ diff --git a/source/raft/visual/upper_arm_link_horns.stl b/source/raft/visual/upper_arm_link_horns.stl new file mode 100644 index 0000000..838eb32 Binary files /dev/null and b/source/raft/visual/upper_arm_link_horns.stl differ diff --git a/source/raft/visual/upper_arm_link_structure.stl b/source/raft/visual/upper_arm_link_structure.stl new file mode 100644 index 0000000..ee5a8b0 Binary files /dev/null and b/source/raft/visual/upper_arm_link_structure.stl differ diff --git a/source/raft/visual/wrist_1_link_fasteners.stl b/source/raft/visual/wrist_1_link_fasteners.stl new file mode 100644 index 0000000..031cafc Binary files /dev/null and b/source/raft/visual/wrist_1_link_fasteners.stl differ diff --git a/source/raft/visual/wrist_1_link_horns.stl b/source/raft/visual/wrist_1_link_horns.stl new file mode 100644 index 0000000..1df7e76 Binary files /dev/null and b/source/raft/visual/wrist_1_link_horns.stl differ diff --git a/source/raft/visual/wrist_1_link_motors.stl b/source/raft/visual/wrist_1_link_motors.stl new file mode 100644 index 0000000..b14979f Binary files /dev/null and b/source/raft/visual/wrist_1_link_motors.stl differ diff --git a/source/raft/visual/wrist_1_link_structure.stl b/source/raft/visual/wrist_1_link_structure.stl new file mode 100644 index 0000000..204f434 Binary files /dev/null and b/source/raft/visual/wrist_1_link_structure.stl differ diff --git a/source/raft/visual/wrist_2_link_fasteners.stl b/source/raft/visual/wrist_2_link_fasteners.stl new file mode 100644 index 0000000..0925626 Binary files /dev/null and b/source/raft/visual/wrist_2_link_fasteners.stl differ diff --git a/source/raft/visual/wrist_2_link_horns.stl b/source/raft/visual/wrist_2_link_horns.stl new file mode 100644 index 0000000..965edcf Binary files /dev/null and b/source/raft/visual/wrist_2_link_horns.stl differ diff --git a/source/raft/visual/wrist_2_link_motors.stl b/source/raft/visual/wrist_2_link_motors.stl new file mode 100644 index 0000000..e83f7be Binary files /dev/null and b/source/raft/visual/wrist_2_link_motors.stl differ diff --git a/source/raft/visual/wrist_2_link_structure.stl b/source/raft/visual/wrist_2_link_structure.stl new file mode 100644 index 0000000..22f1ff1 Binary files /dev/null and b/source/raft/visual/wrist_2_link_structure.stl differ diff --git a/source/raft/visual/wrist_3_link_fasteners.stl b/source/raft/visual/wrist_3_link_fasteners.stl new file mode 100644 index 0000000..e98196f Binary files /dev/null and b/source/raft/visual/wrist_3_link_fasteners.stl differ diff --git a/source/raft/visual/wrist_3_link_horns.stl b/source/raft/visual/wrist_3_link_horns.stl new file mode 100644 index 0000000..35f87d8 Binary files /dev/null and b/source/raft/visual/wrist_3_link_horns.stl differ diff --git a/source/raft/visual/wrist_3_link_motors.stl b/source/raft/visual/wrist_3_link_motors.stl new file mode 100644 index 0000000..a595cd4 Binary files /dev/null and b/source/raft/visual/wrist_3_link_motors.stl differ diff --git a/source/raft/visual/wrist_3_link_structure.stl b/source/raft/visual/wrist_3_link_structure.stl new file mode 100644 index 0000000..2073df9 Binary files /dev/null and b/source/raft/visual/wrist_3_link_structure.stl differ diff --git a/tests/test_licensing.py b/tests/test_licensing.py index bff9d25..5536bc3 100644 --- a/tests/test_licensing.py +++ b/tests/test_licensing.py @@ -9,7 +9,8 @@ @pytest.mark.parametrize('package',[ 'mujoco/par6','mujoco/parol6','mujoco/msg','mujoco/ssg48', 'urdf/par6_description','urdf/parol6_description', - 'urdf/msg_description','urdf/ssg48_description']) + 'urdf/msg_description','urdf/ssg48_description', + 'mujoco/raft','urdf/raft_description']) def test_downloaded_model_license_and_support_code_scope_are_consistent(package): path=ROOT/package data=json.loads((path/'sim2bot.robot.json').read_text()) diff --git a/tests/test_model_colors.py b/tests/test_model_colors.py index 2f38288..9ae1372 100644 --- a/tests/test_model_colors.py +++ b/tests/test_model_colors.py @@ -10,7 +10,7 @@ from model_colors import ROOT, apply_tree, badge_config, color, load, rgba, validate MODELS = sorted((ROOT/'mujoco').glob('*/*.xml')) -MODELS = [p for p in MODELS if p.stem.startswith(p.parent.name+'_')] +MODELS = [p for p in MODELS if p.stem.startswith(p.parent.name+'_') or p.stem == p.parent.name] MODELS += sorted((ROOT/'urdf').glob('*/*.urdf')) @@ -67,7 +67,8 @@ def test_non_default_theme_changes_visuals_and_preserves_all_physics(path): for name in dir(previous): before, after = getattr(previous, name), getattr(selected, name) if isinstance(before, np.ndarray): - if not np.array_equal(before, after): + # a degenerate (zero-area) CAD polygon has a NaN normal in both compiles + if not np.array_equal(before, after, equal_nan=np.issubdtype(before.dtype, np.floating)): changed.add(name) # This compares every compiled array, including all mesh vertices/faces, # contact parameters, body inertials, joint/actuator data and camera poses. diff --git a/tests/test_package_layout.py b/tests/test_package_layout.py index 2612727..6fe876e 100644 --- a/tests/test_package_layout.py +++ b/tests/test_package_layout.py @@ -8,7 +8,8 @@ @pytest.mark.parametrize('package',[ 'mujoco/par6','mujoco/parol6','mujoco/msg','mujoco/ssg48', 'urdf/par6_description','urdf/parol6_description', - 'urdf/msg_description','urdf/ssg48_description']) + 'urdf/msg_description','urdf/ssg48_description', + 'mujoco/raft','urdf/raft_description']) def test_runtime_package_documentation_and_catalog_links_are_portable(package): verify(ROOT/package) assert json.loads((ROOT/package/'docs/colors.json').read_text()) == json.loads( diff --git a/tests/test_raft.py b/tests/test_raft.py new file mode 100644 index 0000000..7a95150 --- /dev/null +++ b/tests/test_raft.py @@ -0,0 +1,48 @@ +import json +import xml.etree.ElementTree as ET + +import build_raft +from model_colors import load +from model_reference import ROOT +from ros_support.robot_description import select_model +import verify_raft + + +def test_raft_both_formats_agree_with_the_raft_reference_kinematics_and_gravity(): + result = verify_raft.check_model(ROOT/'mujoco/raft/scene.xml', ROOT/'urdf/raft_description/raft.urdf', + build_raft.reference()) + assert result['samples'] == 40 and result['max_error'] < 1e-9 + + +def test_raft_visual_pieces_keep_every_cad_triangle_and_follow_the_shared_palette(): + ref, parts = build_raft.reference(), build_raft.partitions() + assert verify_raft.check_pieces(ref, parts) == sum(p['triangles'] for p in parts.values()) + assert verify_raft.check_appearance(ref, parts, load()) == 26 + + +def test_raft_committed_models_are_what_the_build_writes(tmp_path, monkeypatch): + monkeypatch.setattr(build_raft, 'MUJOCO', tmp_path/'mujoco/raft') + monkeypatch.setattr(build_raft, 'URDF', tmp_path/'urdf/raft_description') + build_raft.build() + for package, name in [('mujoco/raft', 'raft.xml'), ('mujoco/raft', 'scene.xml'), + ('urdf/raft_description', 'raft.urdf'), ('urdf/raft_description', 'config/joint_names.yaml')]: + built = (tmp_path/package/name).read_text(encoding='utf-8').replace('\r\n', '\n') + assert built == (ROOT/package/name).read_text(encoding='utf-8').replace('\r\n', '\n'), name + + +def test_raft_ranges_end_at_the_measured_stops_and_keyframes_stay_inside(): + ref = build_raft.reference() + for joint in ref['joints']: + stop = joint['measured_stop_deg'] + assert any(abs(end-(-stop if joint['name'] == 'trigger_joint' else stop)) < 1e-9 for end in joint['range_deg']) + assert joint['range'][0] < joint['range'][1] + for key in ref['keyframes']: + assert all(j['range'][0]-1e-12 <= q <= j['range'][1]+1e-12 for j, q in zip(ref['joints'], key['qpos'])), key['name'] + + +def test_raft_ros_selection_has_one_configuration_and_index_matches_the_model(): + assert select_model('raft') == 'raft.urdf' == select_model('raft', 'msg', 'default') + data = json.loads((ROOT/'urdf/raft_description/sim2bot.robot.json').read_text()) + joints = [j.get('name') for j in ET.parse(ROOT/'urdf/raft_description/raft.urdf').findall('joint')] + assert all(name in joints for name in data['armActuators']+data['gripperActuators']) + assert data['packageVersion'] == ET.parse(ROOT/'urdf/par6_description/package.xml').findtext('version') diff --git a/tests/test_release.py b/tests/test_release.py index 4ee270d..655f4af 100644 --- a/tests/test_release.py +++ b/tests/test_release.py @@ -39,9 +39,9 @@ def test_release_rejects_mixed_package_versions(tmp_path): def test_release_checksums_match_every_downloadable_asset(tmp_path): release_fixture(tmp_path);manifest=prepare(tmp_path,tag='v2.12.1') - assert len(manifest['assets'])==11 + assert len(manifest['assets'])==13 lines=(tmp_path/'dist/SHA256SUMS').read_text().splitlines() - assert len(lines)==12 + assert len(lines)==14 for line in lines: digest,name=line.split(' ') assert digest==hashlib.sha256((tmp_path/'dist'/name).read_bytes()).hexdigest() diff --git a/tools/README.md b/tools/README.md index 18173f6..f13f110 100644 --- a/tools/README.md +++ b/tools/README.md @@ -16,7 +16,7 @@ purpose below. Generated JSON records now live in [reports/](../reports/README.m | Script | Purpose | | --- | --- | | [build.py](build.py) | Main build: generates assets, synchronizes physical data, builds all products, validates them and writes ZIPs. | -| [package_models.py](package_models.py) | Creates all eight independent ZIPs, extracts them and checks their contents/assets. | +| [package_models.py](package_models.py) | Creates all ten independent ZIPs, extracts them and checks their contents/assets. | | [prepare_release.py](prepare_release.py) | Checks version consistency and prepares release assets, manifest and SHA-256 checksums. | | [export_hardware_guide.py](export_hardware_guide.py) | Copies a product guide and its local previews to a hardware repository, linking model/docs paths to a chosen central revision. | @@ -34,6 +34,7 @@ purpose below. Generated JSON records now live in [reports/](../reports/README.m | [build_pneumatic.py](build_pneumatic.py) | Splits the original pneumatic assembly into housing/two moving jaws and generates both formats. | | [build_parol6.py](build_parol6.py) | Generates PAROL6 from included original CAD exports and shared PAR6 tool/physical data. | | [build_standalone_grippers.py](build_standalone_grippers.py) | Exports independent MSG/SSG48 packages from maintained arm-mounted tool subtrees. | +| [build_raft.py](build_raft.py) | Builds RAFT's MuJoCo and URDF packages from `source/raft` (RAFT's model reference, CAD meshes and visual pieces) and the shared palette. | | [build_kinematics_docs.py](build_kinematics_docs.py) | Writes common-frame/DH comparison and actuation documentation. | | [build_review_sheets.py](build_review_sheets.py) | Builds twelve dimensioned sheets and their checks; optionally refreshes OpenGL snapshots or generates the PDF. | | [build_ros_support.py](build_ros_support.py) | Generate native ROS 2 packaging/launch support and retain ROS 1 entry points. | @@ -52,6 +53,7 @@ purpose below. Generated JSON records now live in [reports/](../reports/README.m | [verify_parol6.py](verify_parol6.py) | Checks PAROL6 frames, source-chain correspondence, shared physical data and tool transforms. | | [verify_ssg48_contacts.py](verify_ssg48_contacts.py) | Sweeps both jaw options for unintended SSG48 internal contacts on both arms. | | [verify_standalone_grippers.py](verify_standalone_grippers.py) | Compares standalone and mounted grippers, runs close/open cycles and checks relocated packages. | +| [verify_raft.py](verify_raft.py) | Checks both RAFT formats against the kinematics and gravity torques in RAFT's reference, and the ranges, keyframes, palette and visual pieces. | | [grasp_check.py](grasp_check.py) | Simulates close, lift, hold and release in those fixtures. | ## Render and benchmark @@ -62,6 +64,7 @@ purpose below. Generated JSON records now live in [reports/](../reports/README.m | [render_gripper_previews.py](render_gripper_previews.py) | Optional OpenGL refresh of standalone guide images. MSG uses a temporary upright presentation pose, without changing saved mounting frames. | | [render_msg_camera_previews.py](render_msg_camera_previews.py) | Renders the actual named MSG camera in both existing box grasp fixtures, with recorded demo lighting and PNG/camera metadata. | | [render_parol6_previews.py](render_parol6_previews.py) | Optional OpenGL refresh of PAROL6 preview images. | +| [render_raft_previews.py](render_raft_previews.py) | Optional OpenGL refresh of RAFT's package preview and overview picture. | | [benchmark_visuals.py](benchmark_visuals.py) | Local timing comparison for rendering, physics and clearance queries. Timing records are measurements on a particular machine. | ## Shared helpers and reference import @@ -76,6 +79,7 @@ purpose below. Generated JSON records now live in [reports/](../reports/README.m | [jaw_build_common.py](jaw_build_common.py) | Shared jaw-generation helpers. | | [ros_urdf.py](ros_urdf.py) | Converts relative URDF mesh references to ROS package URIs. | | [import_rcb_reference.py](import_rcb_reference.py) | Explicitly imports a fresh snapshot from a specified RCB checkout. This optional operation needs that external checkout; the normal build does not. | +| [import_raft_reference.py](import_raft_reference.py) | Imports RAFT's model reference and CAD meshes from a RAFT checkout and splits the meshes into visual pieces. Optional, like the RCB import; the normal build does not need the checkout. | ## ROS support templates diff --git a/tools/build.py b/tools/build.py index e3748ab..5ef3beb 100644 --- a/tools/build.py +++ b/tools/build.py @@ -73,6 +73,9 @@ def build(models_only=False, render=False): from build_standalone_grippers import build as grippers from verify_standalone_grippers import verify as gripper_checks grippers();gripper_checks() + from build_raft import build as raft + from verify_raft import verify as raft_checks + raft();raft_checks() from build_ros_support import build as ros_support ros_support() from build_moveit_support import build as moveit_support diff --git a/tools/build_raft.py b/tools/build_raft.py new file mode 100644 index 0000000..cbbd745 --- /dev/null +++ b/tools/build_raft.py @@ -0,0 +1,301 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Build RAFT's MuJoCo and URDF packages from source/raft and the shared palette. + +source/raft/reference.json is RAFT's model reference: frames, mass properties, ranges, keyframes and actuation, +exported by RAFT's firmware generator (tools/import_raft_reference.py imports it with the CAD meshes and their +visual pieces). Colours come from source/appearance/colors.json (raft_* keys; the servos follow `motors`, or the +`raft_motors` override). Both packages are rewritten completely. The shared build steps then add ROS 2 support +(build_ros_support.py), the licences and the documentation index (package_models.py). +""" +import json +from pathlib import Path +import re +import shutil +import xml.etree.ElementTree as ET + +import sys + +import numpy as np + +sys.path.insert(0, str(Path(__file__).resolve().parent)) # isolated (-I) mode, or called from elsewhere +from model_colors import color, load as load_palette, rgba + +ROOT = Path(__file__).resolve().parents[1] +SOURCE = ROOT/'source/raft' +MUJOCO = ROOT/'mujoco/raft' +URDF = ROOT/'urdf/raft_description' +# visual piece -> palette part; the trigger's printed part has its own key +PIECE_COLOR = {'structure': 'raft_structure', 'trigger': 'raft_trigger', 'motors': 'raft_motors', + 'horns': 'raft_horns', 'fasteners': 'raft_fasteners'} +SHINE = {'structure': (.25, .35), 'trigger': (.25, .35), 'motors': (.3, .25), 'horns': (.9, .9), 'fasteners': (.8, .8)} +# PAR6's lengths in the kinematic review (docs/kinematics), the SSG48/MSG tool point for a6 +PAR6_MM = {'a2': 23.420721, 'a3': 180.0, 'a4': 43.5, 'a5': 177.0, 'a6 (TCP)': 140.9} + + +def version(): + return ET.parse(ROOT/'urdf/par6_description/package.xml').findtext('version') + + +def reference(): + data = json.loads((SOURCE/'reference.json').read_text(encoding='utf-8')) + if data.get('schema_version') != 1 or data.get('robot') != 'raft': + raise ValueError('source/raft/reference.json: expected a RAFT model reference, schema version 1') + return data + + +def partitions(): + return json.loads((SOURCE/'visual/partitions.json').read_text(encoding='utf-8')) + + +def fmt(values): + return ' '.join(f'{0.0 if abs(x) < 1e-15 else x:.12g}' for x in values) + + +def piece_key(link, piece): + return 'trigger' if (link == 'trigger_link' and piece == 'structure') else piece + + +def materials(palette): + """material name -> (rgba, specular, shininess)""" + return {f'raft_{key}': (rgba(color(palette, part)), *SHINE[key]) for key, part in PIECE_COLOR.items()} + + +def identity(pose): + return np.allclose(pose['pos'], 0) and np.allclose(pose['quat'], [1, 0, 0, 0]) + + +# ---------------------------------------------------------------- MuJoCo +def write_mjcf(ref, parts, mats, release): + act = ref['actuation'] + links = {link['name']: link for link in ref['links']} + joints = ref['joints'] + child_of = {j['parent']: j for j in joints if j['name'] != 'trigger_joint'} + + def at(pose): + return f'pos="{fmt(pose["pos"])}" quat="{fmt(pose["quat"])}"' + + def inertial(link): + i = link['inertia'] + return (f'') + + def geoms(ind, name): + pose = links[name]['mesh']['origin'] + where = '' if identity(pose) else ' '+at(pose) + return ([f'{ind}' + for p in parts[name]['parts']] + [f'{ind}']) + sites = {ref['j2_spring']['anchor']['body']: ('j2_spring_anchor', ref['j2_spring']['anchor']['pos']), + ref['j2_spring']['pin']['body']: ('j2_spring_pin', ref['j2_spring']['pin']['pos'])} + x = ['', + f' ', + ' ', + ' '] + (MUJOCO/'raft.xml').write_text('\n'.join(x)+'\n', encoding='utf-8') + (MUJOCO/'scene.xml').write_text('\n'.join([ + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ''])+'\n', encoding='utf-8') + + +# ---------------------------------------------------------------- URDF +def write_urdf(ref, parts, mats): + act = ref['actuation'] + + def origin(pose): + return f'' + x = ['', ''] + for link in ref['links']: + name, i, mesh = link['name'], link['inertia'], link['mesh']['origin'] + x += [f' ', ' ', f' ', + f' ', + f' ', + ' '] + for piece, info in parts[name]['parts'].items(): + material = f'raft_{piece_key(name, piece)}' + x += [f' ', f' {origin(mesh)}', + f' ', + f' ', ' '] + x += [f' ', f' {origin(mesh)}', + f' ', ' ', ' '] + x += [' '] + for joint in ref['joints']: + lo, hi = joint['range'] + x += [f' ', f' {origin(joint["origin"])}', + f' ', f' ', + f' ', + f' ', + f' ', ' '] + x += [' ', f' {origin(ref["tcp"]["origin"])}', + f' ', ' ', ' ', ''] + (URDF/'raft.urdf').write_text('\n'.join(x)+'\n', encoding='utf-8') + (URDF/'config/joint_names.yaml').write_text( + 'controller_joint_names:\n'+''.join(f' - {j["name"]}\n' for j in ref['joints'][:6]), encoding='utf-8') + (URDF/'launch/display.launch').write_text(''' + + + + + + +''', encoding='utf-8') + (URDF/'package.xml').write_text(f''' + + raft_description + {version()} + Source Robotics RAFT teleoperation leader arm: PAR6's kinematics at 5/9 scale, six joints and a trigger. + Source Robotics + CC-BY-4.0 + Apache-2.0 + +''', encoding='utf-8') + + +# ---------------------------------------------------------------- documents, previews, index +def tables(ref): + deg = np.rad2deg + dh, joints = ref['dh'], ref['joints'] + rows = ['| Joint | d (mm) | a (mm) | alpha | theta offset |', '|---|---:|---:|---:|---:|'] + rows += [f'| `{joints[i]["name"]}` | {dh["d"][i]*1e3:.3f} | {dh["a"][i]*1e3:.3f} | {deg(dh["alpha"][i]):.0f} deg | ' + f'{deg(dh["theta_offset"][i]):.0f} deg |' for i in range(6)] + raft_mm = {'a2': dh['a'][0], 'a3': dh['a'][1], 'a4': -dh['a'][2], 'a5': -dh['d'][3], 'a6 (TCP)': -dh['d'][5]} + scale = ['| Length | RAFT (mm) | PAR6 (mm) | RAFT / PAR6 |', '|---|---:|---:|---:|'] + scale += [f'| {n} | {raft_mm[n]*1e3:.3f} | {p:g} | {raft_mm[n]*1e3/p:.4f} |' for n, p in PAR6_MM.items()] + limits = ['| Joint | Lower | Upper | Where the numbers come from |', '|---|---:|---:|---|'] + limits += [f'| `{j["name"]}` | {j["range_deg"][0]:.2f} deg | {j["range_deg"][1]:.2f} deg | {j["range_source"]} |' for j in joints] + keys = ['| Keyframe | qpos (deg), base_joint to trigger_joint |', '|---|---|'] + keys += [f'| `{k["name"]}` | {", ".join(f"{x:.0f}" for x in deg(k["qpos"]))} |' for k in ref['keyframes']] + mass = ['| Link | Mass (g) | COM in the link frame (mm) |', '|---|---:|---|'] + mass += [f'| `{link["name"]}` | {link["mass"]*1e3:.2f} | {", ".join(f"{x*1e3:.2f}" for x in link["com"])} |' for link in ref['links']] + total = sum(link['mass'] for link in ref['links']) + mass.append(f'| total | {total*1e3:.2f} | |') + act = ref['actuation'] + actuators = ['| Actuator | Joint | ctrlrange (Nm) |', '|---|---|---|'] + actuators += [f'| `{j["name"].replace("_joint", "_actuator")}` | `{j["name"]}` | +-{act["stall_torque_nm"]:.4f} |' for j in joints] + moving = total-next(link['mass'] for link in ref['links'] if link['name'] == 'base_link') + return {'VERSION': version(), 'DH_TABLE': '\n'.join(rows), 'SCALE_TABLE': '\n'.join(scale), + 'LIMITS_TABLE': '\n'.join(limits), 'KEYS_TABLE': '\n'.join(keys), 'MASS_TABLE': '\n'.join(mass), + 'ACTUATOR_TABLE': '\n'.join(actuators), 'SPEED': f'{act["speed_rad_s"]:g}', 'ARMATURE': f'{act["armature"]:g}', + 'FRICTION': f'{act["frictionloss"]:g}', 'DAMPING': f'{act["damping"]:g}', + 'MASS_TOTAL': f'{total*1e3:.1f}', 'MASS_MOVING': f'{moving*1e3:.1f}'} + + +def render(text, tokens, name): + for key, value in tokens.items(): + text = text.replace('{{'+key+'}}', value) + left = re.findall(r'\{\{[A-Z_]+\}\}', text) + if left: + raise ValueError(f'{name}: unknown tokens {left}') + return text + + +def sim2bot(ref, urdf): + joints = [j['name'] for j in ref['joints']] + arm = joints[:6] if urdf else [j.replace('_joint', '_actuator') for j in joints[:6]] + trigger = [joints[6]] if urdf else [joints[6].replace('_joint', '_actuator')] + entry = 'raft.urdf' if urdf else 'scene.xml' + data = {'version': 1, 'name': 'Source Robotics RAFT'+(' URDF' if urdf else ''), 'manufacturer': 'Source Robotics', + 'category': 'arm', 'license': 'CC-BY-4.0', 'sourceUrl': 'https://github.com/Source-Robotics/RAFT-teleoperation-arm', + 'packageVersion': version(), 'entry': entry, 'dof': 6, 'rootFrame': 'base_link', 'tcpSites': ['tcp'], + 'armActuators': arm, 'gripperActuators': trigger, 'endEffectorBodies': ['wrist_3_link'], + 'preview': 'previews/raft.png', + 'variants': [{'id': 'leader', 'label': 'RAFT leader arm', 'entry': entry, 'tcpSites': ['tcp'], + 'endEffectorBodies': ['wrist_3_link'], 'gripperActuators': trigger, 'preview': 'previews/raft.png'}]} + return json.dumps(data, indent=2)+'\n' + + +def build(): + ref, parts, palette = reference(), partitions(), load_palette() + mats = materials(palette) + tokens = tables(ref) + for package, meshdir, kind in [(MUJOCO, 'assets', 'mujoco'), (URDF, 'meshes', 'urdf')]: + if package.exists(): + shutil.rmtree(package) + (package/meshdir/'visual').mkdir(parents=True) + for link in ref['links']: + shutil.copyfile(SOURCE/'meshes'/f'{link["name"]}.stl', package/meshdir/f'{link["name"]}.stl') + for info in parts[link['name']]['parts'].values(): + shutil.copyfile(SOURCE/info['file'], package/meshdir/info['file']) + (package/'docs').mkdir(); (package/'previews').mkdir() + for path in sorted((SOURCE/'docs').glob('*.md')): + (package/'docs'/path.name).write_text(render(path.read_text(encoding='utf-8'), tokens, path.name), encoding='utf-8') + for path in sorted((SOURCE/'previews').glob('*.png')): + shutil.copyfile(path, package/'previews'/path.name) + front = (SOURCE/f'README.{kind}.md').read_text(encoding='utf-8') + (package/'README.md').write_text(render(front, tokens, f'README.{kind}.md'), encoding='utf-8') + (package/'sim2bot.robot.json').write_text(sim2bot(ref, kind == 'urdf'), encoding='utf-8') + for directory in ['config', 'launch']: + (URDF/directory).mkdir() + write_mjcf(ref, parts, mats, version()) + write_urdf(ref, parts, mats) + # the product guide's picture and the README's overview picture + (ROOT/'docs/images/robots').mkdir(parents=True, exist_ok=True) + shutil.copyfile(SOURCE/'previews/raft.png', ROOT/'docs/images/raft.png') + shutil.copyfile(SOURCE/'previews/raft_isometric.png', ROOT/'docs/images/robots/raft_isometric.png') + print(f'Built RAFT: mujoco/raft and urdf/raft_description ({len(ref["links"])} links, ' + f'{sum(len(p["parts"]) for p in parts.values())} coloured pieces).', flush=True) + + +if __name__ == '__main__': + build() diff --git a/tools/build_ros_support.py b/tools/build_ros_support.py index ac992aa..8754fe5 100644 --- a/tools/build_ros_support.py +++ b/tools/build_ros_support.py @@ -6,7 +6,7 @@ ROOT = Path(__file__).resolve().parents[1] SOURCE = ROOT/'tools/ros_support' -FAMILIES = ('par6', 'parol6', 'msg', 'ssg48') +FAMILIES = ('par6', 'parol6', 'msg', 'ssg48', 'raft') def build(families=FAMILIES): @@ -36,7 +36,7 @@ def build(families=FAMILIES): for version, tool in [(1, 'catkin'), (2, 'ament_cmake')]: ET.SubElement(export, 'build_type', condition=f'$ROS_VERSION == {version}').text = tool ET.indent(tree, space=' '); tree.write(package/'package.xml', encoding='utf-8', xml_declaration=True) - entries = ' '.join(path.name for path in sorted(package.glob(f'{family}_*.urdf'))) + entries = ' '.join(path.name for path in sorted(package.glob(f'{family}_*.urdf')) or [package/f'{family}.urdf']) (package/'CMakeLists.txt').write_text('''# SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.8) project('''+name+''') @@ -61,13 +61,14 @@ def build(families=FAMILIES): parameter = launch.find('param[@name="robot_description"]') argument = 'end_effector' if family in ('par6', 'parol6') else 'jaws' parameter.attrib.pop('textfile', None) + entry = f'{family}.urdf' if family == 'raft' else f'{family}_$(arg {argument}).urdf' parameter.set('command', f'python3 "$(find {name})/scripts/ros_urdf.py" ' - f'"$(find {name})/{family}_$(arg {argument}).urdf" --package {name}') + f'"$(find {name})/{entry}" --package {name}') ET.indent(launch, space=' '); launch.write(package/'launch/display.launch', encoding='utf-8', xml_declaration=True) - fixed = 'base_link' if family in ('par6', 'parol6') else 'flange' - distance = .85 if fixed == 'base_link' else .30 - center_z = .2 if fixed == 'base_link' else .035 - center_x = .10 if fixed == 'base_link' else 0. + fixed = 'base_link' if family in ('par6', 'parol6', 'raft') else 'flange' + distance = .5 if family == 'raft' else .85 if fixed == 'base_link' else .30 + center_z = .11 if family == 'raft' else .2 if fixed == 'base_link' else .035 + center_x = .08 if family == 'raft' else .10 if fixed == 'base_link' else 0. (package/'config/display.rviz').write_text('''# SPDX-License-Identifier: Apache-2.0 Panels: - Class: rviz_common/Displays diff --git a/tools/export_hardware_guide.py b/tools/export_hardware_guide.py index ba214a7..893424e 100644 --- a/tools/export_hardware_guide.py +++ b/tools/export_hardware_guide.py @@ -11,7 +11,7 @@ import xml.etree.ElementTree as ET ROOT = Path(__file__).resolve().parents[1] -PRODUCTS = ('par6', 'parol6', 'msg', 'ssg48') +PRODUCTS = ('par6', 'parol6', 'msg', 'ssg48', 'raft') REPOSITORY = 'https://github.com/Source-Robotics/robot_descriptions' diff --git a/tools/import_raft_reference.py b/tools/import_raft_reference.py new file mode 100644 index 0000000..b8bc56d --- /dev/null +++ b/tools/import_raft_reference.py @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Import RAFT's model reference and CAD meshes from a RAFT-teleoperation-arm checkout into source/raft/. + +RAFT's firmware generator (firmware/tools/gen_model.py) computes the arm's frames, mass properties, ranges and +keyframes from its DH table and CAD; the same run writes the firmware's model. It exports them, with the robot +description names, to firmware/tools/sim/model/raft_reference.json next to the CAD meshes. This optional operation +copies both here and splits every link mesh into its coloured visual pieces (printed parts, servos, horns, screws). +The normal build does not need the RAFT checkout: it builds from the committed copy. + + python tools/import_raft_reference.py --raft ../RAFT-teleoperation-arm +""" +import argparse +import hashlib +import json +from pathlib import Path +import shutil +import subprocess + +import numpy as np +import trimesh + +ROOT = Path(__file__).resolve().parents[1] +SOURCE = ROOT/'source/raft' +# CAD mesh name in the RAFT checkout -> robot description link name, and the servos each link holds +CAD = {'base_link': 'base_link', 'link1': 'shoulder_link', 'link2': 'upper_arm_link', 'link3': 'forearm_link', + 'link4': 'wrist_1_link', 'link5': 'wrist_2_link', 'link6': 'wrist_3_link', 'gripper_trigger': 'trigger_link'} +SERVOS = {'base_link': 1, 'shoulder_link': 1, 'upper_arm_link': 0, 'forearm_link': 2, 'wrist_1_link': 1, + 'wrist_2_link': 1, 'wrist_3_link': 1, 'trigger_link': 0} +PART_OF = {'printed': 'structure', 'horn': 'horns', 'servo_screw': 'fasteners', 'm3': 'fasteners'} + + +def kind(shell): + """sort one closed shell of a RAFT CAD mesh by its size (the same rules as RAFT's assembly pictures)""" + e = np.sort(shell.extents*1000)[::-1] + if shell.is_watertight and abs(e[0]-18) < 2 and abs(e[1]-18) < 2 and e[2] < 6: + return 'horn' # 25-tooth disc horn, 18 mm across + if abs(e[0]-8) < 1 and abs(e[1]-5.5) < .6 and abs(e[2]-5.5) < .6: + return 'm3' # M3 x 8 socket head (a joint stop) + if abs(e[0]-10) < 1 and abs(e[1]-2.8) < .4 and abs(e[2]-2.8) < .4: + return 'm3' # M3 drawn as a plain shank (the base lid) + if abs(e[0]-6.4) < .5 and abs(e[1]-1.9) < .3: + return 'servo_screw' # the servo's own mounting screws + if (shell.is_watertight and abs(shell.volume)*1e9 > 2000) or e[0] > 50: + return 'printed' + return 'servo' + + +def clusters(shells, gap=.0004): + """groups of shells whose bounding boxes touch (a servo is drawn as many surface pieces)""" + parent = list(range(len(shells))) + + def find(i): + while parent[i] != i: + parent[i] = parent[parent[i]]; i = parent[i] + return i + bounds = [s.bounds for s in shells] + for i in range(len(shells)): + for k in range(i+1, len(shells)): + if np.all(bounds[i][0]-gap <= bounds[k][1]) and np.all(bounds[k][0]-gap <= bounds[i][1]): + parent[find(i)] = find(k) + groups = {} + for i, shell in enumerate(shells): + groups.setdefault(find(i), []).append(shell) + return list(groups.values()) + + +def split(mesh, servos): + """every triangle into exactly one piece: connected components, sorted by kind; the largest touching groups of + servo-like shells are the link's servos, smaller groups near one join it, the rest belong to the printed part""" + shells = {k: [] for k in ('printed', 'horn', 'servo_screw', 'm3', 'servo')} + for component in trimesh.graph.connected_components(mesh.face_adjacency, nodes=np.arange(len(mesh.faces)), min_len=1): + piece = mesh.submesh([np.sort(np.asarray(component))], append=True, repair=False) + shells[kind(piece)].append(piece) + parts = {p: [] for p in ('structure', 'motors', 'horns', 'fasteners')} + for k in ('printed', 'horn', 'servo_screw', 'm3'): + parts[PART_OF[k]] += shells[k] + groups = sorted(clusters(shells['servo']), key=lambda g: -sum(x.area for x in g)) if shells['servo'] else [] + seeds = [list(g) for g in groups[:servos]] + centres = [np.mean([x.centroid for x in g], axis=0) for g in seeds] + for group in groups[servos:]: + centre = np.mean([x.centroid for x in group], axis=0) + near = min(range(len(seeds)), key=lambda i: np.linalg.norm(centres[i]-centre)) if seeds else None + if near is not None and np.linalg.norm(centres[near]-centre) < .03: + seeds[near] += group + else: + parts['structure'] += group + for group in seeds: + parts['motors'] += group + return {p: v for p, v in parts.items() if v} + + +def import_reference(raft): + raft = Path(raft).resolve() + model = raft/'firmware/tools/sim/model' + reference = json.loads((model/'raft_reference.json').read_text(encoding='utf-8')) + if reference.get('schema_version') != 1 or reference.get('robot') != 'raft': + raise ValueError('Expected a RAFT model reference, schema version 1') + try: + commit = subprocess.run(['git', '-C', str(raft), 'rev-parse', 'HEAD'], capture_output=True, text=True, + check=True).stdout.strip() + dirty = bool(subprocess.run(['git', '-C', str(raft), 'status', '--porcelain', '--', 'firmware/tools/sim/model'], + capture_output=True, text=True, check=True).stdout.strip()) + except (OSError, subprocess.CalledProcessError): + commit, dirty = None, None + reference['imported_from'] = {'repository': 'https://github.com/Source-Robotics/RAFT-teleoperation-arm', + 'commit': commit, 'uncommitted_changes': dirty, + 'file': 'firmware/tools/sim/model/raft_reference.json'} + for directory in ['meshes', 'visual']: + if (SOURCE/directory).exists(): + shutil.rmtree(SOURCE/directory) + (SOURCE/directory).mkdir(parents=True) + partitions = {} + for link in reference['links']: + cad = model/'meshes'/link['mesh']['cad_file'] + data = cad.read_bytes() + if hashlib.sha256(data).hexdigest() != link['mesh']['sha256']: + raise ValueError(f'{cad.name} does not match the reference: run gen_model.py in the RAFT checkout first') + (SOURCE/'meshes'/f'{link["name"]}.stl').write_bytes(data) + mesh = trimesh.load(cad) # merges duplicate vertices, keeps every triangle + parts = split(mesh, SERVOS[link['name']]) + record = {'source': f'meshes/{link["name"]}.stl', 'triangles': int(len(mesh.faces)), + 'servos': SERVOS[link['name']], 'parts': {}} + for part, shells in parts.items(): + piece = trimesh.util.concatenate(shells) + piece.export(SOURCE/'visual'/f'{link["name"]}_{part}.stl') + record['parts'][part] = {'file': f'visual/{link["name"]}_{part}.stl', 'triangles': int(len(piece.faces))} + total = sum(p['triangles'] for p in record['parts'].values()) + area = sum(trimesh.util.concatenate(s).area for s in parts.values()) + assert total == record['triangles'] and abs(area-mesh.area) < 1e-9*max(1., mesh.area), (link['name'], total) + partitions[link['name']] = record + print(f'{link["name"]:16s} {record["triangles"]:6d} triangles -> ' + + ', '.join(f'{p} {v["triangles"]}' for p, v in record['parts'].items()), flush=True) + (SOURCE/'visual/partitions.json').write_text(json.dumps(partitions, indent=2)+'\n', encoding='utf-8') + (SOURCE/'reference.json').write_text(json.dumps(reference, indent=1)+'\n', encoding='utf-8') + print(f'Imported the RAFT reference from {raft} ({commit or "no git"}{", uncommitted model changes" if dirty else ""}).') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument('--raft', required=True, type=Path, help='a RAFT-teleoperation-arm checkout') + import_reference(parser.parse_args().raft) diff --git a/tools/model_colors.py b/tools/model_colors.py index 2d7670c..cd102e3 100644 --- a/tools/model_colors.py +++ b/tools/model_colors.py @@ -14,12 +14,14 @@ 'structure', 'motors', 'camera_lens', 'msg_solid_jaws', 'msg_soft_holders', 'msg_soft_inserts', 'ssg48_solid_jaws', 'ssg48_fin_ray_jaws', 'pneumatic_body', 'pneumatic_jaws', + 'raft_structure', 'raft_trigger', 'raft_horns', 'raft_fasteners', } OVERRIDES = { 'j1_motor': 'motors', 'j2_motor': 'motors', 'j3_motor': 'motors', 'msg_motor': 'motors', 'msg_body': 'structure', 'ssg48_body': 'structure', 'flange': 'structure', 'par6_structure': 'structure', 'parol6_structure': 'structure', 'par6_panel': 'structure', + 'raft_motors': 'motors', } BADGE_KEYS = {'background', 'emblem', 'symbol', 'wordmark', 'robot_name', 'tagline', 'divider'} MATERIALS = { @@ -27,6 +29,8 @@ 'msg_camera_lens_black': 'camera_lens', 'msg_gripper_motor_black': 'msg_motor', 'msg_jaw_black': 'msg_soft_holders', 'msg_jaw_yellow': 'msg_soft_inserts', 'ssg48_jaw_material': 'ssg48_solid_jaws', + # RAFT's visual pieces carry one material per palette part + **{f'raft_{piece}': f'raft_{piece}' for piece in ('structure', 'trigger', 'motors', 'horns', 'fasteners')}, } ARM_LINKS = {'base_link', 'shoulder_link', 'upper_arm_link', 'forearm_link', 'wrist_1_link', 'wrist_2_link', 'wrist_3_link'} diff --git a/tools/package_models.py b/tools/package_models.py index 613eab9..352e2ce 100644 --- a/tools/package_models.py +++ b/tools/package_models.py @@ -1,4 +1,4 @@ -"""Build and check eight model archives and two MoveIt configuration archives.""" +"""Build and check ten model archives and two MoveIt configuration archives.""" from pathlib import Path import json @@ -35,7 +35,9 @@ def build(): (ROOT/'mujoco/msg','msg_mujoco.zip',True), (ROOT/'urdf/msg_description','msg_urdf.zip',False), (ROOT/'mujoco/ssg48','ssg48_mujoco.zip',True), - (ROOT/'urdf/ssg48_description','ssg48_urdf.zip',False)] + (ROOT/'urdf/ssg48_description','ssg48_urdf.zip',False), + (ROOT/'mujoco/raft','raft_mujoco.zip',True), + (ROOT/'urdf/raft_description','raft_urdf.zip',False)] for package,filename,native in distributions: if not package.is_dir():raise ValueError(f'Build the missing package first: {package.name}') apply_license(package) @@ -63,6 +65,11 @@ def build(): verify_layout(unpacked) metadata=json.loads((unpacked/'sim2bot.robot.json').read_text()) assert metadata['packageVersion']==version + if package.name in ('raft','raft_description'): + from verify_raft import check_package as raft_package + result[filename]={'bytes':archive.stat().st_size,**raft_package(unpacked,native)} + print(filename,result[filename],flush=True) + continue if metadata.get('category')=='gripper': from verify_standalone_grippers import check_package result[filename]={'bytes':archive.stat().st_size,**check_package(unpacked,native)} diff --git a/tools/prepare_release.py b/tools/prepare_release.py index 46a6de3..2493c73 100644 --- a/tools/prepare_release.py +++ b/tools/prepare_release.py @@ -13,7 +13,8 @@ PACKAGES={'par6_mujoco.zip':'par6','par6_urdf.zip':'par6_description', 'parol6_mujoco.zip':'parol6','parol6_urdf.zip':'parol6_description', 'msg_mujoco.zip':'msg','msg_urdf.zip':'msg_description', - 'ssg48_mujoco.zip':'ssg48','ssg48_urdf.zip':'ssg48_description'} + 'ssg48_mujoco.zip':'ssg48','ssg48_urdf.zip':'ssg48_description', + 'raft_mujoco.zip':'raft','raft_urdf.zip':'raft_description'} MOVEIT_PACKAGES={'par6_moveit_config.zip':'par6_moveit_config', 'parol6_moveit_config.zip':'parol6_moveit_config'} @@ -58,7 +59,7 @@ def prepare(root=ROOT,tag=None): files.append(path) checksums=''.join(f'{hashlib.sha256(p.read_bytes()).hexdigest()} {p.name}\n' for p in files) (root/'dist/SHA256SUMS').write_text(checksums,encoding='ascii',newline='\n') - print(f'Prepared {expected}: eight model ZIPs, two MoveIt ZIPs, review PDF, manifest and SHA256SUMS.',flush=True) + print(f'Prepared {expected}: {len(PACKAGES)} model ZIPs, {len(MOVEIT_PACKAGES)} MoveIt ZIPs, review PDF, manifest and SHA256SUMS.',flush=True) return manifest diff --git a/tools/render_raft_previews.py b/tools/render_raft_previews.py new file mode 100644 index 0000000..739b65e --- /dev/null +++ b/tools/render_raft_previews.py @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Optional OpenGL refresh of RAFT's two pictures in source/raft/previews (the build copies them into both packages +and docs/images): + + raft.png the arm with the handle held in front, in the package's viewer scene (1280 x 960) + raft_isometric.png the overview picture for the README's "Meet the models" row, in the style of PAR6's and + PAROL6's: isometric, slate backdrop, soft shadow (1000 x 800) + +Both use the model's palette colours. Run python tools/build.py --models-only afterwards. + + python tools/render_raft_previews.py +""" +from pathlib import Path + +import mujoco +import numpy as np +from PIL import Image, ImageFilter + +ROOT = Path(__file__).resolve().parents[1] +MUJOCO = ROOT/'mujoco/raft' +OUT = ROOT/'source/raft/previews' +W, H = 1280, 960 # scene.xml + + +def camera(lookat, distance, azimuth, elevation): + cam = mujoco.MjvCamera() + cam.type = mujoco.mjtCamera.mjCAMERA_FREE + cam.lookat[:] = lookat; cam.distance = distance; cam.azimuth = azimuth; cam.elevation = elevation + return cam + + +# The hero (chosen 2026-11-06): the palette's colours (as printed), the handle held in front with the base turned toward +# the camera, J6 rolled +40 deg and the trigger opened wide. -55 deg is past the trigger's -39.63 deg stop: picture only. +HERO_SIZE, HERO_SS = (1000, 800), 2 +HERO_POSE = {"base_joint": -28, "shoulder_joint": 24, "elbow_joint": 18, "wrist_1_joint": -20, "wrist_2_joint": 15, + "wrist_3_joint": 40, "trigger_joint": -55} +HERO_CAMERA = dict(azimuth=215.0, elevation=-30.0) # orthographic, as robot_descriptions' heroes +HERO_BACKDROP = ((63, 80, 100), (42, 54, 67)) # robot_descriptions' hero backdrop: lower left, far corner + + +def hero(out): + W_, H_ = HERO_SIZE[0] * HERO_SS, HERO_SIZE[1] * HERO_SS + xml = f""" + + + + + + + + + + + + + +""" + tmp = MUJOCO / "_hero_scene.xml" # next to raft.xml: MuJoCo resolves meshdir from the top file + tmp.write_text(xml, encoding="utf-8") + try: + m = mujoco.MjModel.from_xml_path(str(tmp)) + finally: + tmp.unlink() + d = mujoco.MjData(m) + for name, deg in HERO_POSE.items(): + d.qpos[m.jnt_qposadr[mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_JOINT, name)]] = np.radians(deg) + cam = mujoco.MjvCamera(); cam.type = mujoco.mjtCamera.mjCAMERA_FREE; cam.orthographic = 1 + cam.lookat[:] = [0.075, -0.005, 0.105]; cam.distance = 1.2 + cam.azimuth, cam.elevation = HERO_CAMERA["azimuth"], HERO_CAMERA["elevation"] + a, e = np.radians(cam.azimuth), np.radians(cam.elevation) + fwd = np.array([np.cos(e) * np.cos(a), np.cos(e) * np.sin(a), np.sin(e)]) + F = np.array([np.cos(a), np.sin(a), 0.0]); R = np.cross(F, [0, 0, 1.0]); up = np.cross(R, fwd) + # key light from the picture's upper left, a little toward the camera: a short soft shadow, as on PAR6's hero. + # A directional light's shadow map sits around its position, so the light is placed back along its direction. + kd = 0.32 * R - 0.12 * F - np.array([0, 0, 1.0]); kd /= np.linalg.norm(kd) + key = mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_LIGHT, "key") + m.light_dir[key] = kd; m.light_pos[key] = np.array([0.08, 0.0, 0.1]) - 1.2 * kd + mujoco.mj_forward(m, d) + floor = mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_GEOM, "hero_floor") + with mujoco.Renderer(m, H_, W_) as r: + for _ in range(3): # frame the arm: its outline centred, 84 % of the width or 80 % of the height + r.enable_segmentation_rendering(); r.update_scene(d, camera=cam) + seg = r.render()[..., 0]; r.disable_segmentation_rendering() + ys, xs = np.nonzero((seg != floor) & (seg >= 0)) + ppm = H_ / m.vis.global_.fovy # pixels per metre (orthographic) + dx, dy = ((xs.min() + xs.max()) / 2 - W_ / 2) / ppm, ((ys.min() + ys.max()) / 2 - H_ / 2) / ppm + cam.lookat[:] = cam.lookat + dx * R - dy * up + m.vis.global_.fovy *= max((xs.max() - xs.min()) / (0.84 * W_), (ys.max() - ys.min()) / (0.80 * H_)) + r.enable_segmentation_rendering(); r.update_scene(d, camera=cam) + seg = r.render()[..., 0]; r.disable_segmentation_rendering() + r.update_scene(d, camera=cam) + img = r.render().astype(float) + is_floor = (seg == floor) | (seg < 0) + # the backdrop: lightest at the lower left, darker toward the top right; the floor's shading becomes the shadow + y, x = np.mgrid[0:H_, 0:W_].astype(float) / HERO_SS + t = np.clip(np.hypot(x, y - 0.75 * HERO_SIZE[1]) / np.hypot(HERO_SIZE[0], 0.75 * HERO_SIZE[1]), 0, 1) ** 0.8 + near, far = (np.array(c, dtype=float) for c in HERO_BACKDROP) + bg = near * (1 - t[..., None]) + far * t[..., None] + lum = img.mean(axis=2) + shade = np.clip(lum / np.percentile(lum[is_floor], 99.5), 0, 1) + dark = Image.fromarray((255 * np.clip((1 - shade) / 0.3, 0, 1)).astype(np.uint8)).filter(ImageFilter.GaussianBlur(7 * HERO_SS)) + shadow = 1 - 0.38 * np.asarray(dark, dtype=float) / 255 + pic = np.where(is_floor[..., None], bg * shadow[..., None], img) + Image.fromarray(np.clip(pic, 0, 255).astype(np.uint8)).resize(HERO_SIZE, Image.LANCZOS).save(out / "raft_isometric.png", optimize=True) + + +def main(): + m = mujoco.MjModel.from_xml_path(str(MUJOCO/'scene.xml')); d = mujoco.MjData(m) + mujoco.mj_resetDataKeyframe(m, d, mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_KEY, 'handle_in_front')) + mujoco.mj_forward(m, d) + OUT.mkdir(parents=True, exist_ok=True) + with mujoco.Renderer(m, H, W) as r: + r.update_scene(d, camera=camera([0.09, 0.0, 0.105], 0.42, 128, -18)) + Image.fromarray(r.render()).save(OUT/'raft.png', optimize=True) + hero(OUT) + for p in sorted(OUT.glob('*.png')): + print(f' {p.relative_to(ROOT).as_posix()} {Image.open(p).size} {p.stat().st_size // 1024} kB') + + +if __name__ == '__main__': + main() diff --git a/tools/ros_support/display.launch.py b/tools/ros_support/display.launch.py index 956d378..997f0e2 100644 --- a/tools/ros_support/display.launch.py +++ b/tools/ros_support/display.launch.py @@ -20,7 +20,7 @@ def launch_nodes(context): helper = importlib.util.module_from_spec(spec); spec.loader.exec_module(helper) value = lambda name: LaunchConfiguration(name).perform(context) end_effector = value('end_effector') if FAMILY in ('par6', 'parol6') else 'msg' - entry = helper.select_model(FAMILY, end_effector, value('jaws')) + entry = helper.select_model(FAMILY, end_effector, value('jaws') if FAMILY != 'raft' else 'default') description = ParameterValue(helper.load_description(share/entry, PACKAGE, rviz_materials=value('rviz_materials') == 'true'), value_type=str) parameters = {'robot_description': description, 'use_sim_time': value('use_sim_time') == 'true'} @@ -41,7 +41,7 @@ def launch_nodes(context): def generate_launch_description(): share = Path(get_package_share_directory(PACKAGE)) arm = FAMILY in ('par6', 'parol6') - options = [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', + options = [] if FAMILY == 'raft' else [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', choices=['default', 'solid', 'soft_tpu', 'fin_ray'] if arm else ['solid', 'soft_tpu' if FAMILY == 'msg' else 'fin_ray'])] if arm: diff --git a/tools/ros_support/robot_description.py b/tools/ros_support/robot_description.py index ec8951b..715b8d5 100644 --- a/tools/ros_support/robot_description.py +++ b/tools/ros_support/robot_description.py @@ -8,6 +8,8 @@ def select_model(family, end_effector='msg', jaws='default'): + if family == 'raft': + return 'raft.urdf' # one configuration: the leader arm with its trigger handle if family in JAW_OPTIONS: jaws = 'solid' if jaws == 'default' else jaws if jaws not in JAW_OPTIONS[family]: diff --git a/tools/ros_support/validate_ros2.py b/tools/ros_support/validate_ros2.py index a86e2d7..2bd4731 100644 --- a/tools/ros_support/validate_ros2.py +++ b/tools/ros_support/validate_ros2.py @@ -19,7 +19,7 @@ def product_cases(): ('ssg48', 'solid'), ('ssg48', 'fin_ray'), ('pneumatic', 'default')] return [(family, tool, jaw) for family in ['par6', 'parol6'] for tool, jaw in tools]+[ ('msg', 'msg', 'solid'), ('msg', 'msg', 'soft_tpu'), - ('ssg48', 'msg', 'solid'), ('ssg48', 'msg', 'fin_ray')] + ('ssg48', 'msg', 'solid'), ('ssg48', 'msg', 'fin_ray'), ('raft', 'leader', 'default')] def multiply(a, b): @@ -96,9 +96,10 @@ def receive_tf(msg): subscriptions += [node.create_subscription(TFMessage,'/tf',receive_tf,qos_profile_sensor_data), node.create_subscription(TFMessage,'/tf_static',receive_tf,durable)] publisher = node.create_publisher(JointState, '/joint_states', 10) if external else None - command = ['ros2','launch',family+'_description','display.launch.py',f'jaws:={jaws}', + command = ['ros2','launch',family+'_description','display.launch.py', 'gui:='+str(gui).lower(), 'rviz:='+str(gui).lower(), 'publish_joint_states:='+str(not external).lower()] + if family != 'raft':command.append(f'jaws:={jaws}') if family in ('par6','parol6'):command.append('end_effector:='+tool) log_path = output/(name+'.log') with log_path.open('w') as log: @@ -178,7 +179,7 @@ def main(): results=[] for case in product_cases():results.append(run_case(len(results),*case,args.output)) for case in [('par6','pneumatic','default'),('parol6','msg','soft_tpu'), - ('msg','msg','solid'),('ssg48','msg','fin_ray')]: + ('msg','msg','solid'),('ssg48','msg','fin_ray'),('raft','leader','default')]: results.append(run_case(len(results),*case,args.output,external=True)) for case in [('par6','msg','soft_tpu'),('parol6','ssg48','fin_ray'), ('msg','msg','soft_tpu'),('ssg48','msg','solid')]: @@ -187,9 +188,9 @@ def main(): bad=subprocess.run(command,capture_output=True,text=True,timeout=20) assert bad.returncode != 0 and 'jaws must be one of' in bad.stdout+bad.stderr result={'ros_distro':os.environ['ROS_DISTRO'],'cases':results,'invalid_selection_rejected':True, - 'all_16_variants_checked':True,'gui_cases':4,'external_joint_state_cases':4} + 'all_17_variants_checked':True,'gui_cases':4,'external_joint_state_cases':5} (args.output/'validation.json').write_text(json.dumps(result,indent=2)+'\n') - print('All 24 installed ROS 2 launch/TF/mimic/GUI cases passed.',flush=True) + print(f'All {len(results)} installed ROS 2 launch/TF/mimic/GUI cases passed.',flush=True) if __name__=='__main__':main() diff --git a/tools/verify_raft.py b/tools/verify_raft.py new file mode 100644 index 0000000..042d825 --- /dev/null +++ b/tools/verify_raft.py @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Check RAFT's packages against RAFT's model reference (source/raft/reference.json) and each other. + +The reference carries sample poses with RAFT's own forward kinematics and gravity torques, so both formats are +compared with the numbers RAFT's firmware uses, not with this repository's writer. Also checks the masses, the +ranges against the measured stops, the keyframes, the palette colours, the visual pieces and the mesh paths. +""" +import json +from pathlib import Path +import tempfile +import xml.etree.ElementTree as ET + +import mujoco +import sys + +import numpy as np + +sys.path.insert(0, str(Path(__file__).resolve().parent)) # isolated (-I) mode, or called from elsewhere +from build_raft import MUJOCO, SOURCE, URDF, materials, partitions, piece_key, reference +from model_colors import load as load_palette + +JOINTS = ['base_joint', 'shoulder_joint', 'elbow_joint', 'wrist_1_joint', 'wrist_2_joint', 'wrist_3_joint', 'trigger_joint'] +TOL = 1e-9 + + +def stl_triangles(path): + data = Path(path).read_bytes() + count = int.from_bytes(data[80:84], 'little') + assert len(data) == 84+50*count, f'{path}: not a binary STL' + return count + + +def pose(m, d, kind, name): + T = np.eye(4) + if kind == 'site': + i = mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_SITE, name); T[:3, :3] = d.site_xmat[i].reshape(3, 3); T[:3, 3] = d.site_xpos[i] + else: + i = mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_BODY, name); T[:3, :3] = d.xmat[i].reshape(3, 3); T[:3, 3] = d.xpos[i] + return T + + +def indices(m): + ids = [mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_JOINT, j) for j in JOINTS] + assert min(ids) >= 0, 'joint names' + return [m.jnt_qposadr[i] for i in ids], [m.jnt_dofadr[i] for i in ids], np.array([m.jnt_range[i] for i in ids]) + + +def set_q(m, d, adr, q): + d.qpos[:] = 0; d.qvel[:] = 0 + for k, a in enumerate(adr): + d.qpos[a] = q[k] + mujoco.mj_forward(m, d) + + +def load_urdf(path): + """MuJoCo reads the URDF directly; it needs the mesh subfolders kept (not part of the shipped file)""" + text = Path(path).read_text(encoding='utf-8').replace( + '', '', 1) + with tempfile.NamedTemporaryFile('w', suffix='.urdf', dir=Path(path).parent, delete=False, encoding='utf-8') as f: + f.write(text) + try: + return mujoco.MjModel.from_xml_path(f.name) + finally: + Path(f.name).unlink() + + +def check_model(scene, urdf, ref): + """both formats against the reference; returns counts for the report""" + m = mujoco.MjModel.from_xml_path(str(scene)); d = mujoco.MjData(m) + adr, dof, lim = indices(m) + names = [mujoco.mj_id2name(m, mujoco.mjtObj.mjOBJ_BODY, b) for b in range(1, m.nbody)] + assert names == [link['name'] for link in ref['links']], names + acts = [mujoco.mj_id2name(m, mujoco.mjtObj.mjOBJ_ACTUATOR, a) for a in range(m.nu)] + assert acts == [j.replace('_joint', '_actuator') for j in JOINTS], acts + assert np.allclose(lim, [j['range'] for j in ref['joints']], atol=1e-12), 'ranges' + worst = dict(tcp=0., trigger=0., gravity=0.) + for sample in ref['checks']['samples']: + set_q(m, d, adr, sample['q']) + worst['tcp'] = max(worst['tcp'], np.abs(pose(m, d, 'site', 'tcp')-sample['tcp']).max()) + worst['trigger'] = max(worst['trigger'], np.abs(pose(m, d, 'body', 'trigger_link')-sample['trigger_link']).max()) + worst['gravity'] = max(worst['gravity'], np.abs(d.qfrc_bias[dof]-sample['gravity_torque']).max()) + assert max(worst.values()) < TOL, f'MuJoCo package against the RAFT reference: {worst}' + set_q(m, d, adr, np.zeros(7)) + assert np.allclose(d.site_xpos[mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_SITE, 'tcp')], ref['checks']['tcp_home'], atol=1e-12) + assert abs(m.body_mass.sum()-ref['checks']['mass_total']) < 1e-12, 'total mass' + assert abs(m.body_mass[2:].sum()-ref['checks']['mass_moving']) < 1e-12, 'moving mass' + # keyframes: named as in the reference, inside the ranges, free of contacts, held by the motors + keys = [mujoco.mj_id2name(m, mujoco.mjtObj.mjOBJ_KEY, k) for k in range(m.nkey)] + assert keys == [k['name'] for k in ref['keyframes']], keys + drift = 0. + for k in range(m.nkey): + q = m.key_qpos[k][adr] + assert np.all(q >= lim[:, 0]-1e-12) and np.all(q <= lim[:, 1]+1e-12), f'keyframe {keys[k]} outside the ranges' + mujoco.mj_resetDataKeyframe(m, d, k); mujoco.mj_forward(m, d) + assert d.ncon == 0, f'keyframe {keys[k]}: {d.ncon} contacts' + q0 = d.qpos.copy() + for _ in range(1000): + mujoco.mj_forward(m, d) + d.ctrl[:] = np.clip(d.qfrc_bias[dof], *m.actuator_ctrlrange.T) + mujoco.mj_step(m, d) + drift = max(drift, np.abs(d.qpos-q0).max()) + assert drift < np.deg2rad(.1), f'gravity compensation drifts {np.rad2deg(drift):.3f} deg' + # the URDF, read by MuJoCo, against the MuJoCo package + u = load_urdf(urdf); du = mujoco.MjData(u) + adr_u, dof_u, lim_u = indices(u) + assert np.allclose(lim_u, lim, atol=1e-12), 'URDF ranges' + worst_u = 0. + for sample in ref['checks']['samples']: + set_q(u, du, adr_u, sample['q']); set_q(m, d, adr, sample['q']) + worst_u = max(worst_u, np.abs(pose(u, du, 'body', 'tcp')-pose(m, d, 'site', 'tcp')).max(), + np.abs(du.qfrc_bias[dof_u]-d.qfrc_bias[dof]).max()) + assert worst_u < TOL, f'URDF against the MuJoCo package: {worst_u}' + return {'samples': len(ref['checks']['samples']), 'keyframes': m.nkey, 'max_error': max(max(worst.values()), worst_u)} + + +def check_appearance(ref, parts, palette): + mats = materials(palette) + m = mujoco.MjModel.from_xml_path(str(MUJOCO/'raft.xml')) + for name, (rgba, spec, shine) in mats.items(): + i = mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_MATERIAL, name) + assert i >= 0 and np.allclose(m.mat_rgba[i], rgba, atol=1e-9), name + visual = 0 + for g in range(m.ngeom): + if m.geom_group[g] != 2: + continue + visual += 1 + link = mujoco.mj_id2name(m, mujoco.mjtObj.mjOBJ_BODY, m.geom_bodyid[g]) + piece = mujoco.mj_id2name(m, mujoco.mjtObj.mjOBJ_MESH, m.geom_dataid[g])[len(link)+1:] + assert mujoco.mj_id2name(m, mujoco.mjtObj.mjOBJ_MATERIAL, m.geom_matid[g]) == f'raft_{piece_key(link, piece)}', (link, piece) + pieces = sum(len(p['parts']) for p in parts.values()) + assert visual == pieces, (visual, pieces) + root = ET.parse(URDF/'raft.urdf').getroot() + seen = 0 + for link in root.findall('link'): + for v in link.findall('visual'): + seen += 1 + material = v.find('material') + piece = Path(v.find('geometry/mesh').get('filename')).stem[len(link.get('name'))+1:] + assert material.get('name') == f'raft_{piece_key(link.get("name"), piece)}', piece + assert np.allclose([float(x) for x in material.find('color').get('rgba').split()], mats[material.get('name')][0], atol=1e-9) + assert seen == pieces, (seen, pieces) + return pieces + + +def check_pieces(ref, parts): + for link in ref['links']: + name, record = link['name'], parts[link['name']] + full = stl_triangles(SOURCE/record['source']) + assert full == record['triangles'] == sum(stl_triangles(SOURCE/p['file']) for p in record['parts'].values()), name + for package, meshdir in [(MUJOCO, 'assets'), (URDF, 'meshes')]: + assert (package/meshdir/f'{name}.stl').read_bytes() == (SOURCE/record['source']).read_bytes(), (package, name) + for p in record['parts'].values(): + assert (package/meshdir/p['file']).read_bytes() == (SOURCE/p['file']).read_bytes(), (package, p['file']) + return sum(p['triangles'] for p in parts.values()) + + +def check_package(package, native): + """an extracted ZIP: entry files load, assets stay inside, the index fits the model""" + package = Path(package).resolve() + data = json.loads((package/'sim2bot.robot.json').read_text(encoding='utf-8')) + assert [v['id'] for v in data['variants']] == ['leader'], data['variants'] + entry = package/data['entry'] + if native: + m = mujoco.MjModel.from_xml_path(str(entry)) + assert m.nq == 7 and m.nu == 7 + for name in data['armActuators']+data['gripperActuators']: + assert mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_ACTUATOR, name) >= 0, name + for name in data['tcpSites']: + assert mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_SITE, name) >= 0, name + for name in data['endEffectorBodies']: + assert mujoco.mj_name2id(m, mujoco.mjtObj.mjOBJ_BODY, name) >= 0, name + counts = {'bodies': m.nbody-1, 'geoms': m.ngeom-1} + else: + root = ET.parse(entry).getroot() + for mesh in root.iter('mesh'): + path = (entry.parent/mesh.get('filename')).resolve() + assert path.is_relative_to(package) and path.is_file(), mesh.get('filename') + joints = [j.get('name') for j in root.findall('joint')] + for name in data['armActuators']+data['gripperActuators']: + assert name in joints, name + m = load_urdf(entry) + counts = {'links': len(root.findall('link')), 'meshes': len(list(root.iter('mesh')))} + return {'variants_checked': 1, **counts, 'crc_and_assets_valid': True} + + +def verify(): + ref, parts, palette = reference(), partitions(), load_palette() + triangles = check_pieces(ref, parts) + pieces = check_appearance(ref, parts, palette) + result = check_model(MUJOCO/'scene.xml', URDF/'raft.urdf', ref) + stops = [j['measured_stop_deg'] for j in ref['joints']] + for joint, stop in zip(ref['joints'], stops): + end = -stop if joint['name'] == 'trigger_joint' else stop # the trigger opens toward negative angles + assert np.isclose(end, joint['range_deg']).any(), (joint['name'], stop) + for package, urdf in [(MUJOCO, False), (URDF, True)]: + check_package(package, not urdf) + print(f'RAFT: {pieces} coloured pieces ({triangles} triangles), {result["samples"]} reference poses, ' + f'{result["keyframes"]} keyframes; both formats agree with RAFT\'s reference to {result["max_error"]:.1e}.', flush=True) + return result + + +if __name__ == '__main__': + verify() diff --git a/urdf/msg_description/docs/COLORS.md b/urdf/msg_description/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/urdf/msg_description/docs/COLORS.md +++ b/urdf/msg_description/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/urdf/msg_description/docs/ROS2.md b/urdf/msg_description/docs/ROS2.md index f1dd375..79efeec 100644 --- a/urdf/msg_description/docs/ROS2.md +++ b/urdf/msg_description/docs/ROS2.md @@ -2,7 +2,7 @@ [Back to the model instructions](../README.md) -The four URDF packages provide native `ament_cmake` installation and a ROS 2 +The five URDF packages provide native `ament_cmake` installation and a ROS 2 `display.launch.py` launcher. Choose a tool or jaw option, then view it in RViz and move its joints with sliders. Linux CI checks ROS 2 **Humble / Ubuntu 22.04** and **Jazzy / Ubuntu 24.04**, including installed assets, TF, mimic joints and @@ -55,7 +55,7 @@ colcon build --base-paths src/robot_descriptions/urdf source install/setup.bash ``` -This builds all four packages. Run colcon against the `urdf/` packages; there +This builds all five packages. Run colcon against the `urdf/` packages; there is no need to run `tools/build.py` to use the committed models. After opening a new terminal, source both `/opt/ros//setup.bash` and your workspace's `install/setup.bash` again. @@ -93,6 +93,7 @@ ros2 launch msg_description display.launch.py jaws:=solid ros2 launch msg_description display.launch.py jaws:=soft_tpu ros2 launch ssg48_description display.launch.py jaws:=solid ros2 launch ssg48_description display.launch.py jaws:=fin_ray +ros2 launch raft_description display.launch.py ``` Each launcher opens RViz and a joint-slider window. The two fingers retain diff --git a/urdf/msg_description/docs/colors.json b/urdf/msg_description/docs/colors.json index 6971ca3..09b8a15 100644 --- a/urdf/msg_description/docs/colors.json +++ b/urdf/msg_description/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/urdf/msg_description/launch/display.launch.py b/urdf/msg_description/launch/display.launch.py index c2e5675..8afd592 100644 --- a/urdf/msg_description/launch/display.launch.py +++ b/urdf/msg_description/launch/display.launch.py @@ -20,7 +20,7 @@ def launch_nodes(context): helper = importlib.util.module_from_spec(spec); spec.loader.exec_module(helper) value = lambda name: LaunchConfiguration(name).perform(context) end_effector = value('end_effector') if FAMILY in ('par6', 'parol6') else 'msg' - entry = helper.select_model(FAMILY, end_effector, value('jaws')) + entry = helper.select_model(FAMILY, end_effector, value('jaws') if FAMILY != 'raft' else 'default') description = ParameterValue(helper.load_description(share/entry, PACKAGE, rviz_materials=value('rviz_materials') == 'true'), value_type=str) parameters = {'robot_description': description, 'use_sim_time': value('use_sim_time') == 'true'} @@ -41,7 +41,7 @@ def launch_nodes(context): def generate_launch_description(): share = Path(get_package_share_directory(PACKAGE)) arm = FAMILY in ('par6', 'parol6') - options = [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', + options = [] if FAMILY == 'raft' else [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', choices=['default', 'solid', 'soft_tpu', 'fin_ray'] if arm else ['solid', 'soft_tpu' if FAMILY == 'msg' else 'fin_ray'])] if arm: diff --git a/urdf/msg_description/package.xml b/urdf/msg_description/package.xml index 737e906..d15f17c 100644 --- a/urdf/msg_description/package.xml +++ b/urdf/msg_description/package.xml @@ -1,7 +1,7 @@ msg_description - 2.18.1 + 2.19.0 Standalone Source Robotics MSG gripper with interchangeable jaws. Source Robotics CC-BY-4.0 diff --git a/urdf/msg_description/scripts/robot_description.py b/urdf/msg_description/scripts/robot_description.py index ec8951b..715b8d5 100644 --- a/urdf/msg_description/scripts/robot_description.py +++ b/urdf/msg_description/scripts/robot_description.py @@ -8,6 +8,8 @@ def select_model(family, end_effector='msg', jaws='default'): + if family == 'raft': + return 'raft.urdf' # one configuration: the leader arm with its trigger handle if family in JAW_OPTIONS: jaws = 'solid' if jaws == 'default' else jaws if jaws not in JAW_OPTIONS[family]: diff --git a/urdf/msg_description/sim2bot.robot.json b/urdf/msg_description/sim2bot.robot.json index 2f3f30d..cfb4a7a 100644 --- a/urdf/msg_description/sim2bot.robot.json +++ b/urdf/msg_description/sim2bot.robot.json @@ -5,7 +5,7 @@ "category": "gripper", "license": "CC-BY-4.0", "sourceUrl": "https://github.com/Source-Robotics/robot_descriptions", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "msg_solid.urdf", "dof": 1, "rootFrame": "flange", diff --git a/urdf/par6_description/docs/COLORS.md b/urdf/par6_description/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/urdf/par6_description/docs/COLORS.md +++ b/urdf/par6_description/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/urdf/par6_description/docs/ROS2.md b/urdf/par6_description/docs/ROS2.md index f1dd375..79efeec 100644 --- a/urdf/par6_description/docs/ROS2.md +++ b/urdf/par6_description/docs/ROS2.md @@ -2,7 +2,7 @@ [Back to the model instructions](../README.md) -The four URDF packages provide native `ament_cmake` installation and a ROS 2 +The five URDF packages provide native `ament_cmake` installation and a ROS 2 `display.launch.py` launcher. Choose a tool or jaw option, then view it in RViz and move its joints with sliders. Linux CI checks ROS 2 **Humble / Ubuntu 22.04** and **Jazzy / Ubuntu 24.04**, including installed assets, TF, mimic joints and @@ -55,7 +55,7 @@ colcon build --base-paths src/robot_descriptions/urdf source install/setup.bash ``` -This builds all four packages. Run colcon against the `urdf/` packages; there +This builds all five packages. Run colcon against the `urdf/` packages; there is no need to run `tools/build.py` to use the committed models. After opening a new terminal, source both `/opt/ros//setup.bash` and your workspace's `install/setup.bash` again. @@ -93,6 +93,7 @@ ros2 launch msg_description display.launch.py jaws:=solid ros2 launch msg_description display.launch.py jaws:=soft_tpu ros2 launch ssg48_description display.launch.py jaws:=solid ros2 launch ssg48_description display.launch.py jaws:=fin_ray +ros2 launch raft_description display.launch.py ``` Each launcher opens RViz and a joint-slider window. The two fingers retain diff --git a/urdf/par6_description/docs/colors.json b/urdf/par6_description/docs/colors.json index 6971ca3..09b8a15 100644 --- a/urdf/par6_description/docs/colors.json +++ b/urdf/par6_description/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/urdf/par6_description/launch/display.launch.py b/urdf/par6_description/launch/display.launch.py index fe32d92..a36f2d4 100644 --- a/urdf/par6_description/launch/display.launch.py +++ b/urdf/par6_description/launch/display.launch.py @@ -20,7 +20,7 @@ def launch_nodes(context): helper = importlib.util.module_from_spec(spec); spec.loader.exec_module(helper) value = lambda name: LaunchConfiguration(name).perform(context) end_effector = value('end_effector') if FAMILY in ('par6', 'parol6') else 'msg' - entry = helper.select_model(FAMILY, end_effector, value('jaws')) + entry = helper.select_model(FAMILY, end_effector, value('jaws') if FAMILY != 'raft' else 'default') description = ParameterValue(helper.load_description(share/entry, PACKAGE, rviz_materials=value('rviz_materials') == 'true'), value_type=str) parameters = {'robot_description': description, 'use_sim_time': value('use_sim_time') == 'true'} @@ -41,7 +41,7 @@ def launch_nodes(context): def generate_launch_description(): share = Path(get_package_share_directory(PACKAGE)) arm = FAMILY in ('par6', 'parol6') - options = [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', + options = [] if FAMILY == 'raft' else [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', choices=['default', 'solid', 'soft_tpu', 'fin_ray'] if arm else ['solid', 'soft_tpu' if FAMILY == 'msg' else 'fin_ray'])] if arm: diff --git a/urdf/par6_description/package.xml b/urdf/par6_description/package.xml index 559fe0f..9814efd 100644 --- a/urdf/par6_description/package.xml +++ b/urdf/par6_description/package.xml @@ -1,7 +1,7 @@ par6_description - 2.18.1 + 2.19.0 Portable URDF descriptions for the Source Robotics PAR6 arm. Source Robotics CC-BY-4.0 diff --git a/urdf/par6_description/scripts/robot_description.py b/urdf/par6_description/scripts/robot_description.py index ec8951b..715b8d5 100644 --- a/urdf/par6_description/scripts/robot_description.py +++ b/urdf/par6_description/scripts/robot_description.py @@ -8,6 +8,8 @@ def select_model(family, end_effector='msg', jaws='default'): + if family == 'raft': + return 'raft.urdf' # one configuration: the leader arm with its trigger handle if family in JAW_OPTIONS: jaws = 'solid' if jaws == 'default' else jaws if jaws not in JAW_OPTIONS[family]: diff --git a/urdf/par6_description/sim2bot.robot.json b/urdf/par6_description/sim2bot.robot.json index f884203..e1a27e7 100644 --- a/urdf/par6_description/sim2bot.robot.json +++ b/urdf/par6_description/sim2bot.robot.json @@ -6,7 +6,7 @@ "license": "CC-BY-4.0", "sourceUrl": "https://source-robotics.com/", "sourceRevision": "2025-02-10", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "par6_msg.urdf", "dof": 6, "armActuators": [ diff --git a/urdf/parol6_description/docs/CHANGELOG.md b/urdf/parol6_description/docs/CHANGELOG.md index 7e4db8a..dc83703 100644 --- a/urdf/parol6_description/docs/CHANGELOG.md +++ b/urdf/parol6_description/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # PAROL6 description changelog -## 2.18.1 - 2026-11-03 +## 2.19.0 - 2026-11-03 - Added native ROS 2 installation, RViz display and joint sliders for all six tool profiles. - Retained ROS 1 compatibility and portable model/physical definitions. diff --git a/urdf/parol6_description/docs/COLORS.md b/urdf/parol6_description/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/urdf/parol6_description/docs/COLORS.md +++ b/urdf/parol6_description/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/urdf/parol6_description/docs/ROS2.md b/urdf/parol6_description/docs/ROS2.md index f1dd375..79efeec 100644 --- a/urdf/parol6_description/docs/ROS2.md +++ b/urdf/parol6_description/docs/ROS2.md @@ -2,7 +2,7 @@ [Back to the model instructions](../README.md) -The four URDF packages provide native `ament_cmake` installation and a ROS 2 +The five URDF packages provide native `ament_cmake` installation and a ROS 2 `display.launch.py` launcher. Choose a tool or jaw option, then view it in RViz and move its joints with sliders. Linux CI checks ROS 2 **Humble / Ubuntu 22.04** and **Jazzy / Ubuntu 24.04**, including installed assets, TF, mimic joints and @@ -55,7 +55,7 @@ colcon build --base-paths src/robot_descriptions/urdf source install/setup.bash ``` -This builds all four packages. Run colcon against the `urdf/` packages; there +This builds all five packages. Run colcon against the `urdf/` packages; there is no need to run `tools/build.py` to use the committed models. After opening a new terminal, source both `/opt/ros//setup.bash` and your workspace's `install/setup.bash` again. @@ -93,6 +93,7 @@ ros2 launch msg_description display.launch.py jaws:=solid ros2 launch msg_description display.launch.py jaws:=soft_tpu ros2 launch ssg48_description display.launch.py jaws:=solid ros2 launch ssg48_description display.launch.py jaws:=fin_ray +ros2 launch raft_description display.launch.py ``` Each launcher opens RViz and a joint-slider window. The two fingers retain diff --git a/urdf/parol6_description/docs/colors.json b/urdf/parol6_description/docs/colors.json index 6971ca3..09b8a15 100644 --- a/urdf/parol6_description/docs/colors.json +++ b/urdf/parol6_description/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/urdf/parol6_description/launch/display.launch.py b/urdf/parol6_description/launch/display.launch.py index f2c6295..faa69b5 100644 --- a/urdf/parol6_description/launch/display.launch.py +++ b/urdf/parol6_description/launch/display.launch.py @@ -20,7 +20,7 @@ def launch_nodes(context): helper = importlib.util.module_from_spec(spec); spec.loader.exec_module(helper) value = lambda name: LaunchConfiguration(name).perform(context) end_effector = value('end_effector') if FAMILY in ('par6', 'parol6') else 'msg' - entry = helper.select_model(FAMILY, end_effector, value('jaws')) + entry = helper.select_model(FAMILY, end_effector, value('jaws') if FAMILY != 'raft' else 'default') description = ParameterValue(helper.load_description(share/entry, PACKAGE, rviz_materials=value('rviz_materials') == 'true'), value_type=str) parameters = {'robot_description': description, 'use_sim_time': value('use_sim_time') == 'true'} @@ -41,7 +41,7 @@ def launch_nodes(context): def generate_launch_description(): share = Path(get_package_share_directory(PACKAGE)) arm = FAMILY in ('par6', 'parol6') - options = [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', + options = [] if FAMILY == 'raft' else [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', choices=['default', 'solid', 'soft_tpu', 'fin_ray'] if arm else ['solid', 'soft_tpu' if FAMILY == 'msg' else 'fin_ray'])] if arm: diff --git a/urdf/parol6_description/package.xml b/urdf/parol6_description/package.xml index 268dee6..d45c8e2 100644 --- a/urdf/parol6_description/package.xml +++ b/urdf/parol6_description/package.xml @@ -1,7 +1,7 @@ parol6_description - 2.18.1 + 2.19.0 Portable URDF descriptions for the Source Robotics PAROL6 arm. Source Robotics CC-BY-4.0 diff --git a/urdf/parol6_description/scripts/robot_description.py b/urdf/parol6_description/scripts/robot_description.py index ec8951b..715b8d5 100644 --- a/urdf/parol6_description/scripts/robot_description.py +++ b/urdf/parol6_description/scripts/robot_description.py @@ -8,6 +8,8 @@ def select_model(family, end_effector='msg', jaws='default'): + if family == 'raft': + return 'raft.urdf' # one configuration: the leader arm with its trigger handle if family in JAW_OPTIONS: jaws = 'solid' if jaws == 'default' else jaws if jaws not in JAW_OPTIONS[family]: diff --git a/urdf/parol6_description/sim2bot.robot.json b/urdf/parol6_description/sim2bot.robot.json index 8d5dbcd..a365d5e 100644 --- a/urdf/parol6_description/sim2bot.robot.json +++ b/urdf/parol6_description/sim2bot.robot.json @@ -6,7 +6,7 @@ "license": "CC-BY-4.0", "sourceUrl": "https://github.com/Source-Robotics/robot_descriptions", "sourceRevision": "PAROL6 included export; see source/parol6/model.json in the authoring repository", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "parol6_msg.urdf", "dof": 6, "armActuators": [ diff --git a/urdf/raft_description/CMakeLists.txt b/urdf/raft_description/CMakeLists.txt new file mode 100644 index 0000000..e8ee7e8 --- /dev/null +++ b/urdf/raft_description/CMakeLists.txt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +cmake_minimum_required(VERSION 3.8) +project(raft_description) +if("$ENV{ROS_VERSION}" STREQUAL "1") + find_package(catkin REQUIRED) + catkin_package() + set(description_share ${CATKIN_PACKAGE_SHARE_DESTINATION}) +else() + find_package(ament_cmake REQUIRED) + set(description_share share/${PROJECT_NAME}) +endif() +install(DIRECTORY config launch meshes docs previews scripts + DESTINATION ${description_share}) +install(FILES raft.urdf + README.md LICENSE sim2bot.robot.json DESTINATION ${description_share}) +if(NOT "$ENV{ROS_VERSION}" STREQUAL "1") + ament_package() +endif() diff --git a/urdf/raft_description/LICENSE b/urdf/raft_description/LICENSE new file mode 100644 index 0000000..4ea99c2 --- /dev/null +++ b/urdf/raft_description/LICENSE @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/urdf/raft_description/README.md b/urdf/raft_description/README.md new file mode 100644 index 0000000..137785c --- /dev/null +++ b/urdf/raft_description/README.md @@ -0,0 +1,52 @@ +# Source Robotics RAFT: URDF leader arm + +This self-contained package contains the **RAFT teleoperation leader arm**: six joints like PAR6's at 5/9 scale and +the trigger on its handle, with all meshes, materials, collision geometry and mass properties. Keep the whole folder +together when moving or uploading it. + +![RAFT, the handle held in front](previews/raft.png) + +| File | Use it for | +| --- | --- | +| `raft.urdf` | The arm: links, joints, ranges, inertials, coloured visual meshes and collision meshes. | + +The URDF is at the package root and uses relative mesh paths. Detailed notes are in [docs/](docs/), model images in +[previews/](previews/), and geometry in `meshes/` (`meshes/visual/` holds the separately coloured printed parts, +servos, horns and screws). + +## Open the model + +Open `raft.urdf` in a viewer with `meshes/` alongside it, or install the package in a ROS 2 workspace using +[the ROS 2 guide](docs/ROS2.md), source the workspace and run: + +```bash +ros2 launch raft_description display.launch.py +``` + +## Links, joints and frames + +| Links | Joints | +| --- | --- | +| `base_link`, `shoulder_link`, `upper_arm_link`, `forearm_link`, `wrist_1_link`, `wrist_2_link`, `wrist_3_link`, `trigger_link`, `tcp` | `base_joint`, `shoulder_joint`, `elbow_joint`, `wrist_1_joint`, `wrist_2_joint`, `wrist_3_joint`, `trigger_joint`, `tcp_fixed_joint` | + +`base_link` is the root link. `tcp` is the tool point, fixed to `wrist_3_link`. `q = 0` is the standby pose, PAR6's +standby pose, so RAFT's joint angles map one to one onto PAR6's ([DH](docs/DH.md), +[comparison](docs/KINEMATICS_COMPARISON.md)). Each joint's `effort` is the servo's stall torque and `` +carries the estimated damping and friction ([actuation](docs/ACTUATION.md)). URDF itself does not implement the +servos' current control; the native MuJoCo package has the torque actuators. + +## Physical properties and current limits + +Complete arm mass: **371.3 g**; moving links and trigger: **283.5 g**. Masses, centres of mass +and inertias come from the CAD ([dynamics](docs/DYNAMICS.md)); the joint ranges from the measured mechanical stops +and a CAD sweep ([joint ranges](docs/JOINT_LIMITS.md)). The servos' friction, damping and rotor inertia are +estimates, not measurements. + +The model is built from RAFT's reference export, the same source as the firmware's own model in the +[RAFT repository](https://github.com/Source-Robotics/RAFT-teleoperation-arm). + +## License and attribution + +Models and assets: **CC BY 4.0**. See [attribution and license scope](docs/ATTRIBUTION.md). + +[Changing colors](docs/COLORS.md): shared palette, part overrides and illustrated examples. diff --git a/urdf/raft_description/config/display.rviz b/urdf/raft_description/config/display.rviz new file mode 100644 index 0000000..1edcab4 --- /dev/null +++ b/urdf/raft_description/config/display.rviz @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: Apache-2.0 +Panels: + - Class: rviz_common/Displays + Name: Displays +Visualization Manager: + Class: "" + Displays: + - Class: rviz_default_plugins/Grid + Name: Grid + Enabled: true + Value: true + Cell Size: 0.05 + Plane Cell Count: 20 + Color: 115; 125; 140 + - Class: rviz_default_plugins/RobotModel + Name: RobotModel + Enabled: true + Value: true + Alpha: 1 + Visual Enabled: true + Collision Enabled: false + Description Source: Topic + Description Topic: + Value: /robot_description + Depth: 1 + Durability Policy: Transient Local + History Policy: Keep Last + Reliability Policy: Reliable + - Class: rviz_default_plugins/TF + Name: TF + Enabled: false + Value: false + Global Options: + Background Color: 35; 50; 65 + Fixed Frame: base_link + Frame Rate: 30 + Tools: + - Class: rviz_default_plugins/Interact + - Class: rviz_default_plugins/MoveCamera + Views: + Current: + Class: rviz_default_plugins/Orbit + Name: Current View + Distance: 0.5 + Focal Point: + X: 0.08 + Y: 0 + Z: 0.11 + Pitch: 0.4 + Yaw: 0.8 + Target Frame: +Window Geometry: + Width: 1100 + Height: 800 diff --git a/urdf/raft_description/config/joint_names.yaml b/urdf/raft_description/config/joint_names.yaml new file mode 100644 index 0000000..f6d76d1 --- /dev/null +++ b/urdf/raft_description/config/joint_names.yaml @@ -0,0 +1,7 @@ +controller_joint_names: + - base_joint + - shoulder_joint + - elbow_joint + - wrist_1_joint + - wrist_2_joint + - wrist_3_joint diff --git a/urdf/raft_description/docs/ACTUATION.md b/urdf/raft_description/docs/ACTUATION.md new file mode 100644 index 0000000..c5716eb --- /dev/null +++ b/urdf/raft_description/docs/ACTUATION.md @@ -0,0 +1,43 @@ +# Actuation + +[Documentation index](README.md) + +RAFT's seven Feetech HLS3606 servos run in current (torque) mode: the firmware computes gravity, friction and +force-feedback torques itself and sends a current to each servo. The MuJoCo model works the same way. + +| Actuator | Joint | ctrlrange (Nm) | +|---|---|---| +| `base_actuator` | `base_joint` | +-0.5884 | +| `shoulder_actuator` | `shoulder_joint` | +-0.5884 | +| `elbow_actuator` | `elbow_joint` | +-0.5884 | +| `wrist_1_actuator` | `wrist_1_joint` | +-0.5884 | +| `wrist_2_actuator` | `wrist_2_joint` | +-0.5884 | +| `wrist_3_actuator` | `wrist_3_joint` | +-0.5884 | +| `trigger_actuator` | `trigger_joint` | +-0.5884 | + +- **Actuators** are `motor` actuators: `ctrl` is the joint output torque in Nm, limited to the servo's stall torque + (6 kg·cm at 6 V). A simulation sends what the firmware sends; there are no position servos to tune. +- **The URDF** carries the same limits as `effort` (Nm) and `velocity` = 6 rad/s, a conservative value + below the servo's no-load speed. + +## Joint dynamics (estimates) + +| Value | Model | What it is | +|---|---:|---| +| armature | 0.003 kg·m² | the motor rotor reflected through the 1:205 gear (about 0.7 g·cm² × 205²) | +| frictionloss (URDF `friction`) | 0.02 Nm | gearbox Coulomb friction at the output | +| damping | 0.01 Nm·s/rad | viscous friction at the output | + +These are estimates, not measurements. On the real arm the gears turn so easily from the joint side that the +firmware adds damping on the light wrist joints in gravity mode. + +## J2 counterbalance + +The MuJoCo model has the optional J2 counterbalance as a tendon, `j2_spring`, between the sites `j2_spring_anchor` +(13 mm above the J2 axis, on `shoulder_link`) and `j2_spring_pin` (on `upper_arm_link`). Its stiffness is 0, so the +spring is not fitted. Set it to the spring's rate in N/m to simulate one; the cable geometry gives a J2 torque of +-k · 13 mm · 20 mm · sin(q2). The mount is described in the RAFT repository's [J2 spring guide](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/docs/spring_mount.md). + +## Not modelled + +Servo current limits and derating with temperature, gear backlash, gear efficiency, and cable forces. diff --git a/urdf/raft_description/docs/ATTRIBUTION.md b/urdf/raft_description/docs/ATTRIBUTION.md new file mode 100644 index 0000000..5bfdfd2 --- /dev/null +++ b/urdf/raft_description/docs/ATTRIBUTION.md @@ -0,0 +1,20 @@ +# Model license and attribution + +Copyright 2026 Source Robotics. + +The robot/gripper descriptions, geometry, textures, physical data and +documentation in this package are licensed under **CC-BY-4.0**. +See the [full model license](../LICENSE). + +Source: [Source Robotics robot_descriptions](https://github.com/Source-Robotics/robot_descriptions). +License: [Creative Commons Attribution 4.0 International](https://creativecommons.org/licenses/by/4.0/). + +When sharing the models or adaptations, credit Source Robotics, link to the +source and license, retain supplied notices, and indicate your changes. +For example: + +> Robot models by Source Robotics, https://github.com/Source-Robotics/robot_descriptions, +> CC BY 4.0, https://creativecommons.org/licenses/by/4.0/. +> Changes: describe your modifications, or state that none were made. + +ROS launch/support scripts and CMake code are Apache-2.0; see [LICENSE-CODE.txt](LICENSE-CODE.txt). diff --git a/urdf/raft_description/docs/COLORS.md b/urdf/raft_description/docs/COLORS.md new file mode 100644 index 0000000..3f1f4f4 --- /dev/null +++ b/urdf/raft_description/docs/COLORS.md @@ -0,0 +1,195 @@ +# Changing model colors + +[Back to the repository](../README.md) + +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +The starting palette keeps the existing appearance. + +## Quick example + +In that file, change these entries inside `colors`: + +```json +"structure": "#CAD1EE", +"motors": "#2060E0", +"camera_lens": "#080808", +"msg_soft_inserts": "#FFD21A" +``` + +This gives both robots blue J1/J2/J3 motors and makes the MSG motor blue too. +The camera lens stays black, the structures stay light, and the Soft TPU inserts +stay yellow. Keep the other settings in the file. + +![Starting palette and blue-motor example on PAROL6 with MSG Soft TPU jaws](../previews/color_examples/motors.png) + +The images in this guide are renders of the actual MuJoCo models. Each pair +uses the same model, pose, lighting and camera; only its example colors change. +The shared build applies the same settings to URDF. Examples are independent: +start from the default palette for each one, then change only the listed entries. + +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the +repository root: + +```bash +python tools/build.py --models-only --render +``` + +This uses the existing CAD meshes, applies the palette, rebakes the badge, +refreshes the dimensioned review snapshots, validates the models and exports +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds +reuse the checked snapshots without OpenGL; refresh them after changing colors. +If you also changed CAD/geometry, omit `--models-only`. + +Open a rebuilt model to see the result. Colors are baked into the model files +and badge texture: no extra process runs while viewing or simulating. This is +an editing convenience, not a simulation-speed optimization. It does not add +bodies, joints, geoms, triangles or higher-resolution textures, or change +kinematics, collisions, masses, COM, inertia or controller settings. + +## Common colors + +Each entry accepts `"#RRGGBB"` or `[red, green, blue, 1]`, with channels from +0 to 1. The alpha channel must be 1. Existing precise gray/black values use +arrays; replacing one with a HEX color is fine. + +| Key inside `colors` | Parts controlled | +| --- | --- | +| `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | +| `camera_lens` | MSG lens | +| `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | +| `msg_soft_holders` | MSG Soft TPU black holders and carriages | +| `msg_soft_inserts` | MSG Soft TPU honeycomb inserts | +| `ssg48_solid_jaws` | SSG48 Solid jaws and carriage geometry | +| `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | +| `pneumatic_body` | Pneumatic gripper housing | +| `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | + +A color applies to an existing visual partition. For example, a jaw and its +carriage cannot get separate colors if they share one visual mesh. + +### Example: teal MSG inserts and orange SSG48 jaws + +Change these three entries inside `colors`; keep the other entries as they are: + +```json +"msg_soft_holders": "#26364A", +"msg_soft_inserts": "#20B7A8", +"ssg48_solid_jaws": "#F08020" +``` + +![MSG Soft TPU and SSG48 Solid jaws, starting colors on the left and example colors on the right](../previews/color_examples/jaws.png) + +The dark blue holders and teal honeycomb inserts affect the MSG Soft TPU option. +The orange setting affects SSG48 Solid jaws. Each applies to both fingers, +standalone and arm-mounted, while the other jaw options keep their own colors. + +## Override an individual part + +The optional `overrides` section takes precedence over common colors. For +example, keep the common motor color black but make J1 orange and the MSG motor +blue: + +```json +"overrides": { + "j1_motor": "#F08020", + "msg_motor": "#2060E0" +} +``` + +Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). +Remove an override to return that part to its common color. Tool colors stay +the same whether the tool is standalone or mounted on either robot. + +`par6_panel` affects the textured cover surrounding the badge. Without that +override it follows `par6_structure`, which in turn follows `structure`. +Badge colors are baked into RGB pixels, so array values there are rounded to +the nearest 8-bit channel. PAROL6 retains its existing unbadged cover. + +Unknown keys, duplicate keys, invalid HEX values, non-finite/out-of-range +channels and transparency are rejected before the build changes models. + +## Badge artwork + +The same file has a `badge` section for `background`, `emblem`, `symbol`, +`wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run +the same build command. Badge size, resolution, placement and SVG shapes stay +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). + +### Example: graphite cover and teal logo + +Set `overrides` to the following, or add this entry to your existing overrides: + +```json +"overrides": { + "par6_panel": "#303A48" +} +``` + +Then set the badge section to: + +```json +"badge": { + "background": "#15364A", + "emblem": "#20B7A8", + "symbol": "#15364A", + "wordmark": "#20B7A8", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#20B7A8" +} +``` + +![Close-up of the PAR6 side badge with starting colors and a graphite-and-teal example](../previews/color_examples/badge.png) + +This changes the existing PAR6 cover and badge artwork. The logo shapes, +placement and texture resolution stay the same. PAROL6's unbadged cover is +unaffected by these PAR6-specific settings. + +The color fields retained in the historical badge/jaw/pneumatic geometry +configs are baseline settings for their low-level geometry tools. The complete +build always applies `source/appearance/colors.json` afterwards; use the shared +file for maintained colors. It never rewrites those geometry configurations. + +## Downloaded packages and previews + +Every package includes `docs/colors.json`, a copy of the palette used when it +was built. It is a record, not a runtime configuration. To regenerate a shared +theme, use the central source checkout and the command above. + +For a one-off edit without the build environment, you can still change visual +RGBA values in a downloaded URDF or MJCF. Those changes apply only to the files +you edit. For example, the retained material `j1_actuator_black` controls J1, +`msg_camera_lens_black` controls the lens, and `msg_jaw_yellow` controls the TPU +inserts. Historical material names stay unchanged when recolored. Leave hidden +collision geoms and collision/inertial/joint definitions alone. The PAR6 cover +uses an embedded texture; change its palette through the build instead. + +The `--render` flag refreshes the twelve dimensioned review sheets. Other guide +screenshots are separate presentation assets. To refresh those after a theme +change, run the optional renderers and rebuild to copy them into packages: + +```bash +python tools/render_parol6_previews.py +python tools/render_gripper_previews.py +python tools/render_msg_camera_previews.py +python tools/build.py --models-only +``` + +To regenerate the optional review PDF too, use +`python tools/build_review_sheets.py --pdf` with the review dependencies installed. + +To refresh the three illustrated comparisons in this guide, run +`python tools/render_color_examples.py`, then rebuild the packages. This optional +OpenGL renderer uses temporary model copies and leaves the active palette, +runtime models and checked badge texture unchanged. The exact settings and +viewing poses are recorded in the [render manifest](../previews/color_examples/render_manifest.json). diff --git a/urdf/raft_description/docs/DH.md b/urdf/raft_description/docs/DH.md new file mode 100644 index 0000000..4cfb4eb --- /dev/null +++ b/urdf/raft_description/docs/DH.md @@ -0,0 +1,35 @@ +# RAFT DH convention + +[Documentation index](README.md) + +RAFT uses PAR6's kinematic structure: the standard DH (CM6) table, the same joint directions and the same zero +pose, with every length scaled to about 5/9 ([comparison](KINEMATICS_COMPARISON.md)). + +- **Frames.** Link *i*'s frame is DH frame *i - 1*, and joint *i* turns about its z axis. `wrist_3_link` is DH frame + 5, and the `tcp` frame is DH frame 6. +- **Zero pose.** `q = 0` is the standby pose: upper arm vertical, forearm level and forward, wrist straight. It is + PAR6's standby pose, and the `home` keyframe. +- **PAR6 / RCB angles.** The RCB runtime works in DH angles: theta = q + [0, -90, 180, 0, 0, 180] deg for joints 1 + to 6. They are continuous and never wrapped, as in the runtime. +- **Units.** Metres and radians in the model files; millimetres and degrees in these tables. + +## DH table (model version 2.19.0) + +| Joint | d (mm) | a (mm) | alpha | theta offset | +|---|---:|---:|---:|---:| +| `base_joint` | 51.220 | 13.000 | -90 deg | 0 deg | +| `shoulder_joint` | 0.000 | 100.000 | 180 deg | -90 deg | +| `elbow_joint` | 0.000 | -24.160 | 90 deg | 180 deg | +| `wrist_1_joint` | -97.920 | 0.000 | -90 deg | 0 deg | +| `wrist_2_joint` | 0.000 | 0.000 | 90 deg | 0 deg | +| `wrist_3_joint` | -78.277 | -0.000 | 180 deg | 180 deg | + +The trigger (`trigger_joint`) is not part of the DH chain. Its hinge frame is fixed to `wrist_3_link` (from the CAD +assembly), and it turns about the hinge frame's z axis. + +## Tool point + +`tcp` sits on the trigger loop, 78.277 mm from the J6 axis along the tool axis. That is exactly 5/9 of the +140.9 mm of PAR6's SSG48 and MSG tool points in the +[robot_descriptions kinematic review](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/kinematics/robot_kinematics_review.pdf). +RAFT has no separate flange: the handle is fixed to the J6 horn. diff --git a/urdf/raft_description/docs/DYNAMICS.md b/urdf/raft_description/docs/DYNAMICS.md new file mode 100644 index 0000000..b0ce7fa --- /dev/null +++ b/urdf/raft_description/docs/DYNAMICS.md @@ -0,0 +1,33 @@ +# Masses, centres of mass and inertias + +[Documentation index](README.md) + +The mass properties are SolidWorks' (RAFT inertials.pdf, with the masses corrected to the printed parts at the +print table's settings). Each link includes its printed parts, the servo it carries, the horn it is fixed to and +the screws. They are given in each link's own frame; the inertias are about the centre of mass, and both formats +carry the full tensor, off-diagonal terms included. + +| Link | Mass (g) | COM in the link frame (mm) | +|---|---:|---| +| `base_link` | 87.76 | -7.30, -0.42, 10.52 | +| `shoulder_link` | 43.14 | 6.83, -0.10, 48.72 | +| `upper_arm_link` | 18.20 | 0.00, -50.00, -23.15 | +| `forearm_link` | 71.40 | 14.84, 7.44, 2.50 | +| `wrist_1_link` | 46.10 | -0.02, 21.35, -71.39 | +| `wrist_2_link` | 35.10 | 4.64, 0.03, 0.48 | +| `wrist_3_link` | 55.30 | 1.62, 26.17, -45.71 | +| `trigger_link` | 14.30 | -13.10, 15.20, 12.78 | +| total | 371.30 | | + +## Status + +| Item | Status | +|---|---| +| Link masses | From CAD. Not yet weighed: printed parts vary with filament and infill. | +| Centres of mass and inertias | From CAD. | +| Base | From CAD. The ESP32-PoE board and the cables are not included. | + +RAFT's firmware compensates gravity with the same numbers: one generator in the RAFT repository +([`firmware/tools/gen_model.py`](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/tools/gen_model.py)) writes the firmware's model and the +reference these packages are built from, so a mass that is off shows the same way in both. Weighing each printed +link with its servo and horn is the next improvement. diff --git a/urdf/raft_description/docs/JOINT_LIMITS.md b/urdf/raft_description/docs/JOINT_LIMITS.md new file mode 100644 index 0000000..5b960de --- /dev/null +++ b/urdf/raft_description/docs/JOINT_LIMITS.md @@ -0,0 +1,46 @@ +# Joint ranges + +Angles in the model's convention: 0 is the standby pose (PAR6's standby pose), positive about each joint's z axis. +The URDF `` and the MuJoCo `range` carry the same numbers. + +| Joint | Lower | Upper | Where the numbers come from | +|---|---:|---:|---| +| `base_joint` | -136.28 deg | 136.28 deg | lower: measured stop; upper: the same angle mirrored (the CAD has no J1 stop; not measured) | +| `shoulder_joint` | -83.96 deg | 131.50 deg | lower: measured stop (CAD sweep -83.0); upper: CAD sweep | +| `elbow_joint` | -80.33 deg | 92.81 deg | lower: measured, the forearm lying limp on the upper arm (the CAD parts meet at about -77); upper: measured stop (CAD sweep 92.5) | +| `wrist_1_joint` | -158.56 deg | 153.50 deg | lower: measured stop (CAD sweep -154.0); upper: CAD sweep | +| `wrist_2_joint` | -126.00 deg | 119.72 deg | lower: CAD sweep; upper: measured stop (CAD sweep 117.5) | +| `wrist_3_joint` | -147.50 deg | 149.37 deg | lower: CAD sweep; upper: measured stop (CAD sweep 147.5) | +| `trigger_joint` | -39.63 deg | 0.00 deg | lower: measured stop, trigger released; upper: trigger flush against the handle (CAD) | + +## Where the numbers come from + +- **Measured stops.** Every joint has one mechanical stop. Its angle was measured on the first arm (2026-11-02), + and RAFT's firmware calibrates against it (`kStopDeg` in + [`firmware/src/main/arm_design.h`](https://github.com/Source-Robotics/RAFT-teleoperation-arm/blob/main/firmware/src/main/arm_design.h)). These are the most trustworthy + numbers in the table. +- **CAD sweep.** The other end of a range comes from the CAD meshes: one joint turned, the others at 0, until its + moving parts come within 0.5 mm of the parts it turns against. Where a sweep can be compared with a measured stop + it ends 0.3 to 2.2 deg early (J2, J3, J5, J6) or 4.6 deg early (J4), so these ends are slightly conservative. + They hold with the other joints at 0; some wrist poses reach further or less far. +- **Forearm lying limp.** J3's lower end was read on the first arm with the arm limp and the forearm lying folded + on the upper arm (2026-11-06). The CAD parts meet at about -77 deg; the printed parts settle 3 deg further. +- **Trigger.** 0 is the trigger flush against the handle, as in the CAD; turning it positive would push it into + the handle. It opens toward negative angles, to its stop 39.63 deg from flush. +- **Not measured.** The CAD has no J1 stop. J1's upper end mirrors its measured stop until the real end is + measured. + +The ranges of one arm can be measured with the firmware's `calib_range` command (move every joint gently to both +ends). Report measured ranges in the [RAFT repository's issues](https://github.com/Source-Robotics/RAFT-teleoperation-arm/issues). + +## Keyframes + +| Keyframe | qpos (deg), base_joint to trigger_joint | +|---|---| +| `home` | 0, 0, 0, 0, 0, 0, 0 | +| `parked` | 0, -82, -80, 0, 0, 0, 0 | +| `handle_in_front` | 0, 20, 0, 0, 0, 0, 0 | +| `reach_forward` | 0, 45, 45, 0, 0, 0, 0 | +| `stretched` | 0, 90, 90, 0, 0, 0, 0 | + +`home` is the standby pose. `parked` is RAFT's firmware rest pose: the arm lying back the way it rests limp. diff --git a/urdf/raft_description/docs/KINEMATICS_COMPARISON.md b/urdf/raft_description/docs/KINEMATICS_COMPARISON.md new file mode 100644 index 0000000..14ca91a --- /dev/null +++ b/urdf/raft_description/docs/KINEMATICS_COMPARISON.md @@ -0,0 +1,22 @@ +# RAFT and PAR6 + +[Documentation index](README.md) + +RAFT is a teleoperation leader for PAR6: the same kinematic structure, joint directions and zero pose, scaled to +5/9. Joint angles therefore map one to one, which is how the firmware teleoperates PAR6. + +| Length | RAFT (mm) | PAR6 (mm) | RAFT / PAR6 | +|---|---:|---:|---:| +| a2 | 13.000 | 23.4207 | 0.5551 | +| a3 | 100.000 | 180 | 0.5556 | +| a4 | 24.160 | 43.5 | 0.5554 | +| a5 | 97.920 | 177 | 0.5532 | +| a6 (TCP) | 78.277 | 140.9 | 0.5556 | + +PAR6's lengths are those of the +[robot_descriptions kinematic review](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/kinematics/robot_kinematics_review.pdf), +with the SSG48 or MSG tool point for a6. RAFT's base (d1 = 51.22 mm against PAR6's 110.5 mm) is lower than a pure +scale would make it; the base height does not change any joint angle. + +Not scaled: the joint ranges (RAFT's mechanical stops, [joint ranges](JOINT_LIMITS.md)), the masses, and the +actuation (RAFT's servos run in current mode, [actuation](ACTUATION.md)). diff --git a/urdf/raft_description/docs/LICENSE-CODE.txt b/urdf/raft_description/docs/LICENSE-CODE.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/urdf/raft_description/docs/LICENSE-CODE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/urdf/raft_description/docs/README.md b/urdf/raft_description/docs/README.md new file mode 100644 index 0000000..70eeec1 --- /dev/null +++ b/urdf/raft_description/docs/README.md @@ -0,0 +1,12 @@ +# Package documentation + +[Back to the model instructions](../README.md) + +- [Actuation](ACTUATION.md) +- [Model license and attribution](ATTRIBUTION.md) +- [Changing model colors](COLORS.md) +- [RAFT DH convention](DH.md) +- [Masses, centres of mass and inertias](DYNAMICS.md) +- [Joint ranges](JOINT_LIMITS.md) +- [RAFT and PAR6](KINEMATICS_COMPARISON.md) +- [ROS 2: install and display the models](ROS2.md) diff --git a/urdf/raft_description/docs/ROS2.md b/urdf/raft_description/docs/ROS2.md new file mode 100644 index 0000000..79efeec --- /dev/null +++ b/urdf/raft_description/docs/ROS2.md @@ -0,0 +1,162 @@ +# ROS 2: install and display the models + +[Back to the model instructions](../README.md) + +The five URDF packages provide native `ament_cmake` installation and a ROS 2 +`display.launch.py` launcher. Choose a tool or jaw option, then view it in RViz +and move its joints with sliders. Linux CI checks ROS 2 **Humble / Ubuntu 22.04** +and **Jazzy / Ubuntu 24.04**, including installed assets, TF, mimic joints and +the RViz/joint-slider applications. + +These launchers display the maintained URDFs. The sliders publish preview joint +states; they do not command motors or simulate dynamics. The existing MuJoCo +packages and raw URDF entry files are unchanged. + +For collision-checked motion planning and simulated trajectory execution, add +the matching `par6_moveit_config` or `parol6_moveit_config` package and follow +the [MoveIt 2 guide](https://github.com/Source-Robotics/robot_descriptions/blob/main/docs/MOVEIT2.md). +It supports every mounted tool/jaw option and uses mock controllers. + +## Install one downloaded package + +Start with ROS 2 installed on its matching Ubuntu version. Use a fresh terminal +and source the appropriate distribution (replace `jazzy` with `humble` below if +needed): + +```bash +source /opt/ros/jazzy/setup.bash +sudo apt install python3-colcon-common-extensions python3-rosdep +rosdep update +mkdir -p ~/source_robot_ws/src +unzip ~/Downloads/par6_urdf.zip -d ~/source_robot_ws/src +cd ~/source_robot_ws +rosdep install --from-paths src --ignore-src -r -y +colcon build --packages-select par6_description +source install/setup.bash +ros2 launch par6_description display.launch.py +``` + +If rosdep has never been initialized on the machine, run `sudo rosdep init` +once before `rosdep update`. The default display is PAR6 with MSG Solid jaws. +Use another URDF ZIP/package name for PAROL6, standalone MSG or standalone SSG48. +You only need the selected package; no CAD tools, authoring virtual environment, +MuJoCo or source-generation build is needed to install it in ROS 2. + +## Install from the central checkout instead + +After sourcing ROS 2 and installing colcon/rosdep: + +```bash +mkdir -p ~/source_robot_ws/src +git clone https://github.com/Source-Robotics/robot_descriptions.git ~/source_robot_ws/src/robot_descriptions +cd ~/source_robot_ws +rosdep install --from-paths src/robot_descriptions/urdf --ignore-src -r -y +colcon build --base-paths src/robot_descriptions/urdf +source install/setup.bash +``` + +This builds all five packages. Run colcon against the `urdf/` packages; there +is no need to run `tools/build.py` to use the committed models. After opening a +new terminal, source both `/opt/ros//setup.bash` and your workspace's +`install/setup.bash` again. + +## Choose a robot and tool + +Use `par6_description` or `parol6_description` with the same arguments: + +| Configuration | Arguments after `display.launch.py` | +| --- | --- | +| Flange only | `end_effector:=flange` | +| MSG / Solid jaws (default) | `end_effector:=msg jaws:=solid` | +| MSG / Soft TPU jaws | `end_effector:=msg jaws:=soft_tpu` | +| SSG48 / Solid jaws | `end_effector:=ssg48 jaws:=solid` | +| SSG48 / Fin-ray jaws | `end_effector:=ssg48 jaws:=fin_ray` | +| Pneumatic gripper | `end_effector:=pneumatic` | + +For example: + +```bash +ros2 launch par6_description display.launch.py end_effector:=msg jaws:=soft_tpu +ros2 launch parol6_description display.launch.py end_effector:=ssg48 jaws:=fin_ray +``` + +Run one display at a time. `jaws:=default` selects Solid jaws for either electric +gripper. The historical `end_effector:=msg_tpu_glue` and +`end_effector:=ssg48_stock` spellings also select the existing alternate models. +Incompatible choices are rejected with an explanation, rather than silently +showing a different jaw. Omit `jaws` for flange-only or pneumatic configurations. + +## Display a standalone gripper + +```bash +ros2 launch msg_description display.launch.py jaws:=solid +ros2 launch msg_description display.launch.py jaws:=soft_tpu +ros2 launch ssg48_description display.launch.py jaws:=solid +ros2 launch ssg48_description display.launch.py jaws:=fin_ray +ros2 launch raft_description display.launch.py +``` + +Each launcher opens RViz and a joint-slider window. The two fingers retain +their mimic relation; move the independent finger slider to open or close them. +The gripper-only root frame is `flange`; the arm root is `base_link`. RViz is +configured for the appropriate fixed frame, visual materials and model scale. + +## Display options + +| Argument | Default | Use | +| --- | --- | --- | +| `gui` | `true` | `false` uses the non-GUI joint-state publisher at its initial pose. | +| `rviz` | `true` | `false` publishes the model/TF without opening RViz. | +| `publish_joint_states` | `true` | `false` uses joint states from your own simulator/controller. | +| `rviz_materials` | `true` | Preserves individual part colors in RViz; see the display compatibility note below. | +| `use_sim_time` | `false` | `true` uses an external simulator's `/clock`. | +| `rviz_config` | Installed `config/display.rviz` | Path to your own RViz configuration. | + +For a headless preview: + +```bash +ros2 launch par6_description display.launch.py gui:=false rviz:=false +``` + +To visualize an existing joint-state source: + +```bash +ros2 launch par6_description display.launch.py publish_joint_states:=false +``` + +The selected source must publish matching joint names on `/joint_states`. +The launcher publishes `/robot_description`, `/tf` and `/tf_static`. When using +simulated time, the external source must also supply `/clock`. Stop the launch +with Ctrl+C. RViz displays geometry and frames; the MSG camera frames do not +provide a live camera image or camera driver. + +## Assets and compatibility + +ROS launch resolves the selected URDF's relative asset references to +`package://_description/...` in memory. Models on disk keep their +portable filenames, physical properties, colors, joint names and frames. Meshes +and the PAR6 badge texture are installed with the package, so a normal colcon +install works without the source directory. + +Humble/Jazzy RViz applies one material to multiple visuals on the same URDF +link. The display launcher therefore moves additional visuals to massless +fixed child frames in its in-memory description. Names end in +`_rviz_visual_`; each is attached to the original link by an identity fixed +transform. This preserves separate motor/lens/jaw colors and the textured +badge, while keeping every original joint/frame transform, collision and +inertial definition intact. No model files or meshes on disk are rewritten. +Use `rviz_materials:=false` when another application requires the original +link/joint tree rather than the RViz-compatible display tree. + +During arm startup, KDL logs a root-inertia warning for `base_link`. Its TF +tree does not use the fixed root's inertia. The existing base inertial data +is deliberately retained in the URDF; the warning does not change joint/frame +transforms. This viewer is not computing robot dynamics. + +ROS 1 users retain `display.launch` and the conditional catkin build path when +working in a sourced ROS 1 environment. ROS 2 users should use +`display.launch.py`. The older `gazebo.launch` files are ROS 1 scaffolds; +this release does not add Gazebo or a hardware-control driver. ROS 2 source +installation, ament conventions and robot-state publication are documented by +[ROS 2](https://docs.ros.org/en/jazzy/index.html) and +[robot_state_publisher](https://github.com/ros/robot_state_publisher). diff --git a/urdf/raft_description/docs/colors.json b/urdf/raft_description/docs/colors.json new file mode 100644 index 0000000..09b8a15 --- /dev/null +++ b/urdf/raft_description/docs/colors.json @@ -0,0 +1,29 @@ +{ + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [0.06, 0.06, 0.065, 1], + "camera_lens": [0.06, 0.06, 0.065, 1], + "msg_solid_jaws": [0.29804, 0.29804, 0.29804, 1], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], + "pneumatic_body": [0.72, 0.75, 0.8, 1], + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } +} diff --git a/urdf/raft_description/launch/display.launch b/urdf/raft_description/launch/display.launch new file mode 100644 index 0000000..27fa9ae --- /dev/null +++ b/urdf/raft_description/launch/display.launch @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/urdf/raft_description/launch/display.launch.py b/urdf/raft_description/launch/display.launch.py new file mode 100644 index 0000000..297f6b6 --- /dev/null +++ b/urdf/raft_description/launch/display.launch.py @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Display one installed robot/gripper with optional joint sliders.""" +import importlib.util +from pathlib import Path + +from ament_index_python.packages import get_package_share_directory +from launch import LaunchDescription +from launch.actions import DeclareLaunchArgument, LogInfo, OpaqueFunction +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import Node +from launch_ros.parameter_descriptions import ParameterValue + +PACKAGE = 'raft_description' +FAMILY = 'raft' + + +def launch_nodes(context): + share = Path(get_package_share_directory(PACKAGE)) + spec = importlib.util.spec_from_file_location('source_robot_description', share/'scripts/robot_description.py') + helper = importlib.util.module_from_spec(spec); spec.loader.exec_module(helper) + value = lambda name: LaunchConfiguration(name).perform(context) + end_effector = value('end_effector') if FAMILY in ('par6', 'parol6') else 'msg' + entry = helper.select_model(FAMILY, end_effector, value('jaws') if FAMILY != 'raft' else 'default') + description = ParameterValue(helper.load_description(share/entry, PACKAGE, + rviz_materials=value('rviz_materials') == 'true'), value_type=str) + parameters = {'robot_description': description, 'use_sim_time': value('use_sim_time') == 'true'} + nodes = [LogInfo(msg=f'{FAMILY.upper()}: displaying {entry}'), + Node(package='robot_state_publisher', executable='robot_state_publisher', + name='robot_state_publisher', parameters=[parameters], output='screen')] + if value('publish_joint_states') == 'true': + publisher = 'joint_state_publisher_gui' if value('gui') == 'true' else 'joint_state_publisher' + nodes.append(Node(package=publisher, executable=publisher, name=publisher, + parameters=[dict(parameters, use_mimic_tags=True)], output='screen')) + if value('rviz') == 'true': + nodes.append(Node(package='rviz2', executable='rviz2', name='rviz2', + arguments=['-d', value('rviz_config')], + parameters=[{'use_sim_time': parameters['use_sim_time']}], output='screen')) + return nodes + + +def generate_launch_description(): + share = Path(get_package_share_directory(PACKAGE)) + arm = FAMILY in ('par6', 'parol6') + options = [] if FAMILY == 'raft' else [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', + choices=['default', 'solid', 'soft_tpu', 'fin_ray'] if arm else + ['solid', 'soft_tpu' if FAMILY == 'msg' else 'fin_ray'])] + if arm: + options.insert(0, DeclareLaunchArgument('end_effector', default_value='msg', + choices=['flange', 'msg', 'ssg48', 'pneumatic', 'msg_tpu_glue', 'ssg48_stock'])) + for name, default, help_text in [ + ('gui', 'true', 'Use joint sliders; false publishes a fixed preview pose'), + ('rviz', 'true', 'Open RViz with the saved product view'), + ('publish_joint_states', 'true', 'Publish preview joint states; false uses an external source'), + ('rviz_materials', 'true', 'Preserve per-part materials using massless fixed visual frames'), + ('use_sim_time', 'false', 'Use a simulator /clock topic')]: + options.append(DeclareLaunchArgument(name, default_value=default, choices=['true', 'false'], description=help_text)) + options.append(DeclareLaunchArgument('rviz_config', default_value=str(share/'config/display.rviz'))) + return LaunchDescription([*options, OpaqueFunction(function=launch_nodes)]) diff --git a/urdf/raft_description/meshes/base_link.stl b/urdf/raft_description/meshes/base_link.stl new file mode 100644 index 0000000..2618394 Binary files /dev/null and b/urdf/raft_description/meshes/base_link.stl differ diff --git a/urdf/raft_description/meshes/forearm_link.stl b/urdf/raft_description/meshes/forearm_link.stl new file mode 100644 index 0000000..58ba361 Binary files /dev/null and b/urdf/raft_description/meshes/forearm_link.stl differ diff --git a/urdf/raft_description/meshes/shoulder_link.stl b/urdf/raft_description/meshes/shoulder_link.stl new file mode 100644 index 0000000..d792b47 Binary files /dev/null and b/urdf/raft_description/meshes/shoulder_link.stl differ diff --git a/urdf/raft_description/meshes/trigger_link.stl b/urdf/raft_description/meshes/trigger_link.stl new file mode 100644 index 0000000..faa1adf Binary files /dev/null and b/urdf/raft_description/meshes/trigger_link.stl differ diff --git a/urdf/raft_description/meshes/upper_arm_link.stl b/urdf/raft_description/meshes/upper_arm_link.stl new file mode 100644 index 0000000..77beb41 Binary files /dev/null and b/urdf/raft_description/meshes/upper_arm_link.stl differ diff --git a/urdf/raft_description/meshes/visual/base_link_fasteners.stl b/urdf/raft_description/meshes/visual/base_link_fasteners.stl new file mode 100644 index 0000000..b7915ef Binary files /dev/null and b/urdf/raft_description/meshes/visual/base_link_fasteners.stl differ diff --git a/urdf/raft_description/meshes/visual/base_link_motors.stl b/urdf/raft_description/meshes/visual/base_link_motors.stl new file mode 100644 index 0000000..698e6a0 Binary files /dev/null and b/urdf/raft_description/meshes/visual/base_link_motors.stl differ diff --git a/urdf/raft_description/meshes/visual/base_link_structure.stl b/urdf/raft_description/meshes/visual/base_link_structure.stl new file mode 100644 index 0000000..df8feae Binary files /dev/null and b/urdf/raft_description/meshes/visual/base_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/forearm_link_fasteners.stl b/urdf/raft_description/meshes/visual/forearm_link_fasteners.stl new file mode 100644 index 0000000..3220128 Binary files /dev/null and b/urdf/raft_description/meshes/visual/forearm_link_fasteners.stl differ diff --git a/urdf/raft_description/meshes/visual/forearm_link_motors.stl b/urdf/raft_description/meshes/visual/forearm_link_motors.stl new file mode 100644 index 0000000..e759a6a Binary files /dev/null and b/urdf/raft_description/meshes/visual/forearm_link_motors.stl differ diff --git a/urdf/raft_description/meshes/visual/forearm_link_structure.stl b/urdf/raft_description/meshes/visual/forearm_link_structure.stl new file mode 100644 index 0000000..b159145 Binary files /dev/null and b/urdf/raft_description/meshes/visual/forearm_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/shoulder_link_fasteners.stl b/urdf/raft_description/meshes/visual/shoulder_link_fasteners.stl new file mode 100644 index 0000000..74824c6 Binary files /dev/null and b/urdf/raft_description/meshes/visual/shoulder_link_fasteners.stl differ diff --git a/urdf/raft_description/meshes/visual/shoulder_link_horns.stl b/urdf/raft_description/meshes/visual/shoulder_link_horns.stl new file mode 100644 index 0000000..c382a4e Binary files /dev/null and b/urdf/raft_description/meshes/visual/shoulder_link_horns.stl differ diff --git a/urdf/raft_description/meshes/visual/shoulder_link_motors.stl b/urdf/raft_description/meshes/visual/shoulder_link_motors.stl new file mode 100644 index 0000000..befbcb0 Binary files /dev/null and b/urdf/raft_description/meshes/visual/shoulder_link_motors.stl differ diff --git a/urdf/raft_description/meshes/visual/shoulder_link_structure.stl b/urdf/raft_description/meshes/visual/shoulder_link_structure.stl new file mode 100644 index 0000000..4b8e3cd Binary files /dev/null and b/urdf/raft_description/meshes/visual/shoulder_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/trigger_link_horns.stl b/urdf/raft_description/meshes/visual/trigger_link_horns.stl new file mode 100644 index 0000000..b2846aa Binary files /dev/null and b/urdf/raft_description/meshes/visual/trigger_link_horns.stl differ diff --git a/urdf/raft_description/meshes/visual/trigger_link_structure.stl b/urdf/raft_description/meshes/visual/trigger_link_structure.stl new file mode 100644 index 0000000..f7d2f14 Binary files /dev/null and b/urdf/raft_description/meshes/visual/trigger_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/upper_arm_link_horns.stl b/urdf/raft_description/meshes/visual/upper_arm_link_horns.stl new file mode 100644 index 0000000..838eb32 Binary files /dev/null and b/urdf/raft_description/meshes/visual/upper_arm_link_horns.stl differ diff --git a/urdf/raft_description/meshes/visual/upper_arm_link_structure.stl b/urdf/raft_description/meshes/visual/upper_arm_link_structure.stl new file mode 100644 index 0000000..ee5a8b0 Binary files /dev/null and b/urdf/raft_description/meshes/visual/upper_arm_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_1_link_fasteners.stl b/urdf/raft_description/meshes/visual/wrist_1_link_fasteners.stl new file mode 100644 index 0000000..031cafc Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_1_link_fasteners.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_1_link_horns.stl b/urdf/raft_description/meshes/visual/wrist_1_link_horns.stl new file mode 100644 index 0000000..1df7e76 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_1_link_horns.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_1_link_motors.stl b/urdf/raft_description/meshes/visual/wrist_1_link_motors.stl new file mode 100644 index 0000000..b14979f Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_1_link_motors.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_1_link_structure.stl b/urdf/raft_description/meshes/visual/wrist_1_link_structure.stl new file mode 100644 index 0000000..204f434 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_1_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_2_link_fasteners.stl b/urdf/raft_description/meshes/visual/wrist_2_link_fasteners.stl new file mode 100644 index 0000000..0925626 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_2_link_fasteners.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_2_link_horns.stl b/urdf/raft_description/meshes/visual/wrist_2_link_horns.stl new file mode 100644 index 0000000..965edcf Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_2_link_horns.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_2_link_motors.stl b/urdf/raft_description/meshes/visual/wrist_2_link_motors.stl new file mode 100644 index 0000000..e83f7be Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_2_link_motors.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_2_link_structure.stl b/urdf/raft_description/meshes/visual/wrist_2_link_structure.stl new file mode 100644 index 0000000..22f1ff1 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_2_link_structure.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_3_link_fasteners.stl b/urdf/raft_description/meshes/visual/wrist_3_link_fasteners.stl new file mode 100644 index 0000000..e98196f Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_3_link_fasteners.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_3_link_horns.stl b/urdf/raft_description/meshes/visual/wrist_3_link_horns.stl new file mode 100644 index 0000000..35f87d8 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_3_link_horns.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_3_link_motors.stl b/urdf/raft_description/meshes/visual/wrist_3_link_motors.stl new file mode 100644 index 0000000..a595cd4 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_3_link_motors.stl differ diff --git a/urdf/raft_description/meshes/visual/wrist_3_link_structure.stl b/urdf/raft_description/meshes/visual/wrist_3_link_structure.stl new file mode 100644 index 0000000..2073df9 Binary files /dev/null and b/urdf/raft_description/meshes/visual/wrist_3_link_structure.stl differ diff --git a/urdf/raft_description/meshes/wrist_1_link.stl b/urdf/raft_description/meshes/wrist_1_link.stl new file mode 100644 index 0000000..620f9e3 Binary files /dev/null and b/urdf/raft_description/meshes/wrist_1_link.stl differ diff --git a/urdf/raft_description/meshes/wrist_2_link.stl b/urdf/raft_description/meshes/wrist_2_link.stl new file mode 100644 index 0000000..4aa9ed9 Binary files /dev/null and b/urdf/raft_description/meshes/wrist_2_link.stl differ diff --git a/urdf/raft_description/meshes/wrist_3_link.stl b/urdf/raft_description/meshes/wrist_3_link.stl new file mode 100644 index 0000000..c907022 Binary files /dev/null and b/urdf/raft_description/meshes/wrist_3_link.stl differ diff --git a/urdf/raft_description/package.xml b/urdf/raft_description/package.xml new file mode 100644 index 0000000..cc7d9a2 --- /dev/null +++ b/urdf/raft_description/package.xml @@ -0,0 +1,24 @@ + + + raft_description + 2.19.0 + Source Robotics RAFT teleoperation leader arm: PAR6's kinematics at 5/9 scale, six joints and a trigger. + Source Robotics + CC-BY-4.0 + Apache-2.0 + catkin + ament_cmake + joint_state_publisher + joint_state_publisher_gui + robot_state_publisher + python3 + rviz + ament_index_python + launch + launch_ros + rviz2 + + catkin + ament_cmake + + \ No newline at end of file diff --git a/urdf/raft_description/previews/color_examples/badge.png b/urdf/raft_description/previews/color_examples/badge.png new file mode 100644 index 0000000..6d07e56 Binary files /dev/null and b/urdf/raft_description/previews/color_examples/badge.png differ diff --git a/urdf/raft_description/previews/color_examples/jaws.png b/urdf/raft_description/previews/color_examples/jaws.png new file mode 100644 index 0000000..25860e5 Binary files /dev/null and b/urdf/raft_description/previews/color_examples/jaws.png differ diff --git a/urdf/raft_description/previews/color_examples/motors.png b/urdf/raft_description/previews/color_examples/motors.png new file mode 100644 index 0000000..b5f0240 Binary files /dev/null and b/urdf/raft_description/previews/color_examples/motors.png differ diff --git a/urdf/raft_description/previews/color_examples/render_manifest.json b/urdf/raft_description/previews/color_examples/render_manifest.json new file mode 100644 index 0000000..185c9ea --- /dev/null +++ b/urdf/raft_description/previews/color_examples/render_manifest.json @@ -0,0 +1,666 @@ +{ + "mujoco_version": "3.3.0", + "examples": { + "motors": { + "settings_to_change": { + "colors": { + "structure": "#CAD1EE", + "motors": "#2060E0", + "camera_lens": "#080808", + "msg_soft_inserts": "#FFD21A" + } + }, + "views": [ + { + "source_model": "mujoco/parol6/parol6_msg_tpu_glue.xml", + "model_sha256": "a49d2ea730af7134d6ef91c7411f7002dc4b6b772074e668c95eb2a6980f56ba", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + -0.5, + -1.3, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.2 + ], + "distance": 0.68, + "azimuth": 135.0, + "elevation": -18.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/parol6/parol6_msg_tpu_glue.xml", + "model_sha256": "a49d2ea730af7134d6ef91c7411f7002dc4b6b772074e668c95eb2a6980f56ba", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": "#2060E0", + "camera_lens": "#080808", + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + -0.5, + -1.3, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.2 + ], + "distance": 0.68, + "azimuth": 135.0, + "elevation": -18.0, + "orthographic": false + } + } + ], + "image": "motors.png", + "image_sha256": "ab73b8d2ae868818ba63434d3716817f47287dc460e5214e23b240396a046eb1" + }, + "jaws": { + "settings_to_change": { + "colors": { + "msg_soft_holders": "#26364A", + "msg_soft_inserts": "#20B7A8", + "ssg48_solid_jaws": "#F08020" + } + }, + "views": [ + { + "source_model": "mujoco/msg/msg_soft_tpu.xml", + "model_sha256": "a1238295e5f42186e2b46fbb7df3637d5b357df8eddbfa1228b81ad09ef09965", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": true, + "camera": { + "lookat": [ + -0.035, + 0.0, + 0.022 + ], + "distance": 0.27, + "azimuth": 15.0, + "elevation": -18.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/msg/msg_soft_tpu.xml", + "model_sha256": "a1238295e5f42186e2b46fbb7df3637d5b357df8eddbfa1228b81ad09ef09965", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#26364A", + "msg_soft_inserts": "#20B7A8", + "ssg48_solid_jaws": "#F08020", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": true, + "camera": { + "lookat": [ + -0.035, + 0.0, + 0.022 + ], + "distance": 0.27, + "azimuth": 15.0, + "elevation": -18.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/ssg48/ssg48_solid.xml", + "model_sha256": "9c70383c5bf97558bb095066055dd83eb82849cb9e622efe07ba1b698a60b953", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.045 + ], + "distance": 0.3, + "azimuth": 135.0, + "elevation": -24.0, + "orthographic": false + } + }, + { + "source_model": "mujoco/ssg48/ssg48_solid.xml", + "model_sha256": "9c70383c5bf97558bb095066055dd83eb82849cb9e622efe07ba1b698a60b953", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#26364A", + "msg_soft_inserts": "#20B7A8", + "ssg48_solid_jaws": "#F08020", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.0, + 0.0, + 0.045 + ], + "distance": 0.3, + "azimuth": 135.0, + "elevation": -24.0, + "orthographic": false + } + } + ], + "image": "jaws.png", + "image_sha256": "9ba48709e99132b177d53aa4c78c80f18f3eb75c868ea8c9d228b4fa8cad62b0" + }, + "badge": { + "settings_to_change": { + "overrides": { + "par6_panel": "#303A48" + }, + "badge": { + "background": "#15364A", + "emblem": "#20B7A8", + "symbol": "#15364A", + "wordmark": "#20B7A8", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#20B7A8" + } + }, + "views": [ + { + "source_model": "mujoco/par6/par6_flange.xml", + "model_sha256": "b6e0819df0137110772556c89f45f905211c6217a57e5c57f9b31b52992b9bbb", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": {}, + "badge": { + "background": "#464545", + "emblem": "#FFFFFF", + "symbol": "#464545", + "wordmark": "#FFFFFF", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#FFFFFF" + } + }, + "qpos": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.023421, + 0.081700445476714, + 0.21549965337517302 + ], + "distance": 0.55, + "azimuth": 270.0, + "elevation": 0.0, + "orthographic": true + } + }, + { + "source_model": "mujoco/par6/par6_flange.xml", + "model_sha256": "b6e0819df0137110772556c89f45f905211c6217a57e5c57f9b31b52992b9bbb", + "palette": { + "schema_version": 1, + "colors": { + "structure": "#CAD1EE", + "motors": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "camera_lens": [ + 0.06, + 0.06, + 0.065, + 1 + ], + "msg_solid_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "msg_soft_holders": "#0F0F11", + "msg_soft_inserts": "#FFD21A", + "ssg48_solid_jaws": "#4C4C4C", + "ssg48_fin_ray_jaws": [ + 0.29804, + 0.29804, + 0.29804, + 1 + ], + "pneumatic_body": [ + 0.72, + 0.75, + 0.8, + 1 + ], + "pneumatic_jaws": [ + 0.12, + 0.13, + 0.15, + 1 + ] + }, + "overrides": { + "par6_panel": "#303A48" + }, + "badge": { + "background": "#15364A", + "emblem": "#20B7A8", + "symbol": "#15364A", + "wordmark": "#20B7A8", + "robot_name": "#FFFFFF", + "tagline": "#FFFFFF", + "divider": "#20B7A8" + } + }, + "qpos": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "presentation_root_rotation": false, + "camera": { + "lookat": [ + 0.023421, + 0.081700445476714, + 0.21549965337517302 + ], + "distance": 0.55, + "azimuth": 270.0, + "elevation": 0.0, + "orthographic": true + } + } + ], + "image": "badge.png", + "image_sha256": "76acabf39402dc542280a4c8f026c366c1c9d8e3b3aecdc2b393c19a26223fbb" + } + }, + "description": "Native model renders; only example palettes and documented viewing poses differ.", + "runtime_models_palette_and_checked_texture_unchanged": true +} diff --git a/urdf/raft_description/previews/raft.png b/urdf/raft_description/previews/raft.png new file mode 100644 index 0000000..47752c5 Binary files /dev/null and b/urdf/raft_description/previews/raft.png differ diff --git a/urdf/raft_description/previews/raft_isometric.png b/urdf/raft_description/previews/raft_isometric.png new file mode 100644 index 0000000..a8f75ea Binary files /dev/null and b/urdf/raft_description/previews/raft_isometric.png differ diff --git a/urdf/raft_description/raft.urdf b/urdf/raft_description/raft.urdf new file mode 100644 index 0000000..f6cfefc --- /dev/null +++ b/urdf/raft_description/raft.urdf @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/urdf/raft_description/scripts/robot_description.py b/urdf/raft_description/scripts/robot_description.py new file mode 100644 index 0000000..715b8d5 --- /dev/null +++ b/urdf/raft_description/scripts/robot_description.py @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: Apache-2.0 +"""ROS-independent selection and resource resolution for the installed URDFs.""" +from pathlib import Path, PurePosixPath +import xml.etree.ElementTree as ET + +ARM_TOOLS = ('flange', 'msg', 'ssg48', 'pneumatic', 'msg_tpu_glue', 'ssg48_stock') +JAW_OPTIONS = {'msg': ('solid', 'soft_tpu'), 'ssg48': ('solid', 'fin_ray')} + + +def select_model(family, end_effector='msg', jaws='default'): + if family == 'raft': + return 'raft.urdf' # one configuration: the leader arm with its trigger handle + if family in JAW_OPTIONS: + jaws = 'solid' if jaws == 'default' else jaws + if jaws not in JAW_OPTIONS[family]: + raise ValueError(f'{family}: jaws must be one of {JAW_OPTIONS[family]}') + return f'{family}_{jaws}.urdf' + if family not in ('par6', 'parol6'): + raise ValueError(f'Unknown product: {family}') + if end_effector not in ARM_TOOLS: + raise ValueError(f'end_effector must be one of {ARM_TOOLS}') + aliases = {'msg_tpu_glue': ('msg', 'soft_tpu'), 'ssg48_stock': ('ssg48', 'fin_ray')} + if end_effector in aliases: + tool, implied = aliases[end_effector] + if jaws not in ('default', implied): + raise ValueError(f'{end_effector} already selects {implied} jaws; remove the conflicting jaws argument') + end_effector, jaws = tool, implied + if end_effector in ('flange', 'pneumatic'): + if jaws != 'default': + raise ValueError(f'{end_effector} has no selectable jaws; omit the jaws argument') + profile = end_effector + else: + jaws = 'solid' if jaws == 'default' else jaws + if jaws not in JAW_OPTIONS[end_effector]: + raise ValueError(f'{end_effector}: jaws must be one of {JAW_OPTIONS[end_effector]}') + profile = {('msg', 'solid'): 'msg', ('msg', 'soft_tpu'): 'msg_tpu_glue', + ('ssg48', 'solid'): 'ssg48', ('ssg48', 'fin_ray'): 'ssg48_stock'}[end_effector, jaws] + return f'{family}_{profile}.urdf' + + +def separate_rviz_visuals(root): + """Work around RViz applying the first visual material to an entire link. + + Move additional visuals to massless fixed children. Physical links, their + inertials/collisions and all existing joint/frame transforms stay intact. + """ + existing = {element.get('name') for element in root if element.tag in ('link', 'joint')} + for link in root.findall('link'): + visuals = link.findall('visual') + for index, visual in enumerate(visuals): + material = visual.find('material') + if material is not None and not material.get('name'): + material.set('name', f'{link.get("name")}_rviz_material_{index}') + for index, visual in enumerate(visuals[1:], 1): + name = f'{link.get("name")}_rviz_visual_{index}' + if name in existing or name+'_fixed' in existing: + raise ValueError(f'RViz visual frame conflicts with an existing name: {name}') + existing.update([name, name+'_fixed']) + child = ET.SubElement(root, 'link', name=name) + link.remove(visual); child.append(visual) + joint = ET.SubElement(root, 'joint', name=name+'_fixed', type='fixed') + ET.SubElement(joint, 'parent', link=link.get('name')) + ET.SubElement(joint, 'child', link=name) + ET.SubElement(joint, 'origin', xyz='0 0 0', rpy='0 0 0') + + +def load_description(path, package, rviz_materials=False): + """Resolve asset URIs in memory; never rewrite model or physical data.""" + path = Path(path) + root = ET.parse(path).getroot() + for entry in [*root.findall('.//mesh'), *root.findall('.//material/texture')]: + filename = entry.get('filename') + if not filename or '://' in filename: + continue + relative = PurePosixPath(filename.replace('\\', '/')) + target = (path.parent/relative).resolve() + if relative.is_absolute() or not target.is_relative_to(path.parent.resolve()): + raise ValueError(f'Asset escapes the installed package: {filename}') + if not target.is_file(): + raise FileNotFoundError(f'Missing model asset: {target}') + entry.set('filename', f'package://{package}/{relative.as_posix()}') + if rviz_materials: + separate_rviz_visuals(root) + return ET.tostring(root, encoding='unicode') diff --git a/urdf/raft_description/scripts/ros_urdf.py b/urdf/raft_description/scripts/ros_urdf.py new file mode 100644 index 0000000..abc5a92 --- /dev/null +++ b/urdf/raft_description/scripts/ros_urdf.py @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Print a portable URDF with ROS package:// mesh URIs; no ROS import needed.""" + +from pathlib import Path +import argparse +import sys +import xml.etree.ElementTree as ET + + +def convert(path, package): + tree = ET.parse(path) + for mesh in tree.findall('.//mesh'): + filename = mesh.get('filename') + if '://' not in filename: + mesh.set('filename', f'package://{package}/{Path(filename).as_posix()}') + return ET.tostring(tree.getroot(), encoding='unicode') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('urdf', type=Path) + parser.add_argument('--package', default='parol6_description') + args = parser.parse_args() + sys.stdout.write(convert(args.urdf, args.package)+'\n') diff --git a/urdf/raft_description/sim2bot.robot.json b/urdf/raft_description/sim2bot.robot.json new file mode 100644 index 0000000..ccf0477 --- /dev/null +++ b/urdf/raft_description/sim2bot.robot.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "name": "Source Robotics RAFT URDF", + "manufacturer": "Source Robotics", + "category": "arm", + "license": "CC-BY-4.0", + "sourceUrl": "https://github.com/Source-Robotics/RAFT-teleoperation-arm", + "packageVersion": "2.19.0", + "entry": "raft.urdf", + "dof": 6, + "rootFrame": "base_link", + "tcpSites": [ + "tcp" + ], + "armActuators": [ + "base_joint", + "shoulder_joint", + "elbow_joint", + "wrist_1_joint", + "wrist_2_joint", + "wrist_3_joint" + ], + "gripperActuators": [ + "trigger_joint" + ], + "endEffectorBodies": [ + "wrist_3_link" + ], + "preview": "previews/raft.png", + "variants": [ + { + "id": "leader", + "label": "RAFT leader arm", + "entry": "raft.urdf", + "tcpSites": [ + "tcp" + ], + "endEffectorBodies": [ + "wrist_3_link" + ], + "gripperActuators": [ + "trigger_joint" + ], + "preview": "previews/raft.png" + } + ], + "codeLicense": "Apache-2.0" +} diff --git a/urdf/ssg48_description/docs/COLORS.md b/urdf/ssg48_description/docs/COLORS.md index 2d83510..3f1f4f4 100644 --- a/urdf/ssg48_description/docs/COLORS.md +++ b/urdf/ssg48_description/docs/COLORS.md @@ -2,9 +2,9 @@ [Back to the repository](../README.md) -**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/appearance/colors.json).** -The build applies it to PAR6, PAROL6, standalone MSG and standalone SSG48, in -both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. +**Edit one file: [`source/appearance/colors.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/appearance/colors.json).** +The build applies it to PAR6, PAROL6, standalone MSG, standalone SSG48 and RAFT, +in both URDF and MuJoCo, including every tool/jaw option and the grasp fixtures. The starting palette keeps the existing appearance. ## Quick example @@ -29,7 +29,7 @@ uses the same model, pose, lighting and camera; only its example colors change. The shared build applies the same settings to URDF. Examples are independent: start from the default palette for each one, then change only the listed entries. -After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/docs/maintenance/BUILDING.md), run this from the +After installing the [build dependencies](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/docs/maintenance/BUILDING.md), run this from the repository root: ```bash @@ -38,7 +38,7 @@ python tools/build.py --models-only --render This uses the existing CAD meshes, applies the palette, rebakes the badge, refreshes the dimensioned review snapshots, validates the models and exports -all eight packages to `dist/`. `--render` needs working OpenGL. Normal builds +all ten packages to `dist/`. `--render` needs working OpenGL. Normal builds reuse the checked snapshots without OpenGL; refresh them after changing colors. If you also changed CAD/geometry, omit `--models-only`. @@ -57,7 +57,7 @@ arrays; replacing one with a HEX color is fine. | Key inside `colors` | Parts controlled | | --- | --- | | `structure` | Both robot housings, MSG/SSG48 bodies, flange and the PAR6 badge's surrounding cover | -| `motors` | J1/J2/J3 motor + gearbox and the MSG motor | +| `motors` | J1/J2/J3 motor + gearbox, the MSG motor and RAFT's seven servos | | `camera_lens` | MSG lens | | `msg_solid_jaws` | MSG Solid jaws, including the moving carriage geometry | | `msg_soft_holders` | MSG Soft TPU black holders and carriages | @@ -66,6 +66,10 @@ arrays; replacing one with a HEX color is fine. | `ssg48_fin_ray_jaws` | SSG48 Fin-ray jaws and carriage geometry | | `pneumatic_body` | Pneumatic gripper housing | | `pneumatic_jaws` | Both pneumatic fingers | +| `raft_structure` | RAFT's printed parts, except the trigger | +| `raft_trigger` | RAFT's printed trigger | +| `raft_horns` | RAFT's seven aluminium servo horns | +| `raft_fasteners` | RAFT's servo screws and its M3 stop and lid screws | A color applies to an existing visual partition. For example, a jaw and its carriage cannot get separate colors if they share one visual mesh. @@ -100,7 +104,8 @@ blue: ``` Supported keys are `j1_motor`, `j2_motor`, `j3_motor`, `msg_motor`, `msg_body`, -`ssg48_body`, `flange`, `par6_structure`, `parol6_structure` and `par6_panel`. +`ssg48_body`, `flange`, `par6_structure`, `parol6_structure`, `par6_panel` and +`raft_motors` (RAFT's servos, which otherwise follow `motors`). Remove an override to return that part to its common color. Tool colors stay the same whether the tool is standalone or mounted on either robot. @@ -117,8 +122,8 @@ channels and transparency are rejected before the build changes models. The same file has a `badge` section for `background`, `emblem`, `symbol`, `wordmark`, `robot_name`, `tagline`, `divider`. Change those colors and run the same build command. Badge size, resolution, placement and SVG shapes stay -in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.json) -and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.18.1/source/artwork/par6_side_badge.svg). +in [`source/artwork/par6_side_badge.json`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.json) +and [`par6_side_badge.svg`](https://github.com/Source-Robotics/robot_descriptions/blob/v2.19.0/source/artwork/par6_side_badge.svg). ### Example: graphite cover and teal logo diff --git a/urdf/ssg48_description/docs/ROS2.md b/urdf/ssg48_description/docs/ROS2.md index f1dd375..79efeec 100644 --- a/urdf/ssg48_description/docs/ROS2.md +++ b/urdf/ssg48_description/docs/ROS2.md @@ -2,7 +2,7 @@ [Back to the model instructions](../README.md) -The four URDF packages provide native `ament_cmake` installation and a ROS 2 +The five URDF packages provide native `ament_cmake` installation and a ROS 2 `display.launch.py` launcher. Choose a tool or jaw option, then view it in RViz and move its joints with sliders. Linux CI checks ROS 2 **Humble / Ubuntu 22.04** and **Jazzy / Ubuntu 24.04**, including installed assets, TF, mimic joints and @@ -55,7 +55,7 @@ colcon build --base-paths src/robot_descriptions/urdf source install/setup.bash ``` -This builds all four packages. Run colcon against the `urdf/` packages; there +This builds all five packages. Run colcon against the `urdf/` packages; there is no need to run `tools/build.py` to use the committed models. After opening a new terminal, source both `/opt/ros//setup.bash` and your workspace's `install/setup.bash` again. @@ -93,6 +93,7 @@ ros2 launch msg_description display.launch.py jaws:=solid ros2 launch msg_description display.launch.py jaws:=soft_tpu ros2 launch ssg48_description display.launch.py jaws:=solid ros2 launch ssg48_description display.launch.py jaws:=fin_ray +ros2 launch raft_description display.launch.py ``` Each launcher opens RViz and a joint-slider window. The two fingers retain diff --git a/urdf/ssg48_description/docs/colors.json b/urdf/ssg48_description/docs/colors.json index 6971ca3..09b8a15 100644 --- a/urdf/ssg48_description/docs/colors.json +++ b/urdf/ssg48_description/docs/colors.json @@ -10,7 +10,11 @@ "ssg48_solid_jaws": "#4C4C4C", "ssg48_fin_ray_jaws": [0.29804, 0.29804, 0.29804, 1], "pneumatic_body": [0.72, 0.75, 0.8, 1], - "pneumatic_jaws": [0.12, 0.13, 0.15, 1] + "pneumatic_jaws": [0.12, 0.13, 0.15, 1], + "raft_structure": [0.3, 0.31, 0.335, 1], + "raft_trigger": [0.3, 0.31, 0.335, 1], + "raft_horns": [0.74, 0.76, 0.79, 1], + "raft_fasteners": [0.42, 0.43, 0.46, 1] }, "overrides": {}, "badge": { diff --git a/urdf/ssg48_description/launch/display.launch.py b/urdf/ssg48_description/launch/display.launch.py index f3aa689..d961c49 100644 --- a/urdf/ssg48_description/launch/display.launch.py +++ b/urdf/ssg48_description/launch/display.launch.py @@ -20,7 +20,7 @@ def launch_nodes(context): helper = importlib.util.module_from_spec(spec); spec.loader.exec_module(helper) value = lambda name: LaunchConfiguration(name).perform(context) end_effector = value('end_effector') if FAMILY in ('par6', 'parol6') else 'msg' - entry = helper.select_model(FAMILY, end_effector, value('jaws')) + entry = helper.select_model(FAMILY, end_effector, value('jaws') if FAMILY != 'raft' else 'default') description = ParameterValue(helper.load_description(share/entry, PACKAGE, rviz_materials=value('rviz_materials') == 'true'), value_type=str) parameters = {'robot_description': description, 'use_sim_time': value('use_sim_time') == 'true'} @@ -41,7 +41,7 @@ def launch_nodes(context): def generate_launch_description(): share = Path(get_package_share_directory(PACKAGE)) arm = FAMILY in ('par6', 'parol6') - options = [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', + options = [] if FAMILY == 'raft' else [DeclareLaunchArgument('jaws', default_value='default' if arm else 'solid', choices=['default', 'solid', 'soft_tpu', 'fin_ray'] if arm else ['solid', 'soft_tpu' if FAMILY == 'msg' else 'fin_ray'])] if arm: diff --git a/urdf/ssg48_description/package.xml b/urdf/ssg48_description/package.xml index 30331ed..e56645f 100644 --- a/urdf/ssg48_description/package.xml +++ b/urdf/ssg48_description/package.xml @@ -1,7 +1,7 @@ ssg48_description - 2.18.1 + 2.19.0 Standalone Source Robotics SSG48 gripper with interchangeable jaws. Source Robotics CC-BY-4.0 diff --git a/urdf/ssg48_description/scripts/robot_description.py b/urdf/ssg48_description/scripts/robot_description.py index ec8951b..715b8d5 100644 --- a/urdf/ssg48_description/scripts/robot_description.py +++ b/urdf/ssg48_description/scripts/robot_description.py @@ -8,6 +8,8 @@ def select_model(family, end_effector='msg', jaws='default'): + if family == 'raft': + return 'raft.urdf' # one configuration: the leader arm with its trigger handle if family in JAW_OPTIONS: jaws = 'solid' if jaws == 'default' else jaws if jaws not in JAW_OPTIONS[family]: diff --git a/urdf/ssg48_description/sim2bot.robot.json b/urdf/ssg48_description/sim2bot.robot.json index a6d6b44..113cddd 100644 --- a/urdf/ssg48_description/sim2bot.robot.json +++ b/urdf/ssg48_description/sim2bot.robot.json @@ -5,7 +5,7 @@ "category": "gripper", "license": "CC-BY-4.0", "sourceUrl": "https://github.com/Source-Robotics/robot_descriptions", - "packageVersion": "2.18.1", + "packageVersion": "2.19.0", "entry": "ssg48_solid.urdf", "dof": 1, "rootFrame": "flange",