PageSourceSearch

https://behaviortree.dev/assets/js/b04e2e45.35f15be9.js

js behaviortree.dev collected 2026-10-03 22:09:20 UTC 9,425 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkbehaviortree_docusaurus=globalThis.webpackChunkbehaviortree_docusaurus||[]).push([[5598],{950(e,n,a){a.r(n),a.d(n,{assets:()=>s,contentTitle:()=>o,default:()=>h,frontMatter:()=>l,metadata:()=>t,toc:()=>c});const t=JSON.parse('{"id":"tutorial-advanced/tutorial_16_global_blackboard","title":"Why a \\"global blackboard\\"","description":"Introduced in Bt.CPP 4.6.0","source":"@site/docs/tutorial-advanced/tutorial_16_global_blackboard.md","sourceDirName":"tutorial-advanced","slug":"/tutorial-advanced/tutorial_16_global_blackboard","permalink":"/docs/tutorial-advanced/tutorial_16_global_blackboard","draft":false,"unlisted":false,"editUrl":"https://github.com/BehaviorTree/btcpp_website/tree/master/docs/tutorial-advanced/tutorial_16_global_blackboard.md","tags":[],"version":"current","sidebarPosition":16,"frontMatter":{"sidebar_position":16,"sidebar_label":"16. The global blackboard idiom"},"sidebar":"tutorialSidebar","previous":{"title":"15. Mocking and Nodes Replacement","permalink":"/docs/tutorial-advanced/tutorial_15_replace_rules"},"next":{"title":"Guides","permalink":"/docs/category/guides"}}');var r=a(4848),i=a(8453);const l={sidebar_position:16,sidebar_label:"16. The global blackboard idiom"},o='Why a "global blackboard"',s={},c=[{value:"Blackboards hierarchy",id:"blackboards-hierarchy",level:2},{value:"How to access the top-level blackboard from the tree",id:"how-to-access-the-top-level-blackboard-from-the-tree",level:2},{value:"Full example",id:"full-example",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",header:"header",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"why-a-global-blackboard",children:'Why a "global blackboard"'})}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsx)(n.p,{children:"Introduced in Bt.CPP 4.6.0"})}),"\n",(0,r.jsx)(n.p,{children:'As described in earlier tutorials, BT.CPP insists on the importance of having "scoped blackboards", to\nisolate each subtree as they were independent functions / routines, in a programming language.'}),"\n",(0,r.jsx)(n.p,{children:'Still, there are cases where it could be desirable to have a truly "global" blackboard, that can be\naccessed from every Subtree directly, without remapping.'}),"\n",(0,r.jsx)(n.p,{children:"This makes sense for:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Singletons and global objects that can't be shared as described in ",(0,r.jsx)(n.a,{href:"/docs/tutorial-basics/tutorial_08_additional_args",children:"Tutorial 8"})]}),"\n",(0,r.jsx)(n.li,{children:"Global states of the robot."}),"\n",(0,r.jsx)(n.li,{children:"Data that is written / read outside the Behavior Tree, i.e. in the main loop executing the tick."}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Additionally, since the blackboard is a generic key/value storage, where the value can contain ",(0,r.jsx)(n.strong,{children:"any"})," type,\nit is a perfect data structure to implement what is known in the literature as ",(0,r.jsx)(n.strong,{children:'"World Model"'}),",\ni.e. a place where the states of the environment, the robot and the tasks can be shared with the Behavior Trees."]}),"\n",(0,r.jsx)(n.h2,{id:"blackboards-hierarchy",children:"Blackboards hierarchy"}),"\n",(0,r.jsx)(n.p,{children:"Consider a simple Tree with two subtrees, like this:"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"tree_hierarchy.png",src:a(3639).A+"",width:"997",height:"371"})}),"\n",(0,r.jsx)(n.p,{children:"Each one of the 3 Subtree has its own blackboard; the parent / child relationship between these\nblackboards is exactly the same as the tree, i.e. BB1 is the parent of BB2 and BB3."}),"\n",(0,r.jsx)(n.p,{children:"The lifecycle of these individual blackboards is coupled with their respective Subtree."}),"\n",(0,r.jsx)(n.p,{children:'We can implement an external "global blackboard" like this:'}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cpp",children:'auto global_bb = BT::Blackboard::create();\nauto maintree_bb = BT::Blackboard::create(global_bb);\nauto tree = factory.createTree("MainTree", maintree_bb);\n'})}),"\n",(0,r.jsx)(n.p,{children:"This will create the following blackboards hierarchy:"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{alt:"bb_hierarchy.png",src:a(5576).A+"",width:"1130",height:"407"})}),"\n",(0,r.jsxs)(n.p,{children:["The instance ",(0,r.jsx)(n.code,{children:"global_bb"}),' lives "outside" the behavior tree and will persist if the\nobject ',(0,r.jsx)(n.code,{children:"tree"})," is destroyed."]}),"\n",(0,r.jsxs)(n.p,{children:["Furthermore, it can be easily accessed using ",(0,r.jsx)(n.code,{children:"set"})," and ",(0,r.jsx)(n.code,{children:"get"})," methods."]}),"\n",(0,r.jsx)(n.h2,{id:"how-to-access-the-top-level-blackboard-from-the-tree",children:"How to access the top-level blackboard from the tree"}),"\n",(0,r.jsx)(n.p,{children:'By "top-level blackboard", we mean the one at the root or the hierarchy.'}),"\n",(0,r.jsxs)(n.p,{children:["In the code above, ",(0,r.jsx)(n.code,{children:"global_bb"}
1)," becomes the top-level blackboard."]}),"\n",(0,r.jsxs)(n.p,{children:["Since version ",(0,r.jsx)(n.strong,{children:"4.6"})," of BT.CPP, a new syntax was introduced to access the top-level\nblackboard ",(0,r.jsx)(n.strong,{children:"without remapping"}),", by adding the prefix ",(0,r.jsx)(n.code,{children:"@"})," to the name of the entry."]}),"\n",(0,r.jsx)(n.p,{children:"For instance:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-xml",children:'<PrintNumber val="{@value}" />\n'})}),"\n",(0,r.jsxs)(n.p,{children:["The port ",(0,r.jsx)(n.strong,{children:"val"})," will search the entry ",(0,r.jsx)(n.code,{children:"value"})," in the top-level blackboard, instead of the local one."]}),"\n",(0,r.jsx)(n.h2,{id:"full-example",children:"Full example"}),"\n",(0,r.jsx)(n.p,{children:"Consider this tree:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-xml",children:'  <BehaviorTree ID="MainTree">\n    <Sequence>\n      <PrintNumber name="main_print" val="{@value}" />\n      <SubTree ID="MySub"/>\n    </Sequence>\n  </BehaviorTree>\n\n  <BehaviorTree ID="MySub">\n    <Sequence>\n      <PrintNumber name="sub_print" val="{@value}" />\n      <Script code="@value_sqr := @value * @value" />\n    </Sequence>\n  </BehaviorTree>\n'})}),"\n",(0,r.jsx)(n.p,{children:"And the C++ code:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-cpp",children:'class PrintNumber : public BT::SyncActionNode\n{\npublic:\n  PrintNumber(const std::string& name, const BT::NodeConfig& config)\n    : BT::SyncActionNode(name, config)\n  {}\n  \n  static BT::PortsList providedPorts()\n  {\n    return { BT::InputPort<int>("val") };\n  }\n\n  NodeStatus tick() override\n  {\n    const int val = getInput<int>("val").value();\n    std::cout << "[" << name() << "] val: " << val << std::endl;\n    return NodeStatus::SUCCESS;\n  }\n};\n\nint main()\n{\n  BehaviorTreeFactory factory;\n  factory.registerNodeType<PrintNumber>("PrintNumber");\n  factory.registerBehaviorTreeFromText(xml_main);\n\n  // No one will take the ownership of this blackboard\n  auto global_bb = BT::Blackboard::create();\n  // "MainTree" will own maintree_bb\n  auto maintree_bb = BT::Blackboard::create(global_bb);\n  auto tree = factory.createTree("MainTree", maintree_bb);\n\n  // we can interact directly with global_bb\n  for(int i = 1; i <= 3; i++)\n  {\n    // write the entry "value"\n    global_bb->set("value", i);\n    // tick the tree\n    tree.tickOnce();\n    // read the entry "value_sqr"\n    auto value_sqr = global_bb->get<int>("value_sqr");\n    // print \n    std::cout << "[While loop] value: " << i \n              << " value_sqr: " << value_sqr << "\\n\\n";\n  }\n  return 0;\n}\n'})}),"\n",(0,r.jsx)(n.p,{children:"Output:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"[main_print] val: 1\n[sub_print] val: 1\n[While loop] value: 1 value_sqr: 1\n\n[main_print] val: 2\n[sub_print] val: 2\n[While loop] value: 2 value_sqr: 4\n\n[main_print] val: 3\n[sub_print] val: 3\n[While loop] value: 3 value_sqr: 9\n"})}),"\n",(0,r.jsx)(n.p,{children:"Notes:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:'The prefix "@" works both when used in an Input / Output Port or in the scripting language.'}),"\n",(0,r.jsx)(n.li,{children:"No remapping is needed in the Subtrees."}),"\n",(0,r.jsx)(n.li,{children:'When accessing the blackboard directly in the main loop, no prefix "@" is needed.'}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},5576(e,n,a){a.d(n,{A:()=>t});const t=a.p+"assets/images/bb_hierarchy-c85c83b6c61464605231def7f2db1557.png"},3639(e,n,a){a.d(n,{A:()=>t});const t=a.p+"assets/images/tree_hierarchy-56dab79b138903144f65368898a3ad0f.png"},8453(e,n,a){a.d(n,{R:()=>l,x:()=>o});var t=a(6540);const r={},i=t.createContext(r);function l(e){const n=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),t.createElement(i.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.