Skip to content

The monitor

btview (or btview tui) opens a full-screen view built on Textual. It runs in the alternate screen, so your scrollback is intact when you quit.

┌ MainTree · 192.168.1.42:1667 · v4 ──────────────┬─ node ───────────────────┐
│ ▼ ▶ [!] NavigateRecovery [RecoveryNode]         │ name      FollowPath     │
│ ├── ▼ ▶ [=] NavigateWithReplanning              │ type      FollowPath     │
│ │   ├── ▼ ▶ -^- RateController                  │ uid       8              │
│ │   │   └── ▼ ✔ [!] ComputePathToPose           │ status    ▶ RUNNING      │
│ │   │       ├── · --> ComputePathToPose         │ in state  1.4s           │
│ │   │       └── · --> ClearGlobalCostmap        │ counters  ran 3  ok 2    │
│ │   └── ▼ ▶ [!] FollowPath [RecoveryNode]       │ ports     path = {path}  │
│ │       ├── ▶ --> FollowPath                    ├─ transitions ────────────┤
│ │       └── · --> ClearLocalCostmap             │ 13:13:29.2 ·→▶ FollowPath│
│ └── ▼ · [o] RecoveryFallback                    │ 13:13:28.4 ✔→▶ RateCtrl  │
│     ├── · (?) GoalUpdated                       │ 13:13:28.2 ▶→✔ ComputeP… │
│     └── ▼ · (o) RecoveryActions [RoundRobin]    │ 13:13:27.6 ·→▶ ClearGlo… │
├─────────────────────────────────────────────────┴──────────────────────────┤
│ follow  16 nodes  ▶5 ✔1 ·10  ● live                                        │
│ q quit  r refresh  f follow  a active only  p pause  / search  ? help       │
└────────────────────────────────────────────────────────────────────────────┘

Panels

Tree (left) — one row per node: status glyph, node-type glyph, then the name. The status leads so it is never the thing that gets truncated on a narrow terminal. The border title shows the tree name, the endpoint and the protocol.

Node (top right) — everything known about the row under the cursor: instance name, registration, uid, full path, live status, how long it has held that status, how many times it has run/succeeded/failed this session, its port remappings, and a sparkline of its recent statuses. The sparkline is the quickest way to catch a node that is flapping: a run of alternating green and red shows up there long before you spot it in the event log.

Transitions (bottom right) — every status change, newest first, with a timestamp to the millisecond. This is the panel that answers "what just happened?" after a tree flickers.

Summary bar — node counts by status, the active modes, a connection indicator that turns red with the error text when polling fails, and a sparkline of how many nodes were running over the last few dozen polls. A tree that is ticking normally has a lively strip; a flat line means the robot stopped doing anything.

Keys

Key Action
↑ ↓ / k j Move the cursor
← → Collapse / expand a branch
Space Enter Toggle a branch
e / c Expand all / collapse all
f Follow — keep the cursor on the running leaf
a Active only — hide branches that are neither running nor failing
g Graph — swap the list for a drawn graph of the tree
/ Filter by name (Esc clears)
p Pause / resume polling
r Re-fetch the tree definition
s Save a JSON snapshot to the working directory
b Fetch the blackboard (v4 publishers)
? Help
q Quit

Follow is on by default: the cursor tracks the deepest running leaf, so the inspector always shows what the robot is actually doing. Moving the cursor yourself turns it off; f turns it back on.

Graph view

g swaps the indented list for the tree drawn as a graph, the way people sketch it on a whiteboard:

                    ╭────────────────────╮
                    │ ▶ NavigateRecovery │
                    ╰──────────┬─────────╯
         ╭─────────────────────┴─────────────────────╮
╭────────┴───────────╮                    ╭──────────┴─────────╮
│ ▶ NavigateWithRepl…│                    │ · RecoveryFallback │
╰──────────┬─────────╯                    ╰──────────┬─────────╯

Boxes are coloured by status, and the node in focus gets a heavy border so you can keep track of it while the colours change around it. The pane scrolls (mouse drag or the scrollbars) and, while follow is on, it scrolls itself to keep the running node in view — which matters, because a real Nav2 tree is a few hundred columns wide.

Use the list for hunting a specific node; use the graph for seeing which branch the robot is down and how deep it is. The same drawing is available outside the TUI:

btview snapshot --graph
btview show nav2_bt.xml --graph --box-width 26 > tree.txt

Status glyphs

Glyph Status Colour
▶ RUNNING cyan
✔ SUCCESS green
✖ FAILURE red
⊘ SKIPPED magenta
· IDLE grey
· was SUCCESS idle, but ran earlier this tick cycle dim green

That last one is worth knowing. BehaviorTree.CPP reports a node that has been halted but already produced a result, and btview dims it rather than showing a bare IDLE — so you can still see which branch succeeded before the tree moved on.

Node-type glyphs

Glyph Type Glyph Type
[-] sequence [!] recovery
[o] fallback (o) round robin
{-} {o} with memory -^- decorator
[=] pipeline sequence --> action
/_/ parallel (?) condition
[+] subtree

These follow py_trees' conventions. Types come from the publisher's own node model (<TreeNodesModel> in v4, node_models in v3); when a tree does not advertise one, btview falls back to BehaviorTree.CPP's built-in names and then to the shape of the tree.

Subtrees

SubTree nodes are expanded in place, showing the child tree's nodes inline the way Groot2 does. Repeated instantiations of the same subtree get their own copies with their own uids, so two instances show their own statuses.

Terminals

The UI is built for a 100×30 terminal or larger, and degrades sensibly below that: the sidebar keeps a minimum width, long labels ellipsize, and the transition log truncates names rather than wrapping.

For terminals without unicode or colour:

btview --ascii            # `+-` and `*` instead of `├─` and `▶`
NO_COLOR=1 btview
btview watch --ascii      # no alternate screen at all

Polling happens on a worker thread, so a robot that stops answering never freezes the UI. The tree stays on screen with its last known statuses, the indicator turns red, and the error appears in the summary bar. When the robot comes back, polling resumes on its own — press r if the tree itself changed while you were disconnected.