From d0be433b7b2c93fffe94a9d79e8a742bf30e01b7 Mon Sep 17 00:00:00 2001 From: Charles Madjeri <80175305+charlesmadjeri@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:18:53 +0200 Subject: [PATCH 1/2] chore(inmoov_urdf): migrate from ROS 2 Humble to Jazzy --- .github/workflows/ci.yml | 41 ++++++++++++++------------- README.md | 6 ++-- docs/DEVELOPER.md | 6 ++-- docs/hardware_mapping.md | 2 +- launch/gazebo.launch.py | 6 ++-- launch/joint_preview.launch.py | 2 +- package.xml | 2 +- scripts/autocalibrate_joint_limits.py | 6 ++-- scripts/requirements.txt | 2 +- test/xacro_helper.py | 2 +- 10 files changed, 38 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2458800..5f47c8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,10 +10,10 @@ on: jobs: build-and-test: - name: Build and test (thais_urdf) + name: Build and test (inmoov_urdf) runs-on: ubuntu-latest container: - image: osrf/ros:humble-desktop + image: osrf/ros:jazzy-desktop # Colcon tree must live next to the checkout, not under it: copying $GITHUB_WORKSPACE # into workspace/ws/... would copy a directory into its own subtree ("cannot copy into itself"). # COLCON_WS must be canonical (no ..): upload-artifact@v4 rejects paths with relative segments. @@ -36,7 +36,8 @@ jobs: build-essential \ cmake \ git \ - gz-harmonic + ros-jazzy-ros-gz \ + ros-jazzy-gz-ros2-control # Do not rm -rf /var/lib/apt/lists/* here; rosdep uses apt next and needs indexes. rosdep init || true rosdep update @@ -48,7 +49,7 @@ jobs: run: | set -e mkdir -p "${COLCON_WS}/src" - cp -a "${GITHUB_WORKSPACE}" "${COLCON_WS}/src/thais_urdf" + cp -a "${GITHUB_WORKSPACE}" "${COLCON_WS}/src/inmoov_urdf" - name: rosdep install working-directory: ${{ env.COLCON_WS }} @@ -56,26 +57,26 @@ jobs: run: | set -e apt-get update - source /opt/ros/humble/setup.bash + source /opt/ros/jazzy/setup.bash rosdep install --from-paths src --ignore-src -r -y - name: colcon build working-directory: ${{ env.COLCON_WS }} shell: bash run: | - source /opt/ros/humble/setup.bash + source /opt/ros/jazzy/setup.bash colcon build --symlink-install \ - --packages-select thais_urdf \ + --packages-select inmoov_urdf \ --cmake-args -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON - name: colcon test working-directory: ${{ env.COLCON_WS }} shell: bash run: | - source /opt/ros/humble/setup.bash + source /opt/ros/jazzy/setup.bash source install/setup.bash colcon test \ - --packages-select thais_urdf \ + --packages-select inmoov_urdf \ --event-handlers console_direct+ \ --pytest-args "--tb=short -vv" @@ -83,7 +84,7 @@ jobs: working-directory: ${{ env.COLCON_WS }} shell: bash run: | - source /opt/ros/humble/setup.bash + source /opt/ros/jazzy/setup.bash source install/setup.bash colcon test-result --verbose @@ -93,14 +94,14 @@ jobs: if: success() run: | set -e - source /opt/ros/humble/setup.bash + source /opt/ros/jazzy/setup.bash source install/setup.bash mkdir -p build/coverage_reports - python3 -m pytest src/thais_urdf/test/ \ - --cov=src/thais_urdf/launch \ - --cov=src/thais_urdf/test \ - --cov-report=xml:build/coverage_reports/thais_urdf.xml \ - --cov-report=html:build/coverage_reports/html_thais_urdf \ + python3 -m pytest src/inmoov_urdf/test/ \ + --cov=src/inmoov_urdf/launch \ + --cov=src/inmoov_urdf/test \ + --cov-report=xml:build/coverage_reports/inmoov_urdf.xml \ + --cov-report=html:build/coverage_reports/html_inmoov_urdf \ --cov-report=term-missing \ --cov-branch @@ -109,15 +110,15 @@ jobs: if: success() with: token: ${{ secrets.CODECOV_TOKEN }} - files: ${{ env.COLCON_WS }}/build/coverage_reports/thais_urdf.xml - flags: thais_urdf - name: thais_urdf + files: ${{ env.COLCON_WS }}/build/coverage_reports/inmoov_urdf.xml + flags: inmoov_urdf + name: inmoov_urdf fail_ci_if_error: false - name: Upload coverage artifacts (XML + HTML) uses: actions/upload-artifact@v4 if: always() with: - name: coverage-thais_urdf + name: coverage-inmoov_urdf path: ${{ env.COLCON_WS }}/build/coverage_reports/ retention-days: 14 diff --git a/README.md b/README.md index 2176172..1d8af42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # inmoov_urdf -ROS 2 **Humble** package with the **InMoov-derived** robot description used by Lucy: **URDF/xacro**, **DAE meshes**, **ros2_control** blocks, **Gazebo (gz-sim) physics**, an **RViz** layout, and **launch files** for ros2_control + RViz / Gazebo. +ROS 2 **Jazzy** (Ubuntu 24.04) package with the **InMoov-derived** robot description used by Lucy: **URDF/xacro**, **DAE meshes**, **ros2_control** blocks, **Gazebo (gz-sim) physics**, an **RViz** layout, and **launch files** for ros2_control + RViz / Gazebo. The web control panel (rosbridge + `/config/*` services) is **not** started from here — use `lucy_bringup` (`lucy.launch.py`) or `web_ros_api.launch.py` from `lucy_ros_packages`. @@ -58,7 +58,7 @@ inmoov_urdf/ ## Requirements -ROS 2 Humble plus: `robot_state_publisher`, `controller_manager`, `rviz2`, `ros_gz_sim`, `ros_gz_bridge`, `gz_ros2_control`, `launch_ros`, `lucy_ros2_control`. The auto-cal script also needs **PyBullet** (already in the `lucy_ros2:humble` image, `pip` otherwise). +ROS 2 Jazzy (Ubuntu 24.04) plus: `robot_state_publisher`, `controller_manager`, `rviz2`, `ros_gz_sim`, `ros_gz_bridge`, `gz_ros2_control`, `launch_ros`, `lucy_ros2_control`. The auto-cal script also needs **PyBullet** (already in the `lucy_ros2:jazzy` image, `pip` otherwise). ```bash rosdep install --from-paths src --ignore-src -r -y @@ -69,7 +69,7 @@ For the control panel, also build `lucy_bringup` + `lucy_config_pipeline` (pulle ## Build ```bash -source /opt/ros/humble/setup.bash +source /opt/ros/jazzy/setup.bash cd lucy_ws colcon build --symlink-install --packages-select inmoov_urdf lucy_ros2_control source install/setup.bash diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index ba5214f..5209675 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -1,6 +1,6 @@ # Developer guide — `inmoov_urdf` -ROS 2 **Humble**. For contributors who change URDF/xacro, meshes, RViz config, hardware YAML, or launch files. Bringup, hardware plugin, and cameras live in **`lucy_ros_packages`** ([Sentience-Robotics/lucy_ros_packages](https://github.com/Sentience-Robotics/lucy_ros_packages) → `docs/DEVELOPER.md`). +ROS 2 **Jazzy** (Ubuntu 24.04). For contributors who change URDF/xacro, meshes, RViz config, hardware YAML, or launch files. Bringup, hardware plugin, and cameras live in **`lucy_ros_packages`** ([Sentience-Robotics/lucy_ros_packages](https://github.com/Sentience-Robotics/lucy_ros_packages) → `docs/DEVELOPER.md`). --- @@ -228,7 +228,7 @@ Flags: - `--step-deg`, `--margin-deg` — sweep granularity / safety margin. - `--apply` — write the limits back to `robot_description.urdf.xacro`. -PyBullet ships in the `lucy_ros2:humble` image via `Dockerfile.humble`; for host-side runs, see `scripts/requirements.txt`. +PyBullet ships in the `lucy_ros2:jazzy` image via `Dockerfile.jazzy`; for host-side runs, see `scripts/requirements.txt`. Treat the result as an **upper bound on the kinematic envelope**, not the real end-stop. Override by hand for mechanical stops the URDF can't model (cable tension, gear stops), important poses outside the per-axis sweep from neutral, and finger meshes that overlap at the bind pose. @@ -267,7 +267,7 @@ This restarts `robot_state_publisher` + `ros2_control` with the new URDF. **Cave | `scripts/autocalibrate_joint_limits.py` | Self-collision-bound joint-limit sweep in PyBullet. Optional `--apply` to write back. | `--apply` overwrites previous auto-cal values; manual values can be re-applied on top. | | `scripts/scale_xacro_origins.py` | One-shot maintenance: wrap raw `xyz` / primitive sizes with `${model_scale * N}`. | No — run once per migration; check the diff carefully. | -`scripts/requirements.txt` is for host-side PyBullet runs only; CI / Docker image installs it via `Dockerfile.humble`. +`scripts/requirements.txt` is for host-side PyBullet runs only; CI / Docker image installs it via `Dockerfile.jazzy`. --- diff --git a/docs/hardware_mapping.md b/docs/hardware_mapping.md index 296f0cb..d563a62 100644 --- a/docs/hardware_mapping.md +++ b/docs/hardware_mapping.md @@ -72,7 +72,7 @@ With **SIMULATION ONLY** enabled in the activate workflow, `lucy_config_generato - One `` block per board (`gz_ros2_control/GazeboSimSystem` when `use_gazebo_sim:=true`, otherwise `lucy_ros2_control/LucySystemHardware` with `publish_actuators:=false` for RViz/mock). URDF `min`/`max` on the command interface are enforced by **LucySystemHardware** only (real + mock), not by the stock Gazebo plugin. - `controllers.yaml` with `joint_state_broadcaster` + a single `lucy_sim_controller` listing every actuator `urdf_joint`. -After generation, `lucy_config_pipeline` calls **`/lucy_control/restart`** so the running stack reloads without a full `lucy.launch.py` restart. Structural joint changes still require that restart (Humble does not hot-swap URDF hardware topology). +After generation, `lucy_config_pipeline` calls **`/lucy_control/restart`** so the running stack reloads without a full `lucy.launch.py` restart. Structural joint changes still require that restart (ROS 2 does not hot-swap URDF hardware topology). ## Editing diff --git a/launch/gazebo.launch.py b/launch/gazebo.launch.py index 165204e..0024b06 100644 --- a/launch/gazebo.launch.py +++ b/launch/gazebo.launch.py @@ -51,7 +51,7 @@ def _gz_ros2_control_plugin_path(): share = get_package_share_directory("gz_ros2_control") return os.path.join(os.path.dirname(share), "lib") + os.pathsep + plugin_path except Exception: - return "/opt/ros/humble/lib" + os.pathsep + plugin_path + return "/opt/ros/jazzy/lib" + os.pathsep + plugin_path _DEFAULT_GENERATED_FILES = { @@ -129,7 +129,7 @@ def _sim_camera_topics(pkg_share: str) -> list[tuple[str, str]]: def generate_launch_description(): - ros_distro = os.environ.get("ROS_DISTRO", "humble").lower() + ros_distro = os.environ.get("ROS_DISTRO", "jazzy").lower() pkg_share = get_package_share_directory("inmoov_urdf") default_base = os.path.join(pkg_share, "description") generated = _active_generated_files(pkg_share) @@ -247,7 +247,7 @@ def _camera_compressor(topic: str, compressed_topic: str) -> Node: gz_sim_share = get_package_share_directory("ros_gz_sim") gz_sim_launch_path = os.path.join(gz_sim_share, "launch", "gz_sim.launch.py") except Exception: - gz_sim_launch_path = "/opt/ros/humble/share/ros_gz_sim/launch/gz_sim.launch.py" + gz_sim_launch_path = "/opt/ros/jazzy/share/ros_gz_sim/launch/gz_sim.launch.py" default_world = os.path.join(pkg_share, "worlds", "default.sdf") # When headless: server-only (-s) with EGL rendering (--headless-rendering) # so OGRE2 still renders camera sensors without an X display. Otherwise: diff --git a/launch/joint_preview.launch.py b/launch/joint_preview.launch.py index 3a1abf5..4403e10 100644 --- a/launch/joint_preview.launch.py +++ b/launch/joint_preview.launch.py @@ -92,7 +92,7 @@ def generate_launch_description(): base_path = LaunchConfiguration("base_path") controllers_yaml = LaunchConfiguration("controllers_yaml") - # Force value_type=str so ROS 2 launch (Humble) does not try to YAML-parse + # Force value_type=str so ROS 2 launch does not try to YAML-parse # the xacro output. The URDF starts with ``, which the YAML # loader rejects with "Unable to parse the value of parameter robot_description". robot_description = ParameterValue( diff --git a/package.xml b/package.xml index 6f71c4b..6301943 100644 --- a/package.xml +++ b/package.xml @@ -23,7 +23,7 @@ controller_manager lucy_control_supervisor - + python3-yaml ament_lint_auto diff --git a/scripts/autocalibrate_joint_limits.py b/scripts/autocalibrate_joint_limits.py index 52adea4..e26ca63 100644 --- a/scripts/autocalibrate_joint_limits.py +++ b/scripts/autocalibrate_joint_limits.py @@ -37,7 +37,7 @@ Usage ----- - # pybullet ships with the lucy_ros2:humble image (Dockerfile: pip install pybullet); + # pybullet ships with the lucy_ros2:jazzy image (Dockerfile.jazzy: pip install pybullet); # host-side: pip install --user -r scripts/requirements.txt python3 scripts/autocalibrate_joint_limits.py # headless dry run python3 scripts/autocalibrate_joint_limits.py --apply # write back @@ -96,7 +96,7 @@ def expand_urdf() -> str: elif shutil.which("ros2"): cmd = ["ros2", "run", "xacro", "xacro"] else: - sys.exit("xacro not on PATH (source ROS Humble)") + sys.exit("xacro not on PATH (source ROS 2 Jazzy)") cmd += [ str(URDF_XACRO), f"base_path:={BASE_PATH}", @@ -178,7 +178,7 @@ def setup(self, p, body, name_to_idx, cid) -> None: except ImportError: sys.exit( "[--view rviz] rclpy/sensor_msgs not importable. " - "Source ROS Humble first (e.g. `source install/setup.bash`)." + "Source ROS 2 Jazzy first (e.g. `source install/setup.bash`)." ) if not rclpy.ok(): rclpy.init() diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 7235baa..0074c34 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,3 +1,3 @@ # Optional: URDF joint-limit auto-calibration (scripts/autocalibrate_joint_limits.py). -# Installed in the lucy_ros2:humble image via Dockerfile.humble (pip). +# Installed in the lucy_ros2:jazzy image via Dockerfile.jazzy (pip). pybullet>=3.2.0 diff --git a/test/xacro_helper.py b/test/xacro_helper.py index 770ec6e..d628fe8 100644 --- a/test/xacro_helper.py +++ b/test/xacro_helper.py @@ -35,5 +35,5 @@ def run_xacro(args: Sequence[str], *, timeout: int = 120) -> subprocess.Complete elif shutil.which("ros2"): cmd = ["ros2", "run", "xacro", "xacro", *args] else: - pytest.skip("xacro not on PATH (run inside lucy_ros2:humble image)") + pytest.skip("xacro not on PATH (run inside lucy_ros2:jazzy image)") return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, check=False) From e68b501ae3b4a9b9a2204229f94aafa23f81cf39 Mon Sep 17 00:00:00 2001 From: Charles Madjeri <80175305+charlesmadjeri@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:54:07 +0200 Subject: [PATCH 2/2] fix(lint): fix remaining flake8 errors in launch files --- launch/gazebo.launch.py | 1 - launch/rviz.launch.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/launch/gazebo.launch.py b/launch/gazebo.launch.py index 0024b06..233c641 100644 --- a/launch/gazebo.launch.py +++ b/launch/gazebo.launch.py @@ -43,7 +43,6 @@ import yaml - def _gz_ros2_control_plugin_path(): pkg_share = get_package_prefix("inmoov_urdf") plugin_path = os.path.join(pkg_share, "lib", "mock_sensor") diff --git a/launch/rviz.launch.py b/launch/rviz.launch.py index 90637ed..b5ddd2b 100644 --- a/launch/rviz.launch.py +++ b/launch/rviz.launch.py @@ -19,7 +19,7 @@ from launch.actions import ( DeclareLaunchArgument, ) -from launch.substitutions import PathJoinSubstitution, LaunchConfiguration +from launch.substitutions import LaunchConfiguration, PathJoinSubstitution from launch_ros.actions import Node from launch_ros.substitutions import FindPackageShare