[{"data":1,"prerenderedAt":5467},["ShallowReactive",2],{"wiki-page-\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0400-jie-dian-zhi-jian-chuan-shu-ju":3,"wiki-doc-items-2023-12-30-ros2-tutorial":5048,"wiki:author-members":5455},{"id":4,"title":5,"authors":6,"body":8,"chapterDepth":217,"chapterOrder":5036,"date":5037,"description":89,"docKey":5038,"docRoot":5039,"docTitle":5040,"extension":5041,"isWikiDoc":753,"isWikiIndex":5042,"meta":5043,"navigation":753,"path":5044,"seo":5045,"stem":5046,"wikiDepth":152,"__hash__":5047},"wiki\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0400-节点之间传数据.md","节点之间传数据",[7],"董佳辉",{"type":9,"value":10,"toc":5009},"minimark",[11,16,55,58,65,68,71,74,82,92,109,112,118,132,134,138,141,232,235,316,332,347,351,411,420,429,431,435,439,446,497,507,514,517,523,529,547,550,556,559,561,564,581,592,595,634,652,658,661,667,678,699,723,731,734,3337,3353,3363,3367,3378,3528,3604,3624,3643,3646,3653,3747,3753,3817,3828,3848,3852,3858,3948,4016,4022,4038,4041,4047,4103,4121,4124,4201,4204,4385,4398,4400,4404,4422,4433,4449,4457,4460,4466,4471,4486,4489,4597,4604,4613,4617,4625,4645,4652,4671,4676,4682,4685,4689,4697,4729,4745,4751,4776,4788,4793,4796,4813,4830,4835,4840,4842,4845,4848,4933,4936,4941,4944,4961,4963,4967,4983,4986,4989,4991,4994,5005],[12,13,15],"h2",{"id":14},"用-ports-与-blackboard-传递机器人目标","用 Ports 与 Blackboard 传递机器人目标",[17,18,19],"blockquote",{},[20,21,22,23,30,31,36,37,42,43,48,49,54],"p",{},"对应 BehaviorTree.CPP 3.8 官方文档：Tutorial - Basics 中的 ",[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fwww.behaviortree.dev\u002Fdocs\u002F3.8\u002Ftutorial-basics\u002Ftutorial_02_basic_ports\u002F",[28],"nofollow","Blackboard and ports"," 和 ",[24,32,35],{"href":33,"rel":34},"https:\u002F\u002Fwww.behaviortree.dev\u002Fdocs\u002F3.8\u002Ftutorial-basics\u002Ftutorial_03_generic_ports\u002F",[28],"Ports with generic types","，并参考 ",[24,38,41],{"href":39,"rel":40},"https:\u002F\u002Fgithub.com\u002FBehaviorTree\u002FBehaviorTree.CPP\u002Fblob\u002F3.8.7\u002Finclude\u002Fbehaviortree_cpp_v3\u002Ftree_node.h",[28],"3.8.7 tree_node.h","、",[24,44,47],{"href":45,"rel":46},"https:\u002F\u002Fgithub.com\u002FBehaviorTree\u002FBehaviorTree.CPP\u002Fblob\u002F3.8.7\u002Finclude\u002Fbehaviortree_cpp_v3\u002Fbasic_types.h",[28],"basic_types.h"," 与 ",[24,50,53],{"href":51,"rel":52},"https:\u002F\u002Fgithub.com\u002FBehaviorTree\u002FBehaviorTree.CPP\u002Fblob\u002F3.8.7\u002Finclude\u002Fbehaviortree_cpp_v3\u002Fblackboard.h",[28],"blackboard.h","。",[20,56,57],{},"上一篇已经给出“检查电量 → 选择目标 → 打印目标”的完整工程。\n如果还没有运行，可以先按上一篇的实践部分建立工程；\n本篇另建一个独立示例，保留前一个工程，方便对照。",[20,59,60,61],{},"接下来把字符串目标换成机器人常用的坐标数据，集中解决几个相关问题：",[62,63,64],"strong",{},"不同名称的端口怎样连接，XML 文本怎样变成 C++ 类型，节点怎样读取并修改同一份数据，外部程序怎样与树交换数据。",[20,66,67],{},"本篇仍使用同步节点计算和打印数据，实际移动与导航留到“持续任务与中止”学习。",[69,70],"hr",{},[12,72,73],{"id":73},"从外部位姿计算一个任务目标",[20,75,76,77,81],{},"假设移动机器人当前位姿为 ",[78,79,80],"code",{},"(1.0, 2.0, 0.0)","，\n需要沿当前朝向前进 2 米，\n再给目标的 x 坐标增加 0.5 米的示例偏移：",[83,84,90],"pre",{"className":85,"code":87,"language":88,"meta":89},[86],"language-text","外部程序初始化 Blackboard\n├── robot_pose       = Pose2D{1.0, 2.0, 0.0}\n└── travel_distance  = 2.0\n         ↓\nSequence\n├── CalculateGoal    根据起点、朝向和距离计算目标，写入 mission_goal\n├── OffsetGoal       读取 mission_goal，修改后写回同一个条目\n├── PrintGoal        从 mission_goal 读取计算结果\n└── PrintGoal        从 XML 固定文本读取另一组坐标\n         ↓\n外部程序读取 mission_goal\n","text","",[78,91,87],{"__ignoreMap":89},[20,93,94,95,98,99,42,102,42,105,108],{},"这里的 ",[78,96,97],{},"Pose2D"," 是自行定义的 C++ 结构体，包含 ",[78,100,101],{},"x",[78,103,104],{},"y",[78,106,107],{},"yaw","，不依赖 ROS 消息类型。\n位置单位为米，朝向单位为弧度，示例中的坐标使用同一个参考坐标系。",[20,110,111],{},"计算时使用：",[83,113,116],{"className":114,"code":115,"language":88,"meta":89},[86],"goal.x   = start.x + distance × cos(start.yaw)\ngoal.y   = start.y + distance × sin(start.yaw)\ngoal.yaw = start.yaw\n",[78,117,115],{"__ignoreMap":89},[20,119,120,121,123,124,127,128,131],{},"起始位姿",[78,122,80],{},"，起始朝向为 0，所以朝前（是x）2米，先得到 ",[78,125,126],{},"(3.0, 2.0, 0.0)","；\n加上 x 偏移后，最终任务目标为 ",[78,129,130],{},"(3.5, 2.0, 0.0)","。这是坐标计算示例，不包含路径规划、碰撞检查或导航控制。",[69,133],{},[12,135,137],{"id":136},"端口名称与-blackboard-键各管一层","端口名称与 Blackboard 键各管一层",[20,139,140],{},"本篇用以下连接传递目标：",[83,142,146],{"className":143,"code":144,"language":145,"meta":89,"style":89},"language-xml shiki shiki-themes github-light github-dark","\u003CCalculateGoal start=\"{robot_pose}\" distance=\"{travel_distance}\" goal=\"{mission_goal}\"\u002F>\n\u003COffsetGoal goal=\"{mission_goal}\" offset_x=\"0.5\"\u002F>\n\u003CPrintGoal target=\"{mission_goal}\"\u002F>\n","xml",[78,147,148,191,215],{"__ignoreMap":89},[149,150,153,157,161,165,168,172,175,177,180,183,185,188],"span",{"class":151,"line":152},"line",1,[149,154,156],{"class":155},"sVt8B","\u003C",[149,158,160],{"class":159},"s9eBZ","CalculateGoal",[149,162,164],{"class":163},"sScJk"," start",[149,166,167],{"class":155},"=",[149,169,171],{"class":170},"sZZnC","\"{robot_pose}\"",[149,173,174],{"class":163}," distance",[149,176,167],{"class":155},[149,178,179],{"class":170},"\"{travel_distance}\"",[149,181,182],{"class":163}," goal",[149,184,167],{"class":155},[149,186,187],{"class":170},"\"{mission_goal}\"",[149,189,190],{"class":155},"\u002F>\n",[149,192,194,196,199,201,203,205,208,210,213],{"class":151,"line":193},2,[149,195,156],{"class":155},[149,197,198],{"class":159},"OffsetGoal",[149,200,182],{"class":163},[149,202,167],{"class":155},[149,204,187],{"class":170},[149,206,207],{"class":163}," offset_x",[149,209,167],{"class":155},[149,211,212],{"class":170},"\"0.5\"",[149,214,190],{"class":155},[149,216,218,220,223,226,228,230],{"class":151,"line":217},3,[149,219,156],{"class":155},[149,221,222],{"class":159},"PrintGoal",[149,224,225],{"class":163}," target",[149,227,167],{"class":155},[149,229,187],{"class":170},[149,231,190],{"class":155},[20,233,234],{},"读这段 XML 时，把节点接口和存储位置分开看：",[236,237,238,254],"table",{},[239,240,241],"thead",{},[242,243,244,248,251],"tr",{},[245,246,247],"th",{},"名称",[245,249,250],{},"所属位置",[245,252,253],{},"作用",[255,256,257,274,288,303],"tbody",{},[242,258,259,268,271],{},[260,261,262,264,265],"td",{},[78,263,160],{}," 的 ",[78,266,267],{},"goal",[260,269,270],{},"输出端口",[260,272,273],{},"计算节点写出目标的接口",[242,275,276,282,285],{},[260,277,278,264,280],{},[78,279,198],{},[78,281,267],{},[260,283,284],{},"双向端口",[260,286,287],{},"偏移节点读取并更新目标的接口",[242,289,290,297,300],{},[260,291,292,264,294],{},[78,293,222],{},[78,295,296],{},"target",[260,298,299],{},"输入端口",[260,301,302],{},"打印节点读取目标的接口",[242,304,305,310,313],{},[260,306,307],{},[78,308,309],{},"mission_goal",[260,311,312],{},"Blackboard 键",[260,314,315],{},"保存这几个端口共同使用的目标数据",[20,317,318,319,321,322,324,325,328,329,331],{},"生产者的端口叫 ",[78,320,267],{},"，消费者的端口叫 ",[78,323,296],{},"，仍然可以连接，因为 XML 把它们映射到同一个 ",[78,326,327],{},"{mission_goal}","，而且使用相同的 ",[78,330,97],{}," 类型。",[20,333,334,335,338,339,342,343,346],{},"C++ 中使用的是节点自己的端口名称：",[78,336,337],{},"setOutput(\"goal\", ...)"," 或 ",[78,340,341],{},"getInput\u003CPose2D>(\"target\")","。\n将来把 Blackboard 键改为 ",[78,344,345],{},"delivery_goal","，只需统一调整 XML 中的映射，节点实现不必跟着改名。",[348,349,350],"h3",{"id":350},"三种端口方向",[236,352,353,366],{},[239,354,355],{},[242,356,357,360,363],{},[245,358,359],{},"声明",[245,361,362],{},"读写方法",[245,364,365],{},"适合的职责",[255,367,368,383,398],{},[242,369,370,375,380],{},[260,371,372],{},[78,373,374],{},"BT::InputPort\u003CT>",[260,376,377],{},[78,378,379],{},"getInput\u003CT>()",[260,381,382],{},"读取起点、距离、目标等输入",[242,384,385,390,395],{},[260,386,387],{},[78,388,389],{},"BT::OutputPort\u003CT>",[260,391,392],{},[78,393,394],{},"setOutput()",[260,396,397],{},"产生并写出一个结果",[242,399,400,405,408],{},[260,401,402],{},[78,403,404],{},"BT::BidirectionalPort\u003CT>",[260,406,407],{},"先读取，再按需写出",[260,409,410],{},"读取已有目标，修改后写回",[20,412,413,414,416,417,419],{},"双向端口同时提供读写接口；它不会自动完成修改，也不保证条目已经有值。\n本例先执行 ",[78,415,160],{},"，再执行 ",[78,418,198],{},"，就是为了在读写目标前先产生有效数据。",[20,421,422,425,426,428],{},[78,423,424],{},"getInput()"," 将值读到本地变量，修改这个变量不会直接改变 Blackboard。\n偏移节点需要明确调用 ",[78,427,394],{}," 写回结果。",[69,430],{},[12,432,434],{"id":433},"xml-文本与-c-类型怎样对应","XML 文本与 C++ 类型怎样对应",[348,436,438],{"id":437},"数值可以来自固定文本blackboard-或默认值","数值可以来自固定文本、Blackboard 或默认值",[20,440,441,442,445],{},"计算节点的距离输入声明为 ",[78,443,444],{},"double","，下面三种配置对应三种来源：",[236,447,448,458],{},[239,449,450],{},[242,451,452,455],{},[245,453,454],{},"XML 配置",[245,456,457],{},"取值方式",[255,459,460,472,482],{},[242,461,462,467],{},[260,463,464],{},[78,465,466],{},"distance=\"2.0\"",[260,468,469,470],{},"将固定文本转换成 ",[78,471,444],{},[242,473,474,479],{},[260,475,476],{},[78,477,478],{},"distance=\"{travel_distance}\"",[260,480,481],{},"从 Blackboard 读取对应条目",[242,483,484,491],{},[260,485,486,487,490],{},"省略 ",[78,488,489],{},"distance"," 属性",[260,492,493,494],{},"使用节点声明的默认值，本例为 ",[78,495,496],{},"1.0",[20,498,499,500,503,504,54],{},"BehaviorTree.CPP 已经提供常用数值类型的文本转换。\n默认值通过 ",[78,501,502],{},"providedPorts()"," 声明；\n下面完整程序使用 3.8 的三参数写法 ",[78,505,506],{},"BT::InputPort\u003Cdouble>(\"distance\", 1.0, \"前进距离，单位为米\")",[20,508,509,510,513],{},"默认值用于没有显式配置该输入的情况。\n如果 XML 已经写成 ",[78,511,512],{},"{unknown_distance}","，但该条目没有有效值，读取仍会失败，不会自动退回默认值。",[348,515,516],{"id":516},"自定义类型需要约定文本格式",[20,518,519,520,522],{},"本篇约定 ",[78,521,97],{}," 的文本格式为三个用分号分隔的实数：",[83,524,527],{"className":525,"code":526,"language":88,"meta":89},[86],"x;y;yaw\n2.0;3.0;1.57\n",[78,528,526],{"__ignoreMap":89},[20,530,531,532,535,536,538,539,542,543,546],{},"因此，",[78,533,534],{},"\u003CPrintGoal target=\"2.0;3.0;1.57\"\u002F>"," 需要把字符串转换成 ",[78,537,97],{},"。\n做法是在 ",[78,540,541],{},"BT"," 命名空间中，为 ",[78,544,545],{},"convertFromString\u003CPose2D>()"," 提供模板特化，再分别转换三个数值。\n特化放在使用该类型声明端口的节点类之前。",[20,548,549],{},"两条数据路径有所区别：",[83,551,554],{"className":552,"code":553,"language":88,"meta":89},[86],"C++ 计算出的 Pose2D → setOutput() → Blackboard → getInput\u003CPose2D>()\n                                           直接读取对应类型的值\n\nXML 中的 \"2.0;3.0;1.57\" → getInput\u003CPose2D>() → convertFromString\u003CPose2D>()\n                                           先解析文本，再得到值\n",[78,555,553],{"__ignoreMap":89},[20,557,558],{},"Blackboard 可以保存结构体，不需要把所有数据先转成字符串。\nXML 文本转换也不会让任意两个 C++ 类型自动互相兼容；生产者、消费者和外部程序仍应统一数据类型。",[69,560],{},[12,562,563],{"id":563},"沿用框架建立数据传递工程",[20,565,566,567,571,572,576,577,580],{},"本篇继续使用上一篇的 BT 练习版，目录来自 ",[24,568,570],{"href":569},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-cmake-gong-cheng-mu-ban","CMake工程模板","，\n构建方式见 ",[24,573,575],{"href":574},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0100-cmakepresets-yu-gou-jian-an-zhuang","CMakePresets与构建安装","。\n需要支持 C++20 的编译器、CMake 3.22 或更新版本和 Ninja。\n",[62,578,579],{},"Ubuntu 22.04 可以继续使用 CMake 3.22.1","，沿用上一篇的工程即可；C++20 和预设格式版本 3 都直接沿用当前模板。",[20,582,583,584,587,588,591],{},"如果还没有建立上一篇的框架工程，先按上一篇准备好工程文件。\n本篇直接复制整个 ",[78,585,586],{},"bt_first_tree"," 文件夹，再把副本重命名为 ",[78,589,590],{},"bt_ports","，保留原来的工程供后续对照。",[20,593,594],{},"在系统文件管理器中完成下面几步：",[596,597,598,608,621],"ol",{},[599,600,601,602,604,605,607],"li",{},"打开 ",[78,603,586],{}," 所在的父目录，复制并粘贴整个文件夹，将副本重命名为 ",[78,606,590],{},"，让两个工程位于同一层目录。",[599,609,610,611,613,614,30,617,620],{},"在新副本 ",[78,612,590],{}," 中，删除复制过来的 ",[78,615,616],{},"build",[78,618,619],{},"install"," 文件夹（如果存在）。其中的构建缓存和安装结果属于旧工程，新工程稍后会重新生成。",[599,622,623,624,627,628,631,632,54],{},"在 VSCode 中选择 ",[62,625,626],{},"文件 → 新建窗口","，再选择 ",[62,629,630],{},"文件 → 打开文件夹","，打开新的 ",[78,633,590],{},[20,635,636,42,639,42,642,645,646,30,649,54],{},[78,637,638],{},"cmake",[78,640,641],{},"src",[78,643,644],{},"trees"," 目录及其中的文件都已随工程复制过来，不需要重新建立。\n稍后直接修改顶层项目名，并用本篇内容替换已有的 ",[78,647,648],{},"src\u002Fmain.cpp",[78,650,651],{},"trees\u002Fmission.xml",[20,653,654],{},[655,656],"img",{"alt":89,"src":657},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792289716277-fc3af03e.webp",[20,659,660],{},"新工程的构建目录会在 Configure、Build 时由自己的 preset 重新生成。参与本篇学习的文件为：",[83,662,665],{"className":663,"code":664,"language":88,"meta":89},[86],"bt_ports\u002F\n├── CMakeLists.txt\n├── CMakePresets.json\n├── cmake\u002F\n│   ├── ProjectOptions.cmake\n│   └── BehaviorTree.cmake\n├── src\u002F\n│   ├── CMakeLists.txt\n│   └── main.cpp\n└── trees\u002F\n    └── mission.xml\n",[78,666,664],{"__ignoreMap":89},[20,668,669,670,673,674,677],{},"在新的顶层 ",[78,671,672],{},"CMakeLists.txt"," 中，将 ",[78,675,676],{},"project(...)"," 一行改为：",[83,679,682],{"className":680,"code":681,"language":638,"meta":89,"style":89},"language-cmake shiki shiki-themes github-light github-dark","project(bt_ports VERSION 1.0.0 LANGUAGES C CXX)\n",[78,683,684],{"__ignoreMap":89},[149,685,686,690,693,696],{"class":151,"line":152},[149,687,689],{"class":688},"szBVR","project",[149,691,692],{"class":155},"(bt_ports ",[149,694,695],{"class":163},"VERSION",[149,697,698],{"class":155}," 1.0.0 LANGUAGES C CXX)\n",[20,700,701,702,705,706,709,710,713,714,716,717,719,720,54],{},"其他配置原样沿用：",[78,703,704],{},"BehaviorTree.cmake"," 继续优先使用已安装的 ",[62,707,708],{},"3.8.7","，未找到时再获取同版本源码；\n",[78,711,712],{},"src\u002FCMakeLists.txt"," 自动以新项目名创建 ",[78,715,590],{},"，并将新工程的 ",[78,718,651],{}," 路径交给 ",[78,721,722],{},"BT_XML_PATH",[20,724,725,726,48,728,730],{},"在 VSCode 编辑器中，用下面的完整 C++ 和 XML 分别替换 ",[78,727,648],{},[78,729,651],{}," 的原有内容，再保存。本篇继续使用 CMake Tools 配置、构建和运行。",[348,732,648],{"id":733},"srcmaincpp",[83,735,739],{"className":736,"code":737,"language":738,"meta":89,"style":89},"language-cpp shiki shiki-themes github-light github-dark","#include \"behaviortree_cpp_v3\u002Fbt_factory.h\"\n\n#include \u003Ccmath>\n#include \u003Cexception>\n#include \u003Ciomanip>\n#include \u003Ciostream>\n#include \u003Cstring>\n\nstruct Pose2D\n{\n    double x;\n    double y;\n    double yaw;\n};\n\n\u002F\u002F 统一坐标的打印方式，便于观察每一步的数据\nstd::ostream& operator\u003C\u003C(std::ostream& stream, const Pose2D& pose)\n{\n    return stream \u003C\u003C '(' \u003C\u003C pose.x \u003C\u003C \", \" \u003C\u003C pose.y \u003C\u003C \", \" \u003C\u003C pose.yaw \u003C\u003C ')';\n}\n\nnamespace BT\n{\ntemplate \u003C>\ninline Pose2D convertFromString\u003CPose2D>(StringView text)\n{\n    \u002F\u002F XML 中的位姿格式为 x;y;yaw，朝向单位为弧度\n    const auto parts = splitString(text, ';');\n    if (parts.size() != 3)\n    {\n        throw RuntimeError(\"Pose2D requires x;y;yaw\");\n    }\n\n    Pose2D pose{\n        convertFromString\u003Cdouble>(parts[0]),\n        convertFromString\u003Cdouble>(parts[1]),\n        convertFromString\u003Cdouble>(parts[2])};\n\n    if (!std::isfinite(pose.x) || !std::isfinite(pose.y) || !std::isfinite(pose.yaw))\n    {\n        throw RuntimeError(\"Pose2D values must be finite\");\n    }\n    return pose;\n}\n}  \u002F\u002F namespace BT\n\nclass CalculateGoal : public BT::SyncActionNode\n{\npublic:\n    CalculateGoal(const std::string& name, const BT::NodeConfiguration& config)\n        : BT::SyncActionNode(name, config)\n    {}\n\n    static BT::PortsList providedPorts()\n    {\n        return {\n            BT::InputPort\u003CPose2D>(\"start\"),\n            BT::InputPort\u003Cdouble>(\"distance\", 1.0, \"前进距离，单位为米\"),\n            BT::OutputPort\u003CPose2D>(\"goal\")};\n    }\n\n    BT::NodeStatus tick() override\n    {\n        const auto start = getInput\u003CPose2D>(\"start\");\n        const auto distance = getInput\u003Cdouble>(\"distance\");\n        if (!start)\n        {\n            throw BT::RuntimeError(\"Cannot read input [start]: \", start.error());\n        }\n        if (!distance)\n        {\n            throw BT::RuntimeError(\"Cannot read input [distance]: \", distance.error());\n        }\n        if (!std::isfinite(distance.value()) || distance.value() \u003C 0.0)\n        {\n            throw BT::RuntimeError(\"Input [distance] must be finite and non-negative\");\n        }\n\n        \u002F\u002F 沿起始朝向计算目标，计算结果仍是一个 Pose2D 对象\n        const Pose2D& origin = start.value();\n        const Pose2D goal{\n            origin.x + distance.value() * std::cos(origin.yaw),\n            origin.y + distance.value() * std::sin(origin.yaw),\n            origin.yaw};\n\n        const auto written = setOutput\u003CPose2D>(\"goal\", goal);\n        if (!written)\n        {\n            throw BT::RuntimeError(\"Cannot write output [goal]: \", written.error());\n        }\n        std::cout \u003C\u003C \"[CalculateGoal] distance=\" \u003C\u003C distance.value()\n                  \u003C\u003C \", goal=\" \u003C\u003C goal \u003C\u003C '\\n';\n        return BT::NodeStatus::SUCCESS;\n    }\n};\n\nclass OffsetGoal : public BT::SyncActionNode\n{\npublic:\n    OffsetGoal(const std::string& name, const BT::NodeConfiguration& config)\n        : BT::SyncActionNode(name, config)\n    {}\n\n    static BT::PortsList providedPorts()\n    {\n        return {\n            BT::BidirectionalPort\u003CPose2D>(\"goal\"),\n            BT::InputPort\u003Cdouble>(\"offset_x\", 0.0, \"目标在参考坐标系 x 方向的偏移\")};\n    }\n\n    BT::NodeStatus tick() override\n    {\n        const auto input = getInput\u003CPose2D>(\"goal\");\n        const auto offset = getInput\u003Cdouble>(\"offset_x\");\n        if (!input)\n        {\n            throw BT::RuntimeError(\"Cannot read input [goal]: \", input.error());\n        }\n        if (!offset)\n        {\n            throw BT::RuntimeError(\"Cannot read input [offset_x]: \", offset.error());\n        }\n        if (!std::isfinite(offset.value()))\n        {\n            throw BT::RuntimeError(\"Input [offset_x] must be finite\");\n        }\n\n        \u002F\u002F 先修改本地副本，再通过双向端口写回同一个条目\n        Pose2D goal = input.value();\n        goal.x += offset.value();\n        const auto written = setOutput\u003CPose2D>(\"goal\", goal);\n        if (!written)\n        {\n            throw BT::RuntimeError(\"Cannot write output [goal]: \", written.error());\n        }\n        std::cout \u003C\u003C \"[OffsetGoal] goal=\" \u003C\u003C goal \u003C\u003C '\\n';\n        return BT::NodeStatus::SUCCESS;\n    }\n};\n\nclass PrintGoal : public BT::SyncActionNode\n{\npublic:\n    PrintGoal(const std::string& name, const BT::NodeConfiguration& config)\n        : BT::SyncActionNode(name, config)\n    {}\n\n    static BT::PortsList providedPorts()\n    {\n        return {BT::InputPort\u003CPose2D>(\"target\")};\n    }\n\n    BT::NodeStatus tick() override\n    {\n        const auto target = getInput\u003CPose2D>(\"target\");\n        if (!target)\n        {\n            throw BT::RuntimeError(\"Cannot read input [target]: \", target.error());\n        }\n        std::cout \u003C\u003C \"[PrintGoal:\" \u003C\u003C name() \u003C\u003C \"] target=\" \u003C\u003C target.value() \u003C\u003C '\\n';\n        return BT::NodeStatus::SUCCESS;\n    }\n};\n\nint main()\n{\n    try\n    {\n        \u002F\u002F 在编辑器中修改这个模拟距离，再构建运行；XML 路径由构建配置提供\n        const double distance = 2.0;\n        std::cout \u003C\u003C std::fixed \u003C\u003C std::setprecision(2);\n\n        \u002F\u002F 外部程序在创建树之前，准备带有明确 C++ 类型的输入\n        auto blackboard = BT::Blackboard::create();\n        blackboard->set\u003CPose2D>(\"robot_pose\", Pose2D{1.0, 2.0, 0.0});\n        blackboard->set\u003Cdouble>(\"travel_distance\", distance);\n\n        BT::BehaviorTreeFactory factory;\n        factory.registerNodeType\u003CCalculateGoal>(\"CalculateGoal\");\n        factory.registerNodeType\u003COffsetGoal>(\"OffsetGoal\");\n        factory.registerNodeType\u003CPrintGoal>(\"PrintGoal\");\n        auto tree = factory.createTreeFromFile(BT_XML_PATH, blackboard);\n\n        const BT::NodeStatus result = tree.tickRootWhileRunning();\n        std::cout \u003C\u003C \"Tree result: \" \u003C\u003C BT::toStr(result) \u003C\u003C '\\n';\n        if (result == BT::NodeStatus::SUCCESS)\n        {\n            \u002F\u002F 树执行后，外部程序可以从同一块 Blackboard 取得输出\n            const Pose2D goal = blackboard->get\u003CPose2D>(\"mission_goal\");\n            std::cout \u003C\u003C \"[main] mission_goal=\" \u003C\u003C goal \u003C\u003C '\\n';\n        }\n        return result == BT::NodeStatus::SUCCESS ? 0 : 1;\n    }\n    catch (const std::exception& error)\n    {\n        std::cerr \u003C\u003C \"Error: \" \u003C\u003C error.what() \u003C\u003C '\\n';\n        return 2;\n    }\n}\n","cpp",[78,740,741,749,755,762,770,778,786,794,799,808,814,823,831,839,845,850,857,910,915,962,968,973,982,987,996,1015,1020,1026,1052,1076,1082,1098,1104,1109,1115,1134,1150,1167,1172,1221,1226,1240,1245,1253,1258,1267,1272,1295,1300,1306,1347,1359,1365,1370,1389,1394,1403,1427,1456,1478,1483,1488,1507,1512,1538,1562,1575,1581,1608,1614,1626,1631,1654,1659,1699,1704,1722,1727,1732,1738,1760,1768,1796,1821,1827,1832,1858,1870,1875,1898,1903,1925,1952,1966,1971,1976,1981,1999,2004,2009,2045,2054,2059,2064,2079,2084,2091,2111,2141,2146,2151,2166,2171,2195,2219,2231,2236,2259,2264,2276,2281,2304,2309,2332,2337,2355,2360,2365,2371,2386,2402,2425,2436,2441,2462,2467,2493,2506,2511,2516,2521,2539,2544,2549,2585,2594,2599,2604,2619,2624,2649,2654,2659,2674,2679,2703,2715,2720,2743,2748,2790,2803,2808,2813,2818,2829,2834,2840,2845,2851,2868,2897,2902,2908,2933,2968,2984,2989,2998,3017,3035,3053,3072,3077,3097,3131,3151,3156,3162,3189,3216,3221,3254,3259,3279,3284,3317,3327,3332],{"__ignoreMap":89},[149,742,743,746],{"class":151,"line":152},[149,744,745],{"class":688},"#include",[149,747,748],{"class":170}," \"behaviortree_cpp_v3\u002Fbt_factory.h\"\n",[149,750,751],{"class":151,"line":193},[149,752,754],{"emptyLinePlaceholder":753},true,"\n",[149,756,757,759],{"class":151,"line":217},[149,758,745],{"class":688},[149,760,761],{"class":170}," \u003Ccmath>\n",[149,763,765,767],{"class":151,"line":764},4,[149,766,745],{"class":688},[149,768,769],{"class":170}," \u003Cexception>\n",[149,771,773,775],{"class":151,"line":772},5,[149,774,745],{"class":688},[149,776,777],{"class":170}," \u003Ciomanip>\n",[149,779,781,783],{"class":151,"line":780},6,[149,782,745],{"class":688},[149,784,785],{"class":170}," \u003Ciostream>\n",[149,787,789,791],{"class":151,"line":788},7,[149,790,745],{"class":688},[149,792,793],{"class":170}," \u003Cstring>\n",[149,795,797],{"class":151,"line":796},8,[149,798,754],{"emptyLinePlaceholder":753},[149,800,802,805],{"class":151,"line":801},9,[149,803,804],{"class":688},"struct",[149,806,807],{"class":163}," Pose2D\n",[149,809,811],{"class":151,"line":810},10,[149,812,813],{"class":155},"{\n",[149,815,817,820],{"class":151,"line":816},11,[149,818,819],{"class":688},"    double",[149,821,822],{"class":155}," x;\n",[149,824,826,828],{"class":151,"line":825},12,[149,827,819],{"class":688},[149,829,830],{"class":155}," y;\n",[149,832,834,836],{"class":151,"line":833},13,[149,835,819],{"class":688},[149,837,838],{"class":155}," yaw;\n",[149,840,842],{"class":151,"line":841},14,[149,843,844],{"class":155},"};\n",[149,846,848],{"class":151,"line":847},15,[149,849,754],{"emptyLinePlaceholder":753},[149,851,853],{"class":151,"line":852},16,[149,854,856],{"class":855},"sJ8bj","\u002F\u002F 统一坐标的打印方式，便于观察每一步的数据\n",[149,858,860,863,866,869,872,875,878,881,883,885,887,889,893,896,899,902,904,907],{"class":151,"line":859},17,[149,861,862],{"class":163},"std",[149,864,865],{"class":155},"::",[149,867,868],{"class":163},"ostream",[149,870,871],{"class":688},"&",[149,873,874],{"class":688}," operator",[149,876,877],{"class":163},"\u003C\u003C",[149,879,880],{"class":155},"(",[149,882,862],{"class":163},[149,884,865],{"class":155},[149,886,868],{"class":163},[149,888,871],{"class":688},[149,890,892],{"class":891},"s4XuR"," stream",[149,894,895],{"class":155},", ",[149,897,898],{"class":688},"const",[149,900,901],{"class":163}," Pose2D",[149,903,871],{"class":688},[149,905,906],{"class":891}," pose",[149,908,909],{"class":155},")\n",[149,911,913],{"class":151,"line":912},18,[149,914,813],{"class":155},[149,916,918,921,924,926,929,932,935,937,940,942,945,947,949,951,954,956,959],{"class":151,"line":917},19,[149,919,920],{"class":688},"    return",[149,922,923],{"class":155}," stream ",[149,925,877],{"class":688},[149,927,928],{"class":170}," '('",[149,930,931],{"class":688}," \u003C\u003C",[149,933,934],{"class":155}," pose.x ",[149,936,877],{"class":688},[149,938,939],{"class":170}," \", \"",[149,941,931],{"class":688},[149,943,944],{"class":155}," pose.y ",[149,946,877],{"class":688},[149,948,939],{"class":170},[149,950,931],{"class":688},[149,952,953],{"class":155}," pose.yaw ",[149,955,877],{"class":688},[149,957,958],{"class":170}," ')'",[149,960,961],{"class":155},";\n",[149,963,965],{"class":151,"line":964},20,[149,966,967],{"class":155},"}\n",[149,969,971],{"class":151,"line":970},21,[149,972,754],{"emptyLinePlaceholder":753},[149,974,976,979],{"class":151,"line":975},22,[149,977,978],{"class":688},"namespace",[149,980,981],{"class":163}," BT\n",[149,983,985],{"class":151,"line":984},23,[149,986,813],{"class":155},[149,988,990,993],{"class":151,"line":989},24,[149,991,992],{"class":688},"template",[149,994,995],{"class":155}," \u003C>\n",[149,997,999,1002,1005,1008,1010,1012],{"class":151,"line":998},25,[149,1000,1001],{"class":688},"inline",[149,1003,1004],{"class":155}," Pose2D ",[149,1006,1007],{"class":163},"convertFromString",[149,1009,156],{"class":155},[149,1011,97],{"class":163},[149,1013,1014],{"class":155},">(StringView text)\n",[149,1016,1018],{"class":151,"line":1017},26,[149,1019,813],{"class":155},[149,1021,1023],{"class":151,"line":1022},27,[149,1024,1025],{"class":855},"    \u002F\u002F XML 中的位姿格式为 x;y;yaw，朝向单位为弧度\n",[149,1027,1029,1032,1035,1038,1040,1043,1046,1049],{"class":151,"line":1028},28,[149,1030,1031],{"class":688},"    const",[149,1033,1034],{"class":688}," auto",[149,1036,1037],{"class":155}," parts ",[149,1039,167],{"class":688},[149,1041,1042],{"class":163}," splitString",[149,1044,1045],{"class":155},"(text, ",[149,1047,1048],{"class":170},"';'",[149,1050,1051],{"class":155},");\n",[149,1053,1055,1058,1061,1064,1067,1070,1074],{"class":151,"line":1054},29,[149,1056,1057],{"class":688},"    if",[149,1059,1060],{"class":155}," (parts.",[149,1062,1063],{"class":163},"size",[149,1065,1066],{"class":155},"() ",[149,1068,1069],{"class":688},"!=",[149,1071,1073],{"class":1072},"sj4cs"," 3",[149,1075,909],{"class":155},[149,1077,1079],{"class":151,"line":1078},30,[149,1080,1081],{"class":155},"    {\n",[149,1083,1085,1088,1091,1093,1096],{"class":151,"line":1084},31,[149,1086,1087],{"class":688},"        throw",[149,1089,1090],{"class":163}," RuntimeError",[149,1092,880],{"class":155},[149,1094,1095],{"class":170},"\"Pose2D requires x;y;yaw\"",[149,1097,1051],{"class":155},[149,1099,1101],{"class":151,"line":1100},32,[149,1102,1103],{"class":155},"    }\n",[149,1105,1107],{"class":151,"line":1106},33,[149,1108,754],{"emptyLinePlaceholder":753},[149,1110,1112],{"class":151,"line":1111},34,[149,1113,1114],{"class":155},"    Pose2D pose{\n",[149,1116,1118,1121,1123,1125,1128,1131],{"class":151,"line":1117},35,[149,1119,1120],{"class":163},"        convertFromString",[149,1122,156],{"class":155},[149,1124,444],{"class":688},[149,1126,1127],{"class":155},">(parts[",[149,1129,1130],{"class":1072},"0",[149,1132,1133],{"class":155},"]),\n",[149,1135,1137,1139,1141,1143,1145,1148],{"class":151,"line":1136},36,[149,1138,1120],{"class":163},[149,1140,156],{"class":155},[149,1142,444],{"class":688},[149,1144,1127],{"class":155},[149,1146,1147],{"class":1072},"1",[149,1149,1133],{"class":155},[149,1151,1153,1155,1157,1159,1161,1164],{"class":151,"line":1152},37,[149,1154,1120],{"class":163},[149,1156,156],{"class":155},[149,1158,444],{"class":688},[149,1160,1127],{"class":155},[149,1162,1163],{"class":1072},"2",[149,1165,1166],{"class":155},"])};\n",[149,1168,1170],{"class":151,"line":1169},38,[149,1171,754],{"emptyLinePlaceholder":753},[149,1173,1175,1177,1180,1183,1185,1187,1190,1193,1196,1199,1201,1203,1205,1208,1210,1212,1214,1216,1218],{"class":151,"line":1174},39,[149,1176,1057],{"class":688},[149,1178,1179],{"class":155}," (",[149,1181,1182],{"class":688},"!",[149,1184,862],{"class":163},[149,1186,865],{"class":155},[149,1188,1189],{"class":163},"isfinite",[149,1191,1192],{"class":155},"(pose.x) ",[149,1194,1195],{"class":688},"||",[149,1197,1198],{"class":688}," !",[149,1200,862],{"class":163},[149,1202,865],{"class":155},[149,1204,1189],{"class":163},[149,1206,1207],{"class":155},"(pose.y) ",[149,1209,1195],{"class":688},[149,1211,1198],{"class":688},[149,1213,862],{"class":163},[149,1215,865],{"class":155},[149,1217,1189],{"class":163},[149,1219,1220],{"class":155},"(pose.yaw))\n",[149,1222,1224],{"class":151,"line":1223},40,[149,1225,1081],{"class":155},[149,1227,1229,1231,1233,1235,1238],{"class":151,"line":1228},41,[149,1230,1087],{"class":688},[149,1232,1090],{"class":163},[149,1234,880],{"class":155},[149,1236,1237],{"class":170},"\"Pose2D values must be finite\"",[149,1239,1051],{"class":155},[149,1241,1243],{"class":151,"line":1242},42,[149,1244,1103],{"class":155},[149,1246,1248,1250],{"class":151,"line":1247},43,[149,1249,920],{"class":688},[149,1251,1252],{"class":155}," pose;\n",[149,1254,1256],{"class":151,"line":1255},44,[149,1257,967],{"class":155},[149,1259,1261,1264],{"class":151,"line":1260},45,[149,1262,1263],{"class":155},"}",[149,1265,1266],{"class":855},"  \u002F\u002F namespace BT\n",[149,1268,1270],{"class":151,"line":1269},46,[149,1271,754],{"emptyLinePlaceholder":753},[149,1273,1275,1278,1281,1284,1287,1290,1292],{"class":151,"line":1274},47,[149,1276,1277],{"class":688},"class",[149,1279,1280],{"class":163}," CalculateGoal",[149,1282,1283],{"class":155}," : ",[149,1285,1286],{"class":688},"public",[149,1288,1289],{"class":163}," BT",[149,1291,865],{"class":155},[149,1293,1294],{"class":163},"SyncActionNode\n",[149,1296,1298],{"class":151,"line":1297},48,[149,1299,813],{"class":155},[149,1301,1303],{"class":151,"line":1302},49,[149,1304,1305],{"class":688},"public:\n",[149,1307,1309,1312,1314,1316,1319,1321,1324,1326,1329,1331,1333,1335,1337,1340,1342,1345],{"class":151,"line":1308},50,[149,1310,1311],{"class":163},"    CalculateGoal",[149,1313,880],{"class":155},[149,1315,898],{"class":688},[149,1317,1318],{"class":163}," std",[149,1320,865],{"class":155},[149,1322,1323],{"class":163},"string",[149,1325,871],{"class":688},[149,1327,1328],{"class":891}," name",[149,1330,895],{"class":155},[149,1332,898],{"class":688},[149,1334,1289],{"class":163},[149,1336,865],{"class":155},[149,1338,1339],{"class":163},"NodeConfiguration",[149,1341,871],{"class":688},[149,1343,1344],{"class":891}," config",[149,1346,909],{"class":155},[149,1348,1350,1353,1356],{"class":151,"line":1349},51,[149,1351,1352],{"class":155},"        : BT::",[149,1354,1355],{"class":163},"SyncActionNode",[149,1357,1358],{"class":155},"(name, config)\n",[149,1360,1362],{"class":151,"line":1361},52,[149,1363,1364],{"class":155},"    {}\n",[149,1366,1368],{"class":151,"line":1367},53,[149,1369,754],{"emptyLinePlaceholder":753},[149,1371,1373,1376,1378,1380,1383,1386],{"class":151,"line":1372},54,[149,1374,1375],{"class":688},"    static",[149,1377,1289],{"class":163},[149,1379,865],{"class":155},[149,1381,1382],{"class":163},"PortsList",[149,1384,1385],{"class":163}," providedPorts",[149,1387,1388],{"class":155},"()\n",[149,1390,1392],{"class":151,"line":1391},55,[149,1393,1081],{"class":155},[149,1395,1397,1400],{"class":151,"line":1396},56,[149,1398,1399],{"class":688},"        return",[149,1401,1402],{"class":155}," {\n",[149,1404,1406,1409,1411,1414,1416,1418,1421,1424],{"class":151,"line":1405},57,[149,1407,1408],{"class":163},"            BT",[149,1410,865],{"class":155},[149,1412,1413],{"class":163},"InputPort",[149,1415,156],{"class":155},[149,1417,97],{"class":163},[149,1419,1420],{"class":155},">(",[149,1422,1423],{"class":170},"\"start\"",[149,1425,1426],{"class":155},"),\n",[149,1428,1430,1432,1434,1436,1438,1440,1442,1445,1447,1449,1451,1454],{"class":151,"line":1429},58,[149,1431,1408],{"class":163},[149,1433,865],{"class":155},[149,1435,1413],{"class":163},[149,1437,156],{"class":155},[149,1439,444],{"class":688},[149,1441,1420],{"class":155},[149,1443,1444],{"class":170},"\"distance\"",[149,1446,895],{"class":155},[149,1448,496],{"class":1072},[149,1450,895],{"class":155},[149,1452,1453],{"class":170},"\"前进距离，单位为米\"",[149,1455,1426],{"class":155},[149,1457,1459,1461,1463,1466,1468,1470,1472,1475],{"class":151,"line":1458},59,[149,1460,1408],{"class":163},[149,1462,865],{"class":155},[149,1464,1465],{"class":163},"OutputPort",[149,1467,156],{"class":155},[149,1469,97],{"class":163},[149,1471,1420],{"class":155},[149,1473,1474],{"class":170},"\"goal\"",[149,1476,1477],{"class":155},")};\n",[149,1479,1481],{"class":151,"line":1480},60,[149,1482,1103],{"class":155},[149,1484,1486],{"class":151,"line":1485},61,[149,1487,754],{"emptyLinePlaceholder":753},[149,1489,1491,1494,1496,1499,1502,1504],{"class":151,"line":1490},62,[149,1492,1493],{"class":163},"    BT",[149,1495,865],{"class":155},[149,1497,1498],{"class":163},"NodeStatus",[149,1500,1501],{"class":163}," tick",[149,1503,1066],{"class":155},[149,1505,1506],{"class":688},"override\n",[149,1508,1510],{"class":151,"line":1509},63,[149,1511,1081],{"class":155},[149,1513,1515,1518,1520,1523,1525,1528,1530,1532,1534,1536],{"class":151,"line":1514},64,[149,1516,1517],{"class":688},"        const",[149,1519,1034],{"class":688},[149,1521,1522],{"class":155}," start ",[149,1524,167],{"class":688},[149,1526,1527],{"class":163}," getInput",[149,1529,156],{"class":155},[149,1531,97],{"class":163},[149,1533,1420],{"class":155},[149,1535,1423],{"class":170},[149,1537,1051],{"class":155},[149,1539,1541,1543,1545,1548,1550,1552,1554,1556,1558,1560],{"class":151,"line":1540},65,[149,1542,1517],{"class":688},[149,1544,1034],{"class":688},[149,1546,1547],{"class":155}," distance ",[149,1549,167],{"class":688},[149,1551,1527],{"class":163},[149,1553,156],{"class":155},[149,1555,444],{"class":688},[149,1557,1420],{"class":155},[149,1559,1444],{"class":170},[149,1561,1051],{"class":155},[149,1563,1565,1568,1570,1572],{"class":151,"line":1564},66,[149,1566,1567],{"class":688},"        if",[149,1569,1179],{"class":155},[149,1571,1182],{"class":688},[149,1573,1574],{"class":155},"start)\n",[149,1576,1578],{"class":151,"line":1577},67,[149,1579,1580],{"class":155},"        {\n",[149,1582,1584,1587,1589,1591,1594,1596,1599,1602,1605],{"class":151,"line":1583},68,[149,1585,1586],{"class":688},"            throw",[149,1588,1289],{"class":163},[149,1590,865],{"class":155},[149,1592,1593],{"class":163},"RuntimeError",[149,1595,880],{"class":155},[149,1597,1598],{"class":170},"\"Cannot read input [start]: \"",[149,1600,1601],{"class":155},", start.",[149,1603,1604],{"class":163},"error",[149,1606,1607],{"class":155},"());\n",[149,1609,1611],{"class":151,"line":1610},69,[149,1612,1613],{"class":155},"        }\n",[149,1615,1617,1619,1621,1623],{"class":151,"line":1616},70,[149,1618,1567],{"class":688},[149,1620,1179],{"class":155},[149,1622,1182],{"class":688},[149,1624,1625],{"class":155},"distance)\n",[149,1627,1629],{"class":151,"line":1628},71,[149,1630,1580],{"class":155},[149,1632,1634,1636,1638,1640,1642,1644,1647,1650,1652],{"class":151,"line":1633},72,[149,1635,1586],{"class":688},[149,1637,1289],{"class":163},[149,1639,865],{"class":155},[149,1641,1593],{"class":163},[149,1643,880],{"class":155},[149,1645,1646],{"class":170},"\"Cannot read input [distance]: \"",[149,1648,1649],{"class":155},", distance.",[149,1651,1604],{"class":163},[149,1653,1607],{"class":155},[149,1655,1657],{"class":151,"line":1656},73,[149,1658,1613],{"class":155},[149,1660,1662,1664,1666,1668,1670,1672,1674,1677,1680,1683,1685,1688,1690,1692,1694,1697],{"class":151,"line":1661},74,[149,1663,1567],{"class":688},[149,1665,1179],{"class":155},[149,1667,1182],{"class":688},[149,1669,862],{"class":163},[149,1671,865],{"class":155},[149,1673,1189],{"class":163},[149,1675,1676],{"class":155},"(distance.",[149,1678,1679],{"class":163},"value",[149,1681,1682],{"class":155},"()) ",[149,1684,1195],{"class":688},[149,1686,1687],{"class":155}," distance.",[149,1689,1679],{"class":163},[149,1691,1066],{"class":155},[149,1693,156],{"class":688},[149,1695,1696],{"class":1072}," 0.0",[149,1698,909],{"class":155},[149,1700,1702],{"class":151,"line":1701},75,[149,1703,1580],{"class":155},[149,1705,1707,1709,1711,1713,1715,1717,1720],{"class":151,"line":1706},76,[149,1708,1586],{"class":688},[149,1710,1289],{"class":163},[149,1712,865],{"class":155},[149,1714,1593],{"class":163},[149,1716,880],{"class":155},[149,1718,1719],{"class":170},"\"Input [distance] must be finite and non-negative\"",[149,1721,1051],{"class":155},[149,1723,1725],{"class":151,"line":1724},77,[149,1726,1613],{"class":155},[149,1728,1730],{"class":151,"line":1729},78,[149,1731,754],{"emptyLinePlaceholder":753},[149,1733,1735],{"class":151,"line":1734},79,[149,1736,1737],{"class":855},"        \u002F\u002F 沿起始朝向计算目标，计算结果仍是一个 Pose2D 对象\n",[149,1739,1741,1743,1745,1747,1750,1752,1755,1757],{"class":151,"line":1740},80,[149,1742,1517],{"class":688},[149,1744,901],{"class":155},[149,1746,871],{"class":688},[149,1748,1749],{"class":155}," origin ",[149,1751,167],{"class":688},[149,1753,1754],{"class":155}," start.",[149,1756,1679],{"class":163},[149,1758,1759],{"class":155},"();\n",[149,1761,1763,1765],{"class":151,"line":1762},81,[149,1764,1517],{"class":688},[149,1766,1767],{"class":155}," Pose2D goal{\n",[149,1769,1771,1774,1777,1779,1781,1783,1786,1788,1790,1793],{"class":151,"line":1770},82,[149,1772,1773],{"class":155},"            origin.x ",[149,1775,1776],{"class":688},"+",[149,1778,1687],{"class":155},[149,1780,1679],{"class":163},[149,1782,1066],{"class":155},[149,1784,1785],{"class":688},"*",[149,1787,1318],{"class":163},[149,1789,865],{"class":155},[149,1791,1792],{"class":163},"cos",[149,1794,1795],{"class":155},"(origin.yaw),\n",[149,1797,1799,1802,1804,1806,1808,1810,1812,1814,1816,1819],{"class":151,"line":1798},83,[149,1800,1801],{"class":155},"            origin.y ",[149,1803,1776],{"class":688},[149,1805,1687],{"class":155},[149,1807,1679],{"class":163},[149,1809,1066],{"class":155},[149,1811,1785],{"class":688},[149,1813,1318],{"class":163},[149,1815,865],{"class":155},[149,1817,1818],{"class":163},"sin",[149,1820,1795],{"class":155},[149,1822,1824],{"class":151,"line":1823},84,[149,1825,1826],{"class":155},"            origin.yaw};\n",[149,1828,1830],{"class":151,"line":1829},85,[149,1831,754],{"emptyLinePlaceholder":753},[149,1833,1835,1837,1839,1842,1844,1847,1849,1851,1853,1855],{"class":151,"line":1834},86,[149,1836,1517],{"class":688},[149,1838,1034],{"class":688},[149,1840,1841],{"class":155}," written ",[149,1843,167],{"class":688},[149,1845,1846],{"class":163}," setOutput",[149,1848,156],{"class":155},[149,1850,97],{"class":163},[149,1852,1420],{"class":155},[149,1854,1474],{"class":170},[149,1856,1857],{"class":155},", goal);\n",[149,1859,1861,1863,1865,1867],{"class":151,"line":1860},87,[149,1862,1567],{"class":688},[149,1864,1179],{"class":155},[149,1866,1182],{"class":688},[149,1868,1869],{"class":155},"written)\n",[149,1871,1873],{"class":151,"line":1872},88,[149,1874,1580],{"class":155},[149,1876,1878,1880,1882,1884,1886,1888,1891,1894,1896],{"class":151,"line":1877},89,[149,1879,1586],{"class":688},[149,1881,1289],{"class":163},[149,1883,865],{"class":155},[149,1885,1593],{"class":163},[149,1887,880],{"class":155},[149,1889,1890],{"class":170},"\"Cannot write output [goal]: \"",[149,1892,1893],{"class":155},", written.",[149,1895,1604],{"class":163},[149,1897,1607],{"class":155},[149,1899,1901],{"class":151,"line":1900},90,[149,1902,1613],{"class":155},[149,1904,1906,1909,1912,1914,1917,1919,1921,1923],{"class":151,"line":1905},91,[149,1907,1908],{"class":163},"        std",[149,1910,1911],{"class":155},"::cout ",[149,1913,877],{"class":688},[149,1915,1916],{"class":170}," \"[CalculateGoal] distance=\"",[149,1918,931],{"class":688},[149,1920,1687],{"class":155},[149,1922,1679],{"class":163},[149,1924,1388],{"class":155},[149,1926,1928,1931,1934,1936,1939,1941,1944,1947,1950],{"class":151,"line":1927},92,[149,1929,1930],{"class":688},"                  \u003C\u003C",[149,1932,1933],{"class":170}," \", goal=\"",[149,1935,931],{"class":688},[149,1937,1938],{"class":155}," goal ",[149,1940,877],{"class":688},[149,1942,1943],{"class":170}," '",[149,1945,1946],{"class":1072},"\\n",[149,1948,1949],{"class":170},"'",[149,1951,961],{"class":155},[149,1953,1955,1957,1959,1961,1963],{"class":151,"line":1954},93,[149,1956,1399],{"class":688},[149,1958,1289],{"class":163},[149,1960,865],{"class":155},[149,1962,1498],{"class":163},[149,1964,1965],{"class":155},"::SUCCESS;\n",[149,1967,1969],{"class":151,"line":1968},94,[149,1970,1103],{"class":155},[149,1972,1974],{"class":151,"line":1973},95,[149,1975,844],{"class":155},[149,1977,1979],{"class":151,"line":1978},96,[149,1980,754],{"emptyLinePlaceholder":753},[149,1982,1984,1986,1989,1991,1993,1995,1997],{"class":151,"line":1983},97,[149,1985,1277],{"class":688},[149,1987,1988],{"class":163}," OffsetGoal",[149,1990,1283],{"class":155},[149,1992,1286],{"class":688},[149,1994,1289],{"class":163},[149,1996,865],{"class":155},[149,1998,1294],{"class":163},[149,2000,2002],{"class":151,"line":2001},98,[149,2003,813],{"class":155},[149,2005,2007],{"class":151,"line":2006},99,[149,2008,1305],{"class":688},[149,2010,2012,2015,2017,2019,2021,2023,2025,2027,2029,2031,2033,2035,2037,2039,2041,2043],{"class":151,"line":2011},100,[149,2013,2014],{"class":163},"    OffsetGoal",[149,2016,880],{"class":155},[149,2018,898],{"class":688},[149,2020,1318],{"class":163},[149,2022,865],{"class":155},[149,2024,1323],{"class":163},[149,2026,871],{"class":688},[149,2028,1328],{"class":891},[149,2030,895],{"class":155},[149,2032,898],{"class":688},[149,2034,1289],{"class":163},[149,2036,865],{"class":155},[149,2038,1339],{"class":163},[149,2040,871],{"class":688},[149,2042,1344],{"class":891},[149,2044,909],{"class":155},[149,2046,2048,2050,2052],{"class":151,"line":2047},101,[149,2049,1352],{"class":155},[149,2051,1355],{"class":163},[149,2053,1358],{"class":155},[149,2055,2057],{"class":151,"line":2056},102,[149,2058,1364],{"class":155},[149,2060,2062],{"class":151,"line":2061},103,[149,2063,754],{"emptyLinePlaceholder":753},[149,2065,2067,2069,2071,2073,2075,2077],{"class":151,"line":2066},104,[149,2068,1375],{"class":688},[149,2070,1289],{"class":163},[149,2072,865],{"class":155},[149,2074,1382],{"class":163},[149,2076,1385],{"class":163},[149,2078,1388],{"class":155},[149,2080,2082],{"class":151,"line":2081},105,[149,2083,1081],{"class":155},[149,2085,2087,2089],{"class":151,"line":2086},106,[149,2088,1399],{"class":688},[149,2090,1402],{"class":155},[149,2092,2094,2096,2098,2101,2103,2105,2107,2109],{"class":151,"line":2093},107,[149,2095,1408],{"class":163},[149,2097,865],{"class":155},[149,2099,2100],{"class":163},"BidirectionalPort",[149,2102,156],{"class":155},[149,2104,97],{"class":163},[149,2106,1420],{"class":155},[149,2108,1474],{"class":170},[149,2110,1426],{"class":155},[149,2112,2114,2116,2118,2120,2122,2124,2126,2129,2131,2134,2136,2139],{"class":151,"line":2113},108,[149,2115,1408],{"class":163},[149,2117,865],{"class":155},[149,2119,1413],{"class":163},[149,2121,156],{"class":155},[149,2123,444],{"class":688},[149,2125,1420],{"class":155},[149,2127,2128],{"class":170},"\"offset_x\"",[149,2130,895],{"class":155},[149,2132,2133],{"class":1072},"0.0",[149,2135,895],{"class":155},[149,2137,2138],{"class":170},"\"目标在参考坐标系 x 方向的偏移\"",[149,2140,1477],{"class":155},[149,2142,2144],{"class":151,"line":2143},109,[149,2145,1103],{"class":155},[149,2147,2149],{"class":151,"line":2148},110,[149,2150,754],{"emptyLinePlaceholder":753},[149,2152,2154,2156,2158,2160,2162,2164],{"class":151,"line":2153},111,[149,2155,1493],{"class":163},[149,2157,865],{"class":155},[149,2159,1498],{"class":163},[149,2161,1501],{"class":163},[149,2163,1066],{"class":155},[149,2165,1506],{"class":688},[149,2167,2169],{"class":151,"line":2168},112,[149,2170,1081],{"class":155},[149,2172,2174,2176,2178,2181,2183,2185,2187,2189,2191,2193],{"class":151,"line":2173},113,[149,2175,1517],{"class":688},[149,2177,1034],{"class":688},[149,2179,2180],{"class":155}," input ",[149,2182,167],{"class":688},[149,2184,1527],{"class":163},[149,2186,156],{"class":155},[149,2188,97],{"class":163},[149,2190,1420],{"class":155},[149,2192,1474],{"class":170},[149,2194,1051],{"class":155},[149,2196,2198,2200,2202,2205,2207,2209,2211,2213,2215,2217],{"class":151,"line":2197},114,[149,2199,1517],{"class":688},[149,2201,1034],{"class":688},[149,2203,2204],{"class":155}," offset ",[149,2206,167],{"class":688},[149,2208,1527],{"class":163},[149,2210,156],{"class":155},[149,2212,444],{"class":688},[149,2214,1420],{"class":155},[149,2216,2128],{"class":170},[149,2218,1051],{"class":155},[149,2220,2222,2224,2226,2228],{"class":151,"line":2221},115,[149,2223,1567],{"class":688},[149,2225,1179],{"class":155},[149,2227,1182],{"class":688},[149,2229,2230],{"class":155},"input)\n",[149,2232,2234],{"class":151,"line":2233},116,[149,2235,1580],{"class":155},[149,2237,2239,2241,2243,2245,2247,2249,2252,2255,2257],{"class":151,"line":2238},117,[149,2240,1586],{"class":688},[149,2242,1289],{"class":163},[149,2244,865],{"class":155},[149,2246,1593],{"class":163},[149,2248,880],{"class":155},[149,2250,2251],{"class":170},"\"Cannot read input [goal]: \"",[149,2253,2254],{"class":155},", input.",[149,2256,1604],{"class":163},[149,2258,1607],{"class":155},[149,2260,2262],{"class":151,"line":2261},118,[149,2263,1613],{"class":155},[149,2265,2267,2269,2271,2273],{"class":151,"line":2266},119,[149,2268,1567],{"class":688},[149,2270,1179],{"class":155},[149,2272,1182],{"class":688},[149,2274,2275],{"class":155},"offset)\n",[149,2277,2279],{"class":151,"line":2278},120,[149,2280,1580],{"class":155},[149,2282,2284,2286,2288,2290,2292,2294,2297,2300,2302],{"class":151,"line":2283},121,[149,2285,1586],{"class":688},[149,2287,1289],{"class":163},[149,2289,865],{"class":155},[149,2291,1593],{"class":163},[149,2293,880],{"class":155},[149,2295,2296],{"class":170},"\"Cannot read input [offset_x]: \"",[149,2298,2299],{"class":155},", offset.",[149,2301,1604],{"class":163},[149,2303,1607],{"class":155},[149,2305,2307],{"class":151,"line":2306},122,[149,2308,1613],{"class":155},[149,2310,2312,2314,2316,2318,2320,2322,2324,2327,2329],{"class":151,"line":2311},123,[149,2313,1567],{"class":688},[149,2315,1179],{"class":155},[149,2317,1182],{"class":688},[149,2319,862],{"class":163},[149,2321,865],{"class":155},[149,2323,1189],{"class":163},[149,2325,2326],{"class":155},"(offset.",[149,2328,1679],{"class":163},[149,2330,2331],{"class":155},"()))\n",[149,2333,2335],{"class":151,"line":2334},124,[149,2336,1580],{"class":155},[149,2338,2340,2342,2344,2346,2348,2350,2353],{"class":151,"line":2339},125,[149,2341,1586],{"class":688},[149,2343,1289],{"class":163},[149,2345,865],{"class":155},[149,2347,1593],{"class":163},[149,2349,880],{"class":155},[149,2351,2352],{"class":170},"\"Input [offset_x] must be finite\"",[149,2354,1051],{"class":155},[149,2356,2358],{"class":151,"line":2357},126,[149,2359,1613],{"class":155},[149,2361,2363],{"class":151,"line":2362},127,[149,2364,754],{"emptyLinePlaceholder":753},[149,2366,2368],{"class":151,"line":2367},128,[149,2369,2370],{"class":855},"        \u002F\u002F 先修改本地副本，再通过双向端口写回同一个条目\n",[149,2372,2374,2377,2379,2382,2384],{"class":151,"line":2373},129,[149,2375,2376],{"class":155},"        Pose2D goal ",[149,2378,167],{"class":688},[149,2380,2381],{"class":155}," input.",[149,2383,1679],{"class":163},[149,2385,1759],{"class":155},[149,2387,2389,2392,2395,2398,2400],{"class":151,"line":2388},130,[149,2390,2391],{"class":155},"        goal.x ",[149,2393,2394],{"class":688},"+=",[149,2396,2397],{"class":155}," offset.",[149,2399,1679],{"class":163},[149,2401,1759],{"class":155},[149,2403,2405,2407,2409,2411,2413,2415,2417,2419,2421,2423],{"class":151,"line":2404},131,[149,2406,1517],{"class":688},[149,2408,1034],{"class":688},[149,2410,1841],{"class":155},[149,2412,167],{"class":688},[149,2414,1846],{"class":163},[149,2416,156],{"class":155},[149,2418,97],{"class":163},[149,2420,1420],{"class":155},[149,2422,1474],{"class":170},[149,2424,1857],{"class":155},[149,2426,2428,2430,2432,2434],{"class":151,"line":2427},132,[149,2429,1567],{"class":688},[149,2431,1179],{"class":155},[149,2433,1182],{"class":688},[149,2435,1869],{"class":155},[149,2437,2439],{"class":151,"line":2438},133,[149,2440,1580],{"class":155},[149,2442,2444,2446,2448,2450,2452,2454,2456,2458,2460],{"class":151,"line":2443},134,[149,2445,1586],{"class":688},[149,2447,1289],{"class":163},[149,2449,865],{"class":155},[149,2451,1593],{"class":163},[149,2453,880],{"class":155},[149,2455,1890],{"class":170},[149,2457,1893],{"class":155},[149,2459,1604],{"class":163},[149,2461,1607],{"class":155},[149,2463,2465],{"class":151,"line":2464},135,[149,2466,1613],{"class":155},[149,2468,2470,2472,2474,2476,2479,2481,2483,2485,2487,2489,2491],{"class":151,"line":2469},136,[149,2471,1908],{"class":163},[149,2473,1911],{"class":155},[149,2475,877],{"class":688},[149,2477,2478],{"class":170}," \"[OffsetGoal] goal=\"",[149,2480,931],{"class":688},[149,2482,1938],{"class":155},[149,2484,877],{"class":688},[149,2486,1943],{"class":170},[149,2488,1946],{"class":1072},[149,2490,1949],{"class":170},[149,2492,961],{"class":155},[149,2494,2496,2498,2500,2502,2504],{"class":151,"line":2495},137,[149,2497,1399],{"class":688},[149,2499,1289],{"class":163},[149,2501,865],{"class":155},[149,2503,1498],{"class":163},[149,2505,1965],{"class":155},[149,2507,2509],{"class":151,"line":2508},138,[149,2510,1103],{"class":155},[149,2512,2514],{"class":151,"line":2513},139,[149,2515,844],{"class":155},[149,2517,2519],{"class":151,"line":2518},140,[149,2520,754],{"emptyLinePlaceholder":753},[149,2522,2524,2526,2529,2531,2533,2535,2537],{"class":151,"line":2523},141,[149,2525,1277],{"class":688},[149,2527,2528],{"class":163}," PrintGoal",[149,2530,1283],{"class":155},[149,2532,1286],{"class":688},[149,2534,1289],{"class":163},[149,2536,865],{"class":155},[149,2538,1294],{"class":163},[149,2540,2542],{"class":151,"line":2541},142,[149,2543,813],{"class":155},[149,2545,2547],{"class":151,"line":2546},143,[149,2548,1305],{"class":688},[149,2550,2552,2555,2557,2559,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579,2581,2583],{"class":151,"line":2551},144,[149,2553,2554],{"class":163},"    PrintGoal",[149,2556,880],{"class":155},[149,2558,898],{"class":688},[149,2560,1318],{"class":163},[149,2562,865],{"class":155},[149,2564,1323],{"class":163},[149,2566,871],{"class":688},[149,2568,1328],{"class":891},[149,2570,895],{"class":155},[149,2572,898],{"class":688},[149,2574,1289],{"class":163},[149,2576,865],{"class":155},[149,2578,1339],{"class":163},[149,2580,871],{"class":688},[149,2582,1344],{"class":891},[149,2584,909],{"class":155},[149,2586,2588,2590,2592],{"class":151,"line":2587},145,[149,2589,1352],{"class":155},[149,2591,1355],{"class":163},[149,2593,1358],{"class":155},[149,2595,2597],{"class":151,"line":2596},146,[149,2598,1364],{"class":155},[149,2600,2602],{"class":151,"line":2601},147,[149,2603,754],{"emptyLinePlaceholder":753},[149,2605,2607,2609,2611,2613,2615,2617],{"class":151,"line":2606},148,[149,2608,1375],{"class":688},[149,2610,1289],{"class":163},[149,2612,865],{"class":155},[149,2614,1382],{"class":163},[149,2616,1385],{"class":163},[149,2618,1388],{"class":155},[149,2620,2622],{"class":151,"line":2621},149,[149,2623,1081],{"class":155},[149,2625,2627,2629,2632,2634,2636,2638,2640,2642,2644,2647],{"class":151,"line":2626},150,[149,2628,1399],{"class":688},[149,2630,2631],{"class":155}," {",[149,2633,541],{"class":163},[149,2635,865],{"class":155},[149,2637,1413],{"class":163},[149,2639,156],{"class":155},[149,2641,97],{"class":163},[149,2643,1420],{"class":155},[149,2645,2646],{"class":170},"\"target\"",[149,2648,1477],{"class":155},[149,2650,2652],{"class":151,"line":2651},151,[149,2653,1103],{"class":155},[149,2655,2657],{"class":151,"line":2656},152,[149,2658,754],{"emptyLinePlaceholder":753},[149,2660,2662,2664,2666,2668,2670,2672],{"class":151,"line":2661},153,[149,2663,1493],{"class":163},[149,2665,865],{"class":155},[149,2667,1498],{"class":163},[149,2669,1501],{"class":163},[149,2671,1066],{"class":155},[149,2673,1506],{"class":688},[149,2675,2677],{"class":151,"line":2676},154,[149,2678,1081],{"class":155},[149,2680,2682,2684,2686,2689,2691,2693,2695,2697,2699,2701],{"class":151,"line":2681},155,[149,2683,1517],{"class":688},[149,2685,1034],{"class":688},[149,2687,2688],{"class":155}," target ",[149,2690,167],{"class":688},[149,2692,1527],{"class":163},[149,2694,156],{"class":155},[149,2696,97],{"class":163},[149,2698,1420],{"class":155},[149,2700,2646],{"class":170},[149,2702,1051],{"class":155},[149,2704,2706,2708,2710,2712],{"class":151,"line":2705},156,[149,2707,1567],{"class":688},[149,2709,1179],{"class":155},[149,2711,1182],{"class":688},[149,2713,2714],{"class":155},"target)\n",[149,2716,2718],{"class":151,"line":2717},157,[149,2719,1580],{"class":155},[149,2721,2723,2725,2727,2729,2731,2733,2736,2739,2741],{"class":151,"line":2722},158,[149,2724,1586],{"class":688},[149,2726,1289],{"class":163},[149,2728,865],{"class":155},[149,2730,1593],{"class":163},[149,2732,880],{"class":155},[149,2734,2735],{"class":170},"\"Cannot read input [target]: \"",[149,2737,2738],{"class":155},", target.",[149,2740,1604],{"class":163},[149,2742,1607],{"class":155},[149,2744,2746],{"class":151,"line":2745},159,[149,2747,1613],{"class":155},[149,2749,2751,2753,2755,2757,2760,2762,2764,2766,2768,2771,2773,2776,2778,2780,2782,2784,2786,2788],{"class":151,"line":2750},160,[149,2752,1908],{"class":163},[149,2754,1911],{"class":155},[149,2756,877],{"class":688},[149,2758,2759],{"class":170}," \"[PrintGoal:\"",[149,2761,931],{"class":688},[149,2763,1328],{"class":163},[149,2765,1066],{"class":155},[149,2767,877],{"class":688},[149,2769,2770],{"class":170}," \"] target=\"",[149,2772,931],{"class":688},[149,2774,2775],{"class":155}," target.",[149,2777,1679],{"class":163},[149,2779,1066],{"class":155},[149,2781,877],{"class":688},[149,2783,1943],{"class":170},[149,2785,1946],{"class":1072},[149,2787,1949],{"class":170},[149,2789,961],{"class":155},[149,2791,2793,2795,2797,2799,2801],{"class":151,"line":2792},161,[149,2794,1399],{"class":688},[149,2796,1289],{"class":163},[149,2798,865],{"class":155},[149,2800,1498],{"class":163},[149,2802,1965],{"class":155},[149,2804,2806],{"class":151,"line":2805},162,[149,2807,1103],{"class":155},[149,2809,2811],{"class":151,"line":2810},163,[149,2812,844],{"class":155},[149,2814,2816],{"class":151,"line":2815},164,[149,2817,754],{"emptyLinePlaceholder":753},[149,2819,2821,2824,2827],{"class":151,"line":2820},165,[149,2822,2823],{"class":688},"int",[149,2825,2826],{"class":163}," main",[149,2828,1388],{"class":155},[149,2830,2832],{"class":151,"line":2831},166,[149,2833,813],{"class":155},[149,2835,2837],{"class":151,"line":2836},167,[149,2838,2839],{"class":688},"    try\n",[149,2841,2843],{"class":151,"line":2842},168,[149,2844,1081],{"class":155},[149,2846,2848],{"class":151,"line":2847},169,[149,2849,2850],{"class":855},"        \u002F\u002F 在编辑器中修改这个模拟距离，再构建运行；XML 路径由构建配置提供\n",[149,2852,2854,2856,2859,2861,2863,2866],{"class":151,"line":2853},170,[149,2855,1517],{"class":688},[149,2857,2858],{"class":688}," double",[149,2860,1547],{"class":155},[149,2862,167],{"class":688},[149,2864,2865],{"class":1072}," 2.0",[149,2867,961],{"class":155},[149,2869,2871,2873,2875,2877,2879,2882,2884,2886,2888,2891,2893,2895],{"class":151,"line":2870},171,[149,2872,1908],{"class":163},[149,2874,1911],{"class":155},[149,2876,877],{"class":688},[149,2878,1318],{"class":163},[149,2880,2881],{"class":155},"::fixed ",[149,2883,877],{"class":688},[149,2885,1318],{"class":163},[149,2887,865],{"class":155},[149,2889,2890],{"class":163},"setprecision",[149,2892,880],{"class":155},[149,2894,1163],{"class":1072},[149,2896,1051],{"class":155},[149,2898,2900],{"class":151,"line":2899},172,[149,2901,754],{"emptyLinePlaceholder":753},[149,2903,2905],{"class":151,"line":2904},173,[149,2906,2907],{"class":855},"        \u002F\u002F 外部程序在创建树之前，准备带有明确 C++ 类型的输入\n",[149,2909,2911,2914,2917,2919,2921,2923,2926,2928,2931],{"class":151,"line":2910},174,[149,2912,2913],{"class":688},"        auto",[149,2915,2916],{"class":155}," blackboard ",[149,2918,167],{"class":688},[149,2920,1289],{"class":163},[149,2922,865],{"class":155},[149,2924,2925],{"class":163},"Blackboard",[149,2927,865],{"class":155},[149,2929,2930],{"class":163},"create",[149,2932,1759],{"class":155},[149,2934,2936,2939,2941,2943,2946,2948,2951,2954,2956,2958,2961,2963,2965],{"class":151,"line":2935},175,[149,2937,2938],{"class":155},"        blackboard->set",[149,2940,156],{"class":688},[149,2942,97],{"class":155},[149,2944,2945],{"class":688},">",[149,2947,880],{"class":155},[149,2949,2950],{"class":170},"\"robot_pose\"",[149,2952,2953],{"class":155},", Pose2D{",[149,2955,496],{"class":1072},[149,2957,895],{"class":155},[149,2959,2960],{"class":1072},"2.0",[149,2962,895],{"class":155},[149,2964,2133],{"class":1072},[149,2966,2967],{"class":155},"});\n",[149,2969,2971,2973,2976,2978,2981],{"class":151,"line":2970},176,[149,2972,2938],{"class":155},[149,2974,2975],{"class":688},"\u003Cdouble>",[149,2977,880],{"class":155},[149,2979,2980],{"class":170},"\"travel_distance\"",[149,2982,2983],{"class":155},", distance);\n",[149,2985,2987],{"class":151,"line":2986},177,[149,2988,754],{"emptyLinePlaceholder":753},[149,2990,2992,2995],{"class":151,"line":2991},178,[149,2993,2994],{"class":163},"        BT",[149,2996,2997],{"class":155},"::BehaviorTreeFactory factory;\n",[149,2999,3001,3004,3006,3008,3010,3012,3015],{"class":151,"line":3000},179,[149,3002,3003],{"class":155},"        factory.registerNodeType",[149,3005,156],{"class":688},[149,3007,160],{"class":155},[149,3009,2945],{"class":688},[149,3011,880],{"class":155},[149,3013,3014],{"class":170},"\"CalculateGoal\"",[149,3016,1051],{"class":155},[149,3018,3020,3022,3024,3026,3028,3030,3033],{"class":151,"line":3019},180,[149,3021,3003],{"class":155},[149,3023,156],{"class":688},[149,3025,198],{"class":155},[149,3027,2945],{"class":688},[149,3029,880],{"class":155},[149,3031,3032],{"class":170},"\"OffsetGoal\"",[149,3034,1051],{"class":155},[149,3036,3038,3040,3042,3044,3046,3048,3051],{"class":151,"line":3037},181,[149,3039,3003],{"class":155},[149,3041,156],{"class":688},[149,3043,222],{"class":155},[149,3045,2945],{"class":688},[149,3047,880],{"class":155},[149,3049,3050],{"class":170},"\"PrintGoal\"",[149,3052,1051],{"class":155},[149,3054,3056,3058,3061,3063,3066,3069],{"class":151,"line":3055},182,[149,3057,2913],{"class":688},[149,3059,3060],{"class":155}," tree ",[149,3062,167],{"class":688},[149,3064,3065],{"class":155}," factory.",[149,3067,3068],{"class":163},"createTreeFromFile",[149,3070,3071],{"class":155},"(BT_XML_PATH, blackboard);\n",[149,3073,3075],{"class":151,"line":3074},183,[149,3076,754],{"emptyLinePlaceholder":753},[149,3078,3080,3082,3084,3087,3089,3092,3095],{"class":151,"line":3079},184,[149,3081,1517],{"class":688},[149,3083,1289],{"class":163},[149,3085,3086],{"class":155},"::NodeStatus result ",[149,3088,167],{"class":688},[149,3090,3091],{"class":155}," tree.",[149,3093,3094],{"class":163},"tickRootWhileRunning",[149,3096,1759],{"class":155},[149,3098,3100,3102,3104,3106,3109,3111,3113,3115,3118,3121,3123,3125,3127,3129],{"class":151,"line":3099},185,[149,3101,1908],{"class":163},[149,3103,1911],{"class":155},[149,3105,877],{"class":688},[149,3107,3108],{"class":170}," \"Tree result: \"",[149,3110,931],{"class":688},[149,3112,1289],{"class":163},[149,3114,865],{"class":155},[149,3116,3117],{"class":163},"toStr",[149,3119,3120],{"class":155},"(result) ",[149,3122,877],{"class":688},[149,3124,1943],{"class":170},[149,3126,1946],{"class":1072},[149,3128,1949],{"class":170},[149,3130,961],{"class":155},[149,3132,3134,3136,3139,3142,3144,3146,3148],{"class":151,"line":3133},186,[149,3135,1567],{"class":688},[149,3137,3138],{"class":155}," (result ",[149,3140,3141],{"class":688},"==",[149,3143,1289],{"class":163},[149,3145,865],{"class":155},[149,3147,1498],{"class":163},[149,3149,3150],{"class":155},"::SUCCESS)\n",[149,3152,3154],{"class":151,"line":3153},187,[149,3155,1580],{"class":155},[149,3157,3159],{"class":151,"line":3158},188,[149,3160,3161],{"class":855},"            \u002F\u002F 树执行后，外部程序可以从同一块 Blackboard 取得输出\n",[149,3163,3165,3168,3171,3173,3176,3178,3180,3182,3184,3187],{"class":151,"line":3164},189,[149,3166,3167],{"class":688},"            const",[149,3169,3170],{"class":155}," Pose2D goal ",[149,3172,167],{"class":688},[149,3174,3175],{"class":155}," blackboard->get",[149,3177,156],{"class":688},[149,3179,97],{"class":155},[149,3181,2945],{"class":688},[149,3183,880],{"class":155},[149,3185,3186],{"class":170},"\"mission_goal\"",[149,3188,1051],{"class":155},[149,3190,3192,3195,3197,3199,3202,3204,3206,3208,3210,3212,3214],{"class":151,"line":3191},190,[149,3193,3194],{"class":163},"            std",[149,3196,1911],{"class":155},[149,3198,877],{"class":688},[149,3200,3201],{"class":170}," \"[main] mission_goal=\"",[149,3203,931],{"class":688},[149,3205,1938],{"class":155},[149,3207,877],{"class":688},[149,3209,1943],{"class":170},[149,3211,1946],{"class":1072},[149,3213,1949],{"class":170},[149,3215,961],{"class":155},[149,3217,3219],{"class":151,"line":3218},191,[149,3220,1613],{"class":155},[149,3222,3224,3226,3229,3231,3233,3235,3237,3240,3243,3246,3249,3252],{"class":151,"line":3223},192,[149,3225,1399],{"class":688},[149,3227,3228],{"class":155}," result ",[149,3230,3141],{"class":688},[149,3232,1289],{"class":163},[149,3234,865],{"class":155},[149,3236,1498],{"class":163},[149,3238,3239],{"class":155},"::SUCCESS ",[149,3241,3242],{"class":688},"?",[149,3244,3245],{"class":1072}," 0",[149,3247,3248],{"class":688}," :",[149,3250,3251],{"class":1072}," 1",[149,3253,961],{"class":155},[149,3255,3257],{"class":151,"line":3256},193,[149,3258,1103],{"class":155},[149,3260,3262,3265,3267,3269,3271,3274,3276],{"class":151,"line":3261},194,[149,3263,3264],{"class":688},"    catch",[149,3266,1179],{"class":155},[149,3268,898],{"class":688},[149,3270,1318],{"class":163},[149,3272,3273],{"class":155},"::exception",[149,3275,871],{"class":688},[149,3277,3278],{"class":155}," error)\n",[149,3280,3282],{"class":151,"line":3281},195,[149,3283,1081],{"class":155},[149,3285,3287,3289,3292,3294,3297,3299,3302,3305,3307,3309,3311,3313,3315],{"class":151,"line":3286},196,[149,3288,1908],{"class":163},[149,3290,3291],{"class":155},"::cerr ",[149,3293,877],{"class":688},[149,3295,3296],{"class":170}," \"Error: \"",[149,3298,931],{"class":688},[149,3300,3301],{"class":155}," error.",[149,3303,3304],{"class":163},"what",[149,3306,1066],{"class":155},[149,3308,877],{"class":688},[149,3310,1943],{"class":170},[149,3312,1946],{"class":1072},[149,3314,1949],{"class":170},[149,3316,961],{"class":155},[149,3318,3320,3322,3325],{"class":151,"line":3319},197,[149,3321,1399],{"class":688},[149,3323,3324],{"class":1072}," 2",[149,3326,961],{"class":155},[149,3328,3330],{"class":151,"line":3329},198,[149,3331,1103],{"class":155},[149,3333,3335],{"class":151,"line":3334},199,[149,3336,967],{"class":155},[20,3338,3339,3341,3342,3345,3346,3348,3349,3352],{},[78,3340,379],{}," 返回的是带有成功或错误信息的结果；检查有效后，再用 ",[78,3343,3344],{},".value()"," 取得值。",[78,3347,394],{}," 同样有返回结果，本例也进行检查；类型冲突等情况还可能抛出异常，由 ",[78,3350,3351],{},"main()"," 统一显示。",[20,3354,3355,3356,3358,3359,3362],{},"本例把缺少必要输入、文本格式错误等作为配置或数据错误报告，退出码为 ",[78,3357,1163],{},"。实际任务中可以根据节点职责选择返回 ",[78,3360,3361],{},"FAILURE"," 或使用默认值，不必把所有情况都处理成异常。",[348,3364,3366],{"id":3365},"创建写入和读取-blackboard-的-api","创建、写入和读取 Blackboard 的 API",[20,3368,3369,3370,30,3372,3374,3375,3377],{},"上一篇介绍了节点内部的 ",[78,3371,424],{},[78,3373,394],{},"。\n本篇在 ",[78,3376,3351],{}," 中直接访问 Blackboard，使用的是另一组接口。\n下面列出本例用到的函数原型；声明用于阅读，不用再复制到工程中。",[83,3379,3381],{"className":736,"code":3380,"language":738,"meta":89,"style":89},"\u002F\u002F Blackboard 类中的成员声明\nstatic BT::Blackboard::Ptr create(BT::Blackboard::Ptr parent = {});\n\ntemplate\u003Ctypename T>\nvoid set(const std::string& key, const T& value);\n\ntemplate\u003Ctypename T>\nT get(const std::string& key) const;\n",[78,3382,3383,3388,3428,3432,3447,3483,3487,3499],{"__ignoreMap":89},[149,3384,3385],{"class":151,"line":152},[149,3386,3387],{"class":855},"\u002F\u002F Blackboard 类中的成员声明\n",[149,3389,3390,3393,3395,3397,3399,3401,3404,3407,3409,3411,3413,3415,3417,3419,3422,3425],{"class":151,"line":193},[149,3391,3392],{"class":688},"static",[149,3394,1289],{"class":163},[149,3396,865],{"class":155},[149,3398,2925],{"class":163},[149,3400,865],{"class":155},[149,3402,3403],{"class":163},"Ptr",[149,3405,3406],{"class":163}," create",[149,3408,880],{"class":155},[149,3410,541],{"class":163},[149,3412,865],{"class":155},[149,3414,2925],{"class":163},[149,3416,865],{"class":155},[149,3418,3403],{"class":163},[149,3420,3421],{"class":891}," parent",[149,3423,3424],{"class":688}," =",[149,3426,3427],{"class":155}," {});\n",[149,3429,3430],{"class":151,"line":217},[149,3431,754],{"emptyLinePlaceholder":753},[149,3433,3434,3436,3438,3441,3444],{"class":151,"line":764},[149,3435,992],{"class":688},[149,3437,156],{"class":155},[149,3439,3440],{"class":688},"typename",[149,3442,3443],{"class":163}," T",[149,3445,3446],{"class":155},">\n",[149,3448,3449,3452,3455,3457,3459,3461,3463,3465,3467,3470,3472,3474,3476,3478,3481],{"class":151,"line":772},[149,3450,3451],{"class":688},"void",[149,3453,3454],{"class":163}," set",[149,3456,880],{"class":155},[149,3458,898],{"class":688},[149,3460,1318],{"class":163},[149,3462,865],{"class":155},[149,3464,1323],{"class":163},[149,3466,871],{"class":688},[149,3468,3469],{"class":891}," key",[149,3471,895],{"class":155},[149,3473,898],{"class":688},[149,3475,3443],{"class":163},[149,3477,871],{"class":688},[149,3479,3480],{"class":891}," value",[149,3482,1051],{"class":155},[149,3484,3485],{"class":151,"line":780},[149,3486,754],{"emptyLinePlaceholder":753},[149,3488,3489,3491,3493,3495,3497],{"class":151,"line":788},[149,3490,992],{"class":688},[149,3492,156],{"class":155},[149,3494,3440],{"class":688},[149,3496,3443],{"class":163},[149,3498,3446],{"class":155},[149,3500,3501,3504,3507,3509,3511,3513,3515,3517,3519,3521,3524,3526],{"class":151,"line":796},[149,3502,3503],{"class":163},"T",[149,3505,3506],{"class":163}," get",[149,3508,880],{"class":155},[149,3510,898],{"class":688},[149,3512,1318],{"class":163},[149,3514,865],{"class":155},[149,3516,1323],{"class":163},[149,3518,871],{"class":688},[149,3520,3469],{"class":891},[149,3522,3523],{"class":155},") ",[149,3525,898],{"class":688},[149,3527,961],{"class":155},[236,3529,3530,3543],{},[239,3531,3532],{},[242,3533,3534,3537,3540],{},[245,3535,3536],{},"调用",[245,3538,3539],{},"参数",[245,3541,3542],{},"返回值与作用",[255,3544,3545,3570,3588],{},[242,3546,3547,3552,3559],{},[260,3548,3549],{},[78,3550,3551],{},"BT::Blackboard::create()",[260,3553,3554,3555,3558],{},"本例不传参数；可选参数 ",[78,3556,3557],{},"parent"," 指定父 Blackboard，默认空指针",[260,3560,3561,3562,3565,3566,3569],{},"返回 ",[78,3563,3564],{},"BT::Blackboard::Ptr","，也就是 ",[78,3567,3568],{},"std::shared_ptr\u003CBT::Blackboard>","，用于持有这块 Blackboard",[242,3571,3572,3577,3583],{},[260,3573,3574],{},[78,3575,3576],{},"blackboard->set\u003CPose2D>(\"robot_pose\", Pose2D{1.0, 2.0, 0.0})",[260,3578,3579,3580,3582],{},"模板参数 ",[78,3581,97],{}," 指定值类型；第一个参数是键名，第二个参数是要保存的值",[260,3584,3561,3585,3587],{},[78,3586,3451],{},"，创建或更新条目；类型不兼容等错误可能抛出异常",[242,3589,3590,3595,3598],{},[260,3591,3592],{},[78,3593,3594],{},"blackboard->get\u003CPose2D>(\"mission_goal\")",[260,3596,3597],{},"模板参数指定期望读取的类型；唯一的函数参数是键名",[260,3599,3600,3601,3603],{},"返回一个 ",[78,3602,97],{}," 值；本例使用的这个重载在键不存在或类型转换失败时抛出异常",[20,3605,94,3606,3609,3610,3612,3613,42,3616,3619,3620,3623],{},[78,3607,3608],{},"key"," 是 ",[62,3611,312],{},"。\n节点内部的 ",[78,3614,3615],{},"getInput(\"target\")",[78,3617,3618],{},"setOutput(\"goal\", value)"," 接收的则是 ",[62,3621,3622],{},"节点端口名","，由 XML 决定它们连接哪个键。",[20,3625,3626,3629,3630,3632,3633,3636,3637,3639,3640,54],{},[78,3627,3628],{},"factory.createTreeFromFile(BT_XML_PATH, blackboard)"," 的第一个参数仍是 XML 路径，第二个参数把刚准备好的 Blackboard 交给根树。\n这里传递的是共享指针，因此节点执行时读写的就是 ",[78,3631,3351],{}," 中持有的同一块数据。\n",[78,3634,3635],{},"get\u003CPose2D>()"," 返回值的副本，修改取得的 ",[78,3638,267],{}," 变量不会自动更新 Blackboard；\n需要更新时仍应调用 ",[78,3641,3642],{},"set()",[348,3644,3645],{"id":3645},"带默认值的输入端口与双向端口",[20,3647,3648,3649,3652],{},"本篇第一次使用 ",[78,3650,3651],{},"InputPort\u003CT>()"," 的三参数重载：",[83,3654,3656],{"className":736,"code":3655,"language":738,"meta":89,"style":89},"template\u003Ctypename T = void>\nstd::pair\u003Cstd::string, BT::PortInfo> InputPort(\n    BT::StringView name,\n    const T& default_value,\n    BT::StringView description);\n",[78,3657,3658,3673,3707,3721,3734],{"__ignoreMap":89},[149,3659,3660,3662,3664,3666,3668,3670],{"class":151,"line":152},[149,3661,992],{"class":688},[149,3663,156],{"class":155},[149,3665,3440],{"class":688},[149,3667,3443],{"class":163},[149,3669,3424],{"class":688},[149,3671,3672],{"class":155}," void>\n",[149,3674,3675,3677,3679,3682,3684,3686,3688,3690,3692,3694,3696,3699,3702,3704],{"class":151,"line":193},[149,3676,862],{"class":163},[149,3678,865],{"class":155},[149,3680,3681],{"class":163},"pair",[149,3683,156],{"class":155},[149,3685,862],{"class":163},[149,3687,865],{"class":155},[149,3689,1323],{"class":163},[149,3691,895],{"class":155},[149,3693,541],{"class":163},[149,3695,865],{"class":155},[149,3697,3698],{"class":163},"PortInfo",[149,3700,3701],{"class":155},"> ",[149,3703,1413],{"class":163},[149,3705,3706],{"class":155},"(\n",[149,3708,3709,3711,3713,3716,3718],{"class":151,"line":217},[149,3710,1493],{"class":163},[149,3712,865],{"class":155},[149,3714,3715],{"class":163},"StringView",[149,3717,1328],{"class":891},[149,3719,3720],{"class":155},",\n",[149,3722,3723,3725,3727,3729,3732],{"class":151,"line":764},[149,3724,1031],{"class":688},[149,3726,3443],{"class":163},[149,3728,871],{"class":688},[149,3730,3731],{"class":891}," default_value",[149,3733,3720],{"class":155},[149,3735,3736,3738,3740,3742,3745],{"class":151,"line":772},[149,3737,1493],{"class":163},[149,3739,865],{"class":155},[149,3741,3715],{"class":163},[149,3743,3744],{"class":891}," description",[149,3746,1051],{"class":155},[20,3748,3749,3750,3752],{},"以 ",[78,3751,506],{}," 为例：",[236,3754,3755,3765],{},[239,3756,3757],{},[242,3758,3759,3762],{},[245,3760,3761],{},"项目",[245,3763,3764],{},"含义",[255,3766,3767,3776,3788,3798,3806],{},[242,3768,3769,3773],{},[260,3770,3579,3771],{},[78,3772,444],{},[260,3774,3775],{},"输入值的 C++ 类型",[242,3777,3778,3783],{},[260,3779,3780,3781],{},"第一个参数 ",[78,3782,1444],{},[260,3784,3785,3786,490],{},"节点自己的端口名称，对应 XML 中的 ",[78,3787,489],{},[242,3789,3790,3795],{},[260,3791,3792,3793],{},"第二个参数 ",[78,3794,496],{},[260,3796,3797],{},"没有配置该输入时使用的默认值",[242,3799,3800,3803],{},[260,3801,3802],{},"第三个参数",[260,3804,3805],{},"端口的文字说明，用于描述接口，不参与距离计算",[242,3807,3808,3811],{},[260,3809,3810],{},"返回值",[260,3812,3813,3814,3816],{},"一项端口声明，由 ",[78,3815,502],{}," 收集后交给工厂",[20,3818,3819,3820,3823,3824,3827],{},"在 3.8.7 中，这个默认值重载需要三个参数；没有说明文字时可以把第三个参数写成 ",[78,3821,3822],{},"\"\"","。前面见过的两参数形式 ",[78,3825,3826],{},"InputPort\u003CT>(name, description)"," 中，第二个参数是说明文字，不能把两种重载混为一谈。",[20,3829,3830,3833,3834,3836,3837,3839,3840,3843,3844,3847],{},[78,3831,3832],{},"BT::BidirectionalPort\u003CPose2D>(\"goal\")"," 声明双向端口：模板参数指定 ",[78,3835,97],{}," 类型，第一个参数指定端口名；可选的第二个参数也是说明文字。它同样返回一项端口声明。",[78,3838,198],{}," 随后用 ",[78,3841,3842],{},"getInput\u003CPose2D>(\"goal\")"," 取得原值，再用 ",[78,3845,3846],{},"setOutput(\"goal\", goal)"," 写回修改后的值；声明双向端口本身不会执行读写。",[348,3849,3851],{"id":3850},"拆分文本与转换自定义类型的-api","拆分文本与转换自定义类型的 API",[20,3853,3854,3855,3857],{},"下面两个接口来自 ",[78,3856,541],{}," 命名空间：",[83,3859,3861],{"className":736,"code":3860,"language":738,"meta":89,"style":89},"std::vector\u003CBT::StringView> splitString(\n    const BT::StringView& strToSplit, char delimeter);\n\ntemplate\u003Ctypename T>\nT convertFromString(BT::StringView str);\n",[78,3862,3863,3887,3912,3916,3928],{"__ignoreMap":89},[149,3864,3865,3867,3869,3872,3874,3876,3878,3880,3882,3885],{"class":151,"line":152},[149,3866,862],{"class":163},[149,3868,865],{"class":155},[149,3870,3871],{"class":163},"vector",[149,3873,156],{"class":155},[149,3875,541],{"class":163},[149,3877,865],{"class":155},[149,3879,3715],{"class":163},[149,3881,3701],{"class":155},[149,3883,3884],{"class":163},"splitString",[149,3886,3706],{"class":155},[149,3888,3889,3891,3893,3895,3897,3899,3902,3904,3907,3910],{"class":151,"line":193},[149,3890,1031],{"class":688},[149,3892,1289],{"class":163},[149,3894,865],{"class":155},[149,3896,3715],{"class":163},[149,3898,871],{"class":688},[149,3900,3901],{"class":891}," strToSplit",[149,3903,895],{"class":155},[149,3905,3906],{"class":688},"char",[149,3908,3909],{"class":891}," delimeter",[149,3911,1051],{"class":155},[149,3913,3914],{"class":151,"line":217},[149,3915,754],{"emptyLinePlaceholder":753},[149,3917,3918,3920,3922,3924,3926],{"class":151,"line":764},[149,3919,992],{"class":688},[149,3921,156],{"class":155},[149,3923,3440],{"class":688},[149,3925,3443],{"class":163},[149,3927,3446],{"class":155},[149,3929,3930,3932,3935,3937,3939,3941,3943,3946],{"class":151,"line":772},[149,3931,3503],{"class":163},[149,3933,3934],{"class":163}," convertFromString",[149,3936,880],{"class":155},[149,3938,541],{"class":163},[149,3940,865],{"class":155},[149,3942,3715],{"class":163},[149,3944,3945],{"class":891}," str",[149,3947,1051],{"class":155},[236,3949,3950,3960],{},[239,3951,3952],{},[242,3953,3954,3956,3958],{},[245,3955,3536],{},[245,3957,3539],{},[245,3959,3810],{},[255,3961,3962,3980,3998],{},[242,3963,3964,3969,3974],{},[260,3965,3966],{},[78,3967,3968],{},"splitString(text, ';')",[260,3970,3971,3972],{},"第一个参数是待拆分的文本；第二个参数是分隔字符 ",[78,3973,1048],{},[260,3975,3976,3979],{},[78,3977,3978],{},"std::vector\u003CBT::StringView>","，本例应得到 x、y、yaw 三个文本片段",[242,3981,3982,3987,3992],{},[260,3983,3984],{},[78,3985,3986],{},"convertFromString\u003Cdouble>(parts[0])",[260,3988,3579,3989,3991],{},[78,3990,444],{}," 指定目标类型；函数参数是一个数值文本片段",[260,3993,3994,3995,3997],{},"转换后的 ",[78,3996,444],{},"；无效文本会报告转换错误",[242,3999,4000,4005,4010],{},[260,4001,4002],{},[78,4003,4004],{},"convertFromString\u003CPose2D>(text)",[260,4006,3579,4007,4009],{},[78,4008,97],{}," 指定自定义类型；函数参数是完整的位姿文本",[260,4011,4012,4013,4015],{},"本例特化产生的 ",[78,4014,97],{},"；字段数量错误、数值无效等情况抛出异常",[20,4017,4018,4021],{},[78,4019,4020],{},"BT::StringView"," 是不拥有字符数据的字符串视图，拆分得到的片段仍引用原始文本。本例在转换函数内立即使用它们，不把这些视图保存为节点成员。",[20,4023,4024,4027,4028,4030,4031,4034,4035,4037],{},[78,4025,4026],{},"template \u003C>"," 表示为 ",[78,4029,97],{}," 提供这个函数模板的特化。节点读取 XML 固定文本时，",[78,4032,4033],{},"getInput\u003CPose2D>()"," 会通过库的转换机制调用它，主程序不需要再手动转换一次；从 Blackboard 读取已经保存的 ",[78,4036,97],{}," 时则直接取得对应类型的值。",[348,4039,4040],{"id":4040},"连续驱动行为树与辅助函数",[20,4042,4043,4046],{},[78,4044,4045],{},"tree.tickRootWhileRunning()"," 把重复 tick 的循环放在库内部，其原型为：",[83,4048,4050],{"className":736,"code":4049,"language":738,"meta":89,"style":89},"BT::NodeStatus tickRootWhileRunning(\n    std::chrono::milliseconds sleep_time = std::chrono::milliseconds(10));\n",[78,4051,4052,4065],{"__ignoreMap":89},[149,4053,4054,4056,4058,4060,4063],{"class":151,"line":152},[149,4055,541],{"class":163},[149,4057,865],{"class":155},[149,4059,1498],{"class":163},[149,4061,4062],{"class":163}," tickRootWhileRunning",[149,4064,3706],{"class":155},[149,4066,4067,4070,4072,4075,4077,4080,4083,4085,4087,4089,4091,4093,4095,4097,4100],{"class":151,"line":193},[149,4068,4069],{"class":163},"    std",[149,4071,865],{"class":155},[149,4073,4074],{"class":163},"chrono",[149,4076,865],{"class":155},[149,4078,4079],{"class":163},"milliseconds",[149,4081,4082],{"class":891}," sleep_time",[149,4084,3424],{"class":688},[149,4086,1318],{"class":163},[149,4088,865],{"class":155},[149,4090,4074],{"class":163},[149,4092,865],{"class":155},[149,4094,4079],{"class":163},[149,4096,880],{"class":155},[149,4098,4099],{"class":1072},"10",[149,4101,4102],{"class":155},"));\n",[20,4104,4105,4106,4109,4110,4113,4114,4117,4118,54],{},"唯一的可选参数 ",[78,4107,4108],{},"sleep_time"," 表示两次 tick 之间的最长等待时间，默认 10 毫秒。本例省略它。\n函数先执行一次根 tick；只有结果为 ",[78,4111,4112],{},"RUNNING"," 时才等待并继续 tick，直到得到最终结果，返回 ",[78,4115,4116],{},"BT::NodeStatus","。\n本例全部是同步节点，正常情况下第一次根 tick 就得到 ",[78,4119,4120],{},"SUCCESS",[20,4122,4123],{},"程序中另外几个辅助接口可以按下面理解：",[236,4125,4126,4136],{},[239,4127,4128],{},[242,4129,4130,4133],{},[245,4131,4132],{},"写法",[245,4134,4135],{},"参数与结果",[255,4137,4138,4158,4172,4185],{},[242,4139,4140,4145],{},[260,4141,4142],{},[78,4143,4144],{},"name()",[260,4146,4147,4148,4151,4152,30,4155],{},"节点成员函数，无参数，返回当前实例名称的 ",[78,4149,4150],{},"const std::string&","；本例两个打印节点分别叫 ",[78,4153,4154],{},"computed",[78,4156,4157],{},"literal",[242,4159,4160,4165],{},[260,4161,4162],{},[78,4163,4164],{},"std::isfinite(value)",[260,4166,4167,4168,4171],{},"传入一个浮点数，返回 ",[78,4169,4170],{},"bool","，检查它是否为有限值",[242,4173,4174,4182],{},[260,4175,4176,42,4179],{},[78,4177,4178],{},"std::cos(yaw)",[78,4180,4181],{},"std::sin(yaw)",[260,4183,4184],{},"传入以弧度表示的角度，返回余弦、正弦值，用于分解前进距离",[242,4186,4187,4195],{},[260,4188,4189,42,4192],{},[78,4190,4191],{},"std::fixed",[78,4193,4194],{},"std::setprecision(2)",[260,4196,4197,4198,4200],{},"前者设置固定小数格式，后者的参数 ",[78,4199,1163],{}," 指定显示两位小数；只改变输出格式，不改变 Blackboard 中的数据",[348,4202,651],{"id":4203},"treesmissionxml",[83,4205,4207],{"className":143,"code":4206,"language":145,"meta":89,"style":89},"\u003Croot main_tree_to_execute=\"MainTree\">\n    \u003CBehaviorTree ID=\"MainTree\">\n        \u003CSequence name=\"goal_pipeline\">\n            \u003CCalculateGoal start=\"{robot_pose}\"\n                           distance=\"{travel_distance}\"\n                           goal=\"{mission_goal}\"\u002F>\n            \u003COffsetGoal goal=\"{mission_goal}\" offset_x=\"0.5\"\u002F>\n            \u003CPrintGoal name=\"computed\" target=\"{mission_goal}\"\u002F>\n            \u003CPrintGoal name=\"literal\" target=\"2.0;3.0;1.57\"\u002F>\n        \u003C\u002FSequence>\n    \u003C\u002FBehaviorTree>\n\u003C\u002Froot>\n",[78,4208,4209,4226,4243,4260,4274,4284,4295,4315,4336,4358,4367,4376],{"__ignoreMap":89},[149,4210,4211,4213,4216,4219,4221,4224],{"class":151,"line":152},[149,4212,156],{"class":155},[149,4214,4215],{"class":159},"root",[149,4217,4218],{"class":163}," main_tree_to_execute",[149,4220,167],{"class":155},[149,4222,4223],{"class":170},"\"MainTree\"",[149,4225,3446],{"class":155},[149,4227,4228,4231,4234,4237,4239,4241],{"class":151,"line":193},[149,4229,4230],{"class":155},"    \u003C",[149,4232,4233],{"class":159},"BehaviorTree",[149,4235,4236],{"class":163}," ID",[149,4238,167],{"class":155},[149,4240,4223],{"class":170},[149,4242,3446],{"class":155},[149,4244,4245,4248,4251,4253,4255,4258],{"class":151,"line":217},[149,4246,4247],{"class":155},"        \u003C",[149,4249,4250],{"class":159},"Sequence",[149,4252,1328],{"class":163},[149,4254,167],{"class":155},[149,4256,4257],{"class":170},"\"goal_pipeline\"",[149,4259,3446],{"class":155},[149,4261,4262,4265,4267,4269,4271],{"class":151,"line":764},[149,4263,4264],{"class":155},"            \u003C",[149,4266,160],{"class":159},[149,4268,164],{"class":163},[149,4270,167],{"class":155},[149,4272,4273],{"class":170},"\"{robot_pose}\"\n",[149,4275,4276,4279,4281],{"class":151,"line":772},[149,4277,4278],{"class":163},"                           distance",[149,4280,167],{"class":155},[149,4282,4283],{"class":170},"\"{travel_distance}\"\n",[149,4285,4286,4289,4291,4293],{"class":151,"line":780},[149,4287,4288],{"class":163},"                           goal",[149,4290,167],{"class":155},[149,4292,187],{"class":170},[149,4294,190],{"class":155},[149,4296,4297,4299,4301,4303,4305,4307,4309,4311,4313],{"class":151,"line":788},[149,4298,4264],{"class":155},[149,4300,198],{"class":159},[149,4302,182],{"class":163},[149,4304,167],{"class":155},[149,4306,187],{"class":170},[149,4308,207],{"class":163},[149,4310,167],{"class":155},[149,4312,212],{"class":170},[149,4314,190],{"class":155},[149,4316,4317,4319,4321,4323,4325,4328,4330,4332,4334],{"class":151,"line":796},[149,4318,4264],{"class":155},[149,4320,222],{"class":159},[149,4322,1328],{"class":163},[149,4324,167],{"class":155},[149,4326,4327],{"class":170},"\"computed\"",[149,4329,225],{"class":163},[149,4331,167],{"class":155},[149,4333,187],{"class":170},[149,4335,190],{"class":155},[149,4337,4338,4340,4342,4344,4346,4349,4351,4353,4356],{"class":151,"line":801},[149,4339,4264],{"class":155},[149,4341,222],{"class":159},[149,4343,1328],{"class":163},[149,4345,167],{"class":155},[149,4347,4348],{"class":170},"\"literal\"",[149,4350,225],{"class":163},[149,4352,167],{"class":155},[149,4354,4355],{"class":170},"\"2.0;3.0;1.57\"",[149,4357,190],{"class":155},[149,4359,4360,4363,4365],{"class":151,"line":810},[149,4361,4362],{"class":155},"        \u003C\u002F",[149,4364,4250],{"class":159},[149,4366,3446],{"class":155},[149,4368,4369,4372,4374],{"class":151,"line":816},[149,4370,4371],{"class":155},"    \u003C\u002F",[149,4373,4233],{"class":159},[149,4375,3446],{"class":155},[149,4377,4378,4381,4383],{"class":151,"line":825},[149,4379,4380],{"class":155},"\u003C\u002F",[149,4382,4215],{"class":159},[149,4384,3446],{"class":155},[20,4386,4387,4388,4390,4391,4394,4395,4397],{},"两个 ",[78,4389,222],{}," 是同一 C++ 类型的不同实例。",[78,4392,4393],{},"name"," 帮助区分输出，",[78,4396,296],{}," 则分别配置为 Blackboard 引用和固定文本。",[69,4399],{},[12,4401,4403],{"id":4402},"在-vscode-中运行并跟踪数据变化","在 VSCode 中运行并跟踪数据变化",[20,4405,4406,4407,4410,4411,4414,4415,4418,4419,54],{},"保存所有修改后，在新窗口的 CMake 面板中选择 ",[62,4408,4409],{},"Linux Debug","，对应 ",[78,4412,4413],{},"linux-debug"," Configure 预设，再点击 ",[62,4416,4417],{},"Configure","。确认配置成功后，选择同名的 Build 预设并点击 ",[62,4420,4421],{},"Build",[17,4423,4424],{},[20,4425,4426,4429,4430,4432],{},[62,4427,4428],{},"截图预留：bt_ports 配置和构建成功。"," 展示当前 ",[78,4431,4413],{}," 预设、构建成功信息，以及新工程名。",[20,4434,4435,4436,4439,4440,4442,4443,4445,4446,4448],{},"在 ",[62,4437,4438],{},"Launch"," 中选择 ",[78,4441,590],{},"，点击运行按钮，在集成终端中查看输出。新窗口的运行目标应是 ",[78,4444,590],{},"，与上一篇的 ",[78,4447,586],{}," 区分开。",[17,4450,4451],{},[20,4452,4453,4456],{},[62,4454,4455],{},"截图预留：选择 bt_ports 运行目标。"," 展示 CMake Tools 的 Launch 目标和运行按钮。",[20,4458,4459],{},"预期输出：",[83,4461,4464],{"className":4462,"code":4463,"language":88,"meta":89},[86],"[CalculateGoal] distance=2.00, goal=(3.00, 2.00, 0.00)\n[OffsetGoal] goal=(3.50, 2.00, 0.00)\n[PrintGoal:computed] target=(3.50, 2.00, 0.00)\n[PrintGoal:literal] target=(2.00, 3.00, 1.57)\nTree result: SUCCESS\n[main] mission_goal=(3.50, 2.00, 0.00)\n",[78,4465,4463],{"__ignoreMap":89},[20,4467,4468],{},[655,4469],{"alt":89,"src":4470},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792396719064-b3803148.webp",[20,4472,4473,4474,4477,4478,4481,4482,4485],{},"本篇完整工程已在 Ubuntu 22.04 的 ",[78,4475,4476],{},"ysu_ros_humble"," 容器中，使用 ",[62,4479,4480],{},"C++20、CMake 3.22.1、GCC 11.4"," 和已安装的 ",[62,4483,4484],{},"BehaviorTree.CPP 3.8.7"," 编译运行验证；固定值、默认值、缺失输入和错误格式等对照也已检查。配置沿用上一篇，测试使用临时 Ninja 和工程副本。",[20,4487,4488],{},"数据变化可以对应到每一步：",[236,4490,4491,4504],{},[239,4492,4493],{},[242,4494,4495,4498,4501],{},[245,4496,4497],{},"执行位置",[245,4499,4500],{},"读取什么",[245,4502,4503],{},"写入或使用什么",[255,4505,4506,4524,4539,4554,4568,4583],{},[242,4507,4508,4513,4516],{},[260,4509,4510,4512],{},[78,4511,3351],{}," 初始化",[260,4514,4515],{},"编辑器中设置的模拟距离、示例位姿",[260,4517,4518,30,4521],{},[78,4519,4520],{},"robot_pose",[78,4522,4523],{},"travel_distance",[242,4525,4526,4530,4533],{},[260,4527,4528],{},[78,4529,160],{},[260,4531,4532],{},"起点与距离",[260,4534,4535,4536],{},"写入 ",[78,4537,4538],{},"mission_goal = (3.0, 2.0, 0.0)",[242,4540,4541,4545,4548],{},[260,4542,4543],{},[78,4544,198],{},[260,4546,4547],{},"已有目标与 x 偏移",[260,4549,4550,4551],{},"更新 ",[78,4552,4553],{},"mission_goal = (3.5, 2.0, 0.0)",[242,4555,4556,4561,4565],{},[260,4557,4558],{},[78,4559,4560],{},"PrintGoal:computed",[260,4562,4563],{},[78,4564,309],{},[260,4566,4567],{},"打印当前值",[242,4569,4570,4575,4578],{},[260,4571,4572],{},[78,4573,4574],{},"PrintGoal:literal",[260,4576,4577],{},"XML 固定文本",[260,4579,4580,4581],{},"转换后打印，不修改 ",[78,4582,309],{},[242,4584,4585,4590,4594],{},[260,4586,4587,4589],{},[78,4588,3351],{}," 读取结果",[260,4591,4592],{},[78,4593,309],{},[260,4595,4596],{},"取得树计算后的目标",[20,4598,4599,4600,4603],{},"所有节点都是快速同步动作，本次序列在一次根节点 tick 内完成。",[62,4601,4602],{},"Sequence 负责先后顺序，Ports 与 Blackboard 负责传值","；共同映射到一个键不会自动安排生产者先执行。",[20,4605,4606,4607,4609,4610,4612],{},"与上一篇相同，程序使用构建配置提供的 ",[78,4608,722],{},"，自动读取当前源码工程的 ",[78,4611,651],{},"。在 CMake Tools 中启动程序时，直接使用同一份文件，无需另外设置 XML 路径参数。",[348,4614,4616],{"id":4615},"改变外部输入节点取得新的数值","改变外部输入，节点取得新的数值",[20,4618,4619,4620,264,4622,4624],{},"保持 XML 不变，在编辑器中将 ",[78,4621,648],{},[78,4623,3351],{}," 内模拟距离改为：",[83,4626,4628],{"className":736,"code":4627,"language":738,"meta":89,"style":89},"const double distance = 4.0;\n",[78,4629,4630],{"__ignoreMap":89},[149,4631,4632,4634,4636,4638,4640,4643],{"class":151,"line":152},[149,4633,898],{"class":688},[149,4635,2858],{"class":688},[149,4637,1547],{"class":155},[149,4639,167],{"class":688},[149,4641,4642],{"class":1072}," 4.0",[149,4644,961],{"class":155},[20,4646,4647,4648,4651],{},"保存后点击 ",[62,4649,4650],{},"Build → Launch","。这里改的是 C++，因此需要先重新构建。",[20,4653,4654,4655,4658,4659,54,4662,4664,4665,4667,4668,4670],{},"这次先计算出 ",[78,4656,4657],{},"(5.0, 2.0, 0.0)","，再偏移到 ",[78,4660,4661],{},"(5.5, 2.0, 0.0)",[78,4663,4154],{}," 实例和 ",[78,4666,3351],{}," 都取得新结果，",[78,4669,4157],{}," 实例仍打印 XML 中的固定坐标。",[20,4672,4673],{},[655,4674],{"alt":89,"src":4675},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792396770690-68183273.webp",[20,4677,4678,4679,4681],{},"对照完成后，将模拟距离恢复为 ",[78,4680,2960],{}," 并点击 Build，后面继续使用初始设置。",[20,4683,4684],{},"这里每次运行程序都会重新建立 Blackboard。\n程序持续运行时，也可以由外部更新输入，再发起下一次任务；\n应让更新与树执行有明确的调度关系，本例没有引入并发读写。",[348,4686,4688],{"id":4687},"在-xml-中切换距离来源","在 XML 中切换距离来源",[20,4690,4691,4692,264,4694,4696],{},"将 ",[78,4693,160],{},[78,4695,489],{}," 属性改为固定文本：",[83,4698,4700],{"className":143,"code":4699,"language":145,"meta":89,"style":89},"\u003CCalculateGoal start=\"{robot_pose}\" distance=\"3.0\" goal=\"{mission_goal}\"\u002F>\n",[78,4701,4702],{"__ignoreMap":89},[149,4703,4704,4706,4708,4710,4712,4714,4716,4718,4721,4723,4725,4727],{"class":151,"line":152},[149,4705,156],{"class":155},[149,4707,160],{"class":159},[149,4709,164],{"class":163},[149,4711,167],{"class":155},[149,4713,171],{"class":170},[149,4715,174],{"class":163},[149,4717,167],{"class":155},[149,4719,4720],{"class":170},"\"3.0\"",[149,4722,182],{"class":163},[149,4724,167],{"class":155},[149,4726,187],{"class":170},[149,4728,190],{"class":155},[20,4730,4731,4732,4734,4735,4737,4738,4741,4742,4744],{},"保存 ",[78,4733,651],{}," 后再次点击 ",[62,4736,4438],{},"，计算节点使用 3 米，最终目标为 ",[78,4739,4740],{},"(4.5, 2.0, 0.0)","。外部程序仍然写入 ",[78,4743,4523],{},"，但该节点已不再从这个条目读取距离。",[20,4746,4747,4748,4750],{},"如果完全省略 ",[78,4749,489],{}," 属性：",[83,4752,4754],{"className":143,"code":4753,"language":145,"meta":89,"style":89},"\u003CCalculateGoal start=\"{robot_pose}\" goal=\"{mission_goal}\"\u002F>\n",[78,4755,4756],{"__ignoreMap":89},[149,4757,4758,4760,4762,4764,4766,4768,4770,4772,4774],{"class":151,"line":152},[149,4759,156],{"class":155},[149,4761,160],{"class":159},[149,4763,164],{"class":163},[149,4765,167],{"class":155},[149,4767,171],{"class":170},[149,4769,182],{"class":163},[149,4771,167],{"class":155},[149,4773,187],{"class":170},[149,4775,190],{"class":155},[20,4777,4778,4779,4782,4783,4785,4786,54],{},"则使用声明的默认值 1 米，最终目标为 ",[78,4780,4781],{},"(2.5, 2.0, 0.0)","。这两种修改都只调整 XML，保存后点击 ",[62,4784,4438],{}," 即可，无需手动先点 Build。继续后面的示例前，可以恢复主示例的 ",[78,4787,478],{},[20,4789,4790],{},[655,4791],{"alt":89,"src":4792},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792396952511-9fed2457.webp",[348,4794,4795],{"id":4795},"用断点观察目标被读取和写回",[20,4797,4798,4799,4801,4802,4805,4806,4809,4810,54],{},"恢复主示例的距离和 XML 配置，保存并点击 ",[62,4800,4421],{},"。\n在 ",[78,4803,4804],{},"CalculateGoal::tick()"," 打印目标的语句处，以及 ",[78,4807,4808],{},"OffsetGoal::tick()"," 打印目标的语句处分别设置断点，再点击 CMake Tools 的 ",[62,4811,4812],{},"Debug",[20,4814,4815,4816,4818,4819,4821,4822,4825,4826,4829],{},"第一次暂停时，在变量窗口展开本地变量 ",[78,4817,267],{},"，可以看到 ",[78,4820,126],{},"；按 ",[78,4823,4824],{},"F5"," 继续到偏移节点的断点，同名本地变量的 x 坐标已经变成 ",[78,4827,4828],{},"3.5","。再继续执行，终端会显示完整结果。两次断点所在的函数不同，本地变量也分别属于各自回调。",[20,4831,4832],{},[655,4833],{"alt":89,"src":4834},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792397160504-fdb10c32.webp",[20,4836,4837],{},[655,4838],{"alt":89,"src":4839},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792397182839-84013fa8.webp",[69,4841],{},[12,4843,4844],{"id":4844},"输入错误要在读取位置明确处理",[20,4846,4847],{},"本例常见的数据问题可以按发生位置区分：",[236,4849,4850,4863],{},[239,4851,4852],{},[242,4853,4854,4857,4860],{},[245,4855,4856],{},"修改",[245,4858,4859],{},"发生什么",[245,4861,4862],{},"本例的处理",[255,4864,4865,4883,4900,4916],{},[242,4866,4867,4875,4878],{},[260,4868,4691,4869,4871,4872],{},[78,4870,296],{}," 改为 ",[78,4873,4874],{},"{unknown_goal}",[260,4876,4877],{},"找不到有效的输入值",[260,4879,4880,4882],{},[78,4881,424],{}," 失败，报告读取错误",[242,4884,4885,4891,4894],{},[260,4886,4887,4888],{},"将固定坐标改为 ",[78,4889,4890],{},"2.0;3.0",[260,4892,4893],{},"字段数量不符合约定",[260,4895,4896,4897],{},"转换函数报告 ",[78,4898,4899],{},"Pose2D requires x;y;yaw",[242,4901,4902,4908,4913],{},[260,4903,4904,4905],{},"将距离改为 ",[78,4906,4907],{},"distance=\"far\"",[260,4909,4910,4911],{},"文本无法转换成 ",[78,4912,444],{},[260,4914,4915],{},"读取失败，报告转换错误",[242,4917,4918,4927,4930],{},[260,4919,4920,4921,4923,4924,4926],{},"把 ",[78,4922,198],{}," 移到 ",[78,4925,160],{}," 前面",[260,4928,4929],{},"目标还没有被写入",[260,4931,4932],{},"读取双向端口失败，后面的节点不再执行",[20,4934,4935],{},"这些 XML 对照都在编辑器中修改并保存，再点击 Launch，在集成终端中查看错误信息。",[20,4937,4938],{},[655,4939],{"alt":89,"src":4940},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F12\u002F30\u002F1792397397830-5f03672f.webp",[20,4942,4943],{},"工厂可以在创建树时建立端口和条目的关系，但这不代表每个输入已经有业务值。数据是否准备好，仍要看外部初始化与前面节点的执行结果。",[20,4945,4946,4947,4949,4950,4953,4954,4957,4958,4960],{},"外部读取也要使用对应类型。这里调用 ",[78,4948,3594],{},"，不能因为打印出来像文本，就改成读取 ",[78,4951,4952],{},"std::string","。直接访问 Blackboard 的 ",[78,4955,4956],{},"get()"," 与节点的 ",[78,4959,424],{}," 是不同接口，不应假定它会执行相同的文本转换。",[69,4962],{},[12,4964,4966],{"id":4965},"blackboard-数据不会随节点状态自动清空","Blackboard 数据不会随节点状态自动清空",[20,4968,4969,4970,4972,4973,4975,4976,4978,4979,4982],{},"本例在 ",[78,4971,160],{}," 中产生目标，在 ",[78,4974,198],{}," 中更新目标；树任务结束后，",[78,4977,3351],{}," 仍能读到最终值。\n节点结束或被重置到 ",[78,4980,4981],{},"IDLE","，不会自动删除这些 Blackboard 条目。",[20,4984,4985],{},"这也意味着，上一次任务留下的值不能自动代表下一次任务已经准备好。\n组织重复任务时，应明确哪些输入由外部更新，哪些输出由当前任务重新产生，以及失败后是否允许继续使用已有结果。",[20,4987,4988],{},"本例中的输出节点每次成功执行都会重新写入目标，再由后续节点读取。后面使用子树时，还需要明确不同 Blackboard 范围之间的数据映射；共享一个键的写法不能直接套用到所有子树。",[69,4990],{},[12,4992,4993],{"id":4993},"当前学习进度",[20,4995,4996,4997,5000,5001,5004],{},"本篇把输入、输出、双向端口、自定义类型转换以及外部 Blackboard 读写串成了完整工程。\n下一篇学习",[78,4998,4999],{},"持续任务与中止","：用 ",[78,5002,5003],{},"StatefulActionNode"," 模拟导航，分别处理启动、运行检查和取消，让任务能够跨越多次 tick。",[5006,5007,5008],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":89,"searchDepth":193,"depth":193,"links":5010},[5011,5012,5013,5016,5020,5028,5033,5034,5035],{"id":14,"depth":193,"text":15},{"id":73,"depth":193,"text":73},{"id":136,"depth":193,"text":137,"children":5014},[5015],{"id":350,"depth":217,"text":350},{"id":433,"depth":193,"text":434,"children":5017},[5018,5019],{"id":437,"depth":217,"text":438},{"id":516,"depth":217,"text":516},{"id":563,"depth":193,"text":563,"children":5021},[5022,5023,5024,5025,5026,5027],{"id":733,"depth":217,"text":648},{"id":3365,"depth":217,"text":3366},{"id":3645,"depth":217,"text":3645},{"id":3850,"depth":217,"text":3851},{"id":4040,"depth":217,"text":4040},{"id":4203,"depth":217,"text":651},{"id":4402,"depth":193,"text":4403,"children":5029},[5030,5031,5032],{"id":4615,"depth":217,"text":4616},{"id":4687,"depth":217,"text":4688},{"id":4795,"depth":217,"text":4795},{"id":4844,"depth":193,"text":4844},{"id":4965,"depth":193,"text":4966},{"id":4993,"depth":193,"text":4993},"1200-0400-0200-0400","2023-12-30","2023-12-30-ros2-tutorial","\u002Fwiki\u002F2023-12-30-ros2-tutorial","Ros2 Tutorial","md",false,{},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0400-jie-dian-zhi-jian-chuan-shu-ju",{"title":5,"description":89},"wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0400-节点之间传数据","_Tur_jY6SERmaLK1bvWleMf10AfBhVdCeP8OatAjcJI",[5049,5056,5062,5068,5074,5080,5085,5091,5097,5103,5109,5115,5121,5127,5133,5139,5145,5151,5157,5163,5169,5175,5181,5183,5188,5194,5200,5206,5212,5218,5224,5230,5236,5242,5248,5253,5259,5264,5270,5276,5282,5288,5294,5300,5306,5312,5318,5324,5330,5336,5342,5348,5354,5360,5366,5372,5378,5384,5390,5396,5402,5408,5414,5420,5426,5432,5438,5444,5450],{"path":5050,"stem":5051,"title":5052,"authors":5053,"date":5037,"chapterOrder":5054,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0100-ros2-jie-shao","wiki\u002F2023-12-30-ros2-tutorial\u002F0100-ROS2介绍","ROS2介绍",[7],"0100",0,{"path":5057,"stem":5058,"title":5059,"authors":5060,"date":5037,"chapterOrder":5061,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0200-ru-men-cao-zuo","wiki\u002F2023-12-30-ros2-tutorial\u002F0200-入门操作","入门操作",[7],"0200",{"path":5063,"stem":5064,"title":5065,"authors":5066,"date":5037,"chapterOrder":5067,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0300-gong-zuo-kong-jian-yu-gong-neng-bao","wiki\u002F2023-12-30-ros2-tutorial\u002F0300-工作空间与功能包","工作空间与功能包",[7],"0300",{"path":5069,"stem":5070,"title":5071,"authors":5072,"date":5037,"chapterOrder":5073,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0400-si-da-tong-xin","wiki\u002F2023-12-30-ros2-tutorial\u002F0400-四大通信","四大通信",[7],"0400",{"path":5075,"stem":5076,"title":5077,"authors":5078,"date":5037,"chapterOrder":5079,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0500-ros2-qi-ta-tong-xin-ji-zhi","wiki\u002F2023-12-30-ros2-tutorial\u002F0500-ROS2其他通信机制","ROS2其他通信机制",[7],"0500",{"path":5081,"stem":5082,"title":4438,"authors":5083,"date":5037,"chapterOrder":5084,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0600-launch","wiki\u002F2023-12-30-ros2-tutorial\u002F0600-Launch",[7],"0600",{"path":5086,"stem":5087,"title":5088,"authors":5089,"date":5037,"chapterOrder":5090,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0700-hui-su-rosbag2","wiki\u002F2023-12-30-ros2-tutorial\u002F0700-回溯rosbag2","回溯rosbag2",[7],"0700",{"path":5092,"stem":5093,"title":5094,"authors":5095,"date":5037,"chapterOrder":5096,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0800-zuo-biao-bian-huan-tf","wiki\u002F2023-12-30-ros2-tutorial\u002F0800-坐标变换TF","坐标变换TF",[7],"0800",{"path":5098,"stem":5099,"title":5100,"authors":5101,"date":5037,"chapterOrder":5102,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F0900-ke-shi-hua-ping-tai-rviz2-yu-urdf-jian-mo-yu-yan","wiki\u002F2023-12-30-ros2-tutorial\u002F0900-可视化平台RVIZ2与URDF建模语言","可视化平台RVIZ2与URDF建模语言",[7],"0900",{"path":5104,"stem":5105,"title":5106,"authors":5107,"date":5037,"chapterOrder":5108,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1000-stage-ros2-fang-zhen-ping-tai","wiki\u002F2023-12-30-ros2-tutorial\u002F1000-Stage_Ros2仿真平台","Stage_Ros2仿真平台",[7],"1000",{"path":5110,"stem":5111,"title":5112,"authors":5113,"date":5037,"chapterOrder":5114,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1100-0100-gazeboclassic","wiki\u002F2023-12-30-ros2-tutorial\u002F1100-0100-GazeboClassic","Gezebo Classic",[7],"1100-0100",{"path":5116,"stem":5117,"title":5118,"authors":5119,"date":5037,"chapterOrder":5120,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1100-0200-ignitiongazebo","wiki\u002F2023-12-30-ros2-tutorial\u002F1100-0200-IgnitionGazebo","Ignition Gazebo（Gazebo Fortress）",[7],"1100-0200",{"path":5122,"stem":5123,"title":5124,"authors":5125,"date":5037,"chapterOrder":5126,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1100-0300-gazebosim","wiki\u002F2023-12-30-ros2-tutorial\u002F1100-0300-GazeboSim","Gz Sim（Gazebo Harmonic）",[7],"1100-0300",{"path":5128,"stem":5129,"title":5130,"authors":5131,"date":5037,"chapterOrder":5132,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1100-0400-igngz2gzsim","wiki\u002F2023-12-30-ros2-tutorial\u002F1100-0400-Igngz2gzsim","将Ign Gazebo迁移至Gz Sim",[7],"1100-0400",{"path":5134,"stem":5135,"title":5136,"authors":5137,"date":5037,"chapterOrder":5138,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1100-gezebo-fang-zhen-ping-tai","wiki\u002F2023-12-30-ros2-tutorial\u002F1100-Gezebo仿真平台","Gezebo仿真平台",[7],"1100",{"path":5140,"stem":5141,"title":5142,"authors":5143,"date":5037,"chapterOrder":5144,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0100-humble-ban-ben","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0100-Humble版本","Humble导航仿真",[7],"1200-0100",{"path":5146,"stem":5147,"title":5148,"authors":5149,"date":5037,"chapterOrder":5150,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0200-jazzy-ban-ben","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0200-Jazzy版本","Jazzy导航仿真",[7],"1200-0200",{"path":5152,"stem":5153,"title":5154,"authors":5155,"date":5037,"chapterOrder":5156,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0300-humble2jazzy","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0300-Humble2Jazzy","Humble与Jazzy导航的差异",[7],"1200-0300",{"path":5158,"stem":5159,"title":5160,"authors":5161,"date":5037,"chapterOrder":5162,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0100-you-xian-zhuang-tai-ji-fsm","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0100-有限状态机FSM","有限状态机（Finite State Machine）",[7],"1200-0400-0100",{"path":5164,"stem":5165,"title":5166,"authors":5167,"date":5037,"chapterOrder":5168,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0100-ji-ben-gai-nian","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0100-基本概念","基本概念",[7],"1200-0400-0200-0100",{"path":5170,"stem":5171,"title":5172,"authors":5173,"date":5037,"chapterOrder":5174,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0200-kong-zhi-yu-zhuang-shi-gui-ze","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0200-控制与装饰规则","控制与装饰规则",[7],"1200-0400-0200-0200",{"path":5176,"stem":5177,"title":5178,"authors":5179,"date":5037,"chapterOrder":5180,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0300-di-yi-ge-wan-zheng-cheng-xu","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0300-第一个完整程序","第一个完整程序",[7],"1200-0400-0200-0300",{"path":5044,"stem":5046,"title":5,"authors":5182,"date":5037,"chapterOrder":5036,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},[7],{"path":5184,"stem":5185,"title":4999,"authors":5186,"date":5037,"chapterOrder":5187,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0500-chi-xu-ren-wu-yu-zhong-zhi","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0500-持续任务与中止",[7],"1200-0400-0200-0500",{"path":5189,"stem":5190,"title":5191,"authors":5192,"date":5037,"chapterOrder":5193,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0600-zu-he-shi-ji-ren-wu","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-0600-组合实际任务","组合实际任务",[7],"1200-0400-0200-0600",{"path":5195,"stem":5196,"title":5197,"authors":5198,"date":5037,"chapterOrder":5199,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-xing-wei-shu-bt","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-0200-行为树BT","行为树（BehaviorTree.CPP）",[7],"1200-0400-0200",{"path":5201,"stem":5202,"title":5203,"authors":5204,"date":5037,"chapterOrder":5205,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-fsm-yu-bt","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-0400-FSM与BT","有限状态机FSM与行为树BT",[7],"1200-0400",{"path":5207,"stem":5208,"title":5209,"authors":5210,"date":5037,"chapterOrder":5211,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1200-ji-qi-ren-dao-hang-navigation2-fang-zhen-pian","wiki\u002F2023-12-30-ros2-tutorial\u002F1200-机器人导航Navigation2(仿真篇)","机器人导航Navigation2(仿真篇)",[7],"1200",{"path":5213,"stem":5214,"title":5215,"authors":5216,"date":5037,"chapterOrder":5217,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0100-0100-boost-aiso","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0100-0100-Boost.Aiso","Boost.Asio库",[7],"1300-0100-0100",{"path":5219,"stem":5220,"title":5221,"authors":5222,"date":5037,"chapterOrder":5223,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0100-0200-ros2-serial-driver","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0100-0200-ros2_serial_driver","ROS2 Serial Driver库",[7],"1300-0100-0200",{"path":5225,"stem":5226,"title":5227,"authors":5228,"date":5037,"chapterOrder":5229,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0100-chuan-kou-tong-xin","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0100-串口通信","UART串口通信",[7],"1300-0100",{"path":5231,"stem":5232,"title":5233,"authors":5234,"date":5037,"chapterOrder":5235,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0200-0100-socketcan","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0200-0100-socketcan","SocketCAN",[7],"1300-0200-0100",{"path":5237,"stem":5238,"title":5239,"authors":5240,"date":5037,"chapterOrder":5241,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0200-0200-ros2-socketcan","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0200-0200-ros2_socketcan","ROS2_SocketCAN",[7],"1300-0200-0200",{"path":5243,"stem":5244,"title":5245,"authors":5246,"date":5037,"chapterOrder":5247,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0200-can-tong-xin","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0200-CAN通信","CAN通信",[7],"1300-0200",{"path":5249,"stem":5250,"title":5215,"authors":5251,"date":5037,"chapterOrder":5252,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0300-0100-boost-aiso","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0300-0100-Boost.Aiso",[7],"1300-0300-0100",{"path":5254,"stem":5255,"title":5256,"authors":5257,"date":5037,"chapterOrder":5258,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0300-tcp-tong-xin","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0300-TCP通信","TCP通信",[7],"1300-0300",{"path":5260,"stem":5261,"title":5215,"authors":5262,"date":5037,"chapterOrder":5263,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0400-0100-boost-aiso","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0400-0100-Boost.Aiso",[7],"1300-0400-0100",{"path":5265,"stem":5266,"title":5267,"authors":5268,"date":5037,"chapterOrder":5269,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-0400-udp-tong-xin","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-0400-UDP通信","UDP通信",[7],"1300-0400",{"path":5271,"stem":5272,"title":5273,"authors":5274,"date":5037,"chapterOrder":5275,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1300-linux-ying-jian-tong-xin","wiki\u002F2023-12-30-ros2-tutorial\u002F1300-Linux硬件通信","Linux硬件通信",[7],"1300",{"path":5277,"stem":5278,"title":5279,"authors":5280,"date":5037,"chapterOrder":5281,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0100-ji-qi-ren-zu-cheng","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0100-机器人组成","机器人组成",[7],"1400-0100",{"path":5283,"stem":5284,"title":5285,"authors":5286,"date":5037,"chapterOrder":5287,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0200-0100-yuan-cheng-kai-fa-ssh","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0200-0100-远程开发SSH","远程开发SSH",[7],"1400-0200-0100",{"path":5289,"stem":5290,"title":5291,"authors":5292,"date":5037,"chapterOrder":5293,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0200-0200-yuan-cheng-fang-wen-zhuo-mian","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0200-0200-远程访问桌面","远程访问桌面",[7],"1400-0200-0200",{"path":5295,"stem":5296,"title":5297,"authors":5298,"date":5037,"chapterOrder":5299,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0200-gong-kong-ji-zhi-yuan-cheng-kai-fa-huan-jing","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0200-工控机之远程开发环境","工控机之远程开发环境",[7],"1400-0200",{"path":5301,"stem":5302,"title":5303,"authors":5304,"date":5037,"chapterOrder":5305,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0300-gong-kong-ji-zhi-wai-jie-usb-she-bei","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0300-工控机之外接USB设备","工控机之外接USB设备",[7],"1400-0300",{"path":5307,"stem":5308,"title":5309,"authors":5310,"date":5037,"chapterOrder":5311,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0400-fen-bu-shi-da-jian","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0400-分布式搭建","分布式搭建",[7],"1400-0400",{"path":5313,"stem":5314,"title":5315,"authors":5316,"date":5037,"chapterOrder":5317,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0500-you-hua-ri-zhi","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0500-优化日志","优化日志",[7],"1400-0500",{"path":5319,"stem":5320,"title":5321,"authors":5322,"date":5037,"chapterOrder":5323,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0100-li-cheng-ji-odom","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0100-里程计Odom","里程计Odom",[7],"1400-0600-0100",{"path":5325,"stem":5326,"title":5327,"authors":5328,"date":5037,"chapterOrder":5329,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0200-guan-xing-ji-imu","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0200-惯性计IMU","惯性计IMU",[7],"1400-0600-0200",{"path":5331,"stem":5332,"title":5333,"authors":5334,"date":5037,"chapterOrder":5335,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0300-ji-guang-lei-da-lidar","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0300-激光雷达LiDAR","激光雷达LiDAR",[7],"1400-0600-0300",{"path":5337,"stem":5338,"title":5339,"authors":5340,"date":5037,"chapterOrder":5341,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0400-xiang-ji-camera","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0400-相机Camera","相机Camera",[7],"1400-0600-0400",{"path":5343,"stem":5344,"title":5345,"authors":5346,"date":5037,"chapterOrder":5347,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0500-quan-qiu-ding-wei-gnss","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0500-全球定位GNSS","全球定位GNSS",[7],"1400-0600-0500",{"path":5349,"stem":5350,"title":5351,"authors":5352,"date":5037,"chapterOrder":5353,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0600-shou-bing-joy","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0600-手柄joy","手柄JOY",[7],"1400-0600-0600",{"path":5355,"stem":5356,"title":5357,"authors":5358,"date":5037,"chapterOrder":5359,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0700-jian-pan-kong-zhi-jie-dian","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-0700-键盘控制节点","键盘控制节点",[7],"1400-0600-0700",{"path":5361,"stem":5362,"title":5363,"authors":5364,"date":5037,"chapterOrder":5365,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-ying-jian-ping-tai","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0600-硬件平台","硬件平台",[7],"1400-0600",{"path":5367,"stem":5368,"title":5369,"authors":5370,"date":5037,"chapterOrder":5371,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0700-zuo-biao-xi-yu-hua-ti-guan-xi","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0700-坐标系与话题关系","坐标系与话题关系",[7],"1400-0700",{"path":5373,"stem":5374,"title":5375,"authors":5376,"date":5037,"chapterOrder":5377,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0100-0100-lun-shi-li-cheng-ji-biao-ding","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0100-0100-轮式里程计标定","轮式里程计标定",[7],"1400-0800-0100-0100",{"path":5379,"stem":5380,"title":5381,"authors":5382,"date":5037,"chapterOrder":5383,"chapterDepth":217,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0100-0200-lun-shi-li-cheng-ji-yu-imu-rong-he","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0100-0200-轮式里程计与IMU融合","轮式里程计与IMU融合",[7],"1400-0800-0100-0200",{"path":5385,"stem":5386,"title":5387,"authors":5388,"date":5037,"chapterOrder":5389,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0100-lun-shi-li-cheng-ji-biao-ding-yu-rong-he","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0100-轮式里程计标定与融合","轮式里程计标定与融合",[7],"1400-0800-0100",{"path":5391,"stem":5392,"title":5393,"authors":5394,"date":5037,"chapterOrder":5395,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0200-ji-guang-lei-da-gong-ju","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0200-激光雷达工具","激光雷达工具",[7],"1400-0800-0200",{"path":5397,"stem":5398,"title":5399,"authors":5400,"date":5037,"chapterOrder":5401,"chapterDepth":193,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0300-xiang-ji-shi-yong-jin-jie","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-0300-相机使用进阶","相机使用进阶",[7],"1400-0800-0300",{"path":5403,"stem":5404,"title":5405,"authors":5406,"date":5037,"chapterOrder":5407,"chapterDepth":152,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-ying-jian-ping-tai-jin-jie","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-0800-硬件平台进阶","硬件平台进阶",[7],"1400-0800",{"path":5409,"stem":5410,"title":5411,"authors":5412,"date":5037,"chapterOrder":5413,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1400-ji-qi-ren-ying-jian","wiki\u002F2023-12-30-ros2-tutorial\u002F1400-机器人硬件","机器人硬件",[7],"1400",{"path":5415,"stem":5416,"title":5417,"authors":5418,"date":5037,"chapterOrder":5419,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1500-ros2-control","wiki\u002F2023-12-30-ros2-tutorial\u002F1500-ROS2_Control","ROS2_Control",[7],"1500",{"path":5421,"stem":5422,"title":5423,"authors":5424,"date":5037,"chapterOrder":5425,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1600-moveit2-gong-ye-ji-qi-ren-ji-xie-bi","wiki\u002F2023-12-30-ros2-tutorial\u002F1600-Moveit2工业机器人机械臂","Moveit2工业机器人机械臂",[7],"1600",{"path":5427,"stem":5428,"title":5429,"authors":5430,"date":5037,"chapterOrder":5431,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1700-ji-qi-ren-dao-hang-navigation2-shi-ti-pian","wiki\u002F2023-12-30-ros2-tutorial\u002F1700-机器人导航Navigation2(实体篇)","机器人导航Navigation2(实体篇)",[7],"1700",{"path":5433,"stem":5434,"title":5435,"authors":5436,"date":5037,"chapterOrder":5437,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1800-microros","wiki\u002F2023-12-30-ros2-tutorial\u002F1800-MicroROS","MicroROS",[7],"1800",{"path":5439,"stem":5440,"title":5441,"authors":5442,"date":5037,"chapterOrder":5443,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F1900-webots-fang-zhen-ping-tai","wiki\u002F2023-12-30-ros2-tutorial\u002F1900-Webots仿真平台","Webots仿真平台",[7],"1900",{"path":5445,"stem":5446,"title":5447,"authors":5448,"date":5037,"chapterOrder":5449,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":5042},"\u002Fwiki\u002F2023-12-30-ros2-tutorial\u002F2000-opencv","wiki\u002F2023-12-30-ros2-tutorial\u002F2000-OpenCV","OpenCV",[7],"2000",{"path":5039,"stem":5451,"title":5452,"authors":5453,"date":5037,"chapterOrder":5454,"chapterDepth":5055,"docKey":5038,"docRoot":5039,"docTitle":5040,"isWikiDoc":753,"isWikiIndex":753},"wiki\u002F2023-12-30-ros2-tutorial\u002Findex","ROS2机器人操作系统教程",[7],null,[5456,5458,5461,5464],{"name":7,"image":5457},"\u002Fimages\u002Fmember_photo\u002F董佳辉.jpg",{"name":5459,"image":5460},"宫金良","\u002Fimages\u002Fmember_photo\u002F宫金良.jpg",{"name":5462,"image":5463},"巩丽","\u002Fimages\u002Fmember_photo\u002F巩丽.png",{"name":5465,"image":5466},"张彦斐","\u002Fimages\u002Fmember_photo\u002F张彦斐.jpg",1792431903279]