Planet ROS
Planet ROS - http://planet.ros.org
Planet ROS - http://planet.ros.org
http://planet.ros.org
ROS Discourse General: Pre-announcing ROSCon China 2026
Hi everyone,
We are thrilled to announce that ROSCon China 2026 is officially on the horizon! As one of the most vibrant regional ROS events, ROSCon China continues to bring together developers, researchers, and industry leaders to share the latest advancements in the ROS ecosystem, embodied AI, robotics technologies and commercial robotics deployment.
Whether you are a seasoned core contributor, an enterprise, or a student diving into robot, this event is designed for you.
Event Details
-
Dates: October 16-17, 2026
-
Location: Shanghai, China
-
Format: In-person with live-streaming available
Call for Proposals (CFP)
The official website, speaker application portal, and early-bird registration link will be launched very soon. We are looking for technical presentations, case studies, and lightning talks!
Stay tuned for the official links which we will append to this post. In the meantime, feel free to drop any questions or suggestions in the thread below.
Looking forward to seeing the ROS community gather in China!
Best regards,
Xinyu Zhang
ROS Education Foundation in China
1 post - 1 participant
ROS Discourse General: ROS 2 Lyrical Luth Released! :tada:
ROS 2 Lyrical Luth Released
Happy day before World Turtle Day! Today the ROS 2 Release Team is happy to announce the twelth release of ROS 2: Lyrical Luth (codenamed lyrical).
In addition to the official logo shared previously, we also have a new Lyrical Luth turtlesim and Discourse icon :lyrical: :lyrical: (pending). Our Lyrical T-shirt and swag campaign is still live! You can still grab all the cool merch if you want to support the release. All proceeds help support the Open Source Robotics Foundation (OSRF).
Lyrical Luth is a long term support (LTS) release that will be supported until May 2031. The distribution supports the following platforms:
- Tier 1 platforms:
- Ubuntu 26.04 (Resolute): amd64 and arm64
- Windows 11 (Visual Studio 2022): amd64
- Tier 2 platforms:
- RHEL 10: amd64
- Tier 3 platforms:
- Ubuntu Noble (24.04)
- macOS
- Debian Trixie
- OpenEmbedded
For more information about RMW implementations, compiler/interpreter versions, and system dependency versions please see Lyrical Luth Supported Platforms and Platform Support Tiers.
If you are new to ROS, we recommend trying Lyrical Luth on a Tier 1 supported platform. Check the installation instructions and tutorials on docs.ros.org, and give Lyrical a spin!
Should you run into any difficulties, please ask a question on ROS Stack Exchange or let us know on Zulip. If you would like to try Lyrical but are afraid of commitment you can find Lyrical Docker containers on Docker Hub shortly.
New Features and Enhancements
Lyrical Luth is feature packed and full of improvements! If you want the gritty details feel free to peruse the release notes and complete changelog for core ROS 2 packages. We’ve summarized a few new features we’ll think you’ll love below.
New Executors Deliver Improved Performance
Looking for better executor performance? Check out the new Callback Group Events Executor. Like its predecessor the EventsExecutor, the EventsCBGExecutor uses an events queue to process ready entities. However, EventsCBGExecutor adds support for multiple sources of ROS time and multiple threads. Compared to the Single and Multithreaded executors, the EventsCBGExecutor uses 10% to 15% less CPU! If you are using composable nodes you can Launch a component container with the EventsCBGExecutor using the new --executor-type argument
We want to send a big shout out to Janosch Machowinski, Skyler Medeiros, and Maurice Alexander Purnawan for making this performance improvement happen!
Asyncio in RCLPy with Improved Performance!
Want to use asyncio and rclpy at the same time? Well now you can thanks to a pull request from Nadav Elkabets! Check out our new AsyncNode class. This node runs an asyncio event loop allowing you to call await on any asyncio operation from any ROS subscription, service, or timer callback. Try await client.call(request) to wait for service calls, and the sim-time aware await clock.sleep(...). Best of all, this class uses significantly less CPU compared to the default SingleThreadedExecutor.
To get started check out our Python asyncio Node Tutorial on docs.ros.org.
rosidl::Buffer Improvements Facilitate Zero-Copy GPU Data Transfer
Moving ROS data to or from a GPU just got a lot faster and easier in ROS 2 Lyrical Luth! If your ROS application uses a GPU for data processing you’ll want to upgrade as soon as possible to take advantage of our new zero-copy data transfer capabilities.
Are you publishing data on ROS topics, but using that data elsewhere, like a GPU? Tired of copying data out of the GPU before publishing just to copy it back into the GPU in the subscriber? You can now use rosidl::Buffer to publish and subscribe to ROS messages without performing costly data copy operations. You can learn all about this exciting new feature and how to use it to improve your GPU performance on on docs.ros.org.
To make this performance improvement possible all uint8[] fields now have the type rosidl::Buffer<uint8_t> in C++ instead of std::vector<uint8_t>. You must define your ROS messages with uint8[] fields and install an appropriate rosidl::BufferBackend implementation to take advantage of this feature. Note that only publishers and subscribers using rmw_fastrtps_cpp may use this feature for now, but support in Zenoh is coming soon!
Using a custom hardware accelerator or machine learning library? You can benefit from this too. See Writing a rosidl::Buffer backend to learn how to implement your own rosidl::BufferBackend.
Big thanks to our colleagues at NVIDIA, particularly CY Chen for making this improvement happen!
Miscellaneous Features
ROS 2 Lyrical Luth comes with a ton of other great new features and we’ve compiled a partial list of them below. Remember, if you are coming from Jazzy (or even Humble or Foxy) you’ll also get all of the great features we shipped in Kilted Kaiju.
- Callback Group Events executor (rclcpp)
- Parameter range descriptors check bounds for integer and double arrays (rclcpp)
- AsyncNode lets you use asyncio (rclpy)
- Publish messages without copying data using
rosidl::Buffer - Use YAML tags in parameter files
- More logging options in launch files
- New substitutions in XML and YAML launch files
- Choose ROS logging backend at runtime
- Control bag recording remotely using ROS services
- Control bag Playback and Recording using Python
- Circular recording by bag split
- More descriptive bag split names
- rosbag2 message-loss observability
- fish shell support
ros2 param geta parameter from all nodesros2 param getandsetmultiple parameters on one node- Actions, Services, and Environment variables in
ros2 doctor --report - Verbose service information
ros2 service info --verbose ros2 topic bwmultiple topics at once- URDF improvements
robot_state_publishercan read the robot description from a topic- Resource retriever service
- Call ament_python_install_package multiple times
- New CMake target:
ament_cmake_ros_core::ament_ros_defaults - New thread naming utilities
- New rcutils APIs
- New
rclAPIs - Pass constructor arguments to plugins using
class_loader - Runtime tracing opt-out mechanism
- Long-term tracing improvements
A Community Effort
ROS 2 is truly a community effort, and this could not have been better exemplified by our public beta testing of Lyrical Luth over the past few weeks. Almost 2651 test cases were put through the ringer with several improvements identified along the way. It really helped “harden” the Lyrical distribution. The release team would like to extend a heartfelt thank you to all testers. For our top twenty testers you should get an e-mail in your inbox with a swag coupon code sometime later next week. A full list of our 110 testers can be found below.
Thank you to all 110 T&T Party Contributors! (click for more details)We also want to thanks the 238 contributors who contributed to this release through code changes, documentation, and testing, and especially to our ROS Boss @sloretz! The following individuals made contributions to the Lyrical release:
A big thank you to our 239 contributors to Lyrical Luth! (click for more details)Finally, we’d like to announce the name of the next ROS 2 release for May 2027:
Makoa Mata-mata 
Makoa is a Hawaiian word that means fearless or courageous and the Mata-Mata is a South American species of freshwater turtle found in the Amazon basin and river system of the eastern Guianas.
7 posts - 4 participants
ROS Discourse General: Build a MuJoCo + ROS2 Robotic Arm Workflow for Embodied AI
MuJoCo Simulation and ROS2 Control Workflow | Piper Robotic Arm for Embodied AI
This project is a MuJoCo-based simulation foundation for the Piper robotic arm. It is designed to provide a general-purpose MuJoCo simulation environment for the Piper arm, with support for robot control testing and simulation workflow development.
Project has already completed
- the launch of the C++ based simulation environment
- control testing of the simulated robot using a test ROS 2 control plugin
Recommended Environment
- Operating System: Ubuntu 22.04 or compatible Linux distribution
- ROS Version: ROS 2 Humble
- Compiler: C++17 or later
Reference & Resources
- Reference Project:
https://github.com/unitreerobotics/unitree_mujoco - PiPER robotic arm platform:
https://global.agilex.ai/products/piper
The following sections provide step-by-step guidance for building the current version of the Piper MuJoCo simulation environment and using the ROS 2 test plugin to control the robot in simulation.

1.Project File Structure
agilex@agilex-Bellator-N176B:~/project/agilex_arm_mujoco$ tree -L 3
├── agilex_arm
│ └── agilex_piper
│ ├── assets
│ ├── piper_body.xml
│ ├── piper.png
│ ├── piper.xml
│ └── scene.xml
├── images
│ ├── mujoco_piper.png
│ └── piper.png
├── README_EN.md
├── readme.md
└── simulate
├── CMakeLists.txt
├── config.yaml
├── mujoco
│ ├── bin
│ ├── include
│ ├── lib
│ ├── model
│ ├── sample
│ ├── simulate
│ └── THIRD_PARTY_NOTICES
└── src
├── lodepng
├── main.cc
├── param.h
├── plugin_manager.cc
├── plugin_manager.h
├── ros2_control_plugin.cc
├── ros2_control_plugin.h
└── sim_plugin.h
2.Project Setup & Build
2.1 Build the agilex_arm_mujoco Project
The build process for the agilex_arm_mujoco project follows the same workflow as the unitree_mujoco project.
This section provides a step-by-step guide to setting up the simulation base, including optional instructions for enabling the ROS 2 control plugin.
Step 1.Clone the Repository
First, create a working directory and clone the project repository:
mkdir agilex_arm && cd agilex_arm
git clone https://github.com/yanyuze1/agilex_arm_mujoco.git
By default, the project includes MuJoCo 3.3.0 for ROS 2 control testing.
If you want to use a different MuJoCo version, download and replace it manually before building.
Step 2. Two Build Modes
The agilex_arm_mujoco project supports two build modes:
- Option 1: Build without the ROS 2 plugin
- Option 2: Build with the ROS 2 plugin enabled
Choose the mode based on your development needs.
Option 1: Without the ROS 2 Plugin
- Step 1: Edit
agilex_mujoco/simulate/CMakeLists.txt, change the default enabled ros2 plugin mode toOFF
# from
option(AGILEX_ENABLE_ROS2_CONTROL "Build ROS2 control plugin" ON)
# change to
option(AGILEX_ENABLE_ROS2_CONTROL "Build ROS2 control plugin" OFF)
- Step 2: Build the Project. After editing the file, build the simulation base:
cd agilex_arm_mujoco/simulate
mkdir build && cd build
cmake ..
make -j4
Option 2: With the ROS 2 Plugin Enabled
If you want to test robot control through the ROS 2 plugin, keep the default configuration and build the required ROS 2 dependencies first.
- Step 1: Clone the Dependency Workspace
Create or enter theagilex_armdirectory and clone the repository:
cd agilex_arm
git clone https://github.com/yanyuze1/agilex_ws.git
- Step 2: Build ROS 2 Dependencies
Build the required packages in the ROS 2 workspace:
cd agilex_ws
colcon build --symlink-install --packages-up-to mujoco_ros2_control agilex_piper_mujoco
source install/setup.bash
- Step 3: Build the Simulation Base
After the dependency build is complete, compile the simulation project:
cd agilex_mujoco/simulate
mkdir build && cd build
cmake ..
make -j4
2.2 Run the agilex_arm_mujoco Project
The agilex_arm_mujoco project provides two runtime modes:
- Start the simulation base only
- Start the simulation base with the ROS 2 plugin enabled
Choose the mode according to your development and testing needs.
2.2.1 Start the Simulation Base Only
If you only want to launch the simulation environment without ROS 2 integration, use the following command:
./agilex_mujoco -h
./agilex_mujoco -r piper -s scene.xml
./agilex_mujoco -r piper -s scene.xml -p ros2_control
After successful execution, the MuJoCo simulation environment will launch automatically, displaying the Piper robotic arm in the MuJoCo simulation scene.
2.2.2 Start the Simulation Base with the ROS 2 Plugin Enabled
If you only want to launch the simulation environment with ROS 2 integration, use the following command:
cd agilex_ws
source install/setup.bash
ros2 launch agilex_piper_mujoco bringup_agilex_mujoco_cartesian_motion_controller.launch.py agilex_mujoco_exec:=/home/agilex/project/piper/agilex_mujoco/simulate/build/agilex_mujoco # The current project uses absolute paths. Please modify them according to your own local path.
When the launch is successful, a MuJoCo simulation window will open and display the Piper robotic arm in the simulation environment.
Once the environment is fully loaded, you can start sending ROS 2 control commands.
Usage Situation
1. Send a Target End-Effector Pose
Use the following command to publish a target pose for the end effector:
ros2 topic pub --once /agilex_piper_cartesian_motion_controller/target_frame \
geometry_msgs/msg/PoseStamped "{
header: {frame_id: 'base_link'},
pose: {
position: {x: 0.2, y: 0.0, z: 0.2},
orientation: {x: 0.0, y: 1.0, z: 0.0, w: 0.0}
}
}"

2. Control the Gripper
# Open the Gripper
ros2 topic pub --once /agilex_piper_gripper_position_controller/commands \
std_msgs/msg/Float64MultiArray "{data: [0.035, -0.035]}"
# Close the Gripper
ros2 topic pub --once /agilex_piper_gripper_position_controller/commands \
std_msgs/msg/Float64MultiArray "{data: [0.0, 0.0]}"

3. Check the Robot State
To view the current end-effector pose, use:
ros2 topic echo /agilex_piper_cartesian_motion_controller/current_pose
3.Afterword
This project is built upon and integrated from several open-source repositories. The main credit goes to the original authors and the open-source community.
We would like to sincerely thank the open-source community for making these technologies accessible through shared code and practical experience.
MuJoCo + ROS2 Robotic Manipulation FAQ
1. Why use MuJoCo for the Piper robotic arm instead of Gazebo or Isaac Sim?
MuJoCo provides a lightweight and high-performance physics simulation workflow, making it ideal for rapid robotics prototyping, control validation, and future reinforcement learning research. Compared with heavier simulators, it can run efficiently on more accessible hardware.
2. Can this simulation workflow be transferred to a real Piper robotic arm?
Yes. The ROS2 control architecture is designed to align simulation and real hardware interfaces, making future sim-to-real deployment more practical.
3. Why build a custom MuJoCo + ROS2 workflow instead of using existing robotics simulators?
This project focuses on providing a simpler and more flexible robotics development workflow with lower hardware requirements and easier customization for developers and researchers.
4. Is this MuJoCo robotic arm project suitable for reinforcement learning (RL)?
Yes. The simulation framework can serve as a foundation for reinforcement learning workflows such as PPO training, imitation learning, grasping policy development, and sim-to-real robotic manipulation research.
5. How does ROS2 Control work with MuJoCo simulation?
The project integrates ROS2 Control plugins into the MuJoCo simulation environment, allowing developers to send ROS2 motion commands directly to the simulated robotic arm and receive real-time robot state feedback.
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
ROS Discourse General: Medium articles with overview of useful ROS 2 packages
Hi community.
I have published two articles on useful tools for ROS 2 on Medium. First article on TUI based packages and second article is about web based packages. These articles are presented as short overview and hands-on tutorials on several packages from specific category.
Looking for your comments. Hope it will be useful for everyone in the community.
1 post - 1 participant
ROS Discourse General: 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
ROS Discourse General: [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
ROS Discourse General: 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:
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

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.
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.
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.
3 posts - 2 participants
ROS Discourse General: 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.

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
ROS Discourse General: Hello from a ROS 2 Learner & Wheelchair Robot Builder
Hello ROS Community! ![]()
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
ROS Discourse General: 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 ![]()
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: June 20th, 2026
Location: Milan, Italy
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 ![]()
1 post - 1 participant
ROS Discourse General: 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:
- Working environment? (Indoor / Outdoor / Semi-outdoor)
- Extreme conditions? (Rain, dust, high/low temperature)
- 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
ROS Discourse General: 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
ROS Discourse General: 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 forSYSTEM_DEFAULTpolicies. - CycloneDDS:
CYCLONEDDS_URIcontrols participant-level settings (network, threads), not endpoint QoS (reliability,
durability, depth). No equivalent mechanism exists. - Zenoh (
rmw_zenoh): end-to-end reliability forRELIABLE+VOLATILE+KEEP_LASTis 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
rclbefore handing off to the RMW. - Or alternatively, a formal contract in the RMW interface spec requiring each implementation to document how it resolves
SYSTEM_DEFAULTpolicies 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
ROS Discourse General: 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 interventionmode:=passthrough— wiring test,/safe_cmd_velequals/cmd_velmode:=guard— active mode, can clamp velocity or enterBRAKE_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
ROS Discourse General: 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
ROS Discourse General: Pre-release Distribution Freeze for Lyrical Luth
As we prepare to launch ROS Lyrical Luth later this week, effective immediately we are
freezing
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
ROS Discourse General: 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
-
API docs: https://roboinfra-dashboard.azurewebsites.net/docs
-
Python SDK: https://pypi.org/project/roboinfra-sdk/
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
ROS Discourse General: 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:
- 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.
- 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.
- 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.
- 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
ROS Discourse General: 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:
-
Where tools like this can genuinely help the ROS 2 ecosystem
-
Where we should be careful not to abstract too much away
-
What experienced ROS users would expect from a platform like this
-
What would make it useful for education, prototyping or early-stage robot development
-
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
ROS Discourse General: 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:
-
Robot encounters a real-world failure in production (ex: reflective pallet wrap, localization drift, perception miss, repeated recovery loop)
-
Team saves that incident log
-
Engineer makes changes to the autonomy stack
-
CI runs replay tests against previously recorded failure scenarios
-
Tool verifies whether known failures were reintroduced
-
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:
-
Is this a painful problem at your company, or do most teams already have internal tooling for this?
-
Would rule-based validation be useful, or would teams strongly prefer baseline-vs-baseline output comparison?
-
Is requiring Docker too restrictive?
-
What are the biggest reasons this would fail in your environment?
-
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
ROS Discourse General: 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:
- Does the topic structure and message types fit typical robotics pipelines?
- 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
ROS Discourse General: 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

Autonomous point-to-point navigation in office_v2 scene with 360° LiDAR visualization.
## Links
- **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
ROS Discourse General: 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:
-
/diagnosticstopic - drop-in, no code changes on the publisher side. Works for any package already usingdiagnostic_updater. -
Native
FaultReporterinstrumentation - 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 messierRCLCPP_ERROR/RCLCPP_WARNhistory 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
/diagnosticsmore 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.
- /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
6 posts - 4 participants
ROS Discourse General: 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.”
10 posts - 3 participants
ROS Discourse General: 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








