ROS2 and AI Experiments - slgrobotics/articubot_one GitHub Wiki

Experimenting with pipeline: LLM -> ros-mcp-server -> ROS2/Nav2

See my notes on ROS2/Nav2 and AI integration here.

Contents:


Components used

  • robot Seggy, a general purpose wheeled base, Raspberry Pi 5, LIDAR, Nav2, custom behaviors
  • HuskyLens 2 with WiFi adapter, capable of running its built-in MCP Server (over WiFi) and connected via I2C. It provides visual recognition by running VLMs
  • ros-mcp-server by RobotMCP / Contoro is a broad ecosystem that connects LLMs (like Claude, ChatGPT, or Gemini) to any ROS/ROS 2 robot. It has built-in examples showing an AI agent executing high-level tasks like "Grab a Coke from the fridge and go to the living room" by stringing together Nav2 goals and MoveIt manipulation commands.
  • LLM - ChatGPT Desktop. Runs on an Intel workstation with Nvidia 3060 video card, connects to the Cloud.

Using ChatGPT Desktop and other LLM options

Here are some helpful facts about ChatGPT Desktop (AI assisted research)

Click here to expand
  • What is ChatGPT Desktop and what does it do? answer
  • Is there a way to run ChatGPT Desktop on Ubuntu? answer
  • Can I restrict what ChatGPT Desktop can do on my computer? answer
  • Does ChatGPT Desktop use online ChatGPT models, or can I restrict it to local models? answer

Other options - Claude, Gemini, Ollama

  • Compare ChatGPT/Codex vs. Ollama + local models vs. Claude Code/Gemini CLI specifically for my Ubuntu ROS2 development machine. I intend to drive my Seggy robot which runs ros-mcp-server by RobotMCP / Contoro, while keeping LLM strategy functions on an Intel desktop with Nvidia Geforce RTX 3060 card. I don't mind using online models initially for experiments, but having models local may be beneficial for cost and latency. answer

So, the data flow is as follows:

                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚   Strategy API    β”‚
                         β”‚  model-agnostic   β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                     β”‚             β”‚              β”‚
                     β–Ό             β–Ό              β–Ό
                  GPT/Claude    Ollama        Gemini
                   cloud        local          cloud
                                 RTX 3060
                     β”‚             β”‚              β”‚
                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                  MCP
                                   β”‚
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚  ros-mcp-server   β”‚
                         β”‚      Seggy        β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                                  ROS2
                                   β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚                    β”‚                    β”‚
             Nav2              perception          behaviors
              β”‚                    β”‚                    β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                   β”‚
                              robot hardware
  • In my case perception is delegated to an MCP server running on a dedicated camera device - HuskyLens 2 with WiFi adapter, capable of running its built-in MCP Server (over WiFi) and connected via I2C. It provides visual recognition by running VLMs. How does it fit into my architecture? answer
                    AI SERVICES
                        β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚             β”‚             β”‚
       Strategy      Vision        Future AI
         LLM           VLM           services
          β”‚             β”‚             β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                       MCP
                        β”‚
             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚                     β”‚
        ROS-MCP                Vision-MCP
             β”‚                     β”‚
             β–Ό                     β–Ό
           ROS2                HuskyLens
             β”‚
             β–Ό
           Seggy
  • So, will the reasoning layer have to poll Vision-MCP often? How does it receive perceived info from Vision AI? answer

Note: you will run out of tokens really fast on a free tier. Be prepared to pay.

Installation

I've chosen to install ChatGPT Desktop on my Intel I7 Ubuntu 24.04 workstation. You can use Claude Code, Codex, Gemini etc.

I installed Rosbridge and MCP Server on Seggy.

cd ~/Downloads
sudo apt install ./chatgpt*.deb

# Some day you will need to upgrade it. Use package manager to install later updates:
sudo apt update
sudo apt install --only-upgrade chatgpt
  • Seggy is described here. It needs Rosbridge installed to allow the MCP server to connect to ROS2 over a WebSocket interface.
  • ros-mcp-server install on the workstation is described here. I built it from sources on Seggy instead. The new uv installer is used.
sudo apt install ros-${ROS_DISTRO}-rosbridge-server

# install uv - replaces pip:
curl -LsSf https://astral.sh/uv/install.sh | sh
# re-login or run env setup, this was added to ~/.bashrc:
. "$HOME/.local/bin/env"

# build MCP Server from sources:
mkdir -p ~/mcp_ws/src
cd ~/mcp_ws/src
git clone https://github.com/robotmcp/ros-mcp-server.git
cd ros-mcp-server/
uv sync
uv run server.py --help

Running Rosbridge and MCP Server on Seggy

ros2 launch rosbridge_server rosbridge_websocket_launch.xml

cd ~/mcp_ws/src/ros-mcp-server
# Note the "--host 0.0.0.0" part - it allows connections across LAN:
uv run server.py --transport streamable-http --host 0.0.0.0 --port 9000

Querying MCP Server for its capabilities as a ROS2 "tool"

Let's make Seggy execute commands issued in ChatGPT Desktop in human language.

Click here to expand

In ChatGPT Desktop type:

Connected successfully. It’s `ros-mcp-server` v2.13.0, with 31 tools for ROS/ROS 2 robot inspection and control.

You can:

- Discover connected robots, test ROSbridge connectivity, and detect the ROS version.
- Inspect ROS nodes, topics, services, actions, message types, and parameters.
- Read camera/image-topic data and retrieve saved images.
- Subscribe to topic messages once or over a time window.
- Inspect action state, send action goals, and cancel running goals.
- Call ROS services.
- Change or delete parameters.
- Publish individual messages or timed command sequencesβ€”for example, send velocity commands to `/cmd_vel`.

The server marks robot-affecting operations as destructive, including publishing commands,
 action goals, service calls, and parameter changes. 
Read-only discovery and monitoring tools are separately labeled.
  • What ROS2 topics can you see?
The server currently sees four ROS 2 topics:

     Topic         |    Message type
------------------ | ------------------------------------
/client_count      | std_msgs/msg/Int32
/connected_clients | rosbridge_msgs/msg/ConnectedClients
/parameter_events  | rcl_interfaces/msg/ParameterEvent
/rosout            | rcl_interfaces/msg/Log

These are ROS/rosbridge system topics; no robot sensor or command topics are visible yet.
  • query again (this time I ran Seggy's full stack on its RPi5)
Refresh succeeded: the ROS graph now exposes **133 topics**β€”a Nav2 mobile-robot stack is active.

Key topics include:

- Motion commands: `/cmd_vel`, `/cmd_vel_nav`, `/cmd_vel_teleop`, `/cmd_vel_smoothed`
- Localization/navigation: `/amcl_pose`, `/map`, `/scan`, `/goal_pose`, `/plan`, `/waypoints`
- Robot state: `/odometry/local`, `/odometry/global`, `/joint_states`, `/imu/data`, `/battery/battery_state`
- Navigation costmaps: `/global_costmap/*`, `/local_costmap/*`
- Transforms: `/tf`, `/tf_static`
- Vision/interaction: `/face_gesture_detections`, `/fgs/*`, `/joy`
- Docking and safety: `/dock_pose`, `/docking_trajectory`, `/collision_monitor_state`, `/geofence/*`

The change from four system topics means the robot/navigation system has since come online.
  • Move the robot forward for 2 seconds
Moved forward at 0.1 m/s for 2 seconds, then sent a zero-velocity stop.
The ROS server confirmed 21 messages published to `/cmd_vel` with no errors.
  • Plan and execute a path via Nav2. I want the robot move 1 meter forward. Use /odometry/local for position queries.
Nav2 planned a valid 40-pose path and began executing it.
The robot moved approximately **1.00 m** from its starting odometry position.

It stopped about 11 cm short of the exact Nav2 target while the original goal remained active,
 so I cancelled that lingering goal. Nav2 confirms that original goal is now **aborted**.

Querying HuskyLens 2 built-in MCP server

See this "Getting Started" guide first. More info here.

Check out my ROS 2 package for HuskyLens 2 for device setup and other info.

Here is DFRobot's guide on MCP Server use.

In ChatGPT Desktop type:

  • I have a Huskylens 2 camera MCP server at http://*.*.*.167:3000/sse Query its capabilities (use actual IP address)
Repeated successfully. The HuskyLens MCP server is online as v1.0.1 and exposes the same capabilities:
- Real-time recognition results and image data
- Algorithm listing, switching, parameters, and multi-algorithm mode
- Object learning, naming, and forgetting
- Photo, screenshot, audio recording, and music playback
- Display overlays: draw/clear text and rectangles
- Device controls: backlight, flashlight, volume
- Knowledge-base load/save and scheduled camera tasks

Using HuskyLens 2 I2C interface

If you haven't done it before, install uv (a pip replacement):

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

Re-login to enable environment variables

Create an environment and install libraries:

mkdir ~/husky2_project && cd ~/husky2_project
uv venv husky2_env
source husky2_env/bin/activate

uv pip install pyhuskylens smbus2

Create script:

Click here to expand code
import time
from pyhuskylens import HuskyLens
from pyhuskylens import (
    HuskyLens,
    ALGORITHM_OBJECT_RECOGNITION,
    ALGORITHM_OBJECT_CLASSIFICATION,
)

print("Initializing HuskyLens 2 on I2C...")
# Pass 1 for /dev/i2c-1. The library automatically looks for the 0x50 address.
try:
    hl = HuskyLens(1)
    print("Success! Connected to HuskyLens 2.")
except Exception as e:
    print(f"Connection Failed: {e}")
    print("Double check that your port light is BLUE and i2cdetect shows 0x50.")
    exit()

print("IP: setting OBJECT_RECOGNITION algorithm...")
hl.set_alg(ALGORITHM_OBJECT_RECOGNITION)

# For "learned" objects:
# print("IP: setting ALGORITHM_OBJECT_CLASSIFICATION algorithm...")
# hl.set_alg(ALGORITHM_OBJECT_CLASSIFICATION)

print("Streaming object data... Press Ctrl+C to stop.")
try:
    while True:
        # Grab any active bounding boxes/blocks from the screen
        blocks = hl.get_blocks()
        if blocks:
            for block in blocks:
                # name       β†’ semantic class ("chair")
                # ID         β†’ learned-object/category ID
                # classID    β†’ classification class ID (where supported)
                # confidence β†’ model confidence, but not necessarily retrievable
                print(f"Name: {getattr(block, 'name', None)}  \t" \
                      f"Center: ({block.x}, {block.y})  " \
                      f"Size: {block.width} x {block.height}     " \
                      f"Type: {getattr(block, 'type', None)}  " \
                      f"ID: {getattr(block, 'ID', None)}  " \
                      f"Learned: {getattr(block, 'learned', None)}  " \
                      f"Confidence: {getattr(block, 'confidence', None)}  " \
                      f"Content: {getattr(block, 'content', None)}")
                # print("------------------------------")
        time.sleep(0.2)
except KeyboardInterrupt:
    print("\nExiting script.")

Place something (chair, keyboard, computer monitor) in front of the camera.

HuskyLens Screen

Note: HuskyLens 2 ALGORITHM_OBJECT_RECOGNITION recognizes exactly 80 built-in object categories. DFRobot says the model is trained on MS COCO80, and its current documentation provides the complete list:

Click here to expand list
person
bicycle
car
motorcycle
airplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
couch
potted plant
bed
dining table
toilet
tv
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush

Run the script. The script selects "Object Recognition" mode.

cd ~/husky2_project
source husky2_env/bin/activate
python3 ./test_husky2.py 
  Initializing HuskyLens 2 on I2C...
  Success! Connected to HuskyLens 2.
  Streaming object data... Press Ctrl+C to stop.
  Name: tv  	    Center: (503, 369)  Size: 176 x 122  Type: BLOCK  ID: 0  Learned: False  Confidence: 2  Content: 
  Name: keyboard    Center: (149, 365)  Size: 298 x 130  Type: BLOCK  ID: 0  Learned: False  Confidence: 2  Content: 
  Name: chair  	    Center: (143, 60)   Size: 169 x 121  Type: BLOCK  ID: 0  Learned: False  Confidence: 2  Content: 
  Name: sports ball Center: (319, 327)  Size: 51 x 52    Type: BLOCK  ID: 0  Learned: False  Confidence: 2  Content: 
  ...

See this repository for more information and examples.

Exploring examples

Examples are located in this folder of the repository.


To Be Continued

Back to the Top

⚠️ **GitHub.com Fallback** ⚠️