Query Annotator Testing and Validation

This tutorial is designed to test the functionality of the QueryAnnotator, including goal acceptance, feedback processing, cancellation requests, and handling preemptions. The behavior tree pipeline ensures proper validation, execution flow, and interaction with the Query Action Server.

Query Test Analysis Engine Structure

The AnalysisEngine class (robokudo/descriptors/analysis_engines/query_test.py) is responsible for managing the behavior tree pipeline, ensuring the correct execution flow of query processing and validation. Below is a visual representation of the full behavior tree used in this pipeline:

RootNodeWithGUI
├── VisManager
└── OPExperiments (WithMemory)
    ├── QueryAnnotator
    └── ConditionalSelector (NoMemory)
        ├── Invert Preempt Request
        │   └── ActionServerNoPreemptRequest
        └── TaskSequence (WithMemory)
            ├── CheckQueryType
            └── PrintNumbers

This diagram illustrates how the different components interact within the pipeline, including the conditional selector, query annotator, and task sequence.

Task Execution Flow

Function: Ensures that the query is validated before executing number processing.

TaskSequence (WithMemory)
├── CheckQueryType
└── PrintNumbers
# Define task sequence for execution
 task_sequence = Sequence(name="TaskSequence", memory=True)
 task_sequence.add_children([
     CheckQueryType(),
     PrintNumbers()
 ])

PrintNumbers Functionality

The PrintNumbers annotator processes numbers from 1 to 100 while providing real-time feedback. As each number is processed, it is added to a feedback queue, which helps monitor progress. Once all numbers are processed, the final result is stored in the blackboard under BBIdentifier.QUERY_ANSWER.

CheckQueryType Functionality

The CheckQueryType annotator ensures that only queries of type numbers are processed. It retrieves the query from the CAS (Common Analysis Structure) and verifies its type. If the query type is not numbers, execution stops, and a warning is logged. If the query type matches, processing continues.

Handling Preemption with Conditional Selector

Function: Controls execution flow, ensuring tasks only run when no preemption request is detected.

ConditionalSelector (NoMemory)
├── Invert Preempt Request
│   └── ActionServerNoPreemptRequest
└── TaskSequence
# Create a selector node to manage preemption
conditional_selector = Selector(name="ConditionalSelector", memory=False)
conditional_selector.add_children([
    py_trees.decorators.Inverter(
        name="Invert Preempt Request",
        child=ActionServerNoPreemptRequest()
    ),
    task_sequence
])

ActionServerNoPreemptRequest

The ActionServerNoPreemptRequest behavior checks if a preemption request has been made on the blackboard. If detected, it acknowledges the request and returns FAILURE; otherwise, it returns SUCCESS.


Selector

The Selector node is responsible for decision-making by executing each of its child behaviors in sequence until one of them succeeds. If a child returns SUCCESS or RUNNING, the Selector does not check the remaining children. However, if all children return FAILURE, the Selector itself returns FAILURE.


Inverter

The Inverter in the code wraps ActionServerNoPreemptRequest, ensuring that a FAILURE (preemption detected) gets converted into SUCCESS, causing the Selector to stop and not proceed to TaskSequence. If no preemption is detected (SUCCESS), the Inverter converts it to FAILURE, which allows the Selector to execute TaskSequence.


Memory Configuration (Handling Preemption)

Memory configuration determines how nodes retain execution states between behavior tree ticks:

  • memory=True: The node remembers which child last succeeded or failed and resumes from that point on the next tick.

  • memory=False: The node re-evaluates all children from the beginning on each tick, ensuring up-to-date decisions.

In the given implementation:

  • The Selector node is set to memory=False, meaning it always re-evaluates its children, ensuring that preemption (ActionServerNoPreemptRequest) is checked on every tick.

  • The TaskSequence node is set to memory=True, allowing it to resume from the last executed child without re-evaluating previous ones.


ActionClient Functionality

The action_client.py file (robokudo/action_servers/action_client.py) acts as a client for the Query Action Server, facilitating communication by sending and managing query requests. The DebugActionClient in this file sends a goal of type numbers to the action server, waits for a response, processes feedback, and handles goal cancellations dynamically. It ensures seamless interaction between external commands and the behavior tree pipeline.