The command line
plansys2_tui_cli is the front end to a running system: five ros2
plansys2 verbs and a full-screen dashboard. Four of the verbs report on the
classical half, the plan and who is executing it; the fifth, epistemic, is
the terminal for the half no predicate can express, and is the quickest way to
watch a Kripke model change without writing a node.
Everything on this page talks to nodes that are already running. Bring a system
up as in A First Plan first, and remember epistemic_state:=True, since
the epistemic verb has nothing to talk to without it.
The verbs
Verb |
What it shows |
|---|---|
|
The problem expert’s instances, predicates, functions and goal: the classical facts, redrawn as they change. |
|
The registered action performers and their status, from
|
|
The plan being executed and each action’s progress, from
|
|
The actions currently in flight on |
|
What the agents know: the shape of the model, the goal, whether a formula holds, and the two ways of changing it. |
The four monitors redraw in place until interrupted. --duration bounds how
long they run, and --once on knowledge prints a single snapshot and
exits, which is the form to use in a script. Colour is emitted only to a
terminal, and NO_COLOR in the environment turns it off there too, so piping
any of them to a file yields plain text.
The epistemic verb
Five commands, one per question worth asking of the model:
Command |
Meaning |
|---|---|
|
The latched state: agents, atoms, how many worlds the model holds and
how many of them are designated, and the goal with its provenance.
This one reads the |
|
Does this hold now? A parse failure and a false answer are reported differently, so a typo in a formula never reads as knowledge the fleet does not have. |
|
With no argument, the goal and whether it holds; with one, aim at something else. The formula is parsed against the loaded task, so a goal naming an agent or an atom the task does not have is refused here, before it can fail inside a planning request. |
|
Everyone just learned this. The model is restricted to the worlds where the formula holds, and the reply says how many survived. |
|
This action ran: advance the model by its event model. |
Formulas are written in the model’s own syntax, not EPDDL’s: (K r1
blocked), (Kw r1 blocked), (B r1 blocked), and (not ...),
(and ...), (or ...) around them. --timeout bounds the wait for the
state, and defaults to five seconds.
A worked session
The corridor scenario from Fleet: the blocked corridor, driven by hand. Two robots at a depot, a corridor that may be blocked, and nobody has looked. Start the state on that problem:
ros2 run plansys2_epistemic_executor epistemic_state_node --ros-args \
-p epddl_domain:=<share>/plansys2_epddl_grounder/examples/robot-fleet-domain.epddl \
-p epddl_problem:=<share>/plansys2_epddl_grounder/examples/robot-fleet-problem.epddl
Ask it what it holds:
$ ros2 plansys2 epistemic show
Epistemic state:
agents 2
atoms 1
worlds 2
designated 2
goal (and (Kw r1 blocked) (Kw r2 blocked)) (from the task, does not hold)
Two designated worlds is the whole scenario in one line: the corridor really is one way or the other, and the model cannot say which. That is why the goal is a knowing-whether goal, and why the solution has to branch.
Nobody knows anything about the corridor yet, and the model says so twice over:
$ ros2 plansys2 epistemic check "(Kw r1 blocked)"
does not hold
$ ros2 plansys2 epistemic check "(K r1 blocked)"
does not hold
Now run the sensing action. Because the model designates two worlds it cannot determine the outcome itself, so name the one that occurred:
$ ros2 plansys2 epistemic apply inspect_r1 --observed e-inspect-blocked
applied inspect_r1: observing e-inspect-blocked, 2 worlds, 1 designated
One designated world: r1 looked, and the corridor was blocked. The model still holds two worlds, though, and the reason is the whole of the next answer:
$ ros2 plansys2 epistemic check "(Kw r1 blocked)"
holds
$ ros2 plansys2 epistemic check "(Kw r2 blocked)"
does not hold
That second answer is the point of the scenario. Sensing here is semi-private: r2 watched r1 look and learned that it looked, not what it saw. The goal is about both robots, so the mission is not over.
Saying it out loud is what finishes it:
$ ros2 plansys2 epistemic apply report-blocked_r1
applied report-blocked_r1: 1 worlds, 1 designated
$ ros2 plansys2 epistemic check "(Kw r2 blocked)"
holds
$ ros2 plansys2 epistemic show | tail -1
goal (and (Kw r1 blocked) (Kw r2 blocked)) (from the task, holds)
The second world is gone, and nothing about the corridor changed to remove it. It was the world r2 could not rule out, and what removed it was being told. A predicate would have moved on the first line of this session, when r1 looked; the model moved twice, once for each robot, which is the distinction between a fact and knowledge of it that the epistemic state exists to keep.
announce reaches the same place from outside a plan. Rather than replaying
r1’s report, an operator who has been told the corridor is blocked can put that
into the model directly. From the initial two-world model:
$ ros2 plansys2 epistemic announce blocked
announced: 1 worlds remain, 1 designated
Both robots know it at once, and no one had to drive anywhere. Atoms are
written bare, as blocked and not (blocked); parentheses introduce
an operator, and (blocked) is read as one that does not exist.
The two are not interchangeable, and choosing between them is a modelling
decision, not a matter of taste: apply advances the model by an
action the planner branched on, and announce is information that arrived
outside the plan. That is the same distinction perception draws when it decides
whether a region reports as a sensing outcome or as an announcement; see
Architecture.
An announcement that holds in no world the state considers possible is refused, since applying it would empty the model:
$ ros2 plansys2 epistemic announce "(not blocked)"
announcing '(not blocked)' would leave no possible world: it is false everywhere the state considers possible
Changing the goal without re-grounding the problem is the other thing worth doing from a terminal. The planner subscribes to the latched state and plans for the goal it finds there, so this is enough to re-aim a running system:
$ ros2 plansys2 epistemic goal "(and (K r1 blocked) (K r2 blocked))"
goal set: (and (K r1 blocked) (K r2 blocked)) (already holds)
An empty argument restores the task’s own goal.
The dashboard
plansys2_tui is the four monitors at once, over a single ROS node, in a
Textual full-screen application:
ros2 run plansys2_tui_cli plansys2_tui
┌────────────────────┬─────────────────────┐
│ Action Execution │ Plan Monitor │
│ (actions_hub) │ (executing_plan + │
│ │ action_exec_info) │
├────────────────────┼─────────────────────┤
│ Performers │ Knowledge │
│ (performers_stat) │ (problem_expert/ │
│ │ knowledge) │
└────────────────────┴─────────────────────┘
q or Ctrl+C quits. Each quadrant scrolls on its own, and the ROS node
is spun on a thread of its own so a redraw never blocks a subscription.
Textual is vendored inside the package instead of installed with pip, so the
dashboard runs on a workspace built with nothing but rosdep. Importing
plansys2_tui_cli.tui puts the vendored copy on sys.path and backfills
the names it expects from typing_extensions and platformdirs, which
Jammy, and therefore Humble, ships older than it wants. The dashboard shows the
classical half only; what the agents know is the epistemic verb’s subject
and has no quadrant.
Why the verbs live here
plansys2_terminal would be the obvious home for epistemic, and is the
wrong one. That package is built by every distribution’s workflow, and reaching
the epistemic services from C++ would make the classical terminal depend on the
epistemic message package to do so. A Python verb imports those messages at run
time instead and costs the classical build nothing: ros2 plansys2 keeps
working on a workspace where the epistemic packages were never built, and the
one verb that needs them says what is missing when it is the one invoked.