杰瑞科技汇

Python行为树如何实现高效决策控制?

Of course! Here is a comprehensive guide to implementing and using Behavior Trees in Python.

Python行为树如何实现高效决策控制?-图1
(图片来源网络,侵删)

What is a Behavior Tree?

A Behavior Tree (BT) is a powerful and modular architecture for creating complex, yet understandable, AI for games, robotics, and other interactive systems. It models an agent's behavior as a tree of "nodes," where each node represents an action or a decision.

The core idea is to break down complex AI logic into small, reusable, and composable parts. This makes the AI easier to design, debug, and maintain.

Key Concepts

  1. Nodes: The fundamental building blocks of a BT. Each node performs a specific task or makes a decision.
  2. Execution Flow: The tree is traversed from top to bottom, left to right. Nodes are executed in sequence based on their type and the result of their children.
  3. Node States: Every node, when executed, returns one of three states:
    • SUCCESS: The task completed successfully.
    • FAILURE: The task failed.
    • RUNNING: The task is still running and needs to be checked again on the next tick.
  4. Node Types:
    • Control Nodes: Manage the flow of execution to their children. They decide which children to run and how to interpret their results.
      • Sequence: Runs its children in order. Succeeds if all children succeed. Fails on the first child that fails.
      • Selector (Fallback): Tries its children in order. Succeeds if any child succeeds. Fails only if all children fail.
      • Parallel: Runs all its children simultaneously. Its success/failure criteria can be customized (e.g., succeed if N children succeed).
    • Action Nodes: Perform an action in the world (e.g., move, attack, play an animation). They return RUNNING while the action is in progress and SUCCESS or FAILURE when it's complete.
    • Condition/Leaf Nodes: Check a simple condition (e.g., "Is health below 50%?", "Is enemy visible?"). They don't have children. They return SUCCESS or FAILURE.

Part 1: A Basic, Hardcoded Implementation

Let's start with a simple, self-contained implementation to understand the core mechanics.

# Part 1: Basic Behavior Tree Implementation
from enum import Enum, auto
# 1. Define the Node States
class NodeState(Enum):
    SUCCESS = auto()
    FAILURE = auto()
    RUNNING = auto()
# 2. Define the base Node class
class Node:
    def __init__(self, name="Node"):
        self.name = name
        self.state = NodeState.SUCCESS
        self.children = []
        self.parent = None
    def tick(self):
        """This method is called on every tick of the behavior tree."""
        raise NotImplementedError("You must implement the tick() method in a subclass.")
    def add_child(self, child):
        self.children.append(child)
        child.parent = self
# 3. Define Action Nodes (Leaf Nodes)
class Action(Node):
    def __init__(self, name, action_func):
        super().__init__(name)
        self.action_func = action_func
    def tick(self):
        print(f"-> Executing Action: {self.name}")
        self.state = self.action_func()
        return self.state
# 4. Define Condition Nodes (Leaf Nodes)
class Condition(Node):
    def __init__(self, name, condition_func):
        super().__init__(name)
        self.condition_func = condition_func
    def tick(self):
        print(f"-> Checking Condition: {self.name}")
        self.state = NodeState.SUCCESS if self.condition_func() else NodeState.FAILURE
        return self.state
# 5. Define Control Nodes
class Sequence(Node):
    def __init__(self, name="Sequence"):
        super().__init__(name)
        self.current_child_index = 0
    def tick(self):
        # Run children in sequence
        for i in range(self.current_child_index, len(self.children)):
            child = self.children[i]
            state = child.tick()
            if state == NodeState.RUNNING:
                # If a child is still running, stop and wait for next tick
                self.current_child_index = i
                return NodeState.RUNNING
            if state == NodeState.FAILURE:
                # If a child fails, the whole sequence fails
                self.current_child_index = 0 # Reset for next run
                return NodeState.FAILURE
        # If all children succeeded, the sequence succeeds
        self.current_child_index = 0 # Reset for next run
        return NodeState.SUCCESS
class Selector(Node):
    def __init__(self, name="Selector"):
        super().__init__(name)
        self.current_child_index = 0
    def tick(self):
        # Try children in sequence
        for i in range(self.current_child_index, len(self.children)):
            child = self.children[i]
            state = child.tick()
            if state == NodeState.RUNNING:
                # If a child is still running, stop and wait
                self.current_child_index = i
                return NodeState.RUNNING
            if state == NodeState.SUCCESS:
                # If a child succeeds, the whole selector succeeds
                self.current_child_index = 0 # Reset for next run
                return NodeState.SUCCESS
        # If all children failed, the selector fails
        self.current_child_index = 0 # Reset for next run
        return NodeState.FAILURE
# --- Example Usage ---
# Define some simple action and condition functions
def is_enemy_visible():
    print("  -> Is enemy visible? Yes.")
    return True
def is_low_health():
    print("  -> Is health low? No.")
    return False
def attack_action():
    print("  -> Attacking...")
    return NodeState.SUCCESS
def find_cover_action():
    print("  -> Finding cover...")
    return NodeState.SUCCESS
def patrol_action():
    print("  -> Patrolling...")
    return NodeState.SUCCESS
# Build the Behavior Tree
# Goal: If enemy is visible, attack. Otherwise, if health is low, find cover. Otherwise, patrol.
# Create nodes
root = Selector("Root")
combat_sequence = Sequence("Combat Sequence")
find_cover_sequence = Sequence("Find Cover Sequence")
patrol_action_node = Action("Patrol", patrol_action)
# Build the tree structure
root.add_child(combat_sequence)
combat_sequence.add_child(Condition("Enemy Visible?", is_enemy_visible))
combat_sequence.add_child(Action("Attack", attack_action))
root.add_child(find_cover_sequence)
find_cover_sequence.add_child(Condition("Low Health?", is_low_health))
find_cover_sequence.add_child(Action("Find Cover", find_cover_action))
root.add_child(patrol_action_node)
# Simulate the AI running for a few ticks
print("--- Tick 1: Enemy Visible ---")
root.tick() # Should try to attack
print("\n--- Tick 2: No Enemy, Low Health ---")
# Simulate conditions changing
def is_enemy_visible_no():
    print("  -> Is enemy visible? No.")
    return True # Let's make this false for this tick
is_enemy_visible.__code__ = is_enemy_visible_no.__code__ # Hack to change behavior
def is_low_health_yes():
    print("  -> Is health low? Yes.")
    return True
is_low_health.__code__ = is_low_health_yes.__code__
root.tick() # Should try to find cover
print("\n--- Tick 3: No Enemy, Full Health ---")
def is_low_health_no():
    print("  -> Is health low? No.")
    return False
is_low_health.__code__ = is_low_health_no.__code__
root.tick() # Should patrol

Part 2: A More Flexible and Reusable Implementation

The first implementation is good for learning, but it's not very flexible. Let's create a more robust version that uses a common pattern in BT libraries: Decorators. Decorators are nodes that wrap around other nodes to modify their behavior.

Python行为树如何实现高效决策控制?-图2
(图片来源网络,侵删)

This version will also be much cleaner to build.

# Part 2: Flexible Behavior Tree with Decorators
from enum import Enum, auto
class NodeState(Enum):
    SUCCESS = auto()
    FAILURE = auto()
    RUNNING = auto()
class Node:
    def __init__(self, name="Node"):
        self.name = name
        self.state = None
        self.children = []
        self.parent = None
    def tick(self):
        raise NotImplementedError
    def add_child(self, child):
        self.children.append(child)
        child.parent = self
# --- Leaf Nodes (Actions & Conditions) ---
class Action(Node):
    def __init__(self, name, action_func):
        super().__init__(name)
        self.action_func = action_func
    def tick(self):
        self.state = self.action_func()
        return self.state
class Condition(Node):
    def __init__(self, name, condition_func):
        super().__init__(name)
        self.condition_func = condition_func
    def tick(self):
        self.state = NodeState.SUCCESS if self.condition_func() else NodeState.FAILURE
        return self.state
# --- Control Nodes ---
class Sequence(Node):
    def __init__(self, *children, name="Sequence"):
        super().__init__(name)
        for child in children:
            self.add_child(child)
    def tick(self):
        for child in
Python行为树如何实现高效决策控制?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇