Test trees offline

You can run a behaviour tree with XPARO’s engine from a Python script, without starting xparo_ros and without connecting to the server. It is a quick way to check a tree’s logic, or to test a custom node, on your own computer.

It still needs ROS 2: the engine imports rclpy, so install and build the package as in Install and run and source your workspace. Without ROS 2 sourced, the import fails with ModuleNotFoundError: No module named 'rclpy'.

Run a tree

try_tree.py
from xparo.engine import Engine
from xparo.bt_engine.executor import BehaviorTreeExecutor

# An offline Engine: no server connection, nothing is sent anywhere.
engine = Engine("unused", "local-test", connection_type="offline")
engine.transport.send = lambda message, command_for=None: None
executor = BehaviorTreeExecutor(node=None, engine=engine)

tree = """
<Sequence name="deliver_medicine">
  <ScriptCondition name="battery_ok" code="battery > 20" />
  <Script name="pick_room" code="room := destination" />
  <Wait name="wait_at_door" seconds="1" />
</Sequence>
"""

report = executor.run_with_trace(tree, blackboard={"battery": "55", "destination": "ward_3"}, timeout_s=30)
print(report["outcome"], "-", report["explanation"])
print("blackboard after:", report["blackboard"])
source /opt/ros/jazzy/setup.bash
source ~/xparo_ws/install/setup.bash
python3 try_tree.py

After a few start-up lines it prints:

success - The tree finished with SUCCESS.
blackboard after: {'battery': '55', 'destination': 'ward_3', 'room': 'ward_3'}

Change "battery": "55" to "10" and the result is failed with the reason: The tree finished with FAILURE at battery_ok (ScriptCondition): 'battery > 20' is False.

run_with_trace takes the tree (the part inside <BehaviorTree>, as the Behaviour editor saves it), the starting blackboard and a time limit, and returns the same report a task run produces on a robot: outcome, explanation, failed_node, blackboard, stats and a timeline of every node’s status changes. The node rules, the expression language and the outcomes are in Behaviour trees on the robot.

What works offline

  • All control, decorator and condition nodes, Script, ScriptCondition, Wait and SubTree (pass the other trees as subtrees={"name": "<xml>"}).

  • The placeholder XPARO nodes (NavigateTo, SpeakText…), which only log what they would do, as they do on a robot.

  • ParamSet does not set anything without a ROS 2 node: it reports what it would have set and continues. Pass your own rclpy node as BehaviorTreeExecutor(node=my_node, engine=engine) to really set parameters.

Why the robot needs its ROS 2 node

On a robot, tasks only run inside xparo_ros. An XPARO process started without its node answers every RUN_TASK with:

{"TASK_RESULT": {"task_id": "...", "run_id": "...", "success": false, "duration_s": 0.0,
  "outcome": "no_executor", "retryable": false,
  "error": "This robot's XPARO process is running without its ROS 2 node, so it can't run behaviour trees. Start XPARO on the robot with its ROS 2 launch file (ros2 launch xparo xparo_launch.py).",
  "explanation": ""}}

retryable is false, so Restart on Failure doesn’t loop on it.