Caasi v0.1.0 ROS Ecosystem: ros · nav · moveit · control

ROS Ecosystem

Orchestrate ROS 2, Nav2, MoveIt 2 and ros2_control by delegating to the real ros2 CLI of your installed distro. Caasi never imports rclpy and never speaks DDS itself — it finds your distro, builds ros2 command lines, and turns every long-lived launch into a tracked run.

Distro discovery

Every command on this page resolves the distro the same way:

  1. ROS_DISTRO environment variable → /opt/ros/$ROS_DISTRO;
  2. otherwise, scan /opt/ros for a subdirectory containing setup.bash;
  3. the ros2 binary: <distro>/bin/ros2 first, then ros2 on PATH.

You do not need to source setup.bash before using Caasi — the distro is located directly. When no ros2 binary can be found, commands fail with:

shellcaasi ros list topics
Error: No ROS 2 installation found (no ros2 CLI). Install ROS 2 or set ROS_DISTRO — see 'caasi setup ros2'.

caasi ros

caasi ros status

caasi ros status [--json]

The one-screen ROS overview: distro, root, binary, RMW implementation and how many topics are currently live (a real ros2 topic list call — proves the graph works, not just that files exist). Never fails: “not found” is reported as data.

shellcaasi ros status
ROS 2
  Distro     jazzy
  Root       /opt/ros/jazzy
  Binary     /opt/ros/jazzy/bin/ros2
  RMW        (default)
  Topics     2
caasi ros status --json
{ "available": true, "distro": "jazzy", "root": "/opt/ros/jazzy",
  "binary": "/opt/ros/jazzy/bin/ros2", "rmw": null, "topics": 2 }

caasi ros doctor

caasi ros doctor [--json]

Runs ros2 doctor (60 s timeout) and mirrors its output and exit code — the real health check of your middleware, network and distro setup. JSON mode: {"returncode": 0, "output": "…full report…"}.

caasi ros list

caasi ros list KIND [--json]        # KIND: topics | nodes | services | actions

Thin, uniform wrapper over ros2 topic|node|service|action list:

shellcaasi ros list topics
Topics
  /parameter_events
  /rosout
  /scan
  /tf
caasi ros list nodes --json
["/talker", "/listener"]
caasi ros list topicz
Error: Unknown kind 'topicz'. Valid kinds: topics, nodes, services, actions.

JSON is the raw name array. Empty lists print a “no …” hint, exit 0; missing ros2 exits 1.

caasi ros launch

caasi ros launch TARGET [--package|-p PKG] [--name NAME] [--dry-run] [--json] [-- LAUNCH_ARGS…]
ParameterKindTypeDefaultDescription
TARGETargumentstrrequiredLaunch file path, or (with --package) the launch file name inside a package.
--packageoption (-p)strLaunch from an installed package: ros2 launch <pkg> <target>.
--nameoptionstrtarget stemRun name for the tracked run.
--dry-runoptionflagoffShow the command without starting.
--jsonoptionflagoffPrint the run record.
trailing argspass-throughAppended verbatim (e.g. use_sim_time:=true).

The launch becomes a tracked run (backend: ros, kind: ros) — detached, logged, stoppable, surviving your terminal. A path-like target that doesn't exist fails early: Error: Launch file not found: <path>.

shellcaasi ros launch demo.launch.py -p demo_bringup --dry-run
Dry run — nothing was started:
  command: /opt/ros/jazzy/bin/ros2 launch demo_bringup demo.launch.py
caasi ros launch demo.launch.py -p demo_bringup use_sim_time:=true
Run 20260905-190411-demo-launch started in the background.
  Follow it with: caasi logs 20260905-190411-demo-launch -f
caasi run stop latest

caasi ros topic

caasi ros topic NAME [--echo] [--dry-run] [--json] [-- ECHO_ARGS…]

Two modes in one command:

shellcaasi ros topic /scan
Type: sensor_msgs/msg/LaserScan
Publisher count: 1
Subscription count: 0
caasi ros topic /scan --echo
Run 20260905-191022-echo-scan started in the background.
caasi logs echo-scan -n 4
---
header:
  stamp: {sec: 1757070622, nanosec: 412}
  frame_id: laser_frame
range_min: 0.12
caasi run stop echo-scan

caasi ros node

caasi ros node NAME [--json]

Wraps ros2 node info NAME — subscribers, publishers, service clients/servers. Unknown node → Error: Node '/nope' not found (is the graph up?). exit 1. JSON: {"node": "/talker", "info": "…"}.

caasi ros graph

caasi ros graph [--json]

Quick whole-graph snapshot — all nodes and all topics in one view:

shellcaasi ros graph
ROS Graph
  Nodes (2)
    /listener
    /talker
  Topics (4)
    /parameter_events
    /rosout
    /scan
    /tf
caasi ros graph --json | jq '.nodes'
["/listener", "/talker"]

Nav2 orchestration on top of the same delegation: Caasi checks that nav2_bringup is installed (via ros2 pkg prefix nav2_bringup), starts the standard bringup, and tells you whether the navigation stack is actually up — by looking for its nodes in the live graph.

caasi nav status [--json]

Reports the bringup install prefix and which Nav2 nodes are currently running. Recognized nodes: bt_navigator, planner_server, controller_server, behavior_server, recoveries_server, smoother_server, waypoint_follower, amcl, velocity_smoother.

shellcaasi nav status
Nav2
  Bringup    /opt/ros/jazzy
  Nodes      /bt_navigator, /planner_server, /controller_server, /amcl
caasi nav status --json
{ "installed": true, "bringup": "/opt/ros/jazzy",
  "nodes": ["/bt_navigator", "/planner_server", "/controller_server", "/amcl"] }
caasi nav launch [--params FILE] [--map FILE] [--dry-run] [--json] [-- LAUNCH_ARGS…]
OptionTypeDefaultDescription
--paramspathParams file → launch arg params_file:=<file>.
--mappathMap file → launch arg map:=<file>.
--dry-runflagoffShow the command without starting.
--jsonflagoffPrint the run record.

Starts ros2 launch nav2_bringup bringup_launch.py [map:=…] [params_file:=…] as a tracked run named nav2-bringup (kind: nav). Requires nav2_bringup to be installed:

shellcaasi nav launch --map maps/warehouse.yaml --dry-run
Dry run — nothing was started:
  command: /opt/ros/jazzy/bin/ros2 launch nav2_bringup bringup_launch.py map:=maps/warehouse.yaml
caasi nav launch --map maps/warehouse.yaml --params my_nav2_params.yaml
Run 20260905-192233-nav2-bringup started in the background.
caasi logs nav2-bringup -f
# …wait for "[INFO] [bt_navigator]: bt_navigator started"…

Without Nav2: Error: Nav2 is not installed (no 'nav2_bringup' package). Install it (e.g. sudo apt install ros-jazzy-navigation2 ros-jazzy-nav2-bringup) and try again.

caasi nav inspect [PARAMS_FILE] [--json]

Lists the nodes configured in a Nav2 params file. With no argument, the distro default is used: <bringup prefix>/share/nav2_bringup/params/nav2_params.yaml. Nav2 params files are keyed by node name at the top level, so “which nodes will this bring up?” is one yaml.safe_load away:

shellcaasi nav inspect
Nav2
  Params     /opt/ros/jazzy/share/nav2_bringup/params/nav2_params.yaml
  Nodes      14
    amcl
    behavior_server
    bt_navigator
    controller_server
    planner_server
    …
caasi nav inspect my_params.yaml --json
{ "path": "my_params.yaml", "nodes": ["amcl", "bt_navigator", "planner_server"] }

Missing file → Error: Nav2 params file not found: <path> (exit 1).

caasi nav test [--json]

Readiness gate with three checks — ros2 binary, nav2_bringup package, running Nav2 nodes — and an exit code you can script against:

shellcaasi nav test
✓ ros2              /opt/ros/jazzy/bin/ros2
✓ nav2_bringup      /opt/ros/jazzy
✓ nodes             /bt_navigator, /planner_server, /controller_server, /amcl
Nav2 is ready.
caasi nav test --json | jq '.ok'
true

JSON: {"ok": bool, "checks": [{"check", "ok", "detail"}, …]}. Exit 0 when all checks pass, 1 otherwise.

caasi moveit — MoveIt 2

Same pattern for manipulation: locate moveit_ros_move_group, launch your MoveIt config, and verify the move_group node is up. Planning always happens inside MoveIt 2.

caasi moveit status

caasi moveit status [--json]
shellcaasi moveit status
MoveIt 2
  Package      /opt/ros/jazzy
  move_group   /move_group
caasi moveit status --json
{ "installed": true, "package": "/opt/ros/jazzy",
  "move_group_nodes": ["/move_group"] }

Node detection works even when the package prefix lookup is unavailable — status answers “is move_group running right now?” from the live graph.

caasi moveit launch

caasi moveit launch TARGET [--package|-p PKG] [--name NAME] [--dry-run] [--json] [-- LAUNCH_ARGS…]

Identical semantics to ros launch (path check, pass-through args, tracked run — kind: moveit), pointed at your MoveIt config's demo launch:

shellcaasi moveit launch demo.launch.py -p panda_moveit_config
Run 20260905-193312-demo-launch started in the background.
  Follow it with: caasi logs 20260905-193312-demo-launch -f

caasi moveit plan

caasi moveit plan [--group|-g NAME] [--json]

Answers “can I plan right now?” — i.e. is a move_group node in the graph? The optional --group records which planning group you care about. Exit 0 ready / 1 not ready:

shellcaasi moveit plan --group panda_arm
move_group is up — planning available (group: panda_arm)
caasi moveit plan --json
{ "ready": true, "group": null, "move_group_nodes": ["/move_group"] }
caasi moveit plan; echo $?
Error: move_group node is not running. Launch your MoveIt config first (caasi moveit launch …).
1

caasi moveit test

caasi moveit test [--json]

Three-check readiness gate like nav test: ros2 binary, moveit_ros_move_group package, live move_group node. JSON {"ok", "checks"}, exit 0/1.

caasi control — ros2_control

Controller management via ros2 control (the ros2controlcli package), plus offline validation of controller YAML — the #1 source of “why won't my hardware interface load”.

caasi control status

caasi control status [--json]
shellcaasi control status
ros2_control
  Package             /opt/ros/jazzy
  Controller managers /controller_manager
caasi control status --json
{ "installed": true, "package": "/opt/ros/jazzy",
  "managers": ["/controller_manager"] }

managers are the live graph nodes containing controller_manager — i.e. the robots/hardware interfaces currently serving controllers.

caasi control list

caasi control list [--json]

Runs ros2 control list_controllers and passes the output through:

shellcaasi control list
joint_state_broadcaster    joint_state_broadcaster/JointStateBroadcaster    active
diff_drive_controller      diff_drive_controller/DiffDriveController        active
caasi control list --json
["joint_state_broadcaster    joint_state_broadcaster/JointStateBroadcaster    active", …]

Failure (typically ros2controlcli not installed, or no manager reachable) → Error: ros2_control CLI is not available (is 'ros2controlcli' installed?). exit 1.

caasi control check

caasi control check PARAMS_FILE [--json]

Static validation of a controller params YAML, offline — no ROS needed. It verifies:

controllers.yamlcontroller_manager:
  ros__parameters:
    update_rate: 100
    joint_state_broadcaster:
      type: joint_state_broadcaster/JointStateBroadcaster
    diff_drive_controller:
      type: diff_drive_controller/DiffDriveController
shellcaasi control check controllers.yaml
✓ joint_state_broadcaster
✓ diff_drive_controller
Controller config looks good (2 controller(s)).
caasi control check broken.yaml --json
{
  "path": "broken.yaml",
  "controllers": ["diff_drive_controller", "broken_controller"],
  "issues": [
    "controller_manager.ros__parameters has no 'update_rate'",
    "controller 'broken_controller' has no 'type'"
  ],
  "ok": false
}
exit code: 1

Missing argument → Error: Pass a controller params YAML file…; missing file → params-not-found error; exit 0 clean / 1 any issue (both modes).

The shared pattern

You want…Use
“what's on the graph?”ros status · ros graph · ros list …
“start something long-lived”ros launch · nav launch · moveit launch · ros topic --echo → all become tracked runs
“is the stack ready?” (scriptable)nav test · moveit test · moveit plan · control check → exit 0/1 + --json
“why is my config broken?”nav inspect · control check · ros doctor
anything else ROScaasi native ros … — raw ros2 pass-through