This walkthrough gives you three useful demonstrations: a tiny ROS 2 package you can understand and break on purpose, an optional AI investigation of its failure, and a TurtleBot3 simulation with a larger graph, topics, and launch output. A saved incident result then gives the assistant a specific, reviewable piece of evidence to explain. The commands below target Ubuntu 24.04 Desktop (x86_64), ROS 2 Jazzy, and Gazebo Harmonic. Allow time for the first Gazebo startup. Use separate terminal windows where indicated. The simulation is for desktop evaluation, not robot hardware validation.
This walkthrough uses the installed Flightops beta for Ubuntu 24.04 with Jazzy. On Ubuntu 22.04 with Humble, install the Humble archive instead and use first steps; the TurtleBot3 commands here target Jazzy/Harmonic.
This walkthrough is a procedure, not evidence that your machine passed it. The known limits list what the beta does not cover yet.
Recommended showcase after installation: run the small package, attach Flightops, and stop and restart the launch to show live graph changes. Let Flightops own the launch and show its L log. If you have an AI connection, introduce the deliberate topic mismatch in step 4 and ask for a diagnosis. Then inspect the saved incident in the assistant's Results view and open its browser report (step 6). Add TurtleBot3 when you have time for the larger simulation build and Gazebo startup. The AI turns are optional and may incur provider usage; the ROS observations and saved result remain useful on their own.
1. Install ROS 2 Jazzy
Start with a fresh Ubuntu 24.04 Desktop shell. ROS recommends the Debian packages for this platform. Check that locale shows a UTF-8 locale; if it does not, complete the locale step in the official Jazzy installation guide before continuing. The following repository setup follows that guide. It uses the current ros2-apt-source package instead of a copied repository key.
sudo apt update
sudo apt install -y software-properties-common curl git python3
sudo add-apt-repository universe
ROS_APT_SOURCE_VERSION="$(curl -fsSL https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')"
curl -fL -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.noble_all.deb"
sudo dpkg -i /tmp/ros2-apt-source.deb
sudo apt update
sudo apt upgrade -y
sudo apt install -y ros-jazzy-desktop ros-dev-tools python3-colcon-common-extensions
source /opt/ros/jazzy/setup.bash
printenv ROS_DISTRO
ros2 --helpExpect jazzy and the ROS command help. If ros-jazzy-desktop cannot be located, check the apt-source installation and Ubuntu release before trying another package. The ROS installation page is the authority if repository setup has changed. Source /opt/ros/jazzy/setup.bash in every new terminal used below. Keep the shells on the same ROS_DOMAIN_ID and middleware (RMW_IMPLEMENTATION); with neither set, they share the defaults.
2. Install and activate Flightops
Install the Jazzy archive with the command from installation. It needs no source checkout, Cargo, or sudo. Then activate this device with the key from your beta email (details) and check the install:
source /opt/ros/jazzy/setup.bash
export PATH="$HOME/.local/bin:$PATH"
flightops license activate
command -v flightops
flightops --verify
flightops doctorcommand -v flightops should print ~/.local/bin/flightops (expanded). --verify should pass. doctor should report the Jazzy environment and may show zero nodes until you start something. If it reports a problem, see troubleshooting.
In every later Flightops terminal, run:
source /opt/ros/jazzy/setup.bash
export PATH="$HOME/.local/bin:$PATH"3. Create a small ROS 2 package
This package has a publisher, a subscriber, and one launch file. It makes the first Flightops screens predictable without waiting for Gazebo. The package structure and installed launch-file approach follow the ROS package and launch tutorials. Use a regular ROS shell (you do not need Flightops to build the ROS workspace):
source /opt/ros/jazzy/setup.bash
mkdir -p ~/flightops_demo_ws/src
cd ~/flightops_demo_ws/src
ros2 pkg create --build-type ament_python --license Apache-2.0 flightops_demo --dependencies rclpy std_msgs launch launch_ros
cd flightops_demo
mkdir -p launch
cat > flightops_demo/nodes.py <<'PY'
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__('demo_talker')
self.publisher = self.create_publisher(String, '/demo/heartbeat', 10)
self.count = 0
self.create_timer(1.0, self.tick)
def tick(self):
self.count += 1
message = String()
message.data = f'flightops demo {self.count}'
self.publisher.publish(message)
self.get_logger().info(message.data)
class Listener(Node):
def __init__(self):
super().__init__('demo_listener')
self.create_subscription(String, '/demo/heartbeat', self.receive, 10)
def receive(self, message):
self.get_logger().info(f'heard: {message.data}')
def talker_main():
rclpy.init()
node = Talker()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
if rclpy.ok():
rclpy.shutdown()
def listener_main():
rclpy.init()
node = Listener()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
if rclpy.ok():
rclpy.shutdown()
PY
cat > launch/demo.launch.py <<'PY'
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(package='flightops_demo', executable='talker', output='screen'),
Node(package='flightops_demo', executable='listener', output='screen'),
])
PY
cat > setup.py <<'PY'
from glob import glob
from setuptools import setup
package_name = 'flightops_demo'
setup(
name=package_name,
version='0.0.1',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages', ['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
('share/' + package_name + '/launch', glob('launch/*.launch.py')),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='Demo User',
maintainer_email='demo@example.com',
description='Minimal publisher and subscriber for a Flightops demo',
license='Apache-2.0',
entry_points={'console_scripts': [
'talker = flightops_demo.nodes:talker_main',
'listener = flightops_demo.nodes:listener_main',
]},
)
PY
cd ~/flightops_demo_ws
colcon build --symlink-install --packages-select flightops_demo
source install/local_setup.bash
ros2 pkg prefix flightops_demo
ros2 launch flightops_demo demo.launch.pyExpect the build to finish and both flightops demo N and heard: flightops demo N to repeat in the launch terminal. Press Ctrl+C after confirming. If ros2 launch cannot find the file, rebuild and source ~/flightops_demo_ws/install/local_setup.bash in that shell. Keep the ROS underlay sourced first.
Attach, then cause a visible change
In terminal A, source the underlay and workspace overlay and start the package outside Flightops:
source /opt/ros/jazzy/setup.bash
source ~/flightops_demo_ws/install/local_setup.bash
ros2 launch flightops_demo demo.launch.pyIn terminal B, source the same ROS setup and put Flightops on your PATH:
source /opt/ros/jazzy/setup.bash
source ~/flightops_demo_ws/install/local_setup.bash
export PATH="$HOME/.local/bin:$PATH"
flightops doctor
flightops attachExpect two demo nodes and /demo/heartbeat. In Flightops, use ? for keys, the Graph/Topics/Nodes views to inspect the publisher and subscriber, / to filter for demo, and r to refresh. In terminal A press Ctrl+C; then refresh terminal B and observe the nodes disappear. Start the same ros2 launch command in A again; refresh B and observe them return. Press q in B and confirm A continues printing. This shows that attach observes existing nodes and does not own their process. Stop A with Ctrl+C when done.
Let Flightops own the launch
In terminal B (with the same setup still active), run:
flightops --project ~/flightops_demo_ws/src/flightops_demo \
--session-dir ~/flightops_demo_session launch flightops_demo demo.launch.pyUse the Health and Nodes views; press L to inspect the owned launch log. Close the log with Esc before pressing q. The demo launch should stop when Flightops exits. To inspect the launch's retained session, reopen its directory from the same terminal B:
flightops --project ~/flightops_demo_ws/src/flightops_demo \
--session-dir ~/flightops_demo_session attachReopening shows the retained launch record and log; it does not restart the launch. A plain flightops --project ... attach opens a separate observation session instead, because attach and launch keep separate default sessions. You can add a declared message check for /demo/heartbeat after this basic run. A graph count or one received message shows observation, not full system readiness.
4. Investigate a deliberate mismatch with the assistant (optional)
Connect an assistant while the small package is still familiar. For the shortest path, run flightops ai setup in a Flightops shell, select OpenAI API or Anthropic API, enter a key at the masked prompt, and choose one of the models returned for your account. Setup checks the provider's model list; it does not test an assistant answer. With an OS credential store, the connection can be reused in later Flightops processes. Check it separately:
flightops ai status
flightops ai teststatus reads local configuration. test sends a synthetic greeting to the provider, without project files, and may incur usage. If there is no OS credential store, setup keeps the key only in the app it opens. Stay in that app and press A, then m to enable AI; a separate ai test or new Flightops process cannot use that key. You can keep that app open for the fault investigation below and run the topic-report commands in a third ROS terminal. The AI setup guide also covers the ChatGPT account (preview) and local/custom routes.
Now make one controlled fault in the package you created in step 3. In terminal A, use a regular ROS shell with the Jazzy underlay and demo workspace overlay sourced. With all previous launches stopped, change only the listener's subscription from /demo/heartbeat to /demo/heartbeats:
source /opt/ros/jazzy/setup.bash
source ~/flightops_demo_ws/install/local_setup.bash
sed -i "s|self.create_subscription(String, '/demo/heartbeat', self.receive, 10)|self.create_subscription(String, '/demo/heartbeats', self.receive, 10)|" \
~/flightops_demo_ws/src/flightops_demo/flightops_demo/nodes.py
cd ~/flightops_demo_ws
colcon build --symlink-install --packages-select flightops_demo
source install/local_setup.bash
ros2 launch flightops_demo demo.launch.pyKeep this launch running in terminal A. The talker should still print flightops demo N, while the listener no longer prints heard:. In terminal B, source the same ROS underlay and workspace overlay as in step 3, then run the two topic reports. With a saved AI connection, start a new Flightops observation:
ros2 topic info /demo/heartbeat --verbose
ros2 topic info /demo/heartbeats --verbose
flightops --project ~/flightops_demo_ws/src/flightops_demo --ai attachIf setup left you in a session-only Flightops app, run the two ros2 topic info commands in another sourced ROS terminal instead. Press r to refresh the open Flightops app after starting the faulted launch. If you are skipping AI, use flightops attach for the observation.
The two topic reports should show the publisher on /demo/heartbeat and the subscriber on /demo/heartbeats. In Flightops, inspect the two nodes and topics. If using AI, press A to open the assistant. If AI is still in Standard mode, press m. Then press i, type the question below, and press Enter:
The talker publishes/demo/heartbeatonce a second, but the listener does not printheard:. The topic report shows its subscription on/demo/heartbeats. What is the smallest likely fix, and which observation should I repeat after making it?
Read the pending request's provider, model, prompt, and disclosure before pressing y to send; n rejects it. This is a question using facts you supplied. Opening the assistant does not automatically send the project, launch log, or whole live graph. Compare the suggestion with the two topic reports and the source line. The expected diagnosis is to align the listener's topic name with the talker's and repeat the topic report and heartbeat output; the answer itself is not a ROS test. Stop the faulted launch and close its attached Flightops view. Then restore the subscription and rerun the launch to check the diagnosis:
sed -i "s|self.create_subscription(String, '/demo/heartbeats', self.receive, 10)|self.create_subscription(String, '/demo/heartbeat', self.receive, 10)|" \
~/flightops_demo_ws/src/flightops_demo/flightops_demo/nodes.py
cd ~/flightops_demo_ws
colcon build --symlink-install --packages-select flightops_demo
source install/local_setup.bash
ros2 launch flightops_demo demo.launch.pyThe listener should again print matching heard: values. Stop this launch before starting TurtleBot3. If the assistant is unavailable, the same observations and recovery still show how Flightops supports an investigation. See using the assistant for panel keys and request review.
5. Add a TurtleBot3 simulation
For the more visual demo, follow the ROBOTIS Jazzy PC setup and simulation instructions. Select the Jazzy tab on both pages; they also display Humble and Noetic instructions. This demo uses the Jazzy simulation path, not every TurtleBot3 feature listed on those pages. Use a separate ~/turtlebot3_ws so the small example remains easy to repeat. Open a fresh ROS terminal for this build, without the small package overlay active. This walkthrough uses the ROBOTIS jazzy branches consistently. Do not add a second apt-installed copy of the same TurtleBot3 packages to this overlay.
The ROBOTIS PC setup installs Gazebo Harmonic from the OSRF apt repository. The commands below follow its Jazzy → Install Dependent ROS 2 Packages → Install Gazebo Sim step. Then install the ROS bridge and clone the package sources. Gazebo needs a graphical desktop session and enough resources for a 3D simulator.
source /opt/ros/jazzy/setup.bash
sudo apt install -y curl lsb-release gnupg
sudo curl -fsSL https://packages.osrfoundation.org/gazebo.gpg --output /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
sudo apt update
sudo apt install -y gz-harmonic
sudo apt install -y ros-jazzy-ros-gz
mkdir -p ~/turtlebot3_ws/src
cd ~/turtlebot3_ws/src
git clone -b jazzy https://github.com/ROBOTIS-GIT/DynamixelSDK.git
git clone -b jazzy https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git
git clone -b jazzy https://github.com/ROBOTIS-GIT/turtlebot3.git
git clone -b jazzy https://github.com/ROBOTIS-GIT/turtlebot3_simulations.git
cd ~/turtlebot3_ws
sudo rosdep init
rosdep update
rosdep install --from-paths src --ignore-src --rosdistro jazzy -y
colcon build --symlink-install
source install/local_setup.bash
ros2 pkg prefix turtlebot3_gazeboIf sudo rosdep init reports that sources already exist, continue with rosdep update. If rosdep install fails, resolve its listed dependencies before building. ros2 pkg prefix should resolve inside ~/turtlebot3_ws/install. Start an empty world first; it is quicker to load and makes the next checks easier:
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo empty_world.launch.pyKeep this simulation running in terminal A. The first start may need extra time for assets and shaders. In terminal B:
source /opt/ros/jazzy/setup.bash
source ~/turtlebot3_ws/install/local_setup.bash
export PATH="$HOME/.local/bin:$PATH"
export TURTLEBOT3_MODEL=burger
ros2 topic list
ros2 topic echo /scan --once --qos-reliability best_effort
flightops doctor
flightops attachCheck that Flightops sees simulation nodes and topics such as /scan, /odom, /tf, and /cmd_vel where provided by the running world. The independent ros2 topic echo /scan --once --qos-reliability best_effort check distinguishes a live sensor message from a topic name that merely exists. Use the Graph, Topics, Nodes, and TF views to investigate the graph; use ? for the exact controls in your build. In a third terminal, you can drive the simulated robot:
source /opt/ros/jazzy/setup.bash
source ~/turtlebot3_ws/install/local_setup.bash
export TURTLEBOT3_MODEL=burger
ros2 run turtlebot3_teleop teleop_keyboardReturn to Flightops to inspect activity. Stop teleop when finished.
If your saved AI connection is working, repeat the explanation step with TurtleBot3. Close the attached view while the simulation continues, then run flightops --ai attach from the same sourced Flightops shell. Press A, then i, and ask a narrow question using observations you supply:
In my Burger empty-world run,ros2 topic echo /scan --once --qos-reliability best_effortreturned a LaserScan. I see/odomand/cmd_velin the graph. What additional observations would distinguish a live scan from a navigation stack that is ready to move safely?
Review the pending request and press y only if its destination and text are right. Adjust the prompt to match the topics and messages you actually saw. The assistant has not received those terminal outputs merely because they are visible elsewhere; attach only supported selected evidence and check the pending disclosure before claiming it has seen a result. Treat its answer as a plan for what to inspect next, then verify claims in ROS and Flightops.
Close the attached Flightops view before stopping the simulation in A.
Now show ownership and logs: with no other Gazebo world running, use the Flightops terminal to start the same launch file:
source /opt/ros/jazzy/setup.bash
source ~/turtlebot3_ws/install/local_setup.bash
export PATH="$HOME/.local/bin:$PATH"
export TURTLEBOT3_MODEL=burger
flightops launch turtlebot3_gazebo empty_world.launch.pyPress L to see the simulator's launch output, Esc to close it, and q to stop the world. For a richer second run, replace empty_world.launch.py with turtlebot3_world.launch.py. Start only one Gazebo world at a time. A Gazebo window opening alone is not the acceptance check; confirm ROS topics and at least one message before judging the Flightops graph.
6. Explain a retained result, then show its browser report
Flightops ships a sample result with its offline documentation. It demonstrates the desktop investigation view without depending on the live ROS run. For this evidence retrieval showcase, use a saved OpenAI or Anthropic API connection. The ChatGPT account route (preview) explains reviewed text but cannot retrieve exact source fields. flightops docs prints the path of the documentation's index.html; the sample is next to it. Open it in the terminal Results view:
sample="$(dirname "$(flightops docs)")/assets/user-guide/evidence/incident.json"
flightops-tui workspace --results "$sample"In this same terminal process, press A for the assistant and m to enable AI if it is still in Standard mode. Press t to request the synthetic tool protocol probe. Review its pending destination and scope, then press y to approve or n to reject. The probe can make two provider requests and may incur usage; it sends no project evidence. After it completes, press v to inspect the raw assistant state. Continue with exact retrieval only if capabilities.tool_calls says supported; press v again to return to the readable panel. A successful flightops ai test checks an assistant greeting, not this tool capability. Starting a new terminal workspace would require a new probe.
Close the panel with Esc, press 7 for Results, and select the incident row. Press A, then s to attach that selection. Confirm the panel reports that context was selected; if it reports an error, do not assume the file was attached. Press i, type:
In this retained incident, where is the command dropout, how do the before and after publish gaps compare, and what does this result leave unmeasured? Cite the selected evidence. If the initial receipt omits the exact numbers, request the relevant source fields before concluding.
Press Enter, review the provider, model, exact prompt, and selected evidence, then press y to send or n to reject. The initial attached receipt omits the incident's stage details and custom gap metrics. If the assistant proposes evidence_retrieve, review the proposed JSON pointer and exact field to be disclosed, along with the artifact identity and hash, in its new pending approval before pressing y again. If the probe failed or no retrieval occurs, the assistant has not seen the exact figures; a sound answer should say they are unavailable from its receipt.
Compare any retrieved explanation with the original result: the recorded diagnosis says /cmd_vel was silent from 6.95 to 13 s; the measured maximum publish gap changed from 5.8 s before to 0.05 s after. The sample explicitly leaves ROS transport, message serialization, TF publication, and physics unmeasured. Press e to inspect the canonical evidence if needed. A plausible explanation does not change the saved result's status or verify TurtleBot3. Press q after closing the assistant to leave this terminal workspace before running the browser command below.
Now open the same sample in the browser:
sample="$(dirname "$(flightops docs)")/assets/user-guide/evidence/incident.json"
demo_dir=$(mktemp -d /tmp/flightops-results-XXXXXX)
flightops results "$sample" -o "$demo_dir/results.html" --openThe browser should open an HTML report; Flightops also prints its path. flightops results renders a report; it does not open the assistant. This bundled example captures deterministic callback execution, not evidence from your TurtleBot3 simulation. For your own saved result, follow review saved results and replace the JSON path in both commands. If your key is session-only, the new flightops-tui process cannot reuse it; use a saved connection for this evidence turn or perform the report review without AI.
What to record from your trial
| Check | Expected observation |
|---|---|
flightops doctor before nodes | Jazzy and the intended domain; zero nodes is allowed. |
| Small package direct launch | Publisher and subscriber print matching heartbeat values. |
| Attach and restart | Two nodes and /demo/heartbeat appear, disappear, and return after refresh. |
| Attach quit | The external ros2 launch process stays alive. |
| Flightops launch quit | The process started by Flightops stops; reopening does not rerun it. |
| Controlled mismatch | The talker still prints, the listener falls silent, and the two topic reports expose the name mismatch. |
| Restored package | Matching heard: values resume after rebuild and relaunch. |
| TurtleBot3 | A Gazebo world starts, /scan emits a message, and Flightops shows the live graph. |
| AI connection, if used | ai test succeeds for a saved connection, or the session-only setup app stays open. |
| AI question, if used | The pending request shows the intended provider, model, prompt, and disclosure before approval. |
| Retained evidence, if used | The Results selection attaches; an API probe reports supported tool calls, and any exact-field retrieval gets separate approval. The answer distinguishes retrieved figures from unavailable details. |
| Browser report | The bundled incident report opens; it is identified as sample evidence, separate from the live ROS run. |
Keep the flightops doctor output, ROS distribution, Ubuntu version, and the launch log path if a step fails. See the testing checklist and troubleshooting guide. If Flightops sees zero nodes while ros2 node list does not, compare ROS_DOMAIN_ID, RMW_IMPLEMENTATION, sourced underlay/overlay, and whether both shells use the same machine. If a package is missing, check ros2 pkg prefix PACKAGE in that same shell; it usually reveals an unsourced or failed overlay build.