Skip to content

About

ROS 2 Humble robot-side interface for a visual navigation model on the Kachaka robot: stale-frame checks, clamped cmd_vel bridge, dead-man timer.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

KachakaNavigation

CI

Robot-side interface for running a visual navigation model on the Kachaka mobile robot: four ROS 2 Humble nodes (image relay, model, command sender, robot executor), a clamped cmd_vel bridge with a dead-man timeout, and a typed wrapper over kachaka-api.

What this repository is not: a working navigation system. On main, the NoMaD adapter does not run inference, waypoint commands have no executor, and no closed-loop navigation run on the robot is claimed. The status table lists what works and how each item was checked; it describes main only (see the nomad-integration branch).

Replay of the cmd_vel gate: commands above the limits are clamped, the published Twist falls to zero when commands stop, on a stop command, and when a requested duration is too long

The executor's velocity gate replayed with a simulated clock, without ROS or a robot. Dots are the commands a model sends, the line is what reaches cmd_vel. Data: docs/assets/velocity_gate_timeline.csv, regenerated by python -m kachaka_navigation.scripts.simulate_velocity_gate --plot.

The original French README is kept as README.fr.md.

Context

This code was written by Guilhem Carmouze, robotics engineering student at UPSSITECH (University of Toulouse), during a research internship at the Computer Vision Research Team, Artificial Intelligence Research Center, AIST, Tsukuba, Japan (April to August 2026). The internship subject was the creation of a 360-degree navigation dataset using 3D Gaussian Splatting.

During the internship, a ROS 2 deployment interface for a visual navigation model (NoMaD) on the Kachaka robot was prepared. This repository is that interface:

camera -> image transfer -> navigation model -> command conversion -> Kachaka API

On main, the image transfer and the basic safety layer were prepared; the trained model and the conversion from predicted waypoints to robot velocities were not connected. The dataset side of the internship lives in two other repositories: nav_3dgs_pano (navigation and panoramic rendering in a simulated 3DGS scene) and artifixer-360-pipeline (360-degree video from a plain video). Nothing here uses 3D Gaussian Splatting.

Status

Component State How it was checked
Message contracts, JSON serialization, model registry Works Unit tests
Stale-frame rejection (0.5 s) and stale-command rejection Works Unit tests and fake-clock node tests; stale frames also in the ROS 2 loopback tests
ROS 2 chain: image relay, trajectory generator, command sender, executor in dry-run Works in a ROS 2 Humble container with synthetic images tests/ros2; the four entry points were also started by hand in the container
cmd_vel bridge: clamp, 20 Hz re-publication, dead-man timeout Works up to the Twist topic Fake-clock tests, ROS 2 loopback test, replay above. No robot was listening
kachaka-api wrapper, command executor and the 15 direct command-line scripts Call mapping works against a stand-in client Unit tests. A real run needs the robot; none is recorded in this repository
TCP JSON-lines transport Works on localhost Unit tests. The robot-side client script reads a ROS 1 topic and is untested
ROS 2 Docker image Builds, runs the tests and the nodes Built and run on 5 October 2026
ROS 1 legacy path Untested ROS Noetic ships Python 3.8, this package needs 3.10
NoMaD inference (nomad_original) Not wired on main The adapter fills a 5-frame queue, then raises; every output is a stop command (tested)
Waypoint commands Not executable on main Defined in the contract only; the executors reject them
Closed-loop navigation on the robot Not claimed No run is recorded on main

The nomad-integration branch

This table describes main. The same repository has a second branch, nomad-integration: 13 commits from 9 to 12 June 2026, branched from the original main before any of the October 2026 work. According to its commit messages and files it is an attempt to drive the robot from NoMaD directly through kachaka-api, without ROS (a NoMaD wrapper of about 800 lines, a controller, command-line scripts, two test files). That branch was not merged, reviewed, tested or run for this README, so nothing it may have achieved on the robot is claimed here, and the tests and numbers on this page do not cover it.

Architecture

flowchart TB
    camera(["Camera topic<br/>Image or CompressedImage"])

    subgraph camera_host["Camera or robot host"]
        relay["image_sender<br/>relays any image topic"]
    end

    subgraph model_host["Model host"]
        generator["trajectory_generator<br/>frame older than 0.5 s: stop<br/>model error: stop"]
        model[["NavigationModel<br/>noop, constant_velocity,<br/>nomad_original (not wired)"]]
        sender["command_sender<br/>command older than 0.5 s: stop"]
    end

    subgraph robot_host["Robot host"]
        executor["kachaka_command_executor<br/>dry-run mode logs only"]
        gate["VelocityCommandGate<br/>off unless enabled<br/>clamp 0.2 m/s, 0.5 rad/s<br/>zero Twist after timeout"]
    end

    api(["Kachaka API<br/>gRPC, port 26400"])
    cmdvel(["cmd_vel topic<br/>Twist at 20 Hz"])

    camera --> relay
    relay -- "/kachaka_navigation/image" --> generator
    generator <--> model
    generator -- "/kachaka_navigation/trajectory" --> sender
    sender -- "/kachaka_navigation/robot_command" --> executor
    executor -- "high-level commands" --> api
    executor -- "velocity commands" --> gate
    gate --> cmdvel
Loading

The three hosts can be one machine. Commands travel as JSON in std_msgs/String; image topics use the sensor-data QoS and command topics a queue depth of 1, so the newest message always wins.

A model only sees an ImageFrame and returns a NavigationCommand (stop, noop, velocity, waypoint, move_to_location, return_home, dock_shelf, undock_shelf, cancel, speak). It never talks to the robot, which is what makes models interchangeable. The same contract also runs over a JSON-lines TCP link (run_navigation_server) so that inference can live on another machine without ROS.

Model Purpose Output
noop (default) Validate the wiring Always stop
constant_velocity Exercise the cmd_vel path A fixed small velocity
nomad_original Integration point for NoMaD stop only: inference is not implemented

Safety layer

Check Default Where
Image older than the limit, or stamped in the future, becomes stop 0.5 s core/realtime.py, trajectory generator
Any model exception becomes stop server/navigation_service.py
Command older than the limit becomes stop 0.5 s command sender
Velocity control is off unless --enable-velocity-control is passed off executor
Linear and angular speed are clamped 0.2 m/s, 0.5 rad/s core/velocity_safety.py
Command duration is capped, with a default when none is given 1.0 s, 0.25 s core/velocity_safety.py
Active velocity is re-published, then replaced by one zero Twist when it expires 20 Hz core/velocity_gate.py
stop, cancel and undecodable commands zero the velocity at once executor
NaN or infinite speeds, durations, limits and timestamps are refused core/messages.py, core/velocity_safety.py
--dry-run logs commands instead of acting executor

In the scripted replay shown above (26 commands over 5.6 s), 8 commands exceeded the linear limit and 5 the angular limit; no published Twist exceeded 0.2 m/s or 0.5 rad/s. The zero Twist came 0.23 s after the last command when the stream stopped (0.2 s command duration plus the wait for the next 20 Hz tick), at once on a stop command, and 1.03 s after a command that asked for 5 s. These numbers are in docs/assets/velocity_gate_summary.json, and a test fails if the committed CSV and JSON differ from what the script produces.

Quickstart

Without ROS (Linux, macOS or Windows, Python 3.10 or newer)

git clone https://github.com/guilhem0908/KachakaNavigation.git
cd KachakaNavigation
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
python -m pip install -e ".[dev,ros2]"
python -m pytest
python -m kachaka_navigation.scripts.simulate_velocity_gate

The last command rewrites the CSV and the JSON summary in docs/assets; add --plot (and the figures extra) to redraw the figure. Everything runs on a CPU. Without ROS the node classes are tested on a small fake of the rclpy node API, with a clock the tests advance by hand.

python -m pytest on 5 and 7 October 2026, on a laptop CPU:

Environment Result Duration
Windows 11, Python 3.14 (no kachaka-api, no ROS) 165 passed, 2 skipped 3 to 5 s
Linux container, Python 3.12 (with kachaka-api, no ROS) 180 passed, 1 skipped 2 to 9 s
ROS 2 Humble container, Python 3.10 (see below) 148 passed, 38 skipped 10 to 36 s

Skipped tests are the ones that need ROS 2 or kachaka-api where these are missing. Inside the ROS 2 container the 38 fake-clock node tests are skipped instead, because the real rclpy replaces the fake one and tests/ros2 takes over. The container was run through Docker Desktop on Windows with the sources bind-mounted, and its duration varied between runs; the six loopback tests alone took 8 to 16 s because they wait on real timers.

kachaka-api pins grpcio==1.66.1, which has wheels for CPython 3.8 to 3.12 only. On Python 3.13 and newer the two packages are skipped by an environment marker: the tests above still run, the robot client is unavailable.

With ROS 2 Humble (Docker)

docker compose build ros2
docker compose run --rm ros2 python -m pytest

The second command includes tests/ros2, which runs the four nodes on a real ROS 2 graph with a probe node playing the camera. To start the nodes yourself, open four shells with docker compose run --rm ros2 (or docker exec into one container) and run:

python -m kachaka_navigation.scripts.run_ros2_trajectory_generator --model noop
python -m kachaka_navigation.scripts.run_ros2_command_sender
python -m kachaka_navigation.scripts.run_ros2_image_sender \
  --input-image-topic /camera/image_raw \
  --output-image-topic /kachaka_navigation/image
python -m kachaka_navigation.scripts.run_ros2_kachaka_command_executor --dry-run

Without a camera, publish a synthetic image and watch the commands:

ros2 topic pub -r 10 /camera/image_raw sensor_msgs/msg/Image \
  "{height: 2, width: 2, encoding: 'rgb8', step: 6, data: [255,0,0, 0,255,0, 0,0,255, 9,9,9]}"
ros2 topic echo /kachaka_navigation/robot_command

With the noop model every command is a stop whose reason is No navigation model is active. More in docs/ros2_docker.md and docs/low_level_velocity.md.

With a Kachaka robot (not re-run for this README)

cp .env.example .env               # then set KACHAKA_HOST to the robot address
python -m kachaka_navigation.scripts.check_api_connection
python -m kachaka_navigation.scripts.smoke_test_connection
python -m kachaka_navigation.scripts.list_locations

These need Python 3.10 to 3.12, a robot with the Kachaka API enabled, and network access to its port 26400. The other direct commands are listed in docs/kachaka_api_commands.md. Velocity commands additionally need a ROS 2 bridge on the robot side that subscribes to the cmd_vel topic; this repository only publishes the Twist.

Repository layout

src/kachaka_navigation/
├── core/        contracts and safety logic, no ROS dependency
├── models/      model adapters and their registry
├── server/      NavigationService: one model behind a fail-safe API
├── ros2/        the four ROS 2 nodes, message conversion, lazy rclpy import
├── robot/       command executors and the TCP robot-side loop
├── clients/     typed wrapper over kachaka-api
├── transport/   JSON-lines TCP server and client
├── ros/         ROS 1 legacy image source and command publisher
└── scripts/     entry points, direct Kachaka commands, gate replay
models/          expected location of model assets (none are distributed)
docs/            architecture, model integration, cmd_vel, Docker, API commands
tests/           unit tests, fake-clock node tests, tests/ros2 loopback tests

Defects found while adding tests (October 2026)

The original code came with ten unit tests. Extending them and building the Docker image exposed the problems below, all fixed in the history of this repository:

  • NaN defeated the velocity limits. A NaN speed was clamped to the negative limit (-0.2 m/s) and a NaN duration never expired, so the dead-man timer never fired. NaN survives the JSON transport. Non-finite values are now refused at every level.
  • The default raw-image path could not work in the Docker image. kachaka-api requires NumPy 2.2 or newer, while the ROS 2 Humble cv_bridge and Ubuntu's OpenCV are built against NumPy 1.x and failed to import. Raw images are now decoded with NumPy alone.
  • The image had no python command, although every documented command starts with python -m, and pytest could not start in it.
  • Ctrl+C ended every node with a traceback and exit code 1, because the ROS context was shut down twice.

Limitations

  • No learned model runs on main. Connecting NoMaD to it still requires loading the checkpoint, the original image preprocessing, and a conversion from predicted waypoints to velocities. The nomad-integration branch is described above.
  • The freshness checks compare timestamps taken on different machines and assume synchronised clocks.
  • The dead-man timer compares deadlines with the executor node's ROS clock, which is wall-clock time by default. It assumes a clock that does not step backwards: after a backward step, the active Twist is re-published until the clock reaches the old deadline, which can exceed the 1 s cap.
  • Raw images are limited to the 8-bit encodings bgr8, rgb8, bgra8, rgba8, mono8 and 8UC1; other cameras must publish a compressed topic.
  • The safety layer bounds speed and time only. There is no obstacle detection and no emergency-stop input.
  • The cmd_vel topic name is a default that has to be checked against the bridge running on the robot.
  • docker-compose.yml uses host networking, which behaves as intended on Linux only.
  • No experimental result (success rate, latency, distance travelled) exists for this code.

Authorship and credits

  • Guilhem Carmouze is the author of the repository. The two original commits on main date from 26 May 2026. The October 2026 commits (tests, fixes, continuous integration, English documentation) were written with an AI coding assistant, as their commit trailers state. The internship report likewise declares that AI tools were used for research and for writing code.
  • Host laboratory: Computer Vision Research Team, Artificial Intelligence Research Center, AIST, Tsukuba, Japan.
  • kachaka-api by Preferred Robotics (Apache-2.0) is the gRPC client this package wraps.
  • NoMaD: A. Sridhar, D. Shah, C. Glossop and S. Levine, "NoMaD: Goal Masked Diffusion Policies for Navigation and Exploration", ICRA 2024; code in robodhruv/visualnav-transformer. On main it is referenced as the intended model and is neither included nor loaded.
  • ROS 2 Humble, NumPy, OpenCV, pytest and ruff.

No licence file has been added yet.

About

ROS 2 Humble robot-side interface for a visual navigation model on the Kachaka robot: stale-frame checks, clamped cmd_vel bridge, dead-man timer.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages