Behavior Trees¶
Hierarchical, reactive control for complex missions using behavior trees.
What is a Behavior Tree?¶
A behavior tree is a hierarchical structure for organizing complex behaviors:
Mission Root
├─ Sequence
│ ├─ CheckBattery
│ ├─ Submerge
│ ├─ NavigateToArea
│ └─ ExecuteSurvey
└─ Fallback
└─ ReturnHome
Node Types¶
Composite Nodes¶
Sequence: Execute children in order (AND logic) - Succeeds if all children succeed - Fails if any child fails
Selector (Fallback): Try children until one succeeds (OR logic) - Succeeds if any child succeeds - Fails if all children fail
Parallel: Execute children simultaneously - Various success/failure policies
Decorator Nodes¶
Inverter: Flip success/failure
Repeat: Repeat child N times or until failure
Timeout: Fail if child exceeds time limit
Retry: Retry child on failure
Leaf Nodes (Actions)¶
Actual behaviors executed by vehicle: - GoToWaypoint - FollowPath - TakePhoto - Hover - etc.
Example: Pipeline Survey¶
from aqua.bt import Sequence, Fallback, Action, Repeat
survey_mission = Sequence([
Action('CheckSystems'),
Action('Submerge', depth=-5),
Action('GoToWaypoint', target=[50, 0, -5]),
Repeat(10,
Sequence([
Action('FollowPath', path=pipeline_path),
Action('TakePhoto'),
Action('AdvanceAlongPath', distance=10)
])
),
Fallback([
Action('ReturnToStart'),
Action('EmergencySurface')
])
])
Creating Custom Behaviors¶
from aqua.bt import Behavior, Status
class InspectObject(Behavior):
def __init__(self, object_id):
self.object_id = object_id
def initialize(self):
# Setup
self.photos_taken = 0
def update(self):
# Execute behavior
if self.photos_taken < 4:
self.take_photo_at_angle(self.photos_taken * 90)
self.photos_taken += 1
return Status.RUNNING
else:
return Status.SUCCESS
def terminate(self, status):
# Cleanup
pass
Reactive Behaviors¶
Behavior trees are inherently reactive:
patrol_tree = Sequence([
Repeat(INFINITE,
Fallback([
# Check for threats
Sequence([
Condition('ThreatDetected'),
Action('Investigate')
]),
# Normal patrol
Action('FollowPatrolRoute')
])
)
])
Blackboard¶
Shared data structure for communication between behaviors:
blackboard = {
'battery_level': 85,
'current_waypoint': 3,
'threats_detected': [],
'mission_status': 'active'
}
# Behaviors read/write to blackboard
def check_battery(blackboard):
if blackboard['battery_level'] < 20:
return Status.FAILURE
return Status.SUCCESS
Visualization¶
Behavior trees can be visualized in RViz or custom tools:
XML Format¶
<BehaviorTree ID="SurveyMission">
<Sequence>
<Action ID="CheckSystems"/>
<Action ID="Submerge" depth="-5"/>
<Repeat num_cycles="10">
<Sequence>
<Action ID="FollowPath" path="pipeline"/>
<Action ID="TakePhoto"/>
</Sequence>
</Repeat>
<Action ID="Surface"/>
</Sequence>
</BehaviorTree>
Best Practices¶
✓ Keep behaviors small and focused ✓ Use decorators for retries and timeouts ✓ Design for reactivity ✓ Test individual behaviors first ✓ Use blackboard for shared state