7个不稳定版本 (3个重大更新)

0.4.0 2024年3月3日
0.3.0 2024年2月24日
0.2.3 2023年10月25日
0.1.2 2023年10月23日

机器人技术中排名第162

MIT许可证

56KB
960

bhv

bhv是一个用于处理行为树库。

但什么是行为树呢?

行为树基本上是一种树,每个节点定义了节点类型独有的某些动作/函数。每个节点在执行后返回一个“状态”,这决定了节点是否已经完全执行完成(以及它是否执行成功)或者它仍然需要稍后执行。父节点提供组合(想象一下运行其所有子节点直到其中一个返回“已执行但未成功”的节点),而叶节点充当“原子动作”,即无法进一步分割的动作(想象一下简单的条件检查或向终端打印消息)。为了在节点之间共享状态,将一个“上下文”类型传递给每个正在运行的节点。父节点通常通过其子节点来调整上下文类型,而叶节点通常是定义具体上下文类型的节点。

有一组常用的节点,因此库提供了这些节点,特别是

适配器节点 - 将函数适配成树节点的叶节点,例如在条件成功时执行成功的cond,或者简单地执行函数并返回成功的action

装饰器节点 - 具有一个子节点的节点,可以操纵子节点的状态或根据某些条件多次运行它,例如将执行状态从成功变为失败并反之的inv,或者运行节点直到它完成n次,例如repeat(n)

复合节点 - 至少有一个子节点根据某些条件运行它们的节点,例如 sequence 只要子节点成功执行就运行其子节点,或者 selector 直到其中一个子节点成功执行。

如你所见,基于节点状态的组合提供了类似于编程语言中控制流的逻辑。此外,状态之间的转换变得很简单,只需为节点提供一个条件以及条件成功时需要执行的状态节点(即条件的一个 sequence 然后是其他节点)。这使得行为树成为状态机的替代品,在状态机中,状态之间彼此不了解,因为状态管理逻辑发生在父节点内部而不是子节点内部。

要深入了解和更多示例,请参阅这个GameAIPro章节,这是一个很好的资源。

安装

简单到只需

cargo add bhv
# or for the `events` feature
cargo add bhv --features events

在你想使用库的项目目录下运行。

功能

该库提供了一些在行为树中常见的节点。具体来说,提供的节点分为以下类别

  • 适配器节点,将函数适配为行为树节点。
  • 装饰器节点,改变节点的行为和结果。
  • 复合节点,同时运行多个节点。

有关特定节点的帮助,请参阅crate的文档。

展示

// Guess the number game implemented using behavior trees.
use std::{
    io::{self, Write},
    time::{SystemTime, UNIX_EPOCH},
};

use bhv::*;

fn main() {
    game()
}

// The data that will be used by the nodes
#[derive(Default)]
struct Context {
    guess: u32,
    answer: u32,
}

// the nodes
struct RandomizeAnswer(u32, u32);
struct ReadInput;

macro_rules! print_msg {
    ($($arg:tt)*) => {
        action(|_ctx| print!($($arg)*))
    };
}

fn game() {
    let tree = seq! {
        RandomizeAnswer(0, 101),
        seq! {
            print_msg!("Enter a number from 0 to 100\n"),
            ReadInput,
            sel! {
                seq! {
                    cond(|ctx: &Context| ctx.guess < ctx.answer),
                    print_msg!("Your guess is smaller than the actual number\n").fail(), // The game is not over yet
                },
                seq! {
                    cond(|ctx: &Context| ctx.guess == ctx.answer),
                    print_msg!("Your guess is correct!\n"),
                },
                seq! {
                    cond(|ctx: &Context| ctx.guess > ctx.answer),
                    print_msg!("Your guess is greater than the actual number\n").fail(), // The game is not over yet
                }
            },
        }.repeat_until_pass(),
    };

    tree.execute(&mut Context::default());
}

impl Bhv for RandomizeAnswer {
    type Context = Context;

    fn update(&mut self, ctx: &mut Self::Context) -> Status {
        let time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();
        ctx.answer = (time % ((self.1 - self.0) as u64)) as u32 + self.0;

        Status::Success
    }
}

impl Bhv for ReadInput {
    type Context = Context;

    fn update(&mut self, ctx: &mut Self::Context) -> Status {
        io::stdout().flush().unwrap_or_default();

        let mut buff = String::new();

        io::stdin()
            .read_line(&mut buff)
            .map(|_| match buff.trim().parse() {
                Ok(v) => {
                    ctx.guess = v;
                    Status::Success
                }
                Err(e) => {
                    println!("Error reading from stdin :{}\t buffer: '{}'", e, buff);
                    Status::Failure
                }
            })
            .unwrap_or_else(|_| Status::Running)
    }
}

功能

事件

版本0.3引入了行为树的新模型,在events功能标志下可用。这个实验性功能允许节点只在相关事件发生时运行。当这个功能被认为完整时,它将替换实际默认实现,因此建议将现有代码库迁移到使用此功能。

与“默认”实现相比,events上的节点引入了一个名为should_react_to(&self, kind: EventKind) -> bool的新函数,该函数指示类型为kind的事件是否与此节点相关。默认情况下,所有事件都被视为相关,除非行为被重写。此外,现在的update函数现在是react(&mut self, event: &dyn Event, ctx: &mut Self::Context),其中使用Event trait确保传递的值是要用作事件数据的。这个模型允许节点适应更异步的事件驱动执行模型。

为了反映这些变化,现在 execute 函数接受一个新的参数,该参数可以转换成一个迭代器,该迭代器产生 &dyn Event,并用作节点的事件流。这个函数是 BhvExt 的部分,而不是 Bhv,这意味着它只能有默认实现,而且节点现在可以 ?Sized

至于提供的节点差异,当启用此功能时,.repeat_until(cond) 将不会出现。为了实现与 bhv.repeat_until(cond) 相似的行为,请使用 seq! { bhv.pass(), cond }.repeat_until_pass()。或者,您可以使用新的 .wait_for::<EventType>() 装饰器,该装饰器仅在触发特定事件时运行节点。为了使用此装饰器,事件应实现 EventType。将 EventType 实现者视为简单的 Event 类型,它们不是 enum 类型。

要为您的类型实现 Event,请按照以下步骤进行

  • 对于非 enum 类型,只需将 EventType 实现为
use bhv::*;

struct MyType; // my special event type

impl EventType for MyType {}

// Event is automatically implemented for EventType
  • 对于 enum 类型,实现 Event 如下
use bhv::*;

enum MyEvents {
    A(i32),
    B(char),
    C,
}

impl Event for MyEvents {
    fn event_name(&self) -> &str {
        match self {
            Self::A(_) => "Event::A",
            Self::B(_) => "Event::B",
            Self::C => "Event::C",
        }
    }
}

许可证

此模块采用 MIT 许可证。

无运行时依赖

功能