July 06, 2026
Adaptation needs in robotic systems

Hi robotic developers,

Are you working with Behavior Trees, robotics, ROS/ROS 2, autonomous systems, or robot decision-making?

We are conducting a research study on adaptation needs in robotic systems that use Behavior Trees. If you work with robotic systems, we’d greatly appreciate your input.

We’re conducting a short survey to answer two key questions:

  • The relevance of different adaptation needs in robotic systems.

  • The suitability of different BT adaptation approaches for addressing those needs.

Survey: Adaptation Needs in Robotic Systems: Behavior Trees and Beyond (Questionnaire)

It takes about 20 minutes to complete, and as a bonus, you’ll also get an overview of adaptation needs in robotics, the current limitations of Behavior Trees, and the research efforts to address them.

3 posts - 2 participants

Read full topic

by Mehran on July 06, 2026 12:51 PM

July 04, 2026
Announcing ros-env: Access ROS 2 messages from Rust with just Cargo

Hey all! I’m one of the maintainers of ros2_rust, and we recently released a new crate, ros-env, that aims to unify ROS 2 message consumption in Rust.

If you’re already generating Rust code for your message interfaces with rosidl_generator_rs, you can add ros-env as a regular Cargo dependency and access the message types from any packages in your sourced ROS 2 environment.

// Assuming the Rust crate for `shape_msgs` is in `AMENT_PREFIX_PATH`
use ros_env::shape_msgs::msg::Plane;

You don’t need Colcon, and you don’t need to use ros2_rust. The goal is to provide a common way for Rust ROS 2 clients to consume and share message definitions.

Happy to answer any questions!

1 post - 1 participant

Read full topic

by maspe36 on July 04, 2026 05:32 PM

Real-time robot dashboards in React without a separate visualization server

If you’ve built an operator UI or ground station in React, you’ve probably hit the same wall I did: the moment you need real-time telemetry visualization, your options are to run Foxglove or Grafana as separate infrastructure and iframe them in, or hand-roll canvas components from scratch. Neither is great when what you actually want is telemetry views that live natively inside the app you’re already building.

So I built Altara: a set of MIT-licensed React components for real-time telemetry, with a one-line rosbridge adapter so you can pipe a sensor_msgs topic straight into a chart, gauge, or attitude indicator.

A minimal example:

tsx

import { AltaraProvider, TimeSeries } from '@altara/core'
import { createRosbridgeAdapter } from '@altara/ros'

const imu = createRosbridgeAdapter({
  url: 'ws://localhost:9090',
  topic: '/imu/data',
  messageType: 'sensor_msgs/Imu',
})

<AltaraProvider theme="dark">
  <TimeSeries dataSource={imu} height={240} />
</AltaraProvider>

It’s a monorepo of six packages: @altara/core (the base components), @altara/ros (rosbridge adapter with typed factories for common sensor_msgs types), @altara/mqtt, and three domain packages, @altara/aerospace (PFD, HSI, TCAS and other flight instruments), @altara/av (LiDAR point cloud via Three.js, occupancy grid, SLAM map), and @altara/industrial (SCADA-style panels, waterfall spectrogram, alarm annunciator). 41 components total, every one runs in a mockMode so you can build and demo without hardware.

Everything renders to canvas via requestAnimationFrame with the hot path kept out of React, so high-frequency streams don’t cause re-render jank.

It’s early and I’m the only maintainer, so I’m being upfront about that. The architecture is deliberately simple, plain canvas and React, no exotic dependencies, so it’s straightforward to fork and self-maintain if that matters to you.

Website: https://www.usealtara.dev/

Repo: GitHub - JayaSaiKishanChapparam/altara: React components for real-time telemetry dashboards — robotics, aerospace, autonomous vehicles, industrial IoT. PFD, HSI, time-series, gauges, GPS maps, and ROS2/MQTT adapters at 60fps. · GitHub
Live Storybook (all 41 components): @storybook/core - Storybook

Demo: Altara Demo

What are you currently using for in-app telemetry visualization, and where does it fall short? Trying to understand what’s actually missing for people building custom operator interfaces.

2 posts - 2 participants

Read full topic

by Jaya_Sai_Kishan on July 04, 2026 01:04 PM

July 02, 2026
Plotjuggler 4 (beta!) is here. Unleashing mulit-modal data

After few months of hard work, PlotJuggler 4 is finally ready for an early preview!

Expect bugs and missing features, but also ton of awesomeness unleashed.

Everything was brutally optimized. Playing a compressed MCAP, reading lazily the file from disk, and rendering 3 videos and the two 3D scenes above, for a total of 6 pointclouds, uses only 50% of a single CPU core.

Features: too many to list

  • 2D: images, compressed images, depth, compressed video (H264, AV1).
  • 2D: image rectification!
  • 2D: real-time streaming with WebRTC!
  • 2D: markers!
  • 3D: meshes, occupancygrid, TF2, 3D markers
  • 3D: pointclouds, including compressed ones (Draco and Cloudini)
  • 3D: multiple cameras control, similar to RViz

On the “core” side

  • Refactor data engine that will use up to 5x less memory when loading large datasets.
  • To be opened soon: a marketplace of “Extensions” (think VSCode equivalent) to share plugins with others.
  • a new parallel MCAP loader that can load compressed MCAPs about 4X faster.

And (this is big) integration with Mosaico to access directly data stored in the cloud.

4 posts - 3 participants

Read full topic

by facontidavide on July 02, 2026 11:44 AM

Mastering NERO | How to Configure CAN Leader–Follower Linkage for Dual 7-DoF Robotic Arms

Dual-arm robots are becoming increasingly important in embodied AI, teleoperation, imitation learning, and collaborative manipulation research.

This tutorial demonstrates how to configure two AgileX NERO 7-DOF robotic arms in a leader-follower setup using CAN bus communication. Once configured, the follower arm will automatically replicate the motion of the leader arm in real time.

What You’ll Build

By the end of this guide, you’ll be able to:

:white_check_mark: Synchronize two NERO robot arms

:white_check_mark: Enable real-time leader-follower motion following

:white_check_mark: Configure CAN-based dual-arm communication

:white_check_mark: Validate coordinated dual-arm operation

:white_check_mark: Prepare a platform for teleoperation and imitation learning experiments

Hardware Requirements

Component Quantity
NERO 7-DOF Robotic Arm (Leader) 1
NERO 7-DOF Robotic Arm (Follower) 1
CAN Communication Cable 1
NERO Control Software 1

Step 1: Connect the CAN Bus

The first step is connecting the CAN communication lines between the two robot arms.

Wiring Rules

Wire Color Signal
Yellow CAN H
Blue CAN L

Connect:

  • CAN H ↔ CAN H

  • CAN L ↔ CAN L

Important

Before powering on:

  • Verify all connectors are secure.

  • Ensure there are no loose contacts.

  • Check that CAN H and CAN L are not reversed.

Incorrect wiring may prevent communication between the two robotic arms.

Step 2: Configure Leader and Follower Modes

Once the CAN bus connection is complete, assign the role of each robot arm.

Leader Arm

The leader arm acts as the command source.

Responsibilities:

  • Generates motion commands

  • Broadcasts joint states

  • Controls overall synchronization

Follower Arm

The follower arm executes the motion commanded by the leader arm.

Responsibilities:

  • Receives leader motion data

  • Replicates joint trajectories

  • Mirrors the leader’s pose in real time

After configuration, save all parameters before proceeding.

Safety Warning Before Activation

:warning: Read This Before Enabling Leader-Follower Mode

Before assigning leader-follower roles:

  1. Move both robotic arms close to their home positions.
  2. Ensure the workspace is clear.
  3. Remove any obstacles around the robot.

When synchronization is activated, the follower arm immediately attempts to match the leader’s current pose.

If the initial poses differ significantly, the follower arm may move rapidly, potentially causing:

  • Robot collisions

  • Pinch hazards

  • Hardware damage

  • Personal injury

For safe operation, always align both robots before enabling synchronization.

Step 3: Validate Leader-Follower Synchronization

After configuration, verify that communication and synchronization are functioning correctly.

Validation Procedure

  1. Check CAN Connections

Verify:

  • CAN H is connected correctly

  • CAN L is connected correctly

  • No loose cables exist

  1. Verify Control Modes

Confirm:

  • Leader mode is enabled
  • Follower mode is enabled
  • Parameters have been saved successfully
  1. Move the Leader Arm

Manually operate the leader arm.

  1. Observe the Leader Arm

The Leader arm should:

  • Follow every joint movement
  • Replicate end-effector trajectories
  • Maintain smooth synchronized motion

Successful tracking indicates that Leader-Follower control is operating correctly.

Software Version Notes

Version 1.1 API Limitation

For NERO software version 1.1:

When Leader-Follower mode is enabled:

  • API access is limited to commands issued by the leader arm.
  • Independent follower-arm state information cannot be queried separately.

Developers building custom applications should account for this behavior when logging data or implementing monitoring systems.

Conclusion

Using a simple CAN bus connection, two NERO 7-DOF robotic arms can be configured into a synchronized leader-follower system capable of real-time trajectory replication and coordinated motion control.

This setup is particularly useful for:

  • Embodied AI development
  • Teleoperation platforms
  • Imitation learning pipelines
  • Robot data collection
  • Research and education

By following the wiring, configuration, and safety recommendations in this guide, developers can quickly deploy a reliable dual-arm robotic platform for experimentation and application development.

FAQ

臂gif

Q1:Why does the follower arm move suddenly when leader-follower mode is enabled?

When leader-follower mode is activated, the follower arm immediately attempts to match the current pose of the leader arm.

If the two arms start from significantly different positions, the follower arm may perform a rapid corrective movement.

Q2:The follower arm is not following the leader arm. What should I check first?

Verify the following items:

  • CAN H and CAN L are connected correctly.
  • Leader and follower modes are configured properly.
  • Configuration settings have been saved.
  • The CAN cable is firmly connected.
  • Both robot arms are running compatible software versions.

In most cases, incorrect CAN wiring or unsaved configuration parameters are the root causes.

Q3:Why does the follower arm stop responding after working correctly for a period of time?

This behavior is typically caused by:

  • Loose CAN connectors
  • Unstable power supply
  • CAN bus communication interruptions
  • Software configuration changes

Check cable integrity, power stability, and communication status before restarting the system.

Q4:What should I do if the follower arm’s motion does not exactly match the leader arm?

Check the following:

  • Joint calibration status
  • Home position accuracy
  • Mechanical interference
  • Firmware version consistency
  • CAN communication quality

Small tracking errors can often be reduced by recalibrating both robot arms and ensuring they start from similar initial poses.

:speech_balloon: 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

Read full topic

by Agilex_Robotics on July 02, 2026 03:02 AM

June 30, 2026
ros2_canopen: Natively Integrating CANopen Devices into the ROS 2 Ecosystem

CANopen has long been one of the most widely used communication standards in industrial automation. Built on top of the CAN bus and standardised by CAN in Automation (CiA), it connects motor drives, I/O modules, sensors and other field devices across machines, robots and vehicles. The ros2_canopen stack, maintained under the ROS-Industrial umbrella, brings this ecosystem natively into ROS 2. It lets developers describe a CAN bus, bring up a CANopen master, and talk to every device on the bus through standard ROS 2 nodes, services, topics and ros2_control interfaces.

Built on the lely-core from Lely Industries N.V.

Rather than reimplementing the CANopen protocol, ros2_canopen builds on lely-core, the professional, open-source CANopen library from Lely Industries N.V. lely-core handles the demanding low-level work: the CANopen event loop, NMT state management, SDO and PDO communication, and a configuration toolchain that turns a human-readable bus description into the device configuration files (DCF) the master needs at runtime.

Basic capability

At the heart of the stack is a device container that reads a single YAML bus description. In that file you declare each node on the bus (its node ID, its EDS file, and the driver to load for it) together with any parameters that override the device defaults. From that one description, the container launches the CANopen master and the per-device drivers.

To accommodate a wide range of industrial use cases, ros2_canopen offers three flexible operation modes depending on your application's requirements:

  • Standard ROS 2 Nodes: Best for simple setups. Each CANopen device is run as a standard ROS 2 node, communicating through basic topics and services.
  • Managed Lifecycle Nodes: Adds system reliability and recovery. Devices are wrapped as lifecycle nodes, allowing a manager to bring the entire CAN bus online or offline in a precise, safe sequence.
  • ros2_control Integration: Built for high-performance and low-latency control. This mode exposes CANopen devices directly as hardware interfaces for the broader ROS 2 control framework.

To interface with your hardware, the stack provides two primary drivers out of the box:

  • The Proxy Driver: A generic bridge that forwards raw CANopen messages (such as SDOs and PDOs) to and from ROS 2. It is perfect for custom sensors, debugging, or devices without a standardized profile.
  • The CiA 402 Driver: A specialized motion-control driver that implements the industry-standard profile for motor drives and servo controllers, allowing you to command positions, velocities, and torques natively.

New feature: Multi-drive systems

Multi-drive coordination has become a particular focus of the project's recent development. The stack has always been able to run several drives on a single bus, each as its own CANopen node sharing one master, with the CiA 402 driver providing the full motion-control profile: control and status words, profiled and cyclic position, velocity and torque modes, and interpolated position mode. Every node is configured independently in the bus description, with its own PDO mappings and unit-scaling factors.

The most recent releases added CiA 402 multi-channel support, which lets a single CANopen node expose more than one drive axis. Many modern servo controllers pack two or more axes behind a single node, and the driver now maps each axis to its own channel, with its own state machine and operation mode, so each can be commanded individually. Together with the existing multi-node setup, the stack now covers both ways of building a multi-drive system.

Behind the scenes, the CANopen master automatically coordinates all communication. It manages the flow of commands so you can control multiple motor axes simultaneously without worrying about network conflicts. It also keeps the motors perfectly synchronized, which is essential when multiple joints must move in harmony.

On the ROS 2 side, the stack groups these individual drives together and presents them to the system as a single, unified robot. Each motor axis is mapped directly to a standard robot joint. As a result, you can control your entire multi-axis machine using familiar ROS 2 controllers and visualize its movement in RViz just like any other robot.

Acknowledgments

the ros2_canopen stack provides a robust, modern, and highly flexible framework for integrating industrial CANopen devices into the ROS 2 ecosystem. By leveraging a reliable open-source CANopen engine and integrating tightly with ros2_control, it lowers the barrier to building and controlling complex multi-drive robot systems.

This progress is a true community effort. A huge thank you goes to all the contributing developers and partner organizations whose dedication has shaped the core architecture, hardware interface capabilities, and testing infrastructure. We are particularly grateful for the community-contributed CiA 402 multi-channel support, which has made orchestrating multi-axis drives easier than ever.

To explore the codebase, report issues, or contribute to the project, check out the official https://github.com/ros-industrial/ros2_canopen

by Vishnuprasad Prachandabhanu on June 30, 2026 03:47 PM

Offboard Control with PX4 & ROS 2 (Micro XRCE-DDS)

I wanted to share an architectural pattern for handling autonomous drone navigation using ROS 2 (Jazzy) and PX4 Autopilot via Micro XRCE-DDS, specifically regarding the Offboard mode failsafe.

The Problem: When developing an autonomous flight node in C++, transitioning to PX4’s Offboard mode can be tricky. PX4 implements a strict failsafe: it will reject any VEHICLE_CMD_DO_SET_MODE to Offboard unless it detects a continuous stream of setpoints (e.g., TrajectorySetpoint and OffboardControlMode) at >2Hz prior to the request.

A common beginner mistake is using while loops with std::this_thread::sleep_for() to pre-load these setpoints. This is an anti-pattern in ROS 2 because it blocks the executor’s thread, preventing the node from processing incoming telemetry from the drone.

The Solution: Instead of blocking the thread, we implemented a non-blocking State Machine driven by the ROS 2 Executor using rclcpp::Node::create_wall_timer.

By setting a timer to fire exactly every 50ms (20Hz), we delegate the time-keeping to the Executor. Inside the timer’s single callback, we manage a state machine that handles the initialization sequence safely:

  1. Pre-flight Heartbeat: The callback counts up to 20 cycles (1 second) publishing dummy setpoints to satisfy the PX4 failsafe.

  2. Mode Switch: On the 21st cycle, it publishes the Offboard command.

  3. Arming: It sends the Arm command.

  4. Mission Loop: It continues publishing the actual trajectory setpoints at 20Hz without ever blocking the main thread.

Resources: rclcpp: rclcpp::node::Node Class Reference , Offboard Mode | PX4 User Guide , Create wall timer using callback with parameters.

1 post - 1 participant

Read full topic

by Cesar_Flores on June 30, 2026 04:08 AM

June 29, 2026
Pyspacemouse axis conventions

Thanks to new contributor, we have documentation of, and several options for, axis conventions in pyspacemouse :slight_smile: not yet released to pypi, but please try it out (you can pip install directly from github!) and let me know what you think.

Longer term, the plan is to change the default convention to HID, because the LEGACY convention is, frankly, insane, and I don’t want all users to have to “fix it” on their own.

1 post - 1 participant

Read full topic

by PeterMitrano on June 29, 2026 08:04 PM

Guidelines for AI-Assisted Contributions in the ROS Project

Hello fellow ROS users, maintainers, contributors and anyone else who may be passing by.

Due to a recent uptick in LLM-authored pull requests, the ROS PMC has elected to extend the upstream OSRF policy on the use of generative tools to help provide some boundaries/guidelines on the use of these tools. These tools and technologies are constantly changing, so this isn’t intended to serve as a permanent policy, but rather it is to provide a framework for how to interact on ROS-related projects.

The TLDR, because there are a lot of words below: Feel free to use the tools to build, explore, and understand software (as many of the maintainers also do), but when it comes time to interact on Discourse, Zulip, or Github, try to leave the LLM-centric text behind. A large component of open source software is about building/collaborating with people, and we would generally prefer to interact with people rather than an LLM-proxy.


Maintainer time is a finite and constrained resource. While generative AI tools can be useful development assistants, submitting unverified Large Language Model (LLM) output shifts the engineering, debugging, and verification work onto project maintainers.

This document establishes the rules for interacting with ROS project repositories when using AI tools. It serves as an extension of the upstream OSRF Policy on the Use of Generative Tools in Contributions. While the upstream policy defines the legal, IP, and attribution requirements for AI-generated code, these rules define the quality and behavioral standards required to protect maintainer bandwidth.

The Author Ownership Rule

The upstream OSRF policy states that contributors must have the right to sign the Developer Certificate of Origin (DCO). Operationally, this means you are the sole owner of, and are fully responsible for, the code you submit.

  • Do not use maintainers as an LLM validation service. If a maintainer points out a bug, architectural issue, or syntax error in your PR, you may not simply paste that feedback back into an LLM and copy-paste the unverified output as a response.

  • You must understand every line of code you submit. If a maintainer asks why a specific technical choice was made, responding with “an LLM generated this” or providing a generic, AI-synthesized explanation is unacceptable. You must be able to defend the technical merits of your implementation yourself.

  • Maintainers want to engage with you, the author of the proposed changes. Maintainers want to understand why you want to make the change and why you want to implement it the way the PR proposes. Maintainers do not want to talk to your chatbot.

Zero-Tolerance for Hallucinated API/Dependency Usage

LLMs frequently hallucinate APIs, configuration parameters, or entire library features, especially given the fast-moving nature of ROS releases and underlying middleware (DDS, Zenoh, rclcpp, rclpy, etc.).

  • Any PR that introduces non-existent APIs, impossible configuration keys, or broken logic due to LLM hallucinations will be closed immediately without review.

  • Repeat offenses will result in a temporary or permanent block from the organization’s repositories.

Compliance with Upstream Disclosure & Attribution

The upstream OSRF policy explicitly requires that the use of generative tools be disclosed if they created a material part of the contribution. We enforce this strictly on GitHub:

  • If an LLM was used to generate structural code, tests, or documentation boilerplate, state it clearly in the PR description.

  • Do not LLM dump to generate your PR description. Paragraphs filled with generic AI phrasing (e.g., “This PR enhances the efficiency of the codebase by utilizing modern design patterns and optimizing asynchronous paradigms…”) will be rejected. Write a concise, human-authored summary explaining exactly what changed and why.

Enforcement and Maintainer Recourse

To maintain the health of the project, maintainers have full discretion to handle low-effort, AI-driven contributions efficiently:

  • If an issue or PR is clearly an unverified LLM dump (e.g., containing generic AI comments, formatting that violates project style guides, contains a poorly formatted description pasted directly from an LLM, or obviously broken logic), maintainers are authorized to close the PR immediately.

  • If a contributor responds to code review feedback using obvious, unedited LLM-generated text that fails to address the underlying engineering critique, maintainers are authorized to close the PR immediately.

  • If the contributor fails to respond to reviewer questions or feedback within a reasonable timeframe (e.g. within a week), reviewers may close the PR without further review.

  • If a contributor continues to behave in an unproductive way through repeated submission of inappropriate LLM-dump issues or PRs, repeated copy-and-pasting of LLM-dump comments, or similar, maintainers are authorized to temporarily or permanently block that user from the organization’s repositories.

Guidelines for Responsible AI Use in ROS

If you want to use an LLM to help you successfully contribute to ROS, adhere to the following workflow:

  1. Confirm that the issue is still valid and that any proposed solution is still preferred. Start a conversation on the issue thread to ensure maintainers agree with the approach. Opening PRs for stale or invalid issues simply because an AI bot identified them creates unnecessary overhead for maintainers and is strongly discouraged.

  2. Run the code locally. Verify it compiles, passes linting, and passes all relevant tests before opening a PR.

  3. Before hitting “Submit,” read through your own diff line-by-line. Remove any redundant logic, overly verbose AI-generated comments, or structural weirdness introduced by the model.

  4. Explain your changes in plain technical language. If you can’t describe the change yourself, you aren’t ready to submit it.

13 posts - 9 participants

Read full topic

by mjcarroll on June 29, 2026 07:41 PM

Logging and Observability Guide Review | Cloud Robotics WG Meeting 2026-07-13

The group is not meeting today (2026-06-29) due to lack of available members!

Please come and join us for this coming meeting at Mon, Jul 13, 2026 4:00 PM UTCMon, Jul 13, 2026 5:00 PM UTC, where we will be editing the first draft of the Logging and Observability guide, generated by AI. The AI used the skeleton of the guide from the previous session and transcripts from all meeting recordings to generate a guide. The group will edit this starting point together in the coming session.

Please note: as this session will be a lot of reading and editing, the group does not plan to record the session.

Last session, we created the skeleton for the guide above, as we have been discussing Logging and Observability tips and best practices over the course of several months. Once the guide has been fully written, previous guest speakers will be invited to review it before it’s published. If you’re interested in watching the previous session, the meeting recording is available on YouTube.

The meeting link for next meeting is here, and you can sign up to our calendar or our Google Group for meeting notifications or keep an eye on the Cloud Robotics Hub.

Hopefully we will see you there!

1 post - 1 participant

Read full topic

by mikelikesrobots on June 29, 2026 03:56 PM

A Standard Interface from LLM to Robot Actions

Hi ROS community,

I’m working on ClawsJoy Robotics, a proposal for a standard interface between LLM-based cognitive systems and ROS/2 robots. The core idea: any LLM outputs an action, any robot implements these standard topics.

The Problem

Every robotics company maps “pick up the cup” to different ROS/2 interfaces:

  • Company A: custom service /manipulation/pick

  • Company B: action server /grasp_object

  • Company C: proprietary SDK

There’s no standard way for an LLM agent to tell a robot what to do.

The Proposal: 12 Standard Motion Primitives

navigate_to → /base/cmd_vel (geometry_msgs/Twist)
move_arm → /arm/joint_trajectory (trajectory_msgs/JointTrajectory)
move_base → /base/cmd_vel (geometry_msgs/Twist)
grasp → /gripper/command (std_msgs/Float32, 0=close 1=open)
release → /gripper/command (std_msgs/Float32)
push → /base/cmd_vel (geometry_msgs/Twist)
look_at → camera PTZ control
detect_objects → /perception/objects (vision_msgs/Detection3DArray)
get_pose → /base/odom + /arm/joint_states
speak → /speech/tts (std_msgs/String)
play_gesture → /arm/joint_trajectory (pre-defined trajectories)
wait → no topic (sleep)
emergency_stop → /safety/estop (std_msgs/Bool)
Demo Output

Natural language → task execution (mock mode):
[Result]: :white_check_mark: Task completed in 6 steps
▸ look_at → table
▸ detect_objects → cup detected
▸ navigate_to → table
▸ move_arm → grasp position
▸ grasp → cup_01
▸ speak → “已拿到水杯” (cup fetched)

[Result]: :white_check_mark: Task completed in 6 steps
▸ navigate_to → waypoint_1
▸ look_at → front
▸ speak → “waypoint_1 clear”
▸ navigate_to → waypoint_2
▸ look_at → left
▸ speak → “waypoint_2 clear”
Integration Checklist

To integrate a robot with ClawsJoy Robotics:

  1. Arm: publish /arm/joint_states, subscribe /arm/joint_trajectory

  2. Gripper: subscribe /gripper/command (Float32, 0=close 1=open)

  3. Base: subscribe /base/cmd_vel, publish /base/odom

  4. Camera: publish /camera/rgb, /camera/depth

  5. Perception: publish /perception/objects

  6. Speech: subscribe /speech/tts

  7. Safety: subscribe /safety/estop

Then set mock_mode=False and run.

Code

https://github.com/your-org/clawsjoy_robotics

12 primitives defined in motion_primitives.yaml. Full topic mapping in bridge/ros2_bridge.py. Task planner in planner/planner.py.

Questions for the community

  1. Are these 12 primitives sufficient, or should we add/remove some?

  2. Is trajectory_msgs/JointTrajectory the right interface for move_arm, or should we use ROS/2 Actions?

  3. Should we adopt the existing ros2_control interfaces instead of defining new topics?

  4. Is this worth standardizing, or is every robot too different?

Looking forward to feedback.

3 posts - 2 participants

Read full topic

by ClawsJoy on June 29, 2026 02:28 PM

Cad to urdf conversion

If you’re a robotics engineer, you’ve probably lost hours to URDF generation.
Not because the robot is complex but because the tooling is.

Renaming mates. Fixing joint axes. Recreating reference frames. Starting over because the CAD came from the “wrong” software.

I got tired of that workflow, so I built Jointly.
Upload a STEP assembly then get a draft URDF with inferred joints, inertia, collision meshes, and a live 3D preview.

It’s not magic , you’ll still review the result. But it turns hours of manual work into a couple of minutes.

I’d love to test it on real-world assemblies. If you have a CAD file that breaks your current exporter, Try it Now

4 posts - 2 participants

Read full topic

by Sleem45 on June 29, 2026 02:27 PM

Ros2-dev-suite, a StudioCode extension

As shared in a lightning talk at ROSConFr: I have finally completed and “clauded” a comprehensive VS Code extension for ROS 2 development: a unified explorer, live parameter tuner, service/topic/action caller, log viewer, and syntax highlighting for interface files.

My favorite features of the extension, among others:

  • Dynamically changing node ROS parameters at runtime via a YAML panel and comparing live params vs. edits: very handy for tuning PIDs, covariances, etc. Possibility to edit an existing params yaml file, inject missing parameters or view the all available parameters
  • Displaying the logs of a namespace-filtered node in the output section of the IDE: useful while tuning a single set of nodes whose logs would otherwise be lost in a single launch command.
  • Sending topics, services, or actions at runtime with predefined templates and QoS definitions.
  • … plenty of others! I invite you to test it or check out the README section.

The main structure of the package is quite simple: usual ROS 2 CLIs are used in virtual shells to perform the functions, so no rclpy dependencies are required.

Available on:

GitHub (to install locally from the .vsix file, and for developers to edit, compile, etc.): GitHub - Tanneguydv/ros2-dev-suite: A comprehensive VS Code extension for ROS 2 development · GitHub

Visual Studio Marketplace (to directly install the extension from the IDE): ROS 2 Dev Suite - Visual Studio Marketplace

This package is a child of GitHub - ErickKramer/nvim-ros2: nvim-ros2 is a simple lua plugin that adds useful features to enhance your development workflow while developing ROS 2 modules. · GitHub , incorporating the latest pull request by Thibault Cozic for workspace navigation, ROS 2 tuner, etc. As I do not use Neovim, these features have been adapted to VS Code.

Some Screenshots:

Parameter tuning

Topic sender

Action caller

Service caller and yaml details (on mouse contextual window)

Logging filter for Output section of IDE

Dedicated notification of selected dying node

Filter left panel display by namespace

1 post - 1 participant

Read full topic

by Tanneguydv on June 29, 2026 11:23 AM

June 26, 2026
🍺 ros2-jazzy is now available on Homebrew!

All three ROS 2 distros are now native on Homebrew-macOS:

brew tap idesign0/ros2

brew install ros2-humble
brew install ros2-jazzy
brew install ros2-kilted

One tap to get ROS 2 Jazzy with full simulation and robotics stack support:

:robot: Gazebo (gz-sim8) — Gazebo Harmonic simulation out of the box
:gear: ros2_control — Hardware abstraction & controllers
:mechanical_arm: MoveIt 2 — Motion planning
:world_map: Nav2 — Autonomous navigation

No Docker. No VMs. Native macOS.

And the best part? These aren’t one-time snapshots — they stay in sync with upstream and will keep getting updated as the ecosystem evolves. :counterclockwise_arrows_button:

What’s next? I’ve been quietly collecting -Werror flag failures from Apple Clang across all three distros. Fixing them upstream should meaningfully improve code quality across the stack.

Want to try MoveIt 2 or TurtleBot4 tutorials on macOS? I’ve patched them for all available distros:

:turtle: TurtleBot4 tutorials (Jazzy): https://lnkd.in/e2ZXMXBn
:mechanical_arm: MoveIt 2 tutorials (Jazzy): https://lnkd.in/eWwNCT-G

Project Links:
Ros2_macos repo: https://lnkd.in/ehEtTSJz
Homebrew repo: https://lnkd.in/ek5zDAQB

2 posts - 2 participants

Read full topic

by idesign0 on June 26, 2026 04:57 PM

Sony AI's Ace (Nature cover, beat a pro table tennis player) — running on ROS 2

Can you believe this is running ROS 2? :ping_pong:

Sony’s Ace Robot Beats Top Table Tennis Pros in Real Matches

Sony AI’s Ace just landed on the cover of Nature — the first robot ever to beat a professional table tennis player.

Let that sink in for a second:

• It tracks a ball moving at 20 m/s with spin over 9,000 rpm
• Perceives it in 10.2 ms (event-based vision)
• Decides and swings on a 1 kHz control loop
• And returns the ball at up to 19.6 m/s

This is physical AI right at the edge of what’s possible — and we’ve been working on the ROS 2 integration and support underneath it. The fact that ROS 2 holds up in a system this demanding says a lot about where the ecosystem is heading.

Huge respect to the Sony AI team. Take a look :backhand_index_pointing_down:

11 posts - 6 participants

Read full topic

by tomoyafujita on June 26, 2026 12:51 AM

June 25, 2026
A tutorial to make your quadruped robot on ROS

Hello guys !

I made a tutorial to make your own quadruped using ros2 jazzy !

If you have any feed back I am happy to hear it

1 post - 1 participant

Read full topic

by joschma on June 25, 2026 05:05 PM

Rclnodejs 2.1: Native ESM for a Web-Native ROS 2 SDK

Hi all,

rclnodejs 2.1.0 is out — and it’s a step toward making rclnodejs a web-native SDK for ROS 2: a platform for bringing ROS 2 to the browser and the modern JavaScript ecosystem. With 2.1.0, the whole package is now native ESM, so building web dashboards, teleop UIs, and browser bridges on ROS 2 feels native to today’s web tooling — with the full ROS 2 runtime still underneath whenever you need real nodes.

TL;DR — 2.1.0 makes rclnodejs native ESM end-to-end. import rclnodejs from 'rclnodejs' just works, the browser SDK (rclnodejs/web) brings ROS 2 to the web, and existing require() nodes keep running untouched — the full ROS 2 runtime is still underneath.

:sparkles: A web-native SDK for ROS 2

  • ROS 2 on the web — reach ROS 2 from a web page with rclnodejs/web: typed APIs over WebSocket, no proxy, no codegen.
  • Native ESM — first-class import and top-level await, ready for Vite/esbuild and the modern web toolchain.
  • Full ROS 2 runtime underneath — real nodes, pub/sub, services, actions, parameters, lifecycle, whenever you need them.
  • Backward compatible — every existing CommonJS (require) project upgrades with zero code changes.

:package: One package, both module systems

// Modern Node / browser — ESM
import rclnodejs from 'rclnodejs';        // full ROS 2 node API
import { connect } from 'rclnodejs/web';  // typed browser SDK
// Existing CommonJS nodes — unchanged
const rclnodejs = require('rclnodejs');

:compass: Where this is heading

Each release moves rclnodejs one step closer to a typed, web-native way into ROS 2:

  • 2.0.0 :globe_with_meridians: — a typed browser SDK (rclnodejs/web) backed by a capability runtime that exposes only what web.json declares, with HTTP call / publish for non-JS clients.
  • 2.1.0 :high_voltage: — native ESM across the whole package, so rclnodejs sits naturally alongside the rest of your web stack.

The arc: from “a Node.js client that happens to run in browsers”“a typed, allow-listed Web SDK for ROS 2”

:wrench: Try it

npm i rclnodejs

Feedback welcome — especially from anyone wiring ROS 2 into web frontends. :raising_hands:

Cheers,
Minggang

1 post - 1 participant

Read full topic

by minggangw on June 25, 2026 02:19 AM

Anomaly Detection. Beta testers wanted

Hi, I used to work at a robotics startup that built robotic dishwashers. (https://armstrong.ai/) And part of my job was to set up diagnostics, monitoring and alerting.

Aside from not being the most interesting part of my job, it was just plain tedious to manually set (and continually tune) thresholds for various signals, topics, etc. I frequently thought it would be nice to have a system that would automate that part of my job and now that the company has closed, I built it.

Now I have reached the point where the useful next step is real robots. The system performs well on recorded public datasets and on some production infrastructure (see below), but what I do not have yet is enough hours on a variety of real robots, doing real work.

Details are here at the Transitive Robotics website:

What it is

The system watches your robot’s topics and tells you when something is wrong. No threshold tuning, no hardcoded rules. It learns what normal looks like on your robot, then flags and reports anomalies in real time.

How to get involved

Reply to this thread or email shane@targetnode.ai. I will help you get it installed and monitored.

What you get and what I am asking for

What you get:

  • Anomaly detection for free on your robot with no manual threshold tuning to maintain.

  • Direct influence over the direction of the system/project. I want to know what is missing, what is confusing, and what breaks on real robots in production.

What I am asking for:

  • Honest and detailed feedback

Where it has been tested

I evaluated the system against public datasets that cover real hardware faults across very different platforms. The system has been tested successfully on the following datasets:

  • CASPER. A UR3e collaborative robot arm with real IMU fault data, from Cardiff University. (paper)

  • PADRE, Parrot Bebop 2. Real propeller damage on a quadrotor, recorded from onboard IMUs. (paper)

  • PADRE, 3DR Solo. Real propeller damage on a second quadrotor airframe, with IMU and barometer data.

  • ALFA. A CarbonZ T-28 fixed-wing aircraft with real engine and actuator faults, from Carnegie Mellon University’s AirLab. (data)

  • UAV-SEAD. A large set of real PX4 flights with naturally occurring state-estimation anomalies, from Istanbul Technical University. (paper)

Additionally, I am running the system on my production servers. They are not robots, per se, but they are live production systems that get used every day. Right now the system is watching CPU, memory, disk/network operations (29 signals total) all without me setting or tuning thresholds. In the last 200 hours of operation (since Tuesday, June 16, 2026) there have been no false alerts.

Thanks for checking out this post and if you have any questions, reach out to me at shane@targetnode.ai

2 posts - 2 participants

Read full topic

by Shane_Hill on June 25, 2026 12:01 AM

June 24, 2026
VL53L0X Time-of-Flight sensor as a Nav2 obstacle layer (ESP32 + ROS 2 Humble)

demo

Sharing a small open-source project that integrates a low-cost VL53L0X Time-of-Flight sensor into the Nav2 costmap as an obstacle source, in case it’s useful to anyone working with cheap range sensors on small robots.

Pipeline

VL53L0X --I2C–> ESP32 --USB serial–> serial_bridge → sensor_msgs/Range
→ tof_to_scan → sensor_msgs/LaserScan → nav2_costmap_2d::ObstacleLayer

What’s included

  • ESP32 firmware (Arduino, Pololu VL53L0X library) streaming distance over serial with sensor-disconnect recovery
  • An rclpy serial-bridge node publishing sensor_msgs/Range, with a threaded reader and automatic serial reconnect
  • A Range → LaserScan converter that synthesizes a configurable narrow fan from the single beam (default ±0.15 rad, 31 rays), emitting +inf for out-of-range readings so the obstacle layer can raytrace-clear
  • A full Nav2 costmap config wiring the scan into local and global costmaps (marking + clearing, inf_is_valid: true)
  • RViz config and a troubleshooting guide

Built and tested on ROS 2 Humble / Ubuntu 22.04. Python packages (ament_python).

Scope / limitations (up front)

This is a supplementary obstacle sensor, not a LiDAR replacement — a single narrow-FOV ToF sees one thin cone.

One thing I’d be curious about others’ experience on: raytrace clearing on a stationary base leaves residual cells at the edges of the cone, since the clearing rays never cross them. It resolves once the robot moves and the cone sweeps, but I’d be interested whether anyone has handled static-case clearing for single-beam sensors more elegantly. Widening the FOV and increasing ray count helped, but felt like a workaround.

A config gotcha that may save someone time: obstacle marking worked but clearing silently didn’t, until I set obstacle_max_range < raytrace_max_range, with raytrace_max_range equal to the sensor’s true range_max. A mismatch there breaks clearing while marking still functions, so it presents as “obstacles never disappear.”

Repo (MIT): GitHub - Arjunros/nav2-vl53l1x-obstacle-layer: Low-cost VL53L0X ToF sensor as a Nav2 obstacle layer via ESP32 · GitHub

Feedback and suggestions welcome.

1 post - 1 participant

Read full topic

by Arjun_R on June 24, 2026 07:12 PM

Embeddable React telemetry components — a browser ground station for MAVROS

I maintain Altara, a set of MIT-licensed React components for real-time robotics telemetry (PFD, moving map, gauges, event log), and I wrote up building a small drone ground station with them that talks to MAVROS over rosbridge.

The thing I was trying to solve: I wanted a status dashboard our team could open in a browser tab and drop into our own app, rather than running a separate native GCS. So the components are embeddable and themeable rather than a standalone tool.

Writeup (the rosbridge/MAVROS wiring is the interesting part): [ Building a real-time drone ground control station in React - DEV Community ]
Live demo: [ Altara Demo ] · Source: [https://github.com/JayaSaiKishanChapparam/altara\]

If you’ve built browser dashboards for a ROS2 stack, I’d like to know what you ended up reading off your topics, and where existing tools forced you into a standalone app when you wanted something embedded.

4 posts - 2 participants

Read full topic

by Jaya_Sai_Kishan on June 24, 2026 02:52 PM

KRS Unleashed: Easier Hardware Acceleration Prototyping on FPGA

Hey All,

I want to present KRS Unleashed, the tool I am now developing/refactoring/extending since 1 year as part of my PhD.

Some of you might still know KRS (short for Kria Robotic Stack as originally developed by Xilinx/AMD for their Kria platform) as the toolchain developed until 2023 by the ROS 2 Hardware Acceleration Working Group · GitHub .

(Short Summary for those who don’t)


It was a proof-of-concept framework that allowed to synthesize bitstreams, create a sysroot and cross-compile application against it to create robotics applications running on KV260, KR260 (and also claimed jetson, Ultra96v2,.. but i have never tested/verified that so far). So basically the main point of it is to replace this “docker container to compile” flow with ROS 2 colcon mixin and in parallel integrate all the necessary and optional things for hardware acceleration like sysroot creation or vendor-driven bitstream synthesis in a ROS 2 command.


The flow works, but I found it really hard to use:

  • long build times whenever you cleaned your build/install folders, as even the sysroot and bitstream needed to resynthesize when you just changed a variable in a package (if you know what you are doing you can keep the untouched compiled packages but its not really stable nor trivial)
  • cross-references and dependency cycles made it hard to change configuration like different Vitis version, other firmware OS,..
  • hard to understand what each package actually did and which ones were necessary for your task - the original guide simply copied all available packages in

Basically it wasn’t really fun to use and whenever you start with a new project, you fight cmake configurations for a while until you can start coding..


My tool refactored these individual components into 3 separate logical workspaces:

  • os → for firmware and sysroot creation - allows to reuse across projects (save lots of memory space, no rebuild necessary, parallel development possible by different team)
  • vitis → vendor-driven toolchain necessary to automate your “accelerator bitstream flow”. I refactored also the automation into a nice Python CLI scripted way such that I can also use the Vitis workspace and manually adjust things, but this is more a FPGA developer nice-to-have. Could also be replaced with a flow for other accelerators
  • krs → lightweight integration of firmware via single package, that provides the colcon mixin integration. Further a tracetools-kernels package that allows to trace arbitrary code regions in any ROS 2 application extending ros-tracing.

You can also use any of these components in isolated areas - I use for example also vitis + os for standalone faster iteration on host code variations. It might be a little bit harder to setup for the first time, but I can iterate really fast with it (built times up to 80x faster) and the setup is the same for all projects so once you understand it, its relatively easy and doesn’t require changes all over the project but just in very small, well defined areas.


I also created a Hackster Series for the brave.

I continue to develop it, current public version is still humble but I already have an internal jazzy version which I will release once I published a new paper.

Github: GitHub - TUD-ADS/KRS-Unleashed · GitHub

Hackster Series (5 Parts): 1. Getting Started

Paper: https://ieeexplore.ieee.org/document/11231288/

1 post - 1 participant

Read full topic

by B4demeister on June 24, 2026 01:45 PM

June 23, 2026
Minimum CMake version to support Humble at this time

REP 2000 specifies MacOS support for CMake 3.14.4 in Humble:

This is the lowest number for the whole Humble release.

However, MacOS has this note:

" ** " means that the dependency may see multiple version changes, because the dependency uses a package manager that continually updates the dependency without a stable API.

I don’t work with MacOS myself, so I don’t know how it behaves. Is it still needed nowadays to support 3.14.4 to get full support for Humble on MacOS, or can I just ignore this and upgrade to 3.16.3 from Focal (assuming that MacOS currently uses a much newer version)?

And on the other hand: Homebrew Formulae: cmake shows CMake version 4.3.4 . Does it mean that even people building Humble on MacOS will use this version? So do I need to make sure the package also builds with this version?

3 posts - 2 participants

Read full topic

by peci1 on June 23, 2026 09:51 AM

Dual-Arm Nero Reinforcement Learning in Isaac Lab: Reach Task Training Example

Robotic manipulation remains one of the most important research directions in embodied AI. While traditional kinematics-based controllers provide stable motion execution, they often struggle in unstructured environments where adaptability is required.

Recent advances in Reinforcement Learning (RL) have enabled robotic arms to learn task-oriented behaviors directly from interaction, making it possible to achieve robust control policies without manually designing every motion strategy.

In this project, we extend the original SO-ARM101 Isaac Lab implementation by integrating the AgileX Dual-Arm Nero Manipulator, allowing developers to quickly train and validate dual-arm RL policies using NVIDIA Isaac Lab.

Reposity

Open-source implementation:

GitHub - smalleha/isaac_so_arm101: Isaac Lab external project for SO-ARM100/101 arm robot. · GitHub

Project Structure


├── robots

│   ├── dual_nero

│   │   ├── dual_nero.py

│   │   ├── __init__.py

│   │   ├── meshes

│   │   └── urdf

│   │       └── dual_nero.urdf

├── scripts

│   ├── rl_games

│   │   ├── play.py

│   │   └── train.py

│   └── zero_agent.py

├── tasks

│   ├── __init__.py

│   └── reach

│       ├── agents

│       ├── dual_nero_joint_pos_env_cfg.py

│       ├── dual_nero_reach_env_cfg.py

│       ├── __init__.py

│       └── mdp

└── ui_extension_example.py

Key additions include:

Column 1 Column 2
Component Description
rl_games RL-Games training framework
dual_nero_reach_env_cfg.py Reach task environment definition
dual_nero_joint_pos_env_cfg.py Joint position control configuration
dual_nero Dual-arm robot model

3. Importing the Robot into Isaac Lab

3.1 Preparing the URDF

Before importing the robot into Isaac Lab, mesh references inside the URDF should be converted to relative paths.

For example:


  <link name="base_link">

    <inertial>

      <origin rpy="0 0 0" xyz="-0.0592395620981769 -0.068642440505388 0.0562764736144042"/>

      <mass value="4.46524857458863"/>

      <inertia ixx="0.0608289280191989" ixy="-2.54649959130438E-06" ixz="1.11851948046933E-07" iyy="0.0218722514454004" iyz="-0.000689477252402357" izz="0.0680524540174318"/>

    </inertial>

    <visual>

      <origin rpy="0 0 0" xyz="0 0 0"/>

      <geometry>

        <mesh filename="../meshes/base_link.dae"/>

      </geometry>

      <material name="">

        <color rgba="0.776470588235294 0.756862745098039 0.737254901960784 1"/>

      </material>

    </visual>

    <collision>

      <origin rpy="0 0 0" xyz="0 0 0"/>

      <geometry>

        <mesh filename="../meshes/base_link.dae"/>

      </geometry>

    </collision>

  </link>

This ensures that the asset loader can correctly locate mesh resources during simulation.

3.2 Creating the Robot Configuration

Next, create a robot configuration file:

isaac_so_arm101/robots/dual_nero/dual_nero.py

This file defines:

  • Robot articulation properties

  • Joint stiffness and damping

  • Actuator configuration

  • Initial joint states

  • Gripper settings

The resulting DUAL_NERO_CFG object becomes the robot asset used by Isaac Lab during training.

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

DUAL_NERO_CFG = ArticulationCfg(
    spawn=sim_utils.UrdfFileCfg(
        fix_base=True,
        merge_fixed_joints=False,
        replace_cylinders_with_capsules=True,
        asset_path=f"{TEMPLATE_ASSETS_DATA_DIR}/urdf/dual_nero.urdf",
        activate_contact_sensors=False, # set as false while waiting for capsule implementation
        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={
                "left_joint.*": 0.0,
                "right_joint.*": 0.0,
                "left_gripper_joint.*": 0.0,  
                "right_gripper_joint.*": 0.0,  
        },
        # Set initial joint velocities to zero
        joint_vel={".*": 0.0},
    ),
    actuators={
            "arm": ImplicitActuatorCfg(
                joint_names_expr=["left_joint.*", "right_joint.*"],
                effort_limit=25.0, 
                velocity_limit=1.5,
               
                stiffness={
                    "left_joint1": 200.0, 
                    "left_joint2": 170.0,
                    "left_joint3": 120.0,
                    "left_joint4": 80.0,
                    "left_joint5": 50.0,
                    "left_joint6": 20.0,
                    "left_joint7": 10.0,
                    "right_joint1": 200.0, 
                    "right_joint2": 170.0,
                    "right_joint3": 120.0,
                    "right_joint4": 80.0,
                    "right_joint5": 50.0,
                    "right_joint6": 20.0,
                    "right_joint7": 10.0
                },
               
                damping={
                    "left_joint1": 100.0,
                    "left_joint2": 60.0,
                    "left_joint3": 70.0,
                    "left_joint4": 24.0,
                    "left_joint5": 20.0,
                    "left_joint6": 10.0,
                    "left_joint7": 5,
                    "right_joint1": 100.0,
                    "right_joint2": 60.0,
                    "right_joint3": 70.0,
                    "right_joint4": 24.0,
                    "right_joint5": 20.0,
                    "right_joint6": 10.0,
                    "right_joint7": 5,
                },
            ),
        "gripper": ImplicitActuatorCfg(
            joint_names_expr=["left_gripper_joint.*","right_gripper_joint.*"],
            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,
)

Create an init.py file inside the dual_nero directory so that Python recognizes it as a package.

4. Building the Reach Environment

Two environment configuration files are required:


tasks/reach/

├── dual_nero_joint_pos_env_cfg.py

└── dual_nero_reach_env_cfg.py

4.1 Joint Position Environment Configuration

dual_nero_joint_pos_env_cfg.py specifies:

  • Controlled joints

  • End-effector links

  • Action spaces

  • Command targets

import math

import isaaclab_tasks.manager_based.manipulation.reach.mdp as mdp
from isaaclab.utils import configclass
from isaac_so_arm101.robots import DUAL_NERO_CFG # noqa: F401   
from isaac_so_arm101.tasks.reach.dual_nero_reach_env_cfg import Dual_NeroReachEnvCfg
from isaaclab.assets.articulation import ArticulationCfg

@configclass
class Dual_Nero_ReachEnvCfg(Dual_NeroReachEnvCfg):
    def __post_init__(self):
        # post init of parent
        super().__post_init__()

        # switch robot to OpenArm
        self.scene.robot = DUAL_NERO_CFG.replace(prim_path="{ENV_REGEX_NS}/Robot",)

        # override rewards
        self.rewards.left_end_effector_position_tracking.params["asset_cfg"].body_names = ["left_gripper_base"]
        self.rewards.left_end_effector_position_tracking_fine_grained.params["asset_cfg"].body_names = [
            "left_gripper_base"
        ]
        self.rewards.left_end_effector_orientation_tracking.params["asset_cfg"].body_names = ["left_gripper_base"]

        self.rewards.right_end_effector_position_tracking.params["asset_cfg"].body_names = ["right_gripper_base"]
        self.rewards.right_end_effector_position_tracking_fine_grained.params["asset_cfg"].body_names = [
            "right_gripper_base"
        ]
        self.rewards.right_end_effector_orientation_tracking.params["asset_cfg"].body_names = ["right_gripper_base"]

        # override actions
        self.actions.left_arm_action = mdp.JointPositionActionCfg(
            asset_name="robot",
            joint_names=[
                "left_joint.*",
            ],
            scale=0.5,
            use_default_offset=True,
        )

        self.actions.right_arm_action = mdp.JointPositionActionCfg(
            asset_name="robot",
            joint_names=[
                "right_joint.*",
            ],
            scale=0.5,
            use_default_offset=True,
        )
        # override command generator body
        # end-effector is along z-direction
        self.commands.left_ee_pose.body_name = "left_gripper_base"
        self.commands.right_ee_pose.body_name = "right_gripper_base"

@configclass    
class Dual_Nero_ReachEnvCfg_PLAY(Dual_Nero_ReachEnvCfg):
    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

4.2 Reach Task Definition

dual_nero_reach_env_cfg.py contains the full RL environment definition.

This includes:

1.Scene Configuration

  • Ground plane

  • Lighting

  • Robot asset

2.Command Generation

3.Observation Space

4.Reward Design

5.Curriculum Learning

6.Episode Settings


import math
from dataclasses import MISSING

import isaaclab.sim as sim_utils
from isaaclab.assets import ArticulationCfg, AssetBaseCfg
from isaaclab.envs import ManagerBasedRLEnvCfg
from isaaclab.managers import ActionTermCfg as ActionTerm
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.utils import configclass
from isaaclab.utils.noise import AdditiveUniformNoiseCfg as Unoise

import isaaclab_tasks.manager_based.manipulation.reach.mdp as mdp
##
# Scene definition
##
@configclass
class Dual_NeroReachSceneCfg(InteractiveSceneCfg):
    """Configuration for the scene with a robotic arm."""

    # world
    ground = AssetBaseCfg(
        prim_path="/World/ground",
        spawn=sim_utils.GroundPlaneCfg(),
        init_state=AssetBaseCfg.InitialStateCfg(pos=(0.0, 0.0, 0)),
    )

    # robots
    robot: ArticulationCfg = MISSING

    # lights
    light = AssetBaseCfg(
        prim_path="/World/light",
        spawn=sim_utils.DomeLightCfg(color=(0.75, 0.75, 0.75), intensity=2500.0),
    )
##
# MDP settings
##
@configclass
class CommandsCfg:
    """Command terms for the MDP."""

    left_ee_pose = mdp.UniformPoseCommandCfg(
        asset_name="robot",
        body_name=MISSING,
        resampling_time_range=(4.0, 4.0),
        debug_vis=True,
        ranges=mdp.UniformPoseCommandCfg.Ranges(
            pos_x=(0.5, 0.5),
            pos_y=(0.15, 0.25),
            pos_z=(0.3, 0.5),
            roll=(-math.pi / 6, math.pi / 6),
            pitch=(3 * math.pi / 2, 3 * math.pi / 2),
            yaw=(8 * math.pi / 9, 10 * math.pi / 9),
        ),
    )

    right_ee_pose = mdp.UniformPoseCommandCfg(
        asset_name="robot",
        body_name=MISSING,
        resampling_time_range=(4.0, 4.0),
        debug_vis=True,
        ranges=mdp.UniformPoseCommandCfg.Ranges(
            pos_x=(0.5, 0.5),
            pos_y=(-0.25, -0.15),
            pos_z=(0.3, 0.5),
            roll=(-math.pi / 6, math.pi / 6),
            pitch=(3 * math.pi / 2, 3 * math.pi / 2),
            yaw=(8 * math.pi / 9, 10 * math.pi / 9),
        ),
    )

@configclass
class ActionsCfg:
    """Action specifications for the MDP."""
    left_arm_action: ActionTerm = MISSING
    right_arm_action: ActionTerm = MISSING
@configclass
class ObservationsCfg:
    """Observation specifications for the MDP."""

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

        # observation terms (order preserved)
        left_joint_pos = ObsTerm(
            func=mdp.joint_pos_rel,
            params={
                "asset_cfg": SceneEntityCfg(
                    "robot",
                    joint_names=[
                        "left_joint.*",
                    ],
                )
            },
            noise=Unoise(n_min=-0.01, n_max=0.01),
        )

        right_joint_pos = ObsTerm(
            func=mdp.joint_pos_rel,
            params={
                "asset_cfg": SceneEntityCfg(
                    "robot",
                    joint_names=[
                        "right_joint.*",
                    ],
                )
            },
            noise=Unoise(n_min=-0.01, n_max=0.01),
        )

        left_joint_vel = ObsTerm(
            func=mdp.joint_vel_rel,
            params={
                "asset_cfg": SceneEntityCfg(
                    "robot",
                    joint_names=[
                        "left_joint.*",
                    ],
                )
            },
            noise=Unoise(n_min=-0.01, n_max=0.01),
        )
        right_joint_vel = ObsTerm(
            func=mdp.joint_vel_rel,
            params={
                "asset_cfg": SceneEntityCfg(
                    "robot",
                    joint_names=[
                        "right_joint.*",
                    ],
                )
            },
            noise=Unoise(n_min=-0.01, n_max=0.01),
        )
        left_pose_command = ObsTerm(func=mdp.generated_commands, params={"command_name": "left_ee_pose"})
        right_pose_command = ObsTerm(func=mdp.generated_commands, params={"command_name": "right_ee_pose"})
        left_actions = ObsTerm(func=mdp.last_action, params={"action_name": "left_arm_action"})
        right_actions = ObsTerm(func=mdp.last_action, params={"action_name": "right_arm_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_robot_joints = EventTerm(
        func=mdp.reset_joints_by_scale,
        mode="reset",
        params={
            "position_range": (0.5, 1.5),
            "velocity_range": (0.0, 0.0),
        },
    )

@configclass
class RewardsCfg:
    """Reward terms for the MDP."""
    left_end_effector_position_tracking = RewTerm(
        func=mdp.position_command_error,
        weight=-0.2,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=MISSING),
            "command_name": "left_ee_pose",
        },
    )
    right_end_effector_position_tracking = RewTerm(
        func=mdp.position_command_error,
        weight=-0.25,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=MISSING),
            "command_name": "right_ee_pose",
        },
    )
    left_end_effector_position_tracking_fine_grained = RewTerm(
        func=mdp.position_command_error_tanh,
        weight=0.1,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=MISSING),
            "std": 0.1,
            "command_name": "left_ee_pose",
        },
    )
    right_end_effector_position_tracking_fine_grained = RewTerm(
        func=mdp.position_command_error_tanh,
        weight=0.2,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=MISSING),
            "std": 0.1,
            "command_name": "right_ee_pose",
        },
    )
    left_end_effector_orientation_tracking = RewTerm(
        func=mdp.orientation_command_error,
        weight=-0.1,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=MISSING),
            "command_name": "left_ee_pose",
        },
    )

    right_end_effector_orientation_tracking = RewTerm(
        func=mdp.orientation_command_error,
        weight=-0.1,
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=MISSING),
            "command_name": "right_ee_pose",
        },
    )
    # action penalty
    action_rate = RewTerm(func=mdp.action_rate_l2, weight=-0.0001)
    left_joint_vel = RewTerm(
        func=mdp.joint_vel_l2,
        weight=-0.0001,
        params={
            "asset_cfg": SceneEntityCfg(
                "robot",
                joint_names=[
                    "left_joint.*",
                ],
            )
        },
    )
    right_joint_vel = RewTerm(
        func=mdp.joint_vel_l2,
        weight=-0.0001,
        params={
            "asset_cfg": SceneEntityCfg(
                "robot",
                joint_names=[
                    "right_joint.*",
                ],
            )
        },
    )
@configclass
class TerminationsCfg:
    """Termination terms for the MDP."""
    time_out = DoneTerm(func=mdp.time_out, time_out=True)
@configclass
class CurriculumCfg:
    """Curriculum terms for the MDP."""
    action_rate = CurrTerm(
        func=mdp.modify_reward_weight,
        params={"term_name": "action_rate", "weight": -0.005, "num_steps": 4500},
    )

    left_joint_vel = CurrTerm(
        func=mdp.modify_reward_weight,
        params={"term_name": "left_joint_vel", "weight": -0.001, "num_steps": 4500},
    )

    right_joint_vel = CurrTerm(
        func=mdp.modify_reward_weight,
        params={"term_name": "right_joint_vel", "weight": -0.001, "num_steps": 4500},
    )
##
# Environment configuration
##
@configclass
class Dual_NeroReachEnvCfg(ManagerBasedRLEnvCfg):
    """Configuration for the reach end-effector pose tracking environment."""
    # Scene settings
    scene: Dual_NeroReachSceneCfg = Dual_NeroReachSceneCfg(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.sim.render_interval = self.decimation
        self.episode_length_s = 24.0
        self.viewer.eye = (3.5, 3.5, 3.5)
        # simulation settings
        self.sim.dt = 1.0 / 60.0



\`\`\`



**### 4.3 Registering the Environment**



Register the task inside:\`src/isaac_so_arm101/tasks/reach/\__init_\_.py\`



\`\`\`PYTHON

gym.register(

    id="Isaac-Dual-Nero-Reach-v0",

    entry_point="isaaclab.envs:ManagerBasedRLEnv",

    kwargs={

        "env_cfg_entry_point":f"{\__name_\_}.dual_nero_joint_pos_env_cfg:Dual_Nero_ReachEnvCfg",

        "rsl_rl_cfg_entry_point": f"{agents.\__name_\_}.rsl_rl_ppo_cfg:ReachPPORunnerCfg",

        "rl_games_cfg_entry_point": f"{agents.\__name_\_}:rl_games_ppo_cfg.yaml",



    },

    disable_env_checker=True,

)

5. Training the Reach Policy

Step 1.Activate the Isaac Lab Environment


conda activate env_isaaclab

Step 2.Navigate to the project directory:


cd isaac_so_arm101

Step 3.Train the whole project

Option 1:RSL-RL

  • Train:

uv run train \

    --task Isaac-Dual-Nero-Reach-v0 \

    --headless

  • Evaluate:

uv run play \

    --task Isaac-Dual-Nero-Reach-v0

  • Training result:

dual_nero_rsl_rl.gif

dual_nero_rsl_rl

Option 2: RL-Games

  • Train:

python3 scripts/rl_games/train.py \
    --task Isaac-Dual-Nero-Reach-v0 \
    --headless

  • Evaluate:

python3 scripts/rl_games/play.py \
    --task Isaac-Dual-Nero-Reach-v0
  • Training result:

dual_nero_rl_games.gif

dual_nero_rl_games

6. Results and Observations

Both frameworks successfully learn the dual-arm reaching task.

However, in our experiments:

  • RL-Games converges faster

  • Motion trajectories appear smoother

  • Final reaching accuracy is generally higher

For relatively complex robot morphologies such as dual-arm manipulators, RL-Games currently provides more stable performance and is recommended as the default training backend.

FAQ

Q1: Why is my Dual-Nero robot collapsing or shaking violently after loading the URDF?

This is usually caused by incorrect actuator parameters or unrealistic inertial properties.

Common causes include:

  1. Joint stiffness set too high

  2. Damping values too low

  3. Incorrect mass distribution in the URDF

  4. Self-collision configuration issues

  5. Unstable simulation timestep

Before starting RL training, verify that the robot can remain stable under gravity using only PD control.

Quick check: If the robot cannot stand still without RL, the issue is likely in the robot model rather than the training algorithm.

Q2: Why does the reward improve, but the robot never reaches the target accurately?

A rising reward does not always indicate successful task completion.

Typical reasons include:

  • Reward weights are unbalanced

  • Orientation rewards dominate position rewards

  • Action penalties are too strong

  • Command sampling range is too large

  • End-effector link is incorrectly configured

In most reach tasks, incorrect reward shaping is the primary reason for poor final accuracy.

Q3: How do I verify that the end-effector link is configured correctly?

One of the most common mistakes in Isaac Lab reach tasks is assigning the wrong end-effector body.

For Dual-Nero, the target link should be:


left_gripper_base

right_gripper_base

Symptoms of an incorrect configuration include:

  • Reward remains low

  • Robot moves randomly

  • Training appears to converge but fails visually

  • End-effector does not move toward the target marker

Always verify the body name in Isaac Sim before launching large-scale training.

Q4: Why does RL-Games perform better than RSL-RL for this task?

Both frameworks are PPO-based, but their implementations differ.

For large-scale manipulation environments:

RL-Games generally scales better with thousands of parallel environments

PPO updates are often more stable

Training throughput is higher on modern GPUs

For Dual-Nero reach experiments, RL-Games typically achieves smoother trajectories and faster convergence.

However, results may vary depending on reward design and task complexity.

Q5: My policy works in simulation but fails on the real robot. Why?

This is the most common Sim-to-Real issue.

Possible causes include:

  • Joint friction mismatch

  • Encoder noise

  • Latency differences

  • Payload variations

  • Inaccurate motor models

  • Unmodeled cable effects

To improve transfer performance:

  • Apply domain randomization

  • Add observation noise

  • Randomize dynamics parameters

  • Validate trajectories at low speed first

Successful simulation training is only the first step toward real-world deployment.

:speech_balloon: Have Question?

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

1 post - 1 participant

Read full topic

by Agilex_Robotics on June 23, 2026 03:50 AM

June 22, 2026
Open-source ROS2 LiDAR robot vacuum cleaner

Hello, would you be interested in building a ROS2 vacuum cleaner robot?

If yes

  1. Build/develop vs convert existing?
  2. 3D print vs off-the-shelf?
  3. Convert existing - root or swap board?

I’d really appreciate your input.

As an example of what I’m doing

1 post - 1 participant

Read full topic

by iliao on June 22, 2026 06:12 PM

June 21, 2026
AIC Phase 1: How to run custom aic_model service in Flowstate with AIC_ROUTER_ADDR?

Hi Intrinsic / AIC team,

I’m working on AIC Phase 1 in Flowstate and trying to run my participant model as a custom Service instance named exactly aic_model.

The baseline AIC Phase 1 Submission process expects:

  • lifecycle service: /aic_model/change_state
  • action server: /insert_cable

I have a local Docker image:

my-solution:phase1_basic_control_ladder

The image works in local Docker Compose when launched with the AIC router environment variables. Its entrypoint requires:

AIC_ROUTER_ADDR

and may require AIC_MODEL_PASSWD if ACL is enabled.

From inspecting the SDK/Flowstate tooling, it looks like the possible path is:

inctl asset install <bundle.tar> --org ... --cluster ...
inctl service add <service_asset_id_version> --cluster ... --name aic_model

I can package the Docker image as a Service asset bundle, but I don’t want to guess the router environment contract.

Questions:

  1. For a custom Flowstate Service instance named aic_model, does Flowstate automatically inject AIC_ROUTER_ADDR and AIC_MODEL_PASSWD?
  2. If not, what should AIC_ROUTER_ADDR be set to in simulation inside the Flowstate VM?
  3. What is the approved way to provide AIC_MODEL_PASSWD without exposing secrets?
  4. Is inctl asset install + inctl service add --name aic_model the intended local Flowstate testing workflow for AIC Phase 1, or is there another approved way to make the participant model available to the solution?

Thanks!

1 post - 1 participant

Read full topic

by Young_Jeff on June 21, 2026 06:21 AM


Powered by the awesome: Planet