May 21, 2026
SpatialDDS — open spatial computing protocol with ROS 2 bridge

Hi all,

I’m from Open AR Cloud (openarcloud.org), a volunteer-run non-profit promoting open and interoperable spatial computing. Our initial focus was around world-scale augmented reality and key enablers like spatial discovery and visual positioning.

More recently, we’ve created SpatialDDS (spatialdds.org), a protocol built on DDS for sharing spatial data (detections, poses, maps, zones) across domains — robotics, autonomous vehicles, IoT, digital twins, 6G sensing. It defines typed messages (Detection3D, FramedPose, GeoPose, MapAlignment, etc.) plus spatial discovery and multi-operator namespacing. As an example, it would allow multiple autonomous vehicle fleets to share detections and planned trajectories at an intersection, fused into a model no single fleet could build alone.

There’s a ROS 2 bridge (sensor_msgs, vision_msgs, tf2 frame mapping), plus bridges for MCAP, MQTT, and WebSocket. The multi-operator fusion demo runs with docker compose up.

Spec: spatialdds.org Demo: github.com/OpenArCloud/SpatialDDS-demo

We’d love to get feedback from the ROS community!

Cheers,

James

1 post - 1 participant

Read full topic

by jjackson on May 21, 2026 06:49 AM

[Discussion] Why Vision-Guided Robots Still Fail in Production Even When Detection Works

Hi, guys
I developing diagnostic programs around whether the command stream, feedback stream, timing window, and physical responses in ROS remain consistent, with a lightweight experimental software package named ros2_kinematic_guard . I have identified recurring issues in the vision‑guided assembly system:

The robot “sees correctly” — but the executed grasp/pose slowly diverges from the expected state over time.

This seems especially common in setups involving:

  • RealSense D435i
  • TF-based grasp pipelines
  • MoveIt servoing
  • RGB-D pose estimation
  • asynchronous ROS2 nodes

Typical symptoms observed:

  • hand-eye calibration gradually becoming inconsistent after thermal drift
  • grasp points oscillating despite stable detections
  • TF trees remaining valid while pose execution becomes unstable
  • frame timestamp mismatch causing “see correctly, grasp incorrectly”
  • retry/relocalization logic amplifying small pose residuals

Interestingly, most systems still “look healthy” from standard monitoring:

  • bbox/confidence remain high
  • TF graph exists
  • topics publish normally
  • planners succeed

…but the physical execution path drifts.

Therefore, I intend to adopt a lightweight residual monitoring method from ros2_kinematic_guard , which focuses on three indicators:

1. Pose Residual Drift

Monitoring divergence between:

expected_pose(t)
vs
executed_pose(t)

over time.

Especially useful for detecting thermal/mechanical calibration drift.

2. Temporal Coherence Residual

Tracking timestamp alignment between:

  • image frame
  • TF transform
  • depth frame
  • grasp pose generation

to detect async ordering issues.

3. Action Stability Residual

Detecting oscillation/jitter in generated grasp points or servo actions across adjacent frames.

This catches cases where the vision system is technically “working” but unstable under lighting/reflection disturbances.

The key idea:

Instead of asking:

“Did perception succeed?”

we ask:

“Did the system state remain converged throughout the perception → planning → action path?”

Curious if others in production robotics are already monitoring these kinds of residuals.

Especially interested in:

  • vision-guided assembly
  • dynamic calibration compensation
  • ROS2 observability
  • VLA/VLM action stability
  • production deployment diagnostics

1 post - 1 participant

Read full topic

by zc_Liu on May 21, 2026 01:09 AM

May 20, 2026
How to Build a Robot Arm RL Grasping System in Isaac Lab | NERO Arm

How to Build a Robot Arm RL Grasping System in Isaac Lab | NERO Arm

This project presents a reinforcement learning workflow for Embodied AI manipulation built on the Nero robotic arm, SO-ARM101, and NVIDIA Isaac Lab. It establishes a simulation-driven framework for training and evaluating robotic manipulation policies, with a focus on preparing the system for simulation-to-real transfer.

Project Summary

Tech Stack

  • RL training pipeline
  • policy validation process
  • robotic manipulation task configuration
  • simulation-to-real transfer preparation

Key Specifications

  • Programming Language: Python 3.8+
  • Hardware: Nero Robotic Arm https://global.agilex.ai/products/nero
  • Base Framework: SO-ARM101 https://github.com/MuammerBay/isaac_so_arm101
  • Simulation Platform: NVIDIA Isaac Lab
  • Open-source implementation:https://github.com/agilexrobotics/Agilex-College/tree/master/isaac_sim/agx_arm_IsaacLab

1. Project Setup and Environment Preparation

1.1 Install Isaac Lab

Follow the official guide to install Isaac Lab:
:backhand_index_pointing_right: Isaac Lab Pip Installation Guide

We use the pip-based installation method (recommended).

Environment:

  • Conda virtual environment
  • Python development environment
  • NVIDIA Isaac Lab
  • Nero robotic arm project dependencies

1.2 Install the uv Package Manager

This project uses uv as its Python package manager.

As a fast, next-generation tool, uv delivers:

  • Faster package installation
  • Efficient dependency resolution
  • Built-in virtual environment management

Compared to traditional tools like pip, uv streamlines setup and reduces environment issues in Python-based robotics and embodied AI workflows.

First, install uv with a single command:

curl -LsSf https://astral.sh/uv/install.sh | sh

After installation, restart your terminal or run the following command to activate the uv environment:

source $HOME/.cargo/env

1.3 Clone the Repository and Install Dependencies

Next, clone the project repository, enter the project directory, and use uv to install all required dependencies with one command:

git clone https://github.com/smalleha/isaac_so_arm101.git
cd isaac_so_arm101
uv sync

uv will automatically create a virtual environment and install all necessary dependency packages. The entire process usually takes only a few minutes and is significantly faster than traditional pip-based installation workflows.


2. Environment Validation

To validate the setup for tasks, we first verify that the required simulation environments for the Nero robotic arm and Piper are properly registered:

uv run list_envs

The expected output should include Isaac-Nero-Reach-v0 and Isaac-Piper-Reach-v0, confirming that the environments have been installed successfully.

Next, run a simulation test with a zero-action agent to validate environment execution and ensure the robotic control pipeline works as expected:

# Test the Piper environment with a zero-action command
uv run zero_agent --task Isaac-SO-ARM100-Reach-v0

If the simulation window launches and the robotic arm behaves as intended, the environment is confirmed to be ready.


3. Project File Structure

isaac_so_arm101/
├── CITATION.cff                # Citation metadata for academic referencing
├── CONTRIBUTING.md             # Contribution guidelines (PR process, standards)
├── CONTRIBUTORS.md             # List of project contributors
├── LICENSE                     # BSD-3-Clause open-source license
├── README.md                   # Main project documentation (setup, tasks, usage)
├── pyproject.toml              # Python project metadata (dependencies, build config)
├── uv.lock                     # Dependency lockfile for reproducible environments
└── src/
    └── isaac_so_arm101/
        ├── __init__.py          # Python package initialization
        ├── robots/              # Robot models: SO-ARM100/101 simulation configs
        ├── scripts/             # Executable scripts: training, testing, playback
        ├── tasks/               # RL task definitions (reach, lift)
        └── ui_extension_example.py  # Omniverse UI extension example

This directory structure provides a clear overview of the project organization, making it easy to extend with new use cases such as Nero robotic arm example.


4. Download the URDF Model

This project uses the Nero URDF model from the agx_arm_urdf repository. After cloning the repository,
copy the nero directory into the robots folder of the isaac_so_arm101 project:

git clone https://github.com/agilexrobotics/agx_arm_urdf.git
cd agx_arm_urdf/
cp -r nero/ isaac_so_arm101/robots

Once the model has been copied, modify nero_description.urdf to make it compatible with Isaac Lab. Since the original URDF uses ROS-style package paths, these references must be converted to relative paths so that the link and mesh files can be correctly resolved. The base_link configuration is shown below as an example.

Before Edit

    <link name="base_link">
        <inertial>
            <origin xyz="-0.00319465997 -0.00005467608 0.04321758463" rpy="0 0 0"/>
            <mass value="1.06458435"/>
            <inertia ixx="0.00102659855152" ixy="0.00000186219753" ixz="-0.00000295298037" iyy="0.00114399299508" iyz="-0.00000078763492" izz="0.00090872933022"/>
        </inertial>
        <visual>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <mesh filename="package://agx_arm_description/agx_arm_urdf/nero/meshes/dae/base_link.dae"/>
            </geometry>
        </visual>
        <collision>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <mesh filename="package://agx_arm_description/agx_arm_urdf/nero/meshes/base_link.stl"/>
            </geometry>
        </collision>
    </link>

After Edit

    <link name="base_link">
        <inertial>
            <origin xyz="-0.00319465997 -0.00005467608 0.04321758463" rpy="0 0 0"/>
            <mass value="1.06458435"/>
            <inertia ixx="0.00102659855152" ixy="0.00000186219753" ixz="-0.00000295298037" iyy="0.00114399299508" iyz="-0.00000078763492" izz="0.00090872933022"/>
        </inertial>
        <visual>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <mesh filename="../meshes/dae/base_link.dae"/>
            </geometry>
        </visual>
        <collision>
            <origin xyz="0 0 0" rpy="0 0 0"/>
            <geometry>
                <mesh filename="../meshes/base_link.stl"/>
            </geometry>
        </collision>
    </link>

5.Configuring Isaac Lab Files

Step 1.Importing the URDF Model

After modifying the URDF file, you need to write a Python script to import the URDF model and configure the robotic arm’s motor properties, including stiffness, damping, and other relevant parameters.

This script is typically placed at:
src/isaac_so_arm101/robots/nero/nero.py

The content of this file is shown below:

from pathlib import Path

import isaaclab.sim as sim_utils
from isaaclab.actuators import ImplicitActuatorCfg
from isaaclab.assets.articulation import ArticulationCfg

TEMPLATE_ASSETS_DATA_DIR = Path(__file__).resolve().parent

##
# Configuration
##

NERO_CFG = ArticulationCfg(
    spawn=sim_utils.UrdfFileCfg(
        fix_base=True,
        replace_cylinders_with_capsules=True,
        asset_path=f"{TEMPLATE_ASSETS_DATA_DIR}/urdf/nero_gripper.urdf",
        activate_contact_sensors=False,  # Disable contact sensors until capsule collision implementation is complete
        rigid_props=sim_utils.RigidBodyPropertiesCfg(
            disable_gravity=False,
            max_depenetration_velocity=5.0,
        ),
        articulation_props=sim_utils.ArticulationRootPropertiesCfg(
            enabled_self_collisions=True,
            solver_position_iteration_count=8,
            solver_velocity_iteration_count=0,
        ),
        joint_drive=sim_utils.UrdfConverterCfg.JointDriveCfg(
            gains=sim_utils.UrdfConverterCfg.JointDriveCfg.PDGainsCfg(stiffness=0, damping=0)
        ),
    ),
    init_state=ArticulationCfg.InitialStateCfg(
        rot=(1.0, 0.0, 0.0, 0.0),
        joint_pos={
            "joint1": 0.0,
            "joint2": 0.0,
            "joint3": 0.0,
            "joint4": 2.0,
            "joint5": 0.0,
            "joint6": 0.0,
            "joint7": 0.0,
            "gripper_joint1": 0.05,
            "gripper_joint2": -0.05
        },
        # Set initial joint velocities to zero
        joint_vel={".*": 0.0},
    ),
    actuators={
        "arm": ImplicitActuatorCfg(
            joint_names_expr=["joint.*"],
            effort_limit=25.0,  # Moderate effort limit to prevent instantaneous impact shocks
            velocity_limit=1.5,
            
            # Stiffness: Optimized for the lightweight Piper robotic arm; prioritizes stability over maximum rigidity
            stiffness={
                "joint1": 200.0,
                "joint2": 170.0,
                "joint3": 120.0,
                "joint4": 80.0,
                "joint5": 50.0,
                "joint6": 20.0,
                "joint7": 10.0
            },
            
            # Damping: Critical damping strategy with ratio set to approximately 10%
            damping={
                "joint1": 100.0,
                "joint2": 60.0,
                "joint3": 70.0,
                "joint4": 24.0,
                "joint5": 20.0,
                "joint6": 10.0,
                "joint7": 5,
            },
        ),
        "gripper": ImplicitActuatorCfg(
            joint_names_expr=["gripper_joint1","gripper_joint2"],
            effort_limit_sim=22,  # Increased from 1.9 to 2.5 for stronger grip
            velocity_limit_sim=1.5,
            stiffness=800.0,  # Increased from 25.0 to 60.0 for more reliable closing
            damping=20.0,  # Increased from 10.0 to 20.0 for stability
        ),
    },

    soft_joint_pos_limit_factor=0.9,
)

Next, create an init.py file to initialize the directory as a Python module.

Step 2.Create Task Configuration Files

In the tasks/lift directory, create the following files:

  • nero_joint_pos_env_cfg.py
  • nero_lift_env_cfg.py

The nero_joint_pos_env_cfg.py file defines the environment configuration for joint position control, including the controllable joints, the robot end-effector link, and the basic task parameters.

# Copyright (c) 2024-2025, Muammer Bay (LycheeAI), Louis Le Lay
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

import isaaclab_tasks.manager_based.manipulation.lift.mdp as mdp
from isaaclab.assets import RigidObjectCfg

# from isaaclab.managers NotImplementedError
from isaaclab.sensors.frame_transformer.frame_transformer_cfg import (
    FrameTransformerCfg,
    OffsetCfg,
)
from isaaclab.sim.schemas.schemas_cfg import RigidBodyPropertiesCfg
from isaaclab.sim.spawners.from_files.from_files_cfg import UsdFileCfg
from isaaclab.utils import configclass
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR
from isaac_so_arm101.robots import SO_ARM100_CFG, SO_ARM101_CFG  # noqa: F401
# from isaac_so_arm101.tasks.lift.lift_env_cfg import LiftEnvCfg
from isaac_so_arm101.tasks.lift.nero_lift_env_cfg import LiftEnvCfg
from isaaclab.markers.config import FRAME_MARKER_CFG  # isort: skip
# from isaac_so_arm101.robots.piper_description.piper import PIPER_CFG
from isaac_so_arm101.robots.nero_description.nero import NERO_CFG
@configclass
class NeroLiftCubeEnvCfg(LiftEnvCfg):
    def __post_init__(self):
        # post init of parent
        super().__post_init__()

        # Set so arm as robot
        self.scene.robot = NERO_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot")

        # override actions
        self.actions.arm_action = mdp.JointPositionActionCfg(
            asset_name="robot",
            joint_names=["joint1", "joint2", "joint3", "joint4", "joint5", "joint6","joint7" ],
            scale=0.5,
            use_default_offset=True,
        )
        self.actions.gripper_action = mdp.BinaryJointPositionActionCfg(
            asset_name="robot",
            joint_names=["gripper_joint1","gripper_joint2"],
            open_command_expr={"gripper_joint2": -0.05,"gripper_joint1":0.05},
            close_command_expr={"gripper_joint2": -0.001,"gripper_joint1":0.0},
        )   
        # Set the body name for the end effector
        self.commands.object_pose.body_name = ["gripper_base"]

        # Set Cube as object
        self.scene.object = RigidObjectCfg(
            prim_path="{ENV_REGEX_NS}/Object",
            init_state=RigidObjectCfg.InitialStateCfg(pos=[0.2, 0.0, 0.015], rot=[1, 0, 0, 0]),
            spawn=UsdFileCfg(
                usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Blocks/DexCube/dex_cube_instanceable.usd",
                scale=(0.5, 0.5, 0.5),
                rigid_props=RigidBodyPropertiesCfg(
                    solver_position_iteration_count=16,
                    solver_velocity_iteration_count=1,
                    max_angular_velocity=1000.0,
                    max_linear_velocity=1000.0,
                    max_depenetration_velocity=5.0,
                    disable_gravity=False,
                ),
            ),
        )

        # Listens to the required transforms
        marker_cfg = FRAME_MARKER_CFG.copy()
        marker_cfg.markers["frame"].scale = (0.05, 0.05, 0.05)
        marker_cfg.prim_path = "/Visuals/FrameTransformer"
        self.scene.ee_frame = FrameTransformerCfg(
            prim_path="{ENV_REGEX_NS}/Robot/base_link",
            debug_vis=True,
            visualizer_cfg=marker_cfg,
            target_frames=[
                FrameTransformerCfg.FrameCfg(
                    prim_path="{ENV_REGEX_NS}/Robot/gripper_base",
                    name="end_effector",
                    offset=OffsetCfg(
                        pos=[0.0, 0.0, 0.125],
                    ),
                ),
            ],
        )


@configclass
class NeroLiftCubeEnvCfg_PLAY(NeroLiftCubeEnvCfg):
    def __post_init__(self):
        # post init of parent
        super().__post_init__()
        # make a smaller scene for play
        self.scene.num_envs = 50
        self.scene.env_spacing = 2.5
        # disable randomization for play
        self.observations.policy.enable_corruption = False

The nero_lift_env_cfg.py file provides the base environment configuration for the lifting task. It specifies the task reward, penalties, policy setup, target point position, and block position, which together define the behavior and objective of the environment.

# Copyright (c) 2024-2025, Muammer Bay (LycheeAI), Louis Le Lay
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

from dataclasses import MISSING

import isaaclab.sim as sim_utils

# from . import mdp
import isaac_so_arm101.tasks.lift.mdp as mdp
from isaaclab.assets import (
    ArticulationCfg,
    AssetBaseCfg,
    DeformableObjectCfg,
    RigidObjectCfg,
)
from isaaclab.envs import ManagerBasedRLEnvCfg
from isaaclab.managers import CurriculumTermCfg as CurrTerm
from isaaclab.managers import EventTermCfg as EventTerm
from isaaclab.managers import ObservationGroupCfg as ObsGroup
from isaaclab.managers import ObservationTermCfg as ObsTerm
from isaaclab.managers import RewardTermCfg as RewTerm
from isaaclab.managers import SceneEntityCfg
from isaaclab.managers import TerminationTermCfg as DoneTerm
from isaaclab.scene import InteractiveSceneCfg
from isaaclab.sensors.frame_transformer.frame_transformer_cfg import FrameTransformerCfg
from isaaclab.sim.spawners.from_files.from_files_cfg import GroundPlaneCfg, UsdFileCfg
from isaaclab.utils import configclass
from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR

# from isaaclab.utils.offset import OffsetCfg
# from isaaclab.utils.noise import AdditiveUniformNoiseCfg as Unoise
# from isaaclab.utils.visualizer import FRAME_MARKER_CFG
# from isaaclab.utils.assets import RigidBodyPropertiesCfg


##
# Scene definition
##


@configclass
class ObjectTableSceneCfg(InteractiveSceneCfg):
    """Configuration for the lift scene with a robot and a object.
    This is the abstract base implementation, the exact scene is defined in the derived classes
    which need to set the target object, robot and end-effector frames
    """

    # robots: will be populated by agent env cfg
    robot: ArticulationCfg = MISSING
    # end-effector sensor: will be populated by agent env cfg
    ee_frame: FrameTransformerCfg = MISSING
    # target object: will be populated by agent env cfg
    object: RigidObjectCfg | DeformableObjectCfg = MISSING

    # Table
    table = AssetBaseCfg(
        prim_path="{ENV_REGEX_NS}/Table",
        init_state=AssetBaseCfg.InitialStateCfg(pos=[0.5, 0, 0], rot=[0.707, 0, 0, 0.707]),
        spawn=UsdFileCfg(usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Mounts/SeattleLabTable/table_instanceable.usd"),
    )

    # plane
    plane = AssetBaseCfg(
        prim_path="/World/GroundPlane",
        init_state=AssetBaseCfg.InitialStateCfg(pos=[0, 0, -1.05]),
        spawn=GroundPlaneCfg(),
    )

    # lights
    light = AssetBaseCfg(
        prim_path="/World/light",
        spawn=sim_utils.DomeLightCfg(color=(0.75, 0.75, 0.75), intensity=3000.0),
    )


##
# MDP settings
##


@configclass
class CommandsCfg:
    """Command terms for the MDP."""

    object_pose = mdp.UniformPoseCommandCfg(
        asset_name="robot",
        body_name=MISSING,  # will be set by agent env cfg
        resampling_time_range=(5.0, 5.0),
        debug_vis=True,
        ranges=mdp.UniformPoseCommandCfg.Ranges(
            pos_x=(0.3, 0.35),
            pos_y=(-0.2, 0.2),
            pos_z=(0.2, 0.35),
            roll=(0.0, 0.0),
            pitch=(0.0, 0.0),
            yaw=(0.0, 0.0),
        ),
    )


@configclass
class ActionsCfg:
    """Action specifications for the MDP."""

    # will be set by agent env cfg
    arm_action: mdp.JointPositionActionCfg | mdp.DifferentialInverseKinematicsActionCfg = MISSING
    gripper_action: mdp.BinaryJointPositionActionCfg = MISSING


@configclass
class ObservationsCfg:
    """Observation specifications for the MDP."""

    @configclass
    class PolicyCfg(ObsGroup):
        """Observations for policy group."""

        joint_pos = ObsTerm(func=mdp.joint_pos_rel)
        joint_vel = ObsTerm(func=mdp.joint_vel_rel)
        object_position = ObsTerm(func=mdp.object_position_in_robot_root_frame)
        target_object_position = ObsTerm(func=mdp.generated_commands, params={"command_name": "object_pose"})
        actions = ObsTerm(func=mdp.last_action)

        def __post_init__(self):
            self.enable_corruption = True
            self.concatenate_terms = True

    # observation groups
    policy: PolicyCfg = PolicyCfg()


@configclass
class EventCfg:
    """Configuration for events."""

    reset_all = EventTerm(func=mdp.reset_scene_to_default, mode="reset")

    reset_object_position = EventTerm(
        func=mdp.reset_root_state_uniform,
        mode="reset",
        params={
            "pose_range": {"x": (0.1, 0.2), "y": (-0.1, 0.2), "z": (0.0, 0.0)},
            "velocity_range": {},
            "asset_cfg": SceneEntityCfg("object", body_names="Object"),
        },
    )


@configclass
class RewardsCfg:
    """Reward terms for the MDP."""

    reaching_object = RewTerm(func=mdp.object_ee_distance, params={"std": 0.05}, weight=1.0)

    lifting_object = RewTerm(func=mdp.object_is_lifted, params={"minimal_height": 0.025}, weight=15.0)

    object_goal_tracking = RewTerm(
        func=mdp.object_goal_distance,
        params={"std": 0.3, "minimal_height": 0.025, "command_name": "object_pose"},
        weight=16.0,
    )

    object_goal_tracking_fine_grained = RewTerm(
        func=mdp.object_goal_distance,
        params={"std": 0.05, "minimal_height": 0.025, "command_name": "object_pose"},
        weight=5.0,
    )

    # action penalty
    action_rate = RewTerm(func=mdp.action_rate_l2, weight=-1e-4)

    joint_vel = RewTerm(
        func=mdp.joint_vel_l2,
        weight=-1e-4,
        params={"asset_cfg": SceneEntityCfg("robot")},
    )


@configclass
class TerminationsCfg:
    """Termination terms for the MDP."""

    time_out = DoneTerm(func=mdp.time_out, time_out=True)

    object_dropping = DoneTerm(
        func=mdp.root_height_below_minimum, params={"minimum_height": -0.05, "asset_cfg": SceneEntityCfg("object")}
    )


@configclass
class CurriculumCfg:
    """Curriculum terms for the MDP."""

    action_rate = CurrTerm(
        func=mdp.modify_reward_weight, params={"term_name": "action_rate", "weight": -1e-1, "num_steps": 10000}
    )

    joint_vel = CurrTerm(
        func=mdp.modify_reward_weight, params={"term_name": "joint_vel", "weight": -1e-1, "num_steps": 10000}
    )


##
# Environment configuration
##


@configclass
class LiftEnvCfg(ManagerBasedRLEnvCfg):
    """Configuration for the lifting environment."""

    # Scene settings
    scene: ObjectTableSceneCfg = ObjectTableSceneCfg(num_envs=4096, env_spacing=2.5)
    # Basic settings
    observations: ObservationsCfg = ObservationsCfg()
    actions: ActionsCfg = ActionsCfg()
    commands: CommandsCfg = CommandsCfg()
    # MDP settings
    rewards: RewardsCfg = RewardsCfg()
    terminations: TerminationsCfg = TerminationsCfg()
    events: EventCfg = EventCfg()
    curriculum: CurriculumCfg = CurriculumCfg()

    def __post_init__(self):
        """Post initialization."""
        # general settings
        self.decimation = 2
        self.episode_length_s = 5.0
        self.viewer.eye = (2.5, 2.5, 1.5)
        # simulation settings
        self.sim.dt = 0.01  # 100Hz
        self.sim.render_interval = self.decimation

        self.sim.physx.bounce_threshold_velocity = 0.2
        self.sim.physx.bounce_threshold_velocity = 0.01
        self.sim.physx.gpu_found_lost_aggregate_pairs_capacity = 1024 * 1024 * 4
        self.sim.physx.gpu_total_aggregate_pairs_capacity = 16 * 1024
        self.sim.physx.friction_correlation_distance = 0.00625

Then, the nero reach task needs to be registered in src/isaac_so_arm101/tasks/reach/__init__.py

# Copyright (c) 2024-2025, Muammer Bay (LycheeAI), Louis Le Lay
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

import gymnasium as gym

from . import agents

##
# Register Gym environments.
##

gym.register(
    id="Isaac-SO-ARM100-Lift-Cube-v0",
    entry_point="isaaclab.envs:ManagerBasedRLEnv",
    kwargs={
        "env_cfg_entry_point": f"{__name__}.joint_pos_env_cfg:SoArm100LiftCubeEnvCfg",
        "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:LiftCubePPORunnerCfg",
    },
    disable_env_checker=True,
)

gym.register(
    id="Isaac-SO-ARM100-Lift-Cube-Play-v0",
    entry_point="isaaclab.envs:ManagerBasedRLEnv",
    kwargs={
        "env_cfg_entry_point": f"{__name__}.joint_pos_env_cfg:SoArm100LiftCubeEnvCfg_PLAY",
        "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:LiftCubePPORunnerCfg",
    },
    disable_env_checker=True,
)

gym.register(
    id="Isaac-SO-ARM101-Lift-Cube-v0",
    entry_point="isaaclab.envs:ManagerBasedRLEnv",
    kwargs={
        "env_cfg_entry_point": f"{__name__}.joint_pos_env_cfg:SoArm101LiftCubeEnvCfg",
        "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:LiftCubePPORunnerCfg",
    },
    disable_env_checker=True,
)

gym.register(
    id="Isaac-SO-ARM101-Lift-Cube-Play-v0",
    entry_point="isaaclab.envs:ManagerBasedRLEnv",
    kwargs={
        "env_cfg_entry_point": f"{__name__}.joint_pos_env_cfg:SoArm101LiftCubeEnvCfg_PLAY",
        "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:LiftCubePPORunnerCfg",
    },
    disable_env_checker=True,
)

gym.register(
    id="Isaac-Nero-Lift-Cube-v0",
    entry_point="isaaclab.envs:ManagerBasedRLEnv",
    kwargs={
        "env_cfg_entry_point": f"{__name__}.nero_joint_pos_env_cfg:NeroLiftCubeEnvCfg",
        "rsl_rl_cfg_entry_point": f"{agents.__name__}.rsl_rl_ppo_cfg:LiftCubePPORunnerCfg", 
        "rl_games_cfg_entry_point": f"{agents.__name__}:rl_games_ppo_cfg.yaml",

    },
    disable_env_checker=True,
)

6. An Isaac Lab Training and Evaluation Pipeline

First, activate the Conda environment:

conda activate env_isaaclab

Then switch to the project directory:

cd isaac_so_arm101

Start training the Isaac-Nero-Lift-Cube-v0 task in headless mode to reduce GPU and display overhead:

uv run train --task Isaac-Nero-Lift-Cube-v0 --headless

If your hardware is strong enough, you can also run training with visualization enabled to observe the learning process directly:

uv run train --task Isaac-Nero-Lift-Cube-v0

This task is trained for 1,000 iterations. Once training is finished, use the following command to evaluate the learned policy:

uv run play --task Isaac-Nero-Lift-Cube-v0

nerorl (2)

7. Summary

Key outcomes of this work include:

  • URDF Model Integration
  • Reproducible RL Training Pipeline
  • Environment Validation

The workflow can also be extended to more complex manipulation scenarios, including multi-object grasping and obstacle avoidance, with the Nero robotic arm serving as the deployment target for simulation-to-real transfer.

:speech_balloon: FAQ

What is inverse kinematics in robotics?
Inverse kinematics (IK) calculates the required joint angles for a robotic arm to reach a target position and orientation.

How does ROS2 help with robot arm control?
ROS2 provides communication, motion control, and integration tools for robotic manipulators, including MoveIt2 and RViz.

What is the difference between FK and IK?
Forward kinematics calculates the end-effector position from joint angles, while inverse kinematics calculates joint angles from a target pose.

Why use MoveIt2 for robot arms?
MoveIt2 simplifies robot arm motion planning, collision checking, and trajectory execution in ROS2 environments.

Can this IK solver run on a real robot arm?
Yes. The parametric IK solver can be deployed on physical robot arms after proper calibration and controller integration.

Does NERO Arm support Gazebo simulation?
Yes. The NERO Arm can be simulated in Gazebo and visualized in RViz for testing and development.

:waving_hand: Still Have Question?
If you encounter any issues with environment installation, parameter configuration, or RL training, feel free to leave your questions for further discussion.

2 posts - 2 participants

Read full topic

by Agilex_Robotics on May 20, 2026 03:21 AM

May 19, 2026
FusionCore + icp_odometry feedback loop merged into rtabmap_ros

PR #1419 was merged into introlab/rtabmap_ros last week, which was co-authored by Mathieu Labbe. It adds a TurtleBot3 Gazebo Harmonic demo showing a feedback loop between FusionCore (IMU + wheel UKF) and icp_odometry.

fusioncore_gif

The architecture is worth describing because the feedback direction is non-obvious:

/imu  ──────────────────────┐
/odom (wheel) ──────────────┤──> FusionCore (UKF)
/rtabmap/icp_odometry ──────┘    │
    ^                            │ odom -> base_footprint TF
    │                            │ /fusion/odom
    │        guess_frame_id: odom│
    └──── icp_odometry <─────────┘
             │
             └──> rtabmap SLAM ──> map -> odom TF

FusionCore runs at 100 Hz and owns the odom frame. icp_odometry uses that frame as the initial guess for scan matching via guess_frame_id. A stable initial guess means scan matching succeeds more consistently and with lower residual error. The ICP result feeds back into FusionCore as a second velocity source (encoder2), tightening the UKF state estimate. rtabmap handles loop closure and map correction on top.

Each node tightens the other. Neither is strictly downstream.

To run it on Jazzy:

sudo apt install ros-jazzy-fusioncore-ros ros-jazzy-rtabmap-ros \
                 ros-jazzy-turtlebot3-gazebo ros-jazzy-nav2-bringup
export TURTLEBOT3_MODEL=waffle
ros2 launch rtabmap_demos turtlebot3_sim_fusioncore_icp_demo.launch.py

Full architecture notes and topic/TF table are in the demo README: https://github.com/introlab/rtabmap_ros/tree/ros2/rtabmap_demos/launch/turtlebot3/fusioncore

rtabmap_ros/rtabmap_demos at ros2 · introlab/rtabmap_ros

If you are using rtabmap outdoors with GPS, FusionCore also handles that separately: https://github.com/manankharwar/fusioncore

1 post - 1 participant

Read full topic

by manankharwar on May 19, 2026 06:25 PM

Hello from a ROS 2 Learner & Wheelchair Robot Builder

Hello ROS Community! :waving_hand:

My name is Hariss Abdraman Tahir, and I’m a final-year Computer Engineering student at the University of Maiduguri with a strong passion for robotics, autonomous systems, and embedded hardware.

I recently joined the ROS ecosystem and am currently learning ROS 2 Jazzy Jalisco while building hands-on projects that combine software, hardware, and autonomous systems engineering.

My areas of interest include:

• ROS 2 and robotic software architecture

• Autonomous ground robots and navigation (Nav2)

• Drone development for agricultural and surveillance applications

• Computer vision and perception systems

• Embedded systems and hardware-software integration

• Simulation with Gazebo

• PX4 and MAVSDK for drone autopilot systems

Current Project

For my final-year project, I am designing and building a prototype automated navigation wheelchair for clinical environments. The system integrates autonomous navigation, real-time obstacle avoidance, safety-critical design, and human-machine interaction using a ROS 2-based architecture.

I’m genuinely excited to be part of this community and look forward to learning from experienced developers, contributing to open-source projects, and growing alongside others who share the same passion for robotics.

Any tips, learning resources, or project feedback are always welcome.

Thank you for having me!

5 posts - 4 participants

Read full topic

by Hariss97 on May 19, 2026 04:29 PM

Robotic Agents Hackathon in Milan 🇮🇹 Build with Real Robots + Win Hardware

Hey everyone,

We’re excited to announce that we’re bringing the Robotic Agents Hackathon to Milan, Italy this June :italy:

After hosting our first edition in San Francisco with 100+ developers building robotic agents and physical AI applications, we’re now bringing the same energy to Europe.

:date: Date: June 20th, 2026
:round_pushpin: Location: Milan, Italy
:link: Registration: Robotic Hackathon · Luma

This is a full-day robotics + AI hackathon focused on building with real robotic hardware.

Developers, students, roboticists, AI engineers, and builders will get the opportunity to:

  • Build robotic agents and physical AI applications

  • Work directly with robotic hardware

  • Experiment with simulations, SDKs, and agent workflows

  • Collaborate with other robotics developers

  • Demo projects live

  • Win robotic hardware and builder rewards

We’re especially excited to meet more people from the ROS and open robotics ecosystem in Europe.
If you’re based in Europe (or nearby), come hack with us in Milan :mechanical_arm:

1 post - 1 participant

Read full topic

by Khushi_Sharma on May 19, 2026 04:03 PM

LiDAR Selection Guide: Practical Comparison Between M360 and MID-360 After Purchasing 20+ Units

Our company specializes in robot integration solutions, and over the years, we have used quite a few LiDAR systems. Starting last year, we have extensively used Livox series LiDARs — mainly the MID-360 and M360. To date, we have accumulated over 20 purchases across different projects.

Here’s a practical comparison based on real-world project experience, not just spec sheets.

The First Step: Don’t Start with Parameters

Many people compare parameters first — range, power consumption, blind zone. Parameters matter, but we ask three questions first:

  1. Working environment? (Indoor / Outdoor / Semi-outdoor)
  2. Extreme conditions? (Rain, dust, high/low temperature)
  3. Budget?

These three questions can eliminate a significant number of options.

Practical Comparison: M360 vs MID-360

Range: 50m vs 70m

We tested with white walls (~90% reflectance) and a black sedan (~10% reflectance). Both LiDARs reliably detect over 50m on white walls. On the black sedan: M360 ~20-25m, MID-360 ~25-30m.

For warehouse navigation (3-5m shelf spacing), 50m is more than enough. For outdoor long-distance driving, 70m gives earlier obstacle detection.

Blind Zone: 5cm vs 10cm

This is significant in practice. We had a narrow passage project (1.2m width, 0.8m robot, 20cm clearance each side). With MID-360 (10cm blind zone), occasional wall touches occurred. After switching to M360 (5cm blind zone), wall touches basically disappeared.

For narrow space navigation (tunnels, shelf passages), 5cm blind zone is essential.

Vertical FOV: 70° vs 59°

M360: -10°~+60°. At 1.5m height, covers ground at 26cm and ceiling up to 2.6m.
MID-360: -7°~+52°. Ground at 18cm, ceiling at 1.9m.

For ceiling positioning (e.g., shelf navigation in supermarkets), M360’s advantage is crucial.

Power Consumption: 4.5W vs 6.5W

A 2W difference per unit. With 4 LiDARs (quadruped robot), that’s 8W — potentially 15-20 minutes of battery life difference.

Dual Echo (M360 only)

Practical uses:

  • Rainy day operation: Significantly improved point cloud quality in light rain
  • Glass detection: First echo = glass surface, second echo = object behind glass

IP67

Both are now IP67, but M360’s sealing has been market-validated longer.

IMU

Both have 6-axis IMUs. In practice, no significant accuracy difference. For high-precision pose estimation, use an external IMU (BMI088 or ICM42688).

Scenario-Based Selection Guide

Scenario Recommendation Key Reason
Indoor AGV/AMR MID-360 (budget) or M360 (narrow channels) 5cm blind zone for narrow spaces
Outdoor transport/inspection M360 IP67 + dual echo + wider temp range
Quadruped robots M360 (8S+ batteries or 3-4 units) Power supply + consumption
Mowing robots M360 5cm blind zone + IP67
Humanoid robots M360 (waist mount) 70° FOV better for waist mounting
Handheld scanning M360 5cm blind zone + dual echo for glass

Conclusion

Spec sheet numbers reflect ideal conditions. Real-world performance depends on installation angle, temperature, reflectivity, and motion speed. Always test a prototype in your actual environment before bulk procurement.

The few thousand yuan spent on a prototype is far cheaper than discovering issues after bulk purchase.


Based on actual project testing at SmartBotParts / TanTu ZhiXing. Parameters should be verified against official specifications.
Contact: sara.gao@smartbotparts.com

1 post - 1 participant

Read full topic

by SmartBotParts on May 19, 2026 04:03 PM

ROS2 URDF Mesh File Types

This is highly related to the discussion on this thread, but not really relevant to the title/stated purpose of the thread so I decided to make a new one.

With the release of Blender 5.0, the Collada (dae) format is no longer supported. Of course, we could use older versions of Blender or other applications entirely to edit these files. But this change marks a broader shift away from Collada which is explained in the thread linked above.

I understand that other mesh types, e.g. ply, glb, obj, can be effectively used with many ROS2 applications. That anything that the mesh loader (e.g. Assimp) will load will work. But for the purpose of guidance, I think it would be good if there is a new file format that is agreed upon for new meshes as an alternative to Collada.

The official guidance states the following:

Any geometry format is acceptable but specific application compatibility is dependent on implementation. The recommended format for best texture and color support is Collada .dae files.

Effectively what I am saying is that it would be good to add a new “officially recommended” file type alongside Collada. I am certainly not saying that support should be dropped for Collada in favor of something newer, but creating new Collada files is becoming more tedious so I think that another file type can be used for URDFs that are created in the future.

As a quick example of why I think it is important for a new file to be “officially recommended”, while glb files can be imported to Isaac Sim as a standalone, and glb can also be used as a URDF mesh type and displayed in RViz, the Isaac Sim URDF importer only supports dae, obj, and stl for the mesh types, so a URDF with glb is not easily importable. Presumably, if it was a recommended format, it would be supported by the URDF importer. I am sure that there are probably other applications with similar arbitrary limitations, but that is the first one that comes to mind.

With the disclaimer that I am not particularly familiar with different 3D file formats, I think that gLTF 2.0 (gltf/glb) makes sense. It is compact, widely supported, and supports meshes and textures in one file.

This change could start with just an addition to the quoted section above, but I don’t know the full implications of a change like this.

Are there difficulties that I am overlooking? I would appreciate thoughts on this.

2 posts - 2 participants

Read full topic

by jimmy-mcelwain on May 19, 2026 03:16 PM

Standardizing SYSTEM_DEFAULT QoS resolution across RMW implementations

When a node uses SystemDefaultsQoS() (or leaves any policy to *_SYSTEM_DEFAULT), the intent is clear: delegate QoS
resolution to the underlying middleware. In theory, this is the right hook for system-wide QoS configuration without touching
node code.

In practice, the behavior is inconsistent across RMW implementations:

  • FastDDS: supports full XML-based QoS override via FASTDDS_DEFAULT_PROFILES_FILE + RMW_FASTRTPS_USE_QOS_FROM_XML=1.
    Works as expected for SYSTEM_DEFAULT policies.
  • CycloneDDS: CYCLONEDDS_URI controls participant-level settings (network, threads), not endpoint QoS (reliability,
    durability, depth). No equivalent mechanism exists.
  • Zenoh (rmw_zenoh): end-to-end reliability for RELIABLE + VOLATILE + KEEP_LAST is not yet fully implemented
    (ros2/rmw_zenoh#457), and there is no external configuration mechanism for
    endpoint QoS defaults.

The consequence is that a node written with SystemDefaultsQoS() — which is arguably the correct way to write portable,
externally-configurable nodes — behaves differently depending on which RMW is in use, and is only truly configurable on one of
them.

Root cause

All predefined QoS profiles (SensorDataQoS, ParametersQoS, ServicesQoS, etc.) are defined as static const in
rmw/qos_profiles.h — hardcoded values with no indirection layer:

Profile Reliability Durability Depth
QoS(n) / default RELIABLE VOLATILE 10
SensorDataQoS() BEST_EFFORT VOLATILE 5
ParametersQoS() RELIABLE VOLATILE 1000
ServicesQoS() RELIABLE VOLATILE 10
RosoutQoS() RELIABLE TRANSIENT_LOCAL 1000
SystemDefaultsQoS() SYSTEM_DEFAULT SYSTEM_DEFAULT SYSTEM_DEFAULT

A node calling SensorDataQoS() gets BEST_EFFORT with depth 5, unconditionally. The only escape hatch is
SystemDefaultsQoS(), but its resolution is RMW-specific and undocumented as a standard contract.

Proposal

Define a minimal standardized mechanism at the rcl or rmw interface level for resolving SYSTEM_DEFAULT QoS policies. This
could be:

  • A well-defined env var (e.g. RCL_DEFAULT_QOS_PROFILE) pointing to a vendor-neutral config file (YAML or similar), parsed by
    rcl before handing off to the RMW.
  • Or alternatively, a formal contract in the RMW interface spec requiring each implementation to document how it resolves
    SYSTEM_DEFAULT policies and what configuration mechanism it exposes.

This would make SystemDefaultsQoS() actually useful as a portability and deployment tool — especially relevant as more RMW
implementations (Zenoh, Eclipse Cyclone, proprietary) diverge from the DDS XML model.

Interested in hearing whether others have hit this in practice, and whether there is appetite for a REP or a design doc to
formalize this.

1 post - 1 participant

Read full topic

by antonio_romano on May 19, 2026 01:03 PM

Detecting execution collapse before hard E-stop: ros2_kinematic_guard for ROS 2 AMR/AGV

Hi ROS community,
I’ve been working on a small ROS 2 package called ros2_kinematic_guard. It is a pre-E-stop guard for ROS 2 AMR/AGV systems.The idea is simple: Most mobile robot stacks already have timeouts. If /cmd_vel stops arriving, the base driver or controller can stop the robot.But timeouts only answer one question:

Did a command arrive recently?

They do not answer:

Is the robot still moving according to the command it was just given?

That gap matters in AMR/AGV deployments.

Why pre-E-stop detection matters

Safety-rated E-stop systems, safety PLCs, and safety lidars are the final protection layer. ros2_kinematic_guard does not replace them. The goal is different:

Detect execution collapse earlier, before the certified safety layer is forced to intervene.
Examples:

  • wheel slip on wet or oily floors
  • wheel-speed / odometry mismatch
  • localization jumps from lidar / SLAM glitches
  • stale or replayed command windows
  • bad Wi-Fi / 5G command bursts
  • robot shaking, spinning, or over-correcting before safety lidar cuts power

Frequent hard stops may contribute to:

  • manual recovery time
  • production interruption
  • payload instability
  • mechanical stress on wheels, reducers, and brakes
  • unclear root cause during post-incident debugging

Why not just a timeout?

A timeout can detect silence. It cannot detect a physically inconsistent command-feedback episode. For example:

/cmd_vel is still arriving normallybut /odom no longer matches the commanded motion

That can happen during wheel slip, localization jumps, or command bursts after network buffering. ros2_kinematic_guard watches both /cmd_vel and /odom over a short sliding window. When the robot’s measured motion no longer matches the command stream, it emits a compact KinematicStatus JSON and can optionally clamp or brake the outgoing command.
The goal is to provide a local pre-E-stop diagnostic layer. By detecting and self-correcting minor kinematic inconsistencies early, we can significantly reduce the frequency of unforced hard E-stops, thereby improving operational uptime.

Zero-code modification

The package works as an inline ROS 2 topic filter. You do not need to modify Nav2, behavior trees, planners, controllers, or proprietary base drivers.

Nav2 / teleop / planner
        ↓
      /cmd_vel
        ↓
Kinematic Guard
        ↓
  /safe_cmd_vel
        ↓
base driver

It supports three modes:

  • mode:=observe — passive monitoring only, no control intervention
  • mode:=passthrough — wiring test, /safe_cmd_vel equals /cmd_vel
  • mode:=guard — active mode, can clamp velocity or enter BRAKE_AND_RESYNC

This means first-day deployment can be completely passive.

Example output

Healthy window:

{
  "status": "GREEN",
  "residual": 0.0009,
  "causalAlignment": "ALIGNED",
  "dominantCause": "NONE",
  "guardAction": "OBSERVE_ONLY",
  "safeCmd": {
    "linear_vx": 0.8,
    "angular_wz": 0.0
  }
}

Wheel-slip window in observe mode:

{
  "status": "RESYNCING",
  "causalAlignment": "BROKEN",
  "dominantCause": "WHEEL_SLIP",
  "guardAction": "OBSERVE_ONLY",
  "mode": "observe",
  "controlInterceptionEnabled": false
}

Wheel-slip window in guard mode:

{
  "status": "RESYNCING",
  "causalAlignment": "BROKEN",
  "dominantCause": "WHEEL_SLIP",
  "guardAction": "BRAKE_AND_RESYNC",
  "mode": "guard",
  "controlInterceptionEnabled": true,
  "safeCmd": {
    "linear_vx": 0.0,
    "angular_wz": 0.0
  }
}

5-minute demo

The demo does not require Gazebo, Isaac Sim, or a real robot. It runs a lightweight mock AMR/AGV and injects wheel slip:

/cmd_vel
   ↓
Kinematic Guard
   ↓
/safe_cmd_vel
   ↓
Mock Robot
   ↓
/odom
   ↑
Kinematic Guard

Build:

source /opt/ros/humble/setup.bash
colcon build --symlink-install
source install/setup.bash

Run observe mode:

ros2 launch ros2_kinematic_guard start_pre_estop_demo.launch.py \
  profile:=wheel_slip \
  mode:=observe \
  slip_start_sec:=10.0 \
  slip_duration_sec:=12.0

Publish a smooth command:

ros2 topic pub -r 20 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.8}, angular: {z: 0.0}}"

Watch the guard:

watch -n 0.2 'ros2 topic echo /kinematic_guard/status --field data --once --full-length | awk "/^---$/{exit} {print}" | python3 -m json.tool'

The README also includes a persistent fault debug mode with slip_duration_sec:=9999.0.

Repository:

https://github.com/ZC502/ros2_kinematic_guard.git

I would be very interested in feedback from people working with AMRs/AGVs, Nav2 deployments, base drivers, or post-incident debugging. The core question is:

Would a lightweight command/odom sanity layer be useful before the system escalates to a hard E-stop?

2 posts - 1 participant

Read full topic

by zc_Liu on May 19, 2026 03:57 AM

May 18, 2026
Ouster Lidar TechTalk next Wednesday [May 27, 2026] in San Francisco

For those in San Francisco Bay Area, Ouster is hosting a TechTalk following the successful launch of Ouster REV8 LiDAR sensors with the first native color Lidar. If you are someone who is into Physical AI, robotics, perception, industrial automation or autonomous driving, come join us as we explore the full potential of the latest innovations from Ouster! RSVP: https://luma.com/0yamtymo

P.S. There will be a raffle on some Ouster & StereoLabs sensors during the event.

1 post - 1 participant

Read full topic

by Samahu on May 18, 2026 08:55 PM

Pre-release Distribution Freeze for Lyrical Luth

As we prepare to launch ROS Lyrical Luth later this week, effective immediately we are :ice:freezing :ice: ros/rosdistro lyrical/distribution.yaml and changes to the lyrical branches of ROS core packages. We will reopen rosdistro and the lyrical branches for changes after we release ROS Lyrical on Fri, May 22, 2026 7:00 AM UTC.

During this time we will only consider release-blocking bug fixes. Anything else we will document as a known issue.

Cheers!

1 post - 1 participant

Read full topic

by sloretz on May 18, 2026 07:00 PM

RoboInfra CI/CD APIs for URDF validation, auto-fix, MoveIt config generation, and Isaac Sim USD conversion

Hi everyone, I’ve been building RoboInfra, a developer-focused infrastructure platform for robot model workflows.

The goal is simple:

Stop robotics developers from wasting hours debugging URDFs manually.

RoboInfra provides hosted APIs + CI/CD tooling for common ROS pain points:

Current features

  • URDF/Xacro validation

  • URDF auto-fix & repair

  • Semantic URDF diffing

  • MoveIt config generation

  • URDF → SDF conversion

  • URDF → MJCF conversion

  • URDF → USD conversion for NVIDIA Isaac Sim

  • Mesh analysis

  • 3D model conversion

  • GitHub Action for PR validation

Example GitHub Action

- uses: roboinfra/validate-urdf-action@v1
  with:
    api-key: ${{ secrets.ROBOINFRA_API_KEY }}
    file: urdf/robot.urdf

Broken URDFs fail the PR automatically.

Free public validator

https://roboinfra-dashboard.azurewebsites.net/validator

No signup required.

Example API

curl -X POST https://roboinfra-api.azurewebsites.net/api/urdf/auto-fix \
  -H "X-Api-Key: rk_your_key" \
  -F "file=@broken_robot.urdf"

Docs

Would genuinely appreciate feedback from ROS developers about:

  • missing workflows

  • painful URDF problems

  • MoveIt setup pain points

  • Isaac Sim integration issues

Thanks.

1 post - 1 participant

Read full topic

by Robotic on May 18, 2026 06:10 PM

Open Robotics in the Age of Embodied AI

Open Robotics in the Age of Embodied AI: Why the Stack Has to Be Open All the Way Down

For the last twenty years, “open robotics” has mostly meant ROS — a shared middleware that let researchers stop reinventing message-passing and start sharing perception, planning, and control packages. That was a huge win. But the shape of the field has changed. Today the most interesting robotics work happens at the intersection of vision-language models, reinforcement learning, and physical hardware that costs less than a laptop. And in that new landscape, “open middleware” is no longer enough.

To do credible Embodied AI research, you need the whole stack open — from the PCB to the Python API. This post is about why, and what that actually looks like in practice.

I’ve been building a platform called 3we to push on this idea. It’s an AI-First, fully open robot platform: open Apache-licensed Python SDK, open CERN-OHL-P hardware, open ROS2 stack, with the same Python code running identically in simulation and on real hardware. The point of this post isn’t to pitch 3we — it’s to use it as a worked example of what “open all the way down” means and why it matters.


The Three Layers of Openness

When people say “open source robotics,” they usually mean one of three different things, and conflating them causes real problems.

Layer 1 — Open middleware. ROS, ROS2, micro-ROS. The plumbing that lets nodes talk to each other. This layer has been open and healthy for a long time.

Layer 2 — Open algorithms and models. Nav2, SLAM Toolbox, MoveIt, and increasingly the open VLA models coming out of academic labs. The intelligence layer.

Layer 3 — Open hardware. Schematics, PCB layouts, mechanical CAD, BOMs detailed enough that someone with a soldering iron and $500 can reproduce the platform.

Most platforms are open at Layer 1, partially open at Layer 2, and closed at Layer 3. You can write custom nodes for a TurtleBot, but you can’t fab a new motor controller for it. You can fine-tune a policy on a real robot, but if the IMU goes bad, you’re sending the unit back to the vendor.

That’s a problem for a research field that increasingly demands physical experimentation at scale. If a lab wants ten robots to run a multi-agent RL experiment, the math has to work — both financially and logistically. A $1,200 robot times ten is a grant proposal. A $500 robot times ten is a purchase order.


Why Embodied AI Forces the Issue

Three trends in Embodied AI research make full-stack openness non-optional:

1. Sim2Real is the bottleneck, not the algorithm.

The published results on policy learning are extraordinary, but the gap between “works in Isaac Sim” and “works on a real robot in a real hallway” is still where most projects die. Closing that gap requires you to control both ends — sim environment and physical hardware — and align them at the level of sensor noise, motor dynamics, and timing. You can’t do that if the firmware is a black box. You can’t do that if the IMU calibration routine lives in the vendor’s cloud service.

2. Foundation models want to talk to robots.

A modern VLM-controlled robot is a perception-action loop where a 2-second VLM call sits next to a 50Hz control loop. Making that work means the API the model talks to has to be clean, async-first, and decoupled from the realtime layer. That’s a Python ergonomics problem, not a robotics problem. ROS2 by itself isn’t the answer — you need a layer on top that researchers actually want to use. And that layer needs to be open, so the community can shape it.

3. Reproducibility is in crisis.

“We trained a policy on our robot” is a sentence that’s almost impossible to verify if “our robot” is a custom rig in someone’s lab. Open hardware with a published BOM is the robotics equivalent of releasing your training code. If the next lab can’t rebuild your robot for $500 and rerun your experiment, your paper isn’t reproducible — it’s a demo.


What “AI-First” Actually Means

The phrase gets thrown around. In our case, it means a specific design choice: the primary API surface is a Python class, not a set of ROS2 topics.

from threewe import Robot
async with Robot(backend="mock") as robot:
    image = robot.get_image()
    await robot.move_to(x=2.0, y=1.0)
    result = await robot.execute_instruction("go to the red door")

A researcher writing this code never has to know that under the hood there’s a ROS2 graph publishing /cmd_vel, a Nav2 action server handling NavigateToPose, and an ESP32 running micro-ROS over UART. The ROS2 layer is still there — fully open, fully accessible to anyone who needs it — but it’s not a prerequisite.

Switching from backend="mock" (a zero-dependency 2D kinematic simulator that runs on a laptop with no GPU) to backend="gazebo" (full physics) to backend="isaac_sim" (GPU-accelerated parallel RL) to backend="real" (physical hardware) is a one-string change. Identical API. The same move_to() call resolves to a kinematic update, a Gazebo physics step, an Isaac Sim tensor op, or a real motor command.

That property — Sim2Real with zero code changes — is what unlocks the workflow Embodied AI actually needs: prototype in mock, train in isaac_sim, validate in gazebo, deploy on real, all without rewriting your agent.


The Hardware Side: Open Down to the Copper

The Python API is the part researchers see. The part that makes it credible is everything underneath.

The 3we reference hardware is fully published under CERN-OHL-P v2:

  • KiCad 8 schematic and PCB layout for the main controller board (ESP32-S3 + DRV8833 motor drivers + safety relay + connectors)
  • Mechanical CAD (STEP and DXF) for the chassis
  • Bill of materials with specific part numbers from accessible distributors
  • Assembly guide with photos at each step
  • Production outputs — Gerbers, drill files, pick-and-place — ready to send to a fab

The total reproduction cost is under $500. We’ve kept it intentionally accessible: an ESP32-S3 for motor control and micro-ROS, a Raspberry Pi 5 for the main compute, a Hailo-8L M.2 module (13 TOPS) for AI inference, an LD06 360° LiDAR, a BNO055 9-axis IMU, four N20 motors with Mecanum wheels.

A few design decisions are worth calling out because they’re easy to get wrong:

Hardware emergency stop. The physical E-stop cuts motor power through a hardware relay. Software cannot override this path. ISO 13850-compliant, dual-channel. A common mistake in DIY platforms is implementing E-stop as “the software stops sending velocity commands” — that’s not a safety system, that’s an honor system.

Three-tier watchdog. A 500ms /cmd_vel timeout in the Nav2 layer, a 1-second software watchdog in the ESP32 firmware, and a 1.6-second hardware watchdog (TPS3813) that resets the MCU if the firmware itself hangs. Each tier catches a different failure mode.

Payload bus. A standardized 34-pin connector (we call it PBC-34) so users can hot-plug their own payloads — robotic arms, sensor pods, custom end-effectors — without modifying the base. The payload code runs sandboxed: it can’t directly touch motor control or safety circuits.

These details aren’t glamorous, but they’re the difference between a platform you can do real research on and a platform that catches fire during a demo.


Open Hardware Has a Licensing Story Too

One thing that took me a while to get right: open hardware needs its own license, separate from the code license.

In 3we, the split looks like this:

  • Code — Apache 2.0 (firmware, ROS2 packages, Python SDK, web tools)
  • Hardware — CERN-OHL-P v2 (PCB, mechanical, BOM)
  • Documentation — CC-BY-4.0

CERN-OHL-P (Permissive) is the hardware analog of MIT/Apache: anyone can manufacture, modify, and sell the hardware, with attribution. There’s also CERN-OHL-W (Weakly reciprocal) and CERN-OHL-S (Strongly reciprocal) for projects that want copyleft semantics. Picking the right one is a values decision. Permissive maximizes adoption; reciprocal protects the commons. Neither is wrong.

The deeper point: Apache 2.0 doesn’t actually cover hardware. It’s a software license. Releasing your KiCad files under “Apache” creates legal ambiguity that will bite you the moment a manufacturer wants to commit to a production run. Use a hardware license for hardware.


What “Open” Doesn’t Mean

A few common misconceptions, since this post is going to be read by people who care about open source:

Open ≠ free of all constraints. You can be fully open-source and still have a clear sustainability model. We use a CLA + dual-licensing approach: contributors sign a CLA, the open version stays Apache 2.0 forever, and a commercial license is available for organizations that need different terms (e.g., proprietary derivatives, formal warranty). This is the same pattern Qt, MongoDB (historically), and many others have used. It’s not the only model, but it’s a coherent one.

Open ≠ vendor-free. We use ROS2, NVIDIA Isaac Sim, OpenAI APIs, and HuggingFace Hub. Open source doesn’t mean reinventing every dependency — it means the system you build on top is open, and the user can swap any layer.

Open ≠ unmaintained. “Open source” sometimes carries a connotation of “abandoned hobbyist project.” That’s a perception problem the community needs to push back on. An open platform with a healthy maintainer, a CI pipeline that runs on every PR, and a benchmark leaderboard with real submissions is not a hobby — it’s infrastructure.


The Asks

If you’re reading this and you work on robotics or Embodied AI, here are the things that would actually move the field forward, none of which are 3we-specific:

  1. Publish your hardware BOM. Not just “we used a TurtleBot 4.” The actual sensors, the actual cables, the actual mounting brackets. Every paper that doesn’t do this is a half-published paper.
  2. Pick a hardware license. If your KiCad files are on GitHub under a software license, fix it. CERN-OHL-P is fine. Solderpad is fine. Any of them is better than none.
  3. Make Sim2Real reproducible. Publish the sim config alongside the policy. Document the calibration procedure. The first lab to standardize this will own the citation graph for the next decade.
  4. Treat the API as a research artifact. The shape of the Python class researchers write against is not a footnote. It’s the part that gets used a million times.

What’s Next

3we is early. The SDK works; the hardware reproduces; the simulation backends are stable; the benchmark leaderboard accepts submissions. There’s a lot of unfinished work: VLA model deployment is rough around the edges, the multi-robot story needs more attention, and the documentation is uneven.

If any of this resonates — whether you want to use the platform, contribute to it, or just argue with the framing in this post — the repo is at github.com/telleroutlook/3we-robot-platform. Issues and PRs welcome. Disagreement especially welcome.

Open robotics in 2026 isn’t just about open code. It’s about open hardware, open APIs, open benchmarks, and a shared commitment to making the work reproducible. We’re not there yet as a field. But the path is clear, and there’s a lot of room for more people to walk it.

7 posts - 4 participants

Read full topic

by telleroutlook on May 18, 2026 04:10 PM

Introducing OLO: a browser-based ROS 2 platform for simulation, visualisation and robot programming

Hi everyone,

I wanted to introduce OLO Robotics, a UK-based robotics software company building a browser-based platform for programming, simulating, visualising and controlling robots using ROS 2.

Our aim is not to replace ROS 2, Gazebo, Nav2, MoveIt 2 or the wider open robotics ecosystem. It is the opposite: we are trying to make those tools easier to access, combine and use, particularly for developers, researchers, students and organisations who want to work with robots but may not want to spend their first few days wrestling with local setup, networking, visualisation, simulation and deployment.

At a high level, OLO provides:

  • Cloud-hosted robot simulation in the browser

  • A live 3D visualiser / digital twin

  • Browser-based programming using Python and JavaScript

  • Teleoperation and robot control

  • ROS 2 connectivity through an edge “appliance” running on the robot’s network

  • Support for real robots and simulated robots through the same interface

The platform is built around ROS 2 concepts rather than hiding them completely. Users can work at a high level when they want to, but the underlying model still maps onto topics, services, actions, frames, robot descriptions and standard ROS 2 workflows. We want it to be useful for people learning robotics, but also credible for people already working with ROS 2.

One of the things we are particularly interested in is reducing the friction between simulation and real hardware. For example, a user should be able to start with a robot in a browser-based simulation, write code against it, visualise what is happening, and then move towards a real robot with as little conceptual shift as possible.

We have just opened the platform more widely and would really value input from the ROS community.

In particular, I’d be interested in feedback on:

  1. Where tools like this can genuinely help the ROS 2 ecosystem

  2. Where we should be careful not to abstract too much away

  3. What experienced ROS users would expect from a platform like this

  4. What would make it useful for education, prototyping or early-stage robot development

  5. Whether there are existing projects, packages or conventions we should be aligning with more closely

We are very conscious that this community has built a huge amount of the foundation that makes platforms like ours possible. So I wanted to introduce OLO here properly, explain what we are trying to do, and open up the conversation.

Website: https://olo-robotics.com
You can also set up a free account here. Cloud simulation is currently completely free although this will move to a paid-for service: OLO Robot Management System

I’d be very happy to answer questions, share more technical detail, or hear blunt feedback.

Thanks,
Nick Thompson (CEO)

2 posts - 2 participants

Read full topic

by olo-robotics on May 18, 2026 04:09 PM

May 17, 2026
Title: Would your team use an open-loop regression testing tool for ROS 2 autonomy stacks?

I’m exploring an idea to help improve the CI process for robotics teams and would love honest feedback from engineers who work on deployed robots.

A common workflow I hear from robotics teams looks something like:

A developer makes a perception/localization/planning change → they manually replay a rosbag/MCAP log → inspect outputs in tools like Foxglove / RViz → make a judgment call → ship.

This feels very manual and inconsistent, especially for teams shipping frequent autonomy updates.

The idea I’m exploring is a lightweight regression testing tool for ROS 2 stacks that works like CI for autonomy systems:

  • replay recorded MCAP/rosbag logs

  • run a candidate autonomy stack (likely in Docker)

  • observe selected output topics

  • evaluate pass/fail rules

  • output a CI-friendly test result

Over time, teams could build a regression suite composed of previously recorded real-world failure scenarios.

Example workflow:

  1. Robot encounters a real-world failure in production (ex: reflective pallet wrap, localization drift, perception miss, repeated recovery loop)

  2. Team saves that incident log

  3. Engineer makes changes to the autonomy stack

  4. CI runs replay tests against previously recorded failure scenarios

  5. Tool verifies whether known failures were reintroduced

  6. Engineer gets pass/fail results before deployment

This would be intentionally open-loop only for an MVP:

  • no simulation environment recreation

  • no closed-loop driving

  • no digital twin requirements

The goal is to make regression testing easier for perception/localization/prediction teams using real-world logs.

A few questions I’d love feedback on:

  1. Is this a painful problem at your company, or do most teams already have internal tooling for this?

  2. Would rule-based validation be useful, or would teams strongly prefer baseline-vs-baseline output comparison?

  3. Is requiring Docker too restrictive?

  4. What are the biggest reasons this would fail in your environment?

  5. If your team already does something similar, what does your current workflow look like?

I’m especially interested in hearing from teams operating real robots in production environments (warehouse robots, autonomous forklifts, delivery robots, etc.).

Appreciate any honest feedback.

7 posts - 4 participants

Read full topic

by macoberry on May 17, 2026 11:19 PM

May 16, 2026
QERRA-v2 Classical — Explainable Ethical Scoring Engine with ROS 2 Bridge (open for feedback)

Hi everyone,

I’ve built QERRA-v2 Classical — a 100% classical, fully explainable ethical evaluation engine based on 12 immutable human-centred vectors (SEMEV-12). It returns traceable scores + reasoning with no neural networks.

The repo includes a ready-to-use ROS 2 bridge (ros2_bridge.py) that runs standalone or as a full node (subscribes to /qerra/situation_input, publishes score, decision, and full SEMEV-12 result).

Live API + full documentation:

I would be very grateful for any feedback, especially:

  1. Does the topic structure and message types fit typical robotics pipelines?
  2. What would make the bridge more useful in real Behaviour Trees?

Happy to adapt based on real use cases.

Thank you!

1 post - 1 participant

Read full topic

by marunigno-ship-it on May 16, 2026 08:01 AM

May 15, 2026
3we: AI-First Python API for Mobile Robot Navigation (Open Source, $300 BOM)

Hi everyone,

I’d like to share **3we** — an open-source platform I’ve been building that provides an AI-First Python API on top of ROS2/Nav2, targeting Embodied AI researchers who want to focus on algorithms rather than ROS2 infrastructure.

## The Problem

AI researchers (especially those working with VLMs/VLAs) often want to deploy models on real robots but face:

- Steep ROS2 learning curve (launch files, topics, services, actions)

- No clean path from simulation to hardware

- Existing platforms are either too expensive (TurtleBot 4: $1,200+) or simulation-only (Habitat, Isaac Lab)

## What 3we Does

```python

from threewe import Robot

async with Robot(backend=“gazebo”) as robot:

image = robot.get_camera_image()     # (H,W,3) uint8

scan = robot.get_lidar_scan()        # LaserScan

**await** robot.move_to(x=5.0, y=3.0)   # Nav2 under the hood

```

Change `backend=“gazebo”` to `backend=“real”` — same code runs on physical hardware. The ROS2/Nav2 stack is fully transparent to the user.

**Four backends with identical API:**

- `mock` — zero-dependency 2D kinematics (no ROS2 needed, runs anywhere)

- `gazebo` — Gazebo Harmonic with full physics

- `isaac_sim` — NVIDIA Isaac Sim for GPU-accelerated RL training

- `real` — Physical hardware via ROS2 topics

## Architecture

```

┌─────────────────────────────────────────┐

│ AI-First Python API (user layer) │ ← Researchers write code here

├─────────────────────────────────────────┤

│ 3we-core (middleware) │ ← Backend dispatch, sensor fusion

├─────────────────────────────────────────┤

│ ROS2 / micro-ROS (infrastructure) │ ← Transparent to users

│ Nav2, slam_toolbox, ESP32 drivers │

└─────────────────────────────────────────┘

```

This is NOT a replacement for ROS2 — it’s a layer on top that makes ROS2 accessible to ML researchers while preserving full ROS2 compatibility for roboticists who want low-level access.

## VLM-Controlled Navigation

The killer feature for AI researchers: GPT-4o (or any OpenAI-compatible VLM) can directly control the robot through natural language:

```python

async with Robot(backend=“gazebo”) as robot:

result = **await** robot.execute_instruction(

    "find the red bottle and stop near it"

)

print(f"Success: {result.success}")

```

Internally this runs a perception-action loop: capture image → send to VLM → parse JSON action → execute → repeat until done. Works with GPT-4o, Qwen-VL, or local LLaVA.

## Hardware ($300 BOM)

Fully open reference hardware under CERN-OHL-P v2:

| Component | Selection |

|-----------|-----------|

| Compute | Raspberry Pi 5 (8GB) |

| AI Accelerator | Hailo-8L (13 TOPS) |

| MCU | ESP32-S3 + micro-ROS |

| LiDAR | LD06 (360°, 2D) |

| IMU | BNO055 (9-axis) |

| Drive | 4× N20 motors + Mecanum wheels + DRV8833 |

| Safety | Dual-channel relay (ISO 13850 E-stop) |

KiCad 8 PCB files, DXF mechanical drawings, and assembly docs all included.

## ROS2 Integration Details

For ROS2 developers who want to know what’s under the hood:

- **Navigation**: Nav2 with DWB planner, parameters tuned for mecanum kinematics

- **SLAM**: slam_toolbox (online async) with LD06

- **MCU bridge**: micro-ROS on ESP32-S3 via USB-C serial transport

- **Sensor fusion**: robot_localization EKF (IMU + wheel odometry)

- **Launch**: Composable nodes, configurable via YAML profiles

You can always drop down to raw ROS2 topics/services if needed — the Python API doesn’t hide or lock you out.

## Benchmark Suite

7 standardized scenes with reproducible baselines:

```bash

threewe benchmark run --task pointnav --scene office_v2 --episodes 100

```

Gymnasium-compatible environments for RL:

```python

import gymnasium as gym

env = gym.make(“3we/Navigation-v1”, scene=“office_v2”, backend=“mock”)

```

## Demo

![Navigation Demo](https://img.xuexiao.eu.org/1778836342754-19b737g.gif)

Autonomous point-to-point navigation in office_v2 scene with 360° LiDAR visualization.

## Links

- **GitHub**: GitHub - telleroutlook/3we-robot-platform: 3we Universal Modular Mobile Robot Platform — Open-source firmware, ROS2, hardware, and SDK · GitHub

- **Documentation**: https://3we.org

- **Paper**: Paper - 3we

- **PyPI**: `pip install threewe`

Feedback welcome — especially from Nav2 users on whether the API abstraction makes sense, and from AI researchers on what’s missing for their workflows.

Software: Apache 2.0 | Hardware: CERN-OHL-P v2 | Docs: CC-BY-SA 4.0

6 posts - 3 participants

Read full topic

by telleroutlook on May 15, 2026 03:47 PM

What patterns of logs or warnings should an automated bridge promote to structured faults in ROS 2?

We have been working on ros2_medkit, an Apache 2.0 fault aggregation gateway for ROS 2 that follows the SOVD model (ISO 17978-3). All of it lives in GitHub - selfpatch/ros2_medkit: ros2_medkit - diagnostics gateway for ROS 2 robots. Faults, live data, operations, scripts, locking, triggers, and OTA updates via REST API. No SSH, no custom tooling. · GitHub

Two integration paths today:

  1. /diagnostics topic - drop-in, no code changes on the publisher side. Works for any package already using diagnostic_updater.

  2. Native FaultReporter instrumentation - each failure surface emits a structured fault code directly. We tried this on a manymove fork to see how invasive it is to add per-action-node fault reporting. PR is here for reference: Feat/medkit integration by mfaferek93 · Pull Request #1 · selfpatch/manymove · GitHub - the integration itself was small (one mixin + a fault-codes header), but that fork is a fairly clean codebase. Most production stacks have a much messier RCLCPP_ERROR / RCLCPP_WARN history that nobody is going to retroactively convert.

Native FaultReporter is the right answer when you control the codebase end-to-end - structured codes from day zero, lowest friction long-term. The painful case is the long tail of existing ROS 2 packages that already work fine and never emitted /diagnostics. For those, the drop-in bridge has nothing to subscribe to, and asking maintainers to instrument every node won’t happen. If the goal is to make structured diagnostics adoptable across the ecosystem, plug-and-play needs to mean more than “use /diagnostics”.

That gap is what we want to validate with you.

We are considering a third path: a logs-to-faults bridge that watches /rosout (or arbitrary log streams) and promotes selected patterns to structured fault events, with configurable rules (severity mapping, dedup, rate limiting). Goal: a team can adopt structured diagnostics without touching their existing code. If it works out, it ships in the same open repo as the rest of medkit.

Three questions where your experience would help more than ours:

  • What log patterns in your stack would you actually want auto-promoted to structured faults? (specific examples > taxonomies)

  • What blocks your team from using /diagnostics more widely today?

  • For a logs-to-faults bridge to be useful and not noisy, what would have to be true? (rules engine, allowlist-only, ML, something else?)

Curious what others have tried, especially on the failure-modes side.

How do you currently surface fault/error state from your ROS 2 nodes?
How do you currently surface fault/error state from your ROS 2 nodes?
  • /diagnostics (DiagnosticArray)
  • Custom error/event topics
  • Logs (RCLCPP_ERROR / RCLCPP_WARN)
  • Action results / service error codes
  • Behavior tree / lifecycle state changes
  • Tracing / OpenTelemetry
  • No consistent pattern yet

Click to view the poll.

6 posts - 4 participants

Read full topic

by Michal_Faferek on May 15, 2026 10:04 AM

May 14, 2026
The accountability gap in ROS2: where does "why did the robot do that?" get answered?

A question I keep running into and don’t have a clean answer for: when a
ROS2-based autonomous system makes a consequential decision — a mobile
robot reroutes around a person, an arm stops mid-motion, a drone aborts —
we can answer what it did. ros2 bag captures the topics. But why it
did that, in a form a safety officer, an insurance adjuster, or a regulator
can read, is almost always reconstructed after the fact, by hand.

Four specific observations, curious where I’m wrong:

1. Rule provenance is invisible. When a BehaviorTree node fires, we
log the node, not the human-authored policy that made the node legal. No
first-class link from “robot stopped” to “rule §3.2 of safety policy v4
triggered.”

2. Guardrails are one-way safety, not auditable downgrades. Most ROS2
safety layers I’ve seen are kill switches or velocity caps. They prevent
harm but produce no signed record of “planner wanted X, guardrail
downgraded to Y, here’s the chain.”

3. LLM-in-the-loop adds a new failure mode. With VLA stacks plugging
into task planning, the “why” gets harder. Did the model suggest the
action? Was it followed, overridden, sanitized? I don’t see standard hooks
for any of this in the stack.

4. EU AI Act Article 12 and 14 are now in force for high-risk autonomous
systems.
Most teams I talk to plan to handle “logging” and “human
oversight” with ros2 bag plus a spreadsheet. That will not survive a
regulator audit, and CE marking deadlines for some categories hit in 2027.

Three questions for people deeper in this than me:

  • Is there an active REP or working group on decision provenance that I
    missed? I found scattered threads, no spec.
  • For Nav2 + BehaviorTree.CPP teams: how do you currently answer “why
    did the robot decide that?” for non-engineer stakeholders?
  • Has anyone added cryptographic signing to the rosbag pipeline, or is
    everyone trusting the filesystem and timestamps?

I’ve been building an opinionated implementation of some of this — rule
provenance, signed audit chain, guardrail-downgrade-only pattern, LLM
sanitization — outside of ROS2, and I’m trying to figure out if the pieces
that generalize are worth porting and open-sourcing.

If this resonates, drop a reply or DM. Looking for both “you’re missing
existing work X” and “yes this is broken in our deployment, here’s how.”

9 posts - 3 participants

Read full topic

by altunbulakemre75 on May 14, 2026 07:35 PM

Rviz_2d_plot_plugin: Live 2D Plotting Inside RViz 2

Hi everyone,

I’m happy to share an open-source RViz 2 plugin I have been working on:

rviz_2d_plot_plugin

The plugin provides live 2D plotting directly inside RViz 2 as a screen-space overlay. The goal is to make it easier to monitor ROS 2 topic data, controller signals, odometry-related values, diagnostics, and other runtime signals without leaving the RViz environment.

Some of the current features include:

  • Runtime discovery of plottable ROS 2 topic fields
  • Time-series plotting
  • XY plotting
  • Multi-series plotting from different topics
  • Reference lines, limits, setpoints, and tolerance bands
  • Axis control, auto-scaling, grid, legend, and styling options
  • QoS configuration
  • Pause, clear, and history preservation

The plugin is currently supported and tested on ROS 2 Humble. Support for additional ROS 2 distributions is planned and will be released soon.

The project is released under the MIT license.

I would be happy to receive feedback from the ROS community, especially regarding:

  • API/design improvements
  • Compatibility with other ROS 2 distributions
  • Useful plotting features for robotics debugging workflows
  • Packaging and release suggestions

Repository:

Thanks, and I hope this can be useful for others working with RViz 2 and ROS 2 system visualization.

5 posts - 3 participants

Read full topic

by Abdelrahman on May 14, 2026 03:58 PM

How long did your first cross-device ROS2 setup take?

Setting up ROS2 across multiple devices (different boards, distros, DDS config) for the first time — how many hours or days did it take before you had nodes talking reliably?

Specifically curious about:

  • Device combo (RPi + Jetson, x86 + ARM, etc.)

  • What broke (DDS discovery, distro mismatch, network config?)

  • Rough time lost before it worked

Building a scaffolding tool and want real data, not estimates.

1 post - 1 participant

Read full topic

by ambigram on May 14, 2026 03:58 PM

MBF - My quadruped robot dog

Building a quadruped robot dog has been a personal goal of mine ever since I started engineering. Over the past months (or even years), I’ve been working on MBF, an open-source quadruped robotics platform designed and built entirely from the ground up using affordable and accessible hardware.

This project has pushed me to learn across multiple engineering domains simultaneously — from mechanical design and fabrication to embedded communication, robotics middleware, and locomotion software architecture.

Everything on the robot was independently designed and integrated by myself, including:

  1. Mechanical design and CAD
  2. 3D-printed structural components
  3. Parts sourcing and assembly
  4. Electrical wiring and CAN bus communication
  5. ROS2-based software architecture and control stack
    **
    Current Hardware Highlights:**
  6. Affordable quadruped platform with predominantly 3D-printed components
  7. GIM6010-8 planetary drive actuators running FOC control over CAN bus
  8. Design-for-Assembly (DFA) considerations such as standardized screw sizing and modular assembly layout
  9. Entire robotics stack running on a single Raspberry Pi 4 without additional microcontrollers (selectable inference using either ONNX or Torch C++)

Current Software Stack:

  1. Custom ROS2 joint impedance controller
  2. Custom ros2_control hardware interface over CAN bus
  3. Integration with the CHAMP framework
  4. Extensible reinforcement learning inference node
  5. ROS2/Gazebo simulation workflow for sim-to-sim locomotion deployment withe easy switch to real hardware with a simple argument change

One thing this project taught me is that modern robotics is deeply built on open-source collaboration. A lot of the knowledge, frameworks, and tools used throughout MBF came from developers and researchers who chose to make their work publicly accessible.

While I’m proud of the progress made so far, this project is also a reminder that meaningful robotics development has become more accessible than ever. With enough dedication and willingness to learn, it’s genuinely possible for individuals to build complex systems today thanks to the open-source community. What I am doing here today is to merely return that favor back.

The robot is still actively being developed, but seeing the full software stack communicate reliably with custom hardware has been incredibly rewarding so far.

For more info, please check:
github.com/adwng/mbf_ros2

1 post - 1 participant

Read full topic

by adwng on May 14, 2026 03:57 PM

Polka - Your everything pointcloud node Release v0.2

Polka was intended to be a low latency pointcloud merger which also publishes laser scans, provides you with granular control over filtering, even deskewing, here are some features and bug fixes that have been implemented in the latest release.

pipeline_demo (1)

New Features

  • Per-source IMU topic override: each LiDAR source can specify its own imu_topic for robots with multiple IMUs on different body segments. Falls back to global motion_compensation.imu_topic when unset.

  • Gravity subtraction in deskew: linear acceleration corrected by removing gravity using IMU orientation, improving motion compensation accuracy.

  • Configurable output QoS: full QoS control (reliability, durability, history depth, liveliness, deadline, lifespan) via outputs.cloud.qos / outputs.scan.qos parameters.

  • Multi-LiDAR deskew example config in config/example_params.yaml.

Bug Fixes

  • Fix IMU-to-sensor frame rotation in deskew: angular velocity and acceleration now rotated from IMU frame into each sensor’s frame via TF. Previously only sensors aligned with the IMU got correct deskewing. (fixes #3)

  • Fix degenerate quaternion fallthrough: zeroes acceleration instead of passing raw gravity through when IMU orientation is degenerate.

  • Fix thread safety in SourceAdapter: mutex protection for frame_id/timestamp during concurrent deskewing.

  • Fix stale IMU timestamps: removed dead average_imu(), simplified to atomic snapshot pattern.

  • Fix duplicate missing-intensity warning.

  • Add CUDA error checking in merge engine kernels.

Improvements

3 posts - 2 participants

Read full topic

by Panav on May 14, 2026 12:03 AM

May 13, 2026
jros2 Cellphone Sensor Bridge: Native ROS 2 nodes on Android

Hey everyone,

I was recently reading a book on ROS 2 and went looking for ros2_java. After finding out that project essentially died, I discovered jros2 and decided to just build an app to test its limits.

The result is the jros2 Cellphone Sensor Bridge. It is an Android (Jetpack Compose) application that exports live phone sensor telemetry to ROS 2 using standard sensor_msgs, std_msgs message types, and custom mobile_sensor_msgs definitions over the IHMC jros2-android stack (Fast DDS + JavaCPP JNI).

How it works:

  • It initializes a fully compliant ROS 2 Node (phone_sensor_node) directly on your physical Android smartphone.

  • It streams real-time, high-frequency telemetry from 11+ hardware/software sensors and input devices (IMU, Magnetometer, GPS, Touch Screen, Dual Cameras, etc.).

  • It employs an Android multicast Wi-Fi lock to ensure robust, real-time discovery of DDS participants directly on the local network without intermediary servers.

Here is a quick demo showing the phone acting as a dual-joystick controller and streaming data: https://www.youtube.com/watch?v=skNQdbO8yrw

Repo, architecture details, and the v1.1.0 APK are available here: https://github.com/SinfonIAUniandes/jros2_cellphone_interface/tree/main

1 post - 1 participant

Read full topic

by thegrayguy on May 13, 2026 10:40 PM


Powered by the awesome: Planet