[{"data":1,"prerenderedAt":4803},["ShallowReactive",2],{"wiki-page-/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng":3,"wiki-doc-items-/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng":4377,"language-switcher-data-/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng":4787,"wiki-i18n-paths-/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng":4802},{"id":4,"title":5,"body":6,"canonicalPath":4359,"chapter":4360,"chapterSort":4361,"date":4362,"description":30,"docI18nKey":4363,"docKey":4364,"docRoot":4365,"docTitle":4366,"extension":4367,"i18nKey":4368,"isBlogPost":4369,"isWikiDoc":116,"isWikiIndex":4369,"layout":4370,"legacyPath":4371,"locale":4372,"localeSlug":4373,"meta":4374,"navigation":116,"path":4359,"seo":4375,"sourcePath":4371,"sourceStem":4368,"stem":4368,"wikiDepth":86,"__hash__":4376},"content/wiki/2023-10-05-Cplusplus教学/ch19-1-6-机器人工程写法与ROS2集成.md","机器人工程写法与 ROS2 集成",{"type":7,"value":8,"toc":4311},"minimark",[9,20,23,34,41,44,53,57,60,66,72,75,768,775,778,807,810,816,819,822,825,831,834,836,840,843,846,849,1412,1416,1419,1443,1446,1452,1455,1458,1464,1466,1470,1473,1476,1482,1485,2686,2690,2693,2696,2722,2725,2759,2762,2792,2795,2798,2804,2807,2813,2816,2822,2825,2828,2831,2853,2856,2858,2862,2865,2868,2871,2877,2880,3672,3675,3678,3684,3687,3693,3696,3702,3705,3708,3720,3723,3739,3742,3745,3751,3755,3758,3764,3767,3770,3776,3778,3782,3785,3788,3791,3976,3979,3982,3988,3991,3997,4000,4006,4009,4011,4014,4017,4023,4026,4032,4035,4037,4040,4048,4054,4057,4085,4089,4092,4118,4122,4125,4177,4184,4234,4238,4241,4247,4250,4256,4260,4263,4267,4270,4272,4275,4278,4304,4307],[10,11,12],"blockquote",{},[13,14,15,16,19],"p",{},"前面几节讲的是 Boost.Asio 本体。",[17,18],"br",{},"\n这一节讲你真正做机器人项目时应该怎么组织代码，尤其是：串口驱动、TCP/UDP 通信模块、ROS2 节点之间的关系。",[13,21,22],{},"核心原则：",[24,25,31],"pre",{"className":26,"code":28,"language":29,"meta":30},[27],"language-text","不要在 ROS2 回调里写长时间阻塞的串口 read / TCP read。\n通信模块应该自己用 Asio 异步运行。\nROS2 节点只负责发布、订阅、参数、生命周期和业务逻辑。\n","text","",[32,33,28],"code",{"__ignoreMap":30},[13,35,36,37,40],{},"本节仍然使用 ",[32,38,39],{},"std::bind","。",[42,43],"hr",{},[45,46,48,49,52],"h2",{"id":47},"示例-1普通-main-中模拟机器人协议解析","示例 1：普通 ",[32,50,51],{},"main()"," 中模拟机器人协议解析",[54,55,56],"h3",{"id":56},"程序目标",[13,58,59],{},"很多下位机串口协议都是一行文本，例如：",[24,61,64],{"className":62,"code":63,"language":29,"meta":30},[27],"ODOM 1.0 2.0 0.5\nBAT 24.3\nIMU 0.1 0.2 9.8\n",[32,65,63],{"__ignoreMap":30},[13,67,68,69,71],{},"本例先不接串口，只在普通 ",[32,70,51],{}," 里模拟解析这些字符串。",[54,73,74],{"id":74},"完整代码",[24,76,80],{"className":77,"code":78,"language":79,"meta":30,"style":30},"language-cpp shiki shiki-themes github-light github-dark","#include \u003Ciostream>\n#include \u003Csstream>\n#include \u003Cstring>\n\nvoid parse_line(const std::string& line)\n{\n    std::istringstream iss(line);\n    std::string type;\n    iss >> type;\n\n    if (type == \"ODOM\")\n    {\n        double x = 0.0;\n        double y = 0.0;\n        double yaw = 0.0;\n        iss >> x >> y >> yaw;\n\n        std::cout \u003C\u003C \"解析里程计：x = \" \u003C\u003C x\n                  \u003C\u003C \"，y = \" \u003C\u003C y\n                  \u003C\u003C \"，yaw = \" \u003C\u003C yaw \u003C\u003C std::endl;\n    }\n    else if (type == \"BAT\")\n    {\n        double voltage = 0.0;\n        iss >> voltage;\n\n        std::cout \u003C\u003C \"解析电池电压：\" \u003C\u003C voltage \u003C\u003C \" V\" \u003C\u003C std::endl;\n    }\n    else if (type == \"IMU\")\n    {\n        double ax = 0.0;\n        double ay = 0.0;\n        double az = 0.0;\n        iss >> ax >> ay >> az;\n\n        std::cout \u003C\u003C \"解析 IMU 加速度：\"\n                  \u003C\u003C ax \u003C\u003C \", \" \u003C\u003C ay \u003C\u003C \", \" \u003C\u003C az \u003C\u003C std::endl;\n    }\n    else\n    {\n        std::cout \u003C\u003C \"未知协议行：\" \u003C\u003C line \u003C\u003C std::endl;\n    }\n}\n\nint main()\n{\n    // 程序从 main 函数开始执行，下面的语句会按顺序运行。\n    std::cout \u003C\u003C \"main：开始模拟协议解析\" \u003C\u003C std::endl;\n\n    parse_line(\"ODOM 1.0 2.0 0.5\");\n    parse_line(\"BAT 24.3\");\n    parse_line(\"IMU 0.1 0.2 9.8\");\n    parse_line(\"HELLO STM32\");\n\n    std::cout \u003C\u003C \"main：结束\" \u003C\u003C std::endl;\n\n    // 返回 0 表示程序正常结束。\n    return 0;\n}\n","cpp",[32,81,82,95,103,111,118,154,160,175,183,195,200,217,223,242,256,270,289,294,315,329,348,354,372,377,391,401,406,433,438,454,459,473,487,501,519,524,536,566,571,577,582,605,610,616,621,633,638,645,663,668,682,694,706,718,723,741,746,752,763],{"__ignoreMap":30},[83,84,87,91],"span",{"class":85,"line":86},"line",1,[83,88,90],{"class":89},"szBVR","#include",[83,92,94],{"class":93},"sZZnC"," \u003Ciostream>\n",[83,96,98,100],{"class":85,"line":97},2,[83,99,90],{"class":89},[83,101,102],{"class":93}," \u003Csstream>\n",[83,104,106,108],{"class":85,"line":105},3,[83,107,90],{"class":89},[83,109,110],{"class":93}," \u003Cstring>\n",[83,112,114],{"class":85,"line":113},4,[83,115,117],{"emptyLinePlaceholder":116},true,"\n",[83,119,121,124,128,132,135,138,141,144,147,151],{"class":85,"line":120},5,[83,122,123],{"class":89},"void",[83,125,127],{"class":126},"sScJk"," parse_line",[83,129,131],{"class":130},"sVt8B","(",[83,133,134],{"class":89},"const",[83,136,137],{"class":126}," std",[83,139,140],{"class":130},"::",[83,142,143],{"class":126},"string",[83,145,146],{"class":89},"&",[83,148,150],{"class":149},"s4XuR"," line",[83,152,153],{"class":130},")\n",[83,155,157],{"class":85,"line":156},6,[83,158,159],{"class":130},"{\n",[83,161,163,166,169,172],{"class":85,"line":162},7,[83,164,165],{"class":126},"    std",[83,167,168],{"class":130},"::istringstream ",[83,170,171],{"class":126},"iss",[83,173,174],{"class":130},"(line);\n",[83,176,178,180],{"class":85,"line":177},8,[83,179,165],{"class":126},[83,181,182],{"class":130},"::string type;\n",[83,184,186,189,192],{"class":85,"line":185},9,[83,187,188],{"class":130},"    iss ",[83,190,191],{"class":89},">>",[83,193,194],{"class":130}," type;\n",[83,196,198],{"class":85,"line":197},10,[83,199,117],{"emptyLinePlaceholder":116},[83,201,203,206,209,212,215],{"class":85,"line":202},11,[83,204,205],{"class":89},"    if",[83,207,208],{"class":130}," (type ",[83,210,211],{"class":89},"==",[83,213,214],{"class":93}," \"ODOM\"",[83,216,153],{"class":130},[83,218,220],{"class":85,"line":219},12,[83,221,222],{"class":130},"    {\n",[83,224,226,229,232,235,239],{"class":85,"line":225},13,[83,227,228],{"class":89},"        double",[83,230,231],{"class":130}," x ",[83,233,234],{"class":89},"=",[83,236,238],{"class":237},"sj4cs"," 0.0",[83,240,241],{"class":130},";\n",[83,243,245,247,250,252,254],{"class":85,"line":244},14,[83,246,228],{"class":89},[83,248,249],{"class":130}," y ",[83,251,234],{"class":89},[83,253,238],{"class":237},[83,255,241],{"class":130},[83,257,259,261,264,266,268],{"class":85,"line":258},15,[83,260,228],{"class":89},[83,262,263],{"class":130}," yaw ",[83,265,234],{"class":89},[83,267,238],{"class":237},[83,269,241],{"class":130},[83,271,273,276,278,280,282,284,286],{"class":85,"line":272},16,[83,274,275],{"class":130},"        iss ",[83,277,191],{"class":89},[83,279,231],{"class":130},[83,281,191],{"class":89},[83,283,249],{"class":130},[83,285,191],{"class":89},[83,287,288],{"class":130}," yaw;\n",[83,290,292],{"class":85,"line":291},17,[83,293,117],{"emptyLinePlaceholder":116},[83,295,297,300,303,306,309,312],{"class":85,"line":296},18,[83,298,299],{"class":126},"        std",[83,301,302],{"class":130},"::cout ",[83,304,305],{"class":89},"\u003C\u003C",[83,307,308],{"class":93}," \"解析里程计：x = \"",[83,310,311],{"class":89}," \u003C\u003C",[83,313,314],{"class":130}," x\n",[83,316,318,321,324,326],{"class":85,"line":317},19,[83,319,320],{"class":89},"                  \u003C\u003C",[83,322,323],{"class":93}," \"，y = \"",[83,325,311],{"class":89},[83,327,328],{"class":130}," y\n",[83,330,332,334,337,339,341,343,345],{"class":85,"line":331},20,[83,333,320],{"class":89},[83,335,336],{"class":93}," \"，yaw = \"",[83,338,311],{"class":89},[83,340,263],{"class":130},[83,342,305],{"class":89},[83,344,137],{"class":126},[83,346,347],{"class":130},"::endl;\n",[83,349,351],{"class":85,"line":350},21,[83,352,353],{"class":130},"    }\n",[83,355,357,360,363,365,367,370],{"class":85,"line":356},22,[83,358,359],{"class":89},"    else",[83,361,362],{"class":89}," if",[83,364,208],{"class":130},[83,366,211],{"class":89},[83,368,369],{"class":93}," \"BAT\"",[83,371,153],{"class":130},[83,373,375],{"class":85,"line":374},23,[83,376,222],{"class":130},[83,378,380,382,385,387,389],{"class":85,"line":379},24,[83,381,228],{"class":89},[83,383,384],{"class":130}," voltage ",[83,386,234],{"class":89},[83,388,238],{"class":237},[83,390,241],{"class":130},[83,392,394,396,398],{"class":85,"line":393},25,[83,395,275],{"class":130},[83,397,191],{"class":89},[83,399,400],{"class":130}," voltage;\n",[83,402,404],{"class":85,"line":403},26,[83,405,117],{"emptyLinePlaceholder":116},[83,407,409,411,413,415,418,420,422,424,427,429,431],{"class":85,"line":408},27,[83,410,299],{"class":126},[83,412,302],{"class":130},[83,414,305],{"class":89},[83,416,417],{"class":93}," \"解析电池电压：\"",[83,419,311],{"class":89},[83,421,384],{"class":130},[83,423,305],{"class":89},[83,425,426],{"class":93}," \" V\"",[83,428,311],{"class":89},[83,430,137],{"class":126},[83,432,347],{"class":130},[83,434,436],{"class":85,"line":435},28,[83,437,353],{"class":130},[83,439,441,443,445,447,449,452],{"class":85,"line":440},29,[83,442,359],{"class":89},[83,444,362],{"class":89},[83,446,208],{"class":130},[83,448,211],{"class":89},[83,450,451],{"class":93}," \"IMU\"",[83,453,153],{"class":130},[83,455,457],{"class":85,"line":456},30,[83,458,222],{"class":130},[83,460,462,464,467,469,471],{"class":85,"line":461},31,[83,463,228],{"class":89},[83,465,466],{"class":130}," ax ",[83,468,234],{"class":89},[83,470,238],{"class":237},[83,472,241],{"class":130},[83,474,476,478,481,483,485],{"class":85,"line":475},32,[83,477,228],{"class":89},[83,479,480],{"class":130}," ay ",[83,482,234],{"class":89},[83,484,238],{"class":237},[83,486,241],{"class":130},[83,488,490,492,495,497,499],{"class":85,"line":489},33,[83,491,228],{"class":89},[83,493,494],{"class":130}," az ",[83,496,234],{"class":89},[83,498,238],{"class":237},[83,500,241],{"class":130},[83,502,504,506,508,510,512,514,516],{"class":85,"line":503},34,[83,505,275],{"class":130},[83,507,191],{"class":89},[83,509,466],{"class":130},[83,511,191],{"class":89},[83,513,480],{"class":130},[83,515,191],{"class":89},[83,517,518],{"class":130}," az;\n",[83,520,522],{"class":85,"line":521},35,[83,523,117],{"emptyLinePlaceholder":116},[83,525,527,529,531,533],{"class":85,"line":526},36,[83,528,299],{"class":126},[83,530,302],{"class":130},[83,532,305],{"class":89},[83,534,535],{"class":93}," \"解析 IMU 加速度：\"\n",[83,537,539,541,543,545,548,550,552,554,556,558,560,562,564],{"class":85,"line":538},37,[83,540,320],{"class":89},[83,542,466],{"class":130},[83,544,305],{"class":89},[83,546,547],{"class":93}," \", \"",[83,549,311],{"class":89},[83,551,480],{"class":130},[83,553,305],{"class":89},[83,555,547],{"class":93},[83,557,311],{"class":89},[83,559,494],{"class":130},[83,561,305],{"class":89},[83,563,137],{"class":126},[83,565,347],{"class":130},[83,567,569],{"class":85,"line":568},38,[83,570,353],{"class":130},[83,572,574],{"class":85,"line":573},39,[83,575,576],{"class":89},"    else\n",[83,578,580],{"class":85,"line":579},40,[83,581,222],{"class":130},[83,583,585,587,589,591,594,596,599,601,603],{"class":85,"line":584},41,[83,586,299],{"class":126},[83,588,302],{"class":130},[83,590,305],{"class":89},[83,592,593],{"class":93}," \"未知协议行：\"",[83,595,311],{"class":89},[83,597,598],{"class":130}," line ",[83,600,305],{"class":89},[83,602,137],{"class":126},[83,604,347],{"class":130},[83,606,608],{"class":85,"line":607},42,[83,609,353],{"class":130},[83,611,613],{"class":85,"line":612},43,[83,614,615],{"class":130},"}\n",[83,617,619],{"class":85,"line":618},44,[83,620,117],{"emptyLinePlaceholder":116},[83,622,624,627,630],{"class":85,"line":623},45,[83,625,626],{"class":89},"int",[83,628,629],{"class":126}," main",[83,631,632],{"class":130},"()\n",[83,634,636],{"class":85,"line":635},46,[83,637,159],{"class":130},[83,639,641],{"class":85,"line":640},47,[83,642,644],{"class":643},"sJ8bj","    // 程序从 main 函数开始执行，下面的语句会按顺序运行。\n",[83,646,648,650,652,654,657,659,661],{"class":85,"line":647},48,[83,649,165],{"class":126},[83,651,302],{"class":130},[83,653,305],{"class":89},[83,655,656],{"class":93}," \"main：开始模拟协议解析\"",[83,658,311],{"class":89},[83,660,137],{"class":126},[83,662,347],{"class":130},[83,664,666],{"class":85,"line":665},49,[83,667,117],{"emptyLinePlaceholder":116},[83,669,671,674,676,679],{"class":85,"line":670},50,[83,672,673],{"class":126},"    parse_line",[83,675,131],{"class":130},[83,677,678],{"class":93},"\"ODOM 1.0 2.0 0.5\"",[83,680,681],{"class":130},");\n",[83,683,685,687,689,692],{"class":85,"line":684},51,[83,686,673],{"class":126},[83,688,131],{"class":130},[83,690,691],{"class":93},"\"BAT 24.3\"",[83,693,681],{"class":130},[83,695,697,699,701,704],{"class":85,"line":696},52,[83,698,673],{"class":126},[83,700,131],{"class":130},[83,702,703],{"class":93},"\"IMU 0.1 0.2 9.8\"",[83,705,681],{"class":130},[83,707,709,711,713,716],{"class":85,"line":708},53,[83,710,673],{"class":126},[83,712,131],{"class":130},[83,714,715],{"class":93},"\"HELLO STM32\"",[83,717,681],{"class":130},[83,719,721],{"class":85,"line":720},54,[83,722,117],{"emptyLinePlaceholder":116},[83,724,726,728,730,732,735,737,739],{"class":85,"line":725},55,[83,727,165],{"class":126},[83,729,302],{"class":130},[83,731,305],{"class":89},[83,733,734],{"class":93}," \"main：结束\"",[83,736,311],{"class":89},[83,738,137],{"class":126},[83,740,347],{"class":130},[83,742,744],{"class":85,"line":743},56,[83,745,117],{"emptyLinePlaceholder":116},[83,747,749],{"class":85,"line":748},57,[83,750,751],{"class":643},"    // 返回 0 表示程序正常结束。\n",[83,753,755,758,761],{"class":85,"line":754},58,[83,756,757],{"class":89},"    return",[83,759,760],{"class":237}," 0",[83,762,241],{"class":130},[83,764,766],{"class":85,"line":765},59,[83,767,615],{"class":130},[13,769,770,774],{},[771,772,773],"strong",{},"运行结果","：见下方“运行输出与时间顺序”；如果示例涉及定时器、线程、网络或外部设备，具体时间和顺序可能会随环境略有变化。",[54,776,777],{"id":777},"编译运行",[24,779,783],{"className":780,"code":781,"language":782,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark","g++ demo1_protocol_parse.cpp -o demo1_protocol_parse -std=c++17\n./demo1_protocol_parse\n","bash",[32,784,785,802],{"__ignoreMap":30},[83,786,787,790,793,796,799],{"class":85,"line":86},[83,788,789],{"class":126},"g++",[83,791,792],{"class":93}," demo1_protocol_parse.cpp",[83,794,795],{"class":237}," -o",[83,797,798],{"class":93}," demo1_protocol_parse",[83,800,801],{"class":237}," -std=c++17\n",[83,803,804],{"class":85,"line":97},[83,805,806],{"class":126},"./demo1_protocol_parse\n",[54,808,809],{"id":809},"运行输出",[24,811,814],{"className":812,"code":813,"language":29,"meta":30},[27],"main：开始模拟协议解析\n解析里程计：x = 1，y = 2，yaw = 0.5\n解析电池电压：24.3 V\n解析 IMU 加速度：0.1, 0.2, 9.8\n未知协议行：HELLO STM32\nmain：结束\n",[32,815,813],{"__ignoreMap":30},[54,817,818],{"id":818},"本示例需要注意的点",[13,820,821],{},"在接串口之前，先把协议解析函数单独写好、单独测试。这样调试效率更高。",[13,823,824],{},"不要一上来就把：",[24,826,829],{"className":827,"code":828,"language":29,"meta":30},[27],"串口读取\n协议解析\nROS2 发布\n控制逻辑\n",[32,830,828],{"__ignoreMap":30},[13,832,833],{},"全部混在一个函数里。",[42,835],{},[45,837,839],{"id":838},"示例-2类里封装机器人协议解析器","示例 2：类里封装机器人协议解析器",[54,841,56],{"id":842},"程序目标-1",[13,844,845],{},"把示例 1 改成类封装，为后面接串口回调做准备。",[54,847,74],{"id":848},"完整代码-1",[24,850,852],{"className":77,"code":851,"language":79,"meta":30,"style":30},"#include \u003Ciostream>\n#include \u003Csstream>\n#include \u003Cstring>\n\nclass RobotProtocolParser\n{\npublic:\n    void parse_line(const std::string& line)\n    {\n        std::istringstream iss(line);\n        std::string type;\n        iss >> type;\n\n        if (type == \"ODOM\")\n        {\n            parse_odom(iss);\n        }\n        else if (type == \"BAT\")\n        {\n            parse_battery(iss);\n        }\n        else\n        {\n            std::cout \u003C\u003C \"未知协议行：\" \u003C\u003C line \u003C\u003C std::endl;\n        }\n    }\n\nprivate:\n    void parse_odom(std::istringstream& iss)\n    {\n        double x = 0.0;\n        double y = 0.0;\n        double yaw = 0.0;\n        iss >> x >> y >> yaw;\n\n        std::cout \u003C\u003C \"RobotProtocolParser：ODOM x = \" \u003C\u003C x\n                  \u003C\u003C \"，y = \" \u003C\u003C y\n                  \u003C\u003C \"，yaw = \" \u003C\u003C yaw \u003C\u003C std::endl;\n    }\n\n    void parse_battery(std::istringstream& iss)\n    {\n        double voltage = 0.0;\n        iss >> voltage;\n\n        std::cout \u003C\u003C \"RobotProtocolParser：BAT voltage = \"\n                  \u003C\u003C voltage \u003C\u003C \" V\" \u003C\u003C std::endl;\n    }\n};\n\nint main()\n{\n    // 程序从 main 函数开始执行，下面的语句会按顺序运行。\n    RobotProtocolParser parser;\n\n    std::cout \u003C\u003C \"main：开始解析\" \u003C\u003C std::endl;\n\n    parser.parse_line(\"ODOM 0.1 0.2 0.3\");\n    parser.parse_line(\"BAT 23.8\");\n    parser.parse_line(\"ERROR abc\");\n\n    std::cout \u003C\u003C \"main：结束\" \u003C\u003C std::endl;\n\n    // 返回 0 表示程序正常结束。\n    return 0;\n}\n",[32,853,854,860,866,872,876,884,888,893,916,920,930,936,944,948,961,966,974,979,994,998,1005,1009,1014,1018,1039,1043,1047,1051,1056,1080,1084,1096,1108,1120,1136,1140,1155,1165,1181,1185,1189,1210,1214,1226,1234,1238,1249,1265,1269,1274,1278,1286,1290,1294,1299,1303,1320,1324,1339,1352,1366,1371,1388,1393,1398,1407],{"__ignoreMap":30},[83,855,856,858],{"class":85,"line":86},[83,857,90],{"class":89},[83,859,94],{"class":93},[83,861,862,864],{"class":85,"line":97},[83,863,90],{"class":89},[83,865,102],{"class":93},[83,867,868,870],{"class":85,"line":105},[83,869,90],{"class":89},[83,871,110],{"class":93},[83,873,874],{"class":85,"line":113},[83,875,117],{"emptyLinePlaceholder":116},[83,877,878,881],{"class":85,"line":120},[83,879,880],{"class":89},"class",[83,882,883],{"class":126}," RobotProtocolParser\n",[83,885,886],{"class":85,"line":156},[83,887,159],{"class":130},[83,889,890],{"class":85,"line":162},[83,891,892],{"class":89},"public:\n",[83,894,895,898,900,902,904,906,908,910,912,914],{"class":85,"line":177},[83,896,897],{"class":89},"    void",[83,899,127],{"class":126},[83,901,131],{"class":130},[83,903,134],{"class":89},[83,905,137],{"class":126},[83,907,140],{"class":130},[83,909,143],{"class":126},[83,911,146],{"class":89},[83,913,150],{"class":149},[83,915,153],{"class":130},[83,917,918],{"class":85,"line":185},[83,919,222],{"class":130},[83,921,922,924,926,928],{"class":85,"line":197},[83,923,299],{"class":126},[83,925,168],{"class":130},[83,927,171],{"class":126},[83,929,174],{"class":130},[83,931,932,934],{"class":85,"line":202},[83,933,299],{"class":126},[83,935,182],{"class":130},[83,937,938,940,942],{"class":85,"line":219},[83,939,275],{"class":130},[83,941,191],{"class":89},[83,943,194],{"class":130},[83,945,946],{"class":85,"line":225},[83,947,117],{"emptyLinePlaceholder":116},[83,949,950,953,955,957,959],{"class":85,"line":244},[83,951,952],{"class":89},"        if",[83,954,208],{"class":130},[83,956,211],{"class":89},[83,958,214],{"class":93},[83,960,153],{"class":130},[83,962,963],{"class":85,"line":258},[83,964,965],{"class":130},"        {\n",[83,967,968,971],{"class":85,"line":272},[83,969,970],{"class":126},"            parse_odom",[83,972,973],{"class":130},"(iss);\n",[83,975,976],{"class":85,"line":291},[83,977,978],{"class":130},"        }\n",[83,980,981,984,986,988,990,992],{"class":85,"line":296},[83,982,983],{"class":89},"        else",[83,985,362],{"class":89},[83,987,208],{"class":130},[83,989,211],{"class":89},[83,991,369],{"class":93},[83,993,153],{"class":130},[83,995,996],{"class":85,"line":317},[83,997,965],{"class":130},[83,999,1000,1003],{"class":85,"line":331},[83,1001,1002],{"class":126},"            parse_battery",[83,1004,973],{"class":130},[83,1006,1007],{"class":85,"line":350},[83,1008,978],{"class":130},[83,1010,1011],{"class":85,"line":356},[83,1012,1013],{"class":89},"        else\n",[83,1015,1016],{"class":85,"line":374},[83,1017,965],{"class":130},[83,1019,1020,1023,1025,1027,1029,1031,1033,1035,1037],{"class":85,"line":379},[83,1021,1022],{"class":126},"            std",[83,1024,302],{"class":130},[83,1026,305],{"class":89},[83,1028,593],{"class":93},[83,1030,311],{"class":89},[83,1032,598],{"class":130},[83,1034,305],{"class":89},[83,1036,137],{"class":126},[83,1038,347],{"class":130},[83,1040,1041],{"class":85,"line":393},[83,1042,978],{"class":130},[83,1044,1045],{"class":85,"line":403},[83,1046,353],{"class":130},[83,1048,1049],{"class":85,"line":408},[83,1050,117],{"emptyLinePlaceholder":116},[83,1052,1053],{"class":85,"line":435},[83,1054,1055],{"class":89},"private:\n",[83,1057,1058,1060,1063,1065,1068,1070,1073,1075,1078],{"class":85,"line":440},[83,1059,897],{"class":89},[83,1061,1062],{"class":126}," parse_odom",[83,1064,131],{"class":130},[83,1066,1067],{"class":126},"std",[83,1069,140],{"class":130},[83,1071,1072],{"class":126},"istringstream",[83,1074,146],{"class":89},[83,1076,1077],{"class":149}," iss",[83,1079,153],{"class":130},[83,1081,1082],{"class":85,"line":456},[83,1083,222],{"class":130},[83,1085,1086,1088,1090,1092,1094],{"class":85,"line":461},[83,1087,228],{"class":89},[83,1089,231],{"class":130},[83,1091,234],{"class":89},[83,1093,238],{"class":237},[83,1095,241],{"class":130},[83,1097,1098,1100,1102,1104,1106],{"class":85,"line":475},[83,1099,228],{"class":89},[83,1101,249],{"class":130},[83,1103,234],{"class":89},[83,1105,238],{"class":237},[83,1107,241],{"class":130},[83,1109,1110,1112,1114,1116,1118],{"class":85,"line":489},[83,1111,228],{"class":89},[83,1113,263],{"class":130},[83,1115,234],{"class":89},[83,1117,238],{"class":237},[83,1119,241],{"class":130},[83,1121,1122,1124,1126,1128,1130,1132,1134],{"class":85,"line":503},[83,1123,275],{"class":130},[83,1125,191],{"class":89},[83,1127,231],{"class":130},[83,1129,191],{"class":89},[83,1131,249],{"class":130},[83,1133,191],{"class":89},[83,1135,288],{"class":130},[83,1137,1138],{"class":85,"line":521},[83,1139,117],{"emptyLinePlaceholder":116},[83,1141,1142,1144,1146,1148,1151,1153],{"class":85,"line":526},[83,1143,299],{"class":126},[83,1145,302],{"class":130},[83,1147,305],{"class":89},[83,1149,1150],{"class":93}," \"RobotProtocolParser：ODOM x = \"",[83,1152,311],{"class":89},[83,1154,314],{"class":130},[83,1156,1157,1159,1161,1163],{"class":85,"line":538},[83,1158,320],{"class":89},[83,1160,323],{"class":93},[83,1162,311],{"class":89},[83,1164,328],{"class":130},[83,1166,1167,1169,1171,1173,1175,1177,1179],{"class":85,"line":568},[83,1168,320],{"class":89},[83,1170,336],{"class":93},[83,1172,311],{"class":89},[83,1174,263],{"class":130},[83,1176,305],{"class":89},[83,1178,137],{"class":126},[83,1180,347],{"class":130},[83,1182,1183],{"class":85,"line":573},[83,1184,353],{"class":130},[83,1186,1187],{"class":85,"line":579},[83,1188,117],{"emptyLinePlaceholder":116},[83,1190,1191,1193,1196,1198,1200,1202,1204,1206,1208],{"class":85,"line":584},[83,1192,897],{"class":89},[83,1194,1195],{"class":126}," parse_battery",[83,1197,131],{"class":130},[83,1199,1067],{"class":126},[83,1201,140],{"class":130},[83,1203,1072],{"class":126},[83,1205,146],{"class":89},[83,1207,1077],{"class":149},[83,1209,153],{"class":130},[83,1211,1212],{"class":85,"line":607},[83,1213,222],{"class":130},[83,1215,1216,1218,1220,1222,1224],{"class":85,"line":612},[83,1217,228],{"class":89},[83,1219,384],{"class":130},[83,1221,234],{"class":89},[83,1223,238],{"class":237},[83,1225,241],{"class":130},[83,1227,1228,1230,1232],{"class":85,"line":618},[83,1229,275],{"class":130},[83,1231,191],{"class":89},[83,1233,400],{"class":130},[83,1235,1236],{"class":85,"line":623},[83,1237,117],{"emptyLinePlaceholder":116},[83,1239,1240,1242,1244,1246],{"class":85,"line":635},[83,1241,299],{"class":126},[83,1243,302],{"class":130},[83,1245,305],{"class":89},[83,1247,1248],{"class":93}," \"RobotProtocolParser：BAT voltage = \"\n",[83,1250,1251,1253,1255,1257,1259,1261,1263],{"class":85,"line":640},[83,1252,320],{"class":89},[83,1254,384],{"class":130},[83,1256,305],{"class":89},[83,1258,426],{"class":93},[83,1260,311],{"class":89},[83,1262,137],{"class":126},[83,1264,347],{"class":130},[83,1266,1267],{"class":85,"line":647},[83,1268,353],{"class":130},[83,1270,1271],{"class":85,"line":665},[83,1272,1273],{"class":130},"};\n",[83,1275,1276],{"class":85,"line":670},[83,1277,117],{"emptyLinePlaceholder":116},[83,1279,1280,1282,1284],{"class":85,"line":684},[83,1281,626],{"class":89},[83,1283,629],{"class":126},[83,1285,632],{"class":130},[83,1287,1288],{"class":85,"line":696},[83,1289,159],{"class":130},[83,1291,1292],{"class":85,"line":708},[83,1293,644],{"class":643},[83,1295,1296],{"class":85,"line":720},[83,1297,1298],{"class":130},"    RobotProtocolParser parser;\n",[83,1300,1301],{"class":85,"line":725},[83,1302,117],{"emptyLinePlaceholder":116},[83,1304,1305,1307,1309,1311,1314,1316,1318],{"class":85,"line":743},[83,1306,165],{"class":126},[83,1308,302],{"class":130},[83,1310,305],{"class":89},[83,1312,1313],{"class":93}," \"main：开始解析\"",[83,1315,311],{"class":89},[83,1317,137],{"class":126},[83,1319,347],{"class":130},[83,1321,1322],{"class":85,"line":748},[83,1323,117],{"emptyLinePlaceholder":116},[83,1325,1326,1329,1332,1334,1337],{"class":85,"line":754},[83,1327,1328],{"class":130},"    parser.",[83,1330,1331],{"class":126},"parse_line",[83,1333,131],{"class":130},[83,1335,1336],{"class":93},"\"ODOM 0.1 0.2 0.3\"",[83,1338,681],{"class":130},[83,1340,1341,1343,1345,1347,1350],{"class":85,"line":765},[83,1342,1328],{"class":130},[83,1344,1331],{"class":126},[83,1346,131],{"class":130},[83,1348,1349],{"class":93},"\"BAT 23.8\"",[83,1351,681],{"class":130},[83,1353,1355,1357,1359,1361,1364],{"class":85,"line":1354},60,[83,1356,1328],{"class":130},[83,1358,1331],{"class":126},[83,1360,131],{"class":130},[83,1362,1363],{"class":93},"\"ERROR abc\"",[83,1365,681],{"class":130},[83,1367,1369],{"class":85,"line":1368},61,[83,1370,117],{"emptyLinePlaceholder":116},[83,1372,1374,1376,1378,1380,1382,1384,1386],{"class":85,"line":1373},62,[83,1375,165],{"class":126},[83,1377,302],{"class":130},[83,1379,305],{"class":89},[83,1381,734],{"class":93},[83,1383,311],{"class":89},[83,1385,137],{"class":126},[83,1387,347],{"class":130},[83,1389,1391],{"class":85,"line":1390},63,[83,1392,117],{"emptyLinePlaceholder":116},[83,1394,1396],{"class":85,"line":1395},64,[83,1397,751],{"class":643},[83,1399,1401,1403,1405],{"class":85,"line":1400},65,[83,1402,757],{"class":89},[83,1404,760],{"class":237},[83,1406,241],{"class":130},[83,1408,1410],{"class":85,"line":1409},66,[83,1411,615],{"class":130},[13,1413,1414,774],{},[771,1415,773],{},[54,1417,777],{"id":1418},"编译运行-1",[24,1420,1422],{"className":780,"code":1421,"language":782,"meta":30,"style":30},"g++ demo2_protocol_parser_class.cpp -o demo2_protocol_parser_class -std=c++17\n./demo2_protocol_parser_class\n",[32,1423,1424,1438],{"__ignoreMap":30},[83,1425,1426,1428,1431,1433,1436],{"class":85,"line":86},[83,1427,789],{"class":126},[83,1429,1430],{"class":93}," demo2_protocol_parser_class.cpp",[83,1432,795],{"class":237},[83,1434,1435],{"class":93}," demo2_protocol_parser_class",[83,1437,801],{"class":237},[83,1439,1440],{"class":85,"line":97},[83,1441,1442],{"class":126},"./demo2_protocol_parser_class\n",[54,1444,809],{"id":1445},"运行输出-1",[24,1447,1450],{"className":1448,"code":1449,"language":29,"meta":30},[27],"main：开始解析\nRobotProtocolParser：ODOM x = 0.1，y = 0.2，yaw = 0.3\nRobotProtocolParser：BAT voltage = 23.8 V\n未知协议行：ERROR abc\nmain：结束\n",[32,1451,1449],{"__ignoreMap":30},[54,1453,818],{"id":1454},"本示例需要注意的点-1",[13,1456,1457],{},"协议解析器不应该依赖 Boost.Asio，也不应该依赖 ROS2。",[13,1459,1460,1461,1463],{},"它最好是一个纯 C++ 类。这样你可以用普通 ",[32,1462,51],{}," 或单元测试直接验证它。",[42,1465],{},[45,1467,1469],{"id":1468},"示例-3asio-串口读取-协议解析器组合","示例 3：Asio 串口读取 + 协议解析器组合",[54,1471,56],{"id":1472},"程序目标-2",[13,1474,1475],{},"把前面的异步串口读取和协议解析器组合起来：",[24,1477,1480],{"className":1478,"code":1479,"language":29,"meta":30},[27],"串口收到一行\nSerialRobotDriver::on_read() 被调用\n取出字符串\n交给 RobotProtocolParser 解析\n继续注册下一次串口读取\n",[32,1481,1479],{"__ignoreMap":30},[54,1483,74],{"id":1484},"完整代码-2",[24,1486,1488],{"className":77,"code":1487,"language":79,"meta":30,"style":30},"#include \u003Cboost/asio.hpp>\n#include \u003Cboost/system/error_code.hpp>\n#include \u003Cfunctional>\n#include \u003Ciostream>\n#include \u003Cistream>\n#include \u003Csstream>\n#include \u003Cstring>\n\nclass RobotProtocolParser\n{\npublic:\n    void parse_line(const std::string& line)\n    {\n        std::istringstream iss(line);\n        std::string type;\n        iss >> type;\n\n        if (type == \"ODOM\")\n        {\n            double x = 0.0;\n            double y = 0.0;\n            double yaw = 0.0;\n            iss >> x >> y >> yaw;\n\n            std::cout \u003C\u003C \"解析 ODOM：x = \" \u003C\u003C x\n                      \u003C\u003C \"，y = \" \u003C\u003C y\n                      \u003C\u003C \"，yaw = \" \u003C\u003C yaw \u003C\u003C std::endl;\n        }\n        else if (type == \"BAT\")\n        {\n            double voltage = 0.0;\n            iss >> voltage;\n\n            std::cout \u003C\u003C \"解析 BAT：\" \u003C\u003C voltage \u003C\u003C \" V\" \u003C\u003C std::endl;\n        }\n        else\n        {\n            std::cout \u003C\u003C \"未知数据：\" \u003C\u003C line \u003C\u003C std::endl;\n        }\n    }\n};\n\nclass SerialRobotDriver\n{\npublic:\n    SerialRobotDriver(boost::asio::io_context& io, const std::string& port_name)\n        : serial_(io)\n    {\n        boost::system::error_code ec;\n\n        serial_.open(port_name, ec);\n        if (ec)\n        {\n            std::cout \u003C\u003C \"打开串口失败：\" \u003C\u003C ec.message() \u003C\u003C std::endl;\n            return;\n        }\n\n        serial_.set_option(boost::asio::serial_port_base::baud_rate(115200));\n\n        std::cout \u003C\u003C \"SerialRobotDriver：串口打开成功\" \u003C\u003C std::endl;\n\n        start_read();\n    }\n\nprivate:\n    void start_read()\n    {\n        boost::asio::async_read_until(serial_,\n                                      buffer_,\n                                      '\\n',\n                                      std::bind(&SerialRobotDriver::on_read,\n                                                this,\n                                                std::placeholders::_1,\n                                                std::placeholders::_2));\n    }\n\n    void on_read(const boost::system::error_code& ec, std::size_t bytes_transferred)\n    {\n        if (ec)\n        {\n            std::cout \u003C\u003C \"串口读取错误：\" \u003C\u003C ec.message() \u003C\u003C std::endl;\n            return;\n        }\n\n        std::istream is(&buffer_);\n        std::string line;\n        std::getline(is, line);\n\n        std::cout \u003C\u003C \"收到串口行，bytes = \" \u003C\u003C bytes_transferred\n                  \u003C\u003C \"，line = \" \u003C\u003C line \u003C\u003C std::endl;\n\n        parser_.parse_line(line);\n\n        start_read();\n    }\n\nprivate:\n    // serial_port 表示串口设备，后续读写都通过它完成。\n    boost::asio::serial_port serial_;\n    boost::asio::streambuf buffer_;\n    RobotProtocolParser parser_;\n};\n\nint main(int argc, char* argv[])\n{\n    // 程序从 main 函数开始执行，argc/argv 用来接收命令行参数。\n    std::string port_name = \"/dev/pts/3\";\n\n    if (argc >= 2)\n    {\n        port_name = argv[1];\n    }\n\n    // io_context 是 Asio 的事件循环对象，异步任务需要靠它调度。\n    boost::asio::io_context io;\n    SerialRobotDriver driver(io, port_name);\n\n    std::cout \u003C\u003C \"main：开始 io.run()\" \u003C\u003C std::endl;\n\n    // 启动事件循环，前面注册的异步任务会在这里被调度执行。\n    io.run();\n\n    std::cout \u003C\u003C \"main：io.run() 返回\" \u003C\u003C std::endl;\n\n    return 0;\n}\n",[32,1489,1490,1497,1504,1511,1517,1524,1530,1536,1540,1546,1550,1554,1576,1580,1590,1596,1604,1608,1620,1624,1637,1649,1661,1678,1682,1697,1708,1724,1728,1742,1746,1758,1766,1770,1795,1799,1803,1807,1828,1832,1836,1840,1844,1851,1855,1859,1902,1913,1917,1930,1934,1945,1952,1956,1984,1991,1995,1999,2032,2036,2053,2057,2065,2069,2073,2077,2086,2091,2108,2114,2129,2150,2158,2172,2184,2189,2194,2237,2242,2249,2254,2280,2287,2292,2297,2315,2323,2336,2341,2358,2376,2381,2391,2396,2403,2408,2413,2418,2424,2437,2449,2455,2460,2465,2490,2495,2501,2516,2521,2537,2542,2559,2564,2569,2575,2587,2599,2604,2622,2627,2633,2644,2649,2667,2672,2681],{"__ignoreMap":30},[83,1491,1492,1494],{"class":85,"line":86},[83,1493,90],{"class":89},[83,1495,1496],{"class":93}," \u003Cboost/asio.hpp>\n",[83,1498,1499,1501],{"class":85,"line":97},[83,1500,90],{"class":89},[83,1502,1503],{"class":93}," \u003Cboost/system/error_code.hpp>\n",[83,1505,1506,1508],{"class":85,"line":105},[83,1507,90],{"class":89},[83,1509,1510],{"class":93}," \u003Cfunctional>\n",[83,1512,1513,1515],{"class":85,"line":113},[83,1514,90],{"class":89},[83,1516,94],{"class":93},[83,1518,1519,1521],{"class":85,"line":120},[83,1520,90],{"class":89},[83,1522,1523],{"class":93}," \u003Cistream>\n",[83,1525,1526,1528],{"class":85,"line":156},[83,1527,90],{"class":89},[83,1529,102],{"class":93},[83,1531,1532,1534],{"class":85,"line":162},[83,1533,90],{"class":89},[83,1535,110],{"class":93},[83,1537,1538],{"class":85,"line":177},[83,1539,117],{"emptyLinePlaceholder":116},[83,1541,1542,1544],{"class":85,"line":185},[83,1543,880],{"class":89},[83,1545,883],{"class":126},[83,1547,1548],{"class":85,"line":197},[83,1549,159],{"class":130},[83,1551,1552],{"class":85,"line":202},[83,1553,892],{"class":89},[83,1555,1556,1558,1560,1562,1564,1566,1568,1570,1572,1574],{"class":85,"line":219},[83,1557,897],{"class":89},[83,1559,127],{"class":126},[83,1561,131],{"class":130},[83,1563,134],{"class":89},[83,1565,137],{"class":126},[83,1567,140],{"class":130},[83,1569,143],{"class":126},[83,1571,146],{"class":89},[83,1573,150],{"class":149},[83,1575,153],{"class":130},[83,1577,1578],{"class":85,"line":225},[83,1579,222],{"class":130},[83,1581,1582,1584,1586,1588],{"class":85,"line":244},[83,1583,299],{"class":126},[83,1585,168],{"class":130},[83,1587,171],{"class":126},[83,1589,174],{"class":130},[83,1591,1592,1594],{"class":85,"line":258},[83,1593,299],{"class":126},[83,1595,182],{"class":130},[83,1597,1598,1600,1602],{"class":85,"line":272},[83,1599,275],{"class":130},[83,1601,191],{"class":89},[83,1603,194],{"class":130},[83,1605,1606],{"class":85,"line":291},[83,1607,117],{"emptyLinePlaceholder":116},[83,1609,1610,1612,1614,1616,1618],{"class":85,"line":296},[83,1611,952],{"class":89},[83,1613,208],{"class":130},[83,1615,211],{"class":89},[83,1617,214],{"class":93},[83,1619,153],{"class":130},[83,1621,1622],{"class":85,"line":317},[83,1623,965],{"class":130},[83,1625,1626,1629,1631,1633,1635],{"class":85,"line":331},[83,1627,1628],{"class":89},"            double",[83,1630,231],{"class":130},[83,1632,234],{"class":89},[83,1634,238],{"class":237},[83,1636,241],{"class":130},[83,1638,1639,1641,1643,1645,1647],{"class":85,"line":350},[83,1640,1628],{"class":89},[83,1642,249],{"class":130},[83,1644,234],{"class":89},[83,1646,238],{"class":237},[83,1648,241],{"class":130},[83,1650,1651,1653,1655,1657,1659],{"class":85,"line":356},[83,1652,1628],{"class":89},[83,1654,263],{"class":130},[83,1656,234],{"class":89},[83,1658,238],{"class":237},[83,1660,241],{"class":130},[83,1662,1663,1666,1668,1670,1672,1674,1676],{"class":85,"line":374},[83,1664,1665],{"class":130},"            iss ",[83,1667,191],{"class":89},[83,1669,231],{"class":130},[83,1671,191],{"class":89},[83,1673,249],{"class":130},[83,1675,191],{"class":89},[83,1677,288],{"class":130},[83,1679,1680],{"class":85,"line":379},[83,1681,117],{"emptyLinePlaceholder":116},[83,1683,1684,1686,1688,1690,1693,1695],{"class":85,"line":393},[83,1685,1022],{"class":126},[83,1687,302],{"class":130},[83,1689,305],{"class":89},[83,1691,1692],{"class":93}," \"解析 ODOM：x = \"",[83,1694,311],{"class":89},[83,1696,314],{"class":130},[83,1698,1699,1702,1704,1706],{"class":85,"line":403},[83,1700,1701],{"class":89},"                      \u003C\u003C",[83,1703,323],{"class":93},[83,1705,311],{"class":89},[83,1707,328],{"class":130},[83,1709,1710,1712,1714,1716,1718,1720,1722],{"class":85,"line":408},[83,1711,1701],{"class":89},[83,1713,336],{"class":93},[83,1715,311],{"class":89},[83,1717,263],{"class":130},[83,1719,305],{"class":89},[83,1721,137],{"class":126},[83,1723,347],{"class":130},[83,1725,1726],{"class":85,"line":435},[83,1727,978],{"class":130},[83,1729,1730,1732,1734,1736,1738,1740],{"class":85,"line":440},[83,1731,983],{"class":89},[83,1733,362],{"class":89},[83,1735,208],{"class":130},[83,1737,211],{"class":89},[83,1739,369],{"class":93},[83,1741,153],{"class":130},[83,1743,1744],{"class":85,"line":456},[83,1745,965],{"class":130},[83,1747,1748,1750,1752,1754,1756],{"class":85,"line":461},[83,1749,1628],{"class":89},[83,1751,384],{"class":130},[83,1753,234],{"class":89},[83,1755,238],{"class":237},[83,1757,241],{"class":130},[83,1759,1760,1762,1764],{"class":85,"line":475},[83,1761,1665],{"class":130},[83,1763,191],{"class":89},[83,1765,400],{"class":130},[83,1767,1768],{"class":85,"line":489},[83,1769,117],{"emptyLinePlaceholder":116},[83,1771,1772,1774,1776,1778,1781,1783,1785,1787,1789,1791,1793],{"class":85,"line":503},[83,1773,1022],{"class":126},[83,1775,302],{"class":130},[83,1777,305],{"class":89},[83,1779,1780],{"class":93}," \"解析 BAT：\"",[83,1782,311],{"class":89},[83,1784,384],{"class":130},[83,1786,305],{"class":89},[83,1788,426],{"class":93},[83,1790,311],{"class":89},[83,1792,137],{"class":126},[83,1794,347],{"class":130},[83,1796,1797],{"class":85,"line":521},[83,1798,978],{"class":130},[83,1800,1801],{"class":85,"line":526},[83,1802,1013],{"class":89},[83,1804,1805],{"class":85,"line":538},[83,1806,965],{"class":130},[83,1808,1809,1811,1813,1815,1818,1820,1822,1824,1826],{"class":85,"line":568},[83,1810,1022],{"class":126},[83,1812,302],{"class":130},[83,1814,305],{"class":89},[83,1816,1817],{"class":93}," \"未知数据：\"",[83,1819,311],{"class":89},[83,1821,598],{"class":130},[83,1823,305],{"class":89},[83,1825,137],{"class":126},[83,1827,347],{"class":130},[83,1829,1830],{"class":85,"line":573},[83,1831,978],{"class":130},[83,1833,1834],{"class":85,"line":579},[83,1835,353],{"class":130},[83,1837,1838],{"class":85,"line":584},[83,1839,1273],{"class":130},[83,1841,1842],{"class":85,"line":607},[83,1843,117],{"emptyLinePlaceholder":116},[83,1845,1846,1848],{"class":85,"line":612},[83,1847,880],{"class":89},[83,1849,1850],{"class":126}," SerialRobotDriver\n",[83,1852,1853],{"class":85,"line":618},[83,1854,159],{"class":130},[83,1856,1857],{"class":85,"line":623},[83,1858,892],{"class":89},[83,1860,1861,1864,1866,1869,1871,1874,1876,1879,1881,1884,1887,1889,1891,1893,1895,1897,1900],{"class":85,"line":635},[83,1862,1863],{"class":126},"    SerialRobotDriver",[83,1865,131],{"class":130},[83,1867,1868],{"class":126},"boost",[83,1870,140],{"class":130},[83,1872,1873],{"class":126},"asio",[83,1875,140],{"class":130},[83,1877,1878],{"class":126},"io_context",[83,1880,146],{"class":89},[83,1882,1883],{"class":149}," io",[83,1885,1886],{"class":130},", ",[83,1888,134],{"class":89},[83,1890,137],{"class":126},[83,1892,140],{"class":130},[83,1894,143],{"class":126},[83,1896,146],{"class":89},[83,1898,1899],{"class":149}," port_name",[83,1901,153],{"class":130},[83,1903,1904,1907,1910],{"class":85,"line":640},[83,1905,1906],{"class":130},"        : ",[83,1908,1909],{"class":126},"serial_",[83,1911,1912],{"class":130},"(io)\n",[83,1914,1915],{"class":85,"line":647},[83,1916,222],{"class":130},[83,1918,1919,1922,1924,1927],{"class":85,"line":665},[83,1920,1921],{"class":126},"        boost",[83,1923,140],{"class":130},[83,1925,1926],{"class":126},"system",[83,1928,1929],{"class":130},"::error_code ec;\n",[83,1931,1932],{"class":85,"line":670},[83,1933,117],{"emptyLinePlaceholder":116},[83,1935,1936,1939,1942],{"class":85,"line":684},[83,1937,1938],{"class":130},"        serial_.",[83,1940,1941],{"class":126},"open",[83,1943,1944],{"class":130},"(port_name, ec);\n",[83,1946,1947,1949],{"class":85,"line":696},[83,1948,952],{"class":89},[83,1950,1951],{"class":130}," (ec)\n",[83,1953,1954],{"class":85,"line":708},[83,1955,965],{"class":130},[83,1957,1958,1960,1962,1964,1967,1969,1972,1975,1978,1980,1982],{"class":85,"line":720},[83,1959,1022],{"class":126},[83,1961,302],{"class":130},[83,1963,305],{"class":89},[83,1965,1966],{"class":93}," \"打开串口失败：\"",[83,1968,311],{"class":89},[83,1970,1971],{"class":130}," ec.",[83,1973,1974],{"class":126},"message",[83,1976,1977],{"class":130},"() ",[83,1979,305],{"class":89},[83,1981,137],{"class":126},[83,1983,347],{"class":130},[83,1985,1986,1989],{"class":85,"line":725},[83,1987,1988],{"class":89},"            return",[83,1990,241],{"class":130},[83,1992,1993],{"class":85,"line":743},[83,1994,978],{"class":130},[83,1996,1997],{"class":85,"line":748},[83,1998,117],{"emptyLinePlaceholder":116},[83,2000,2001,2003,2006,2008,2010,2012,2014,2016,2019,2021,2024,2026,2029],{"class":85,"line":754},[83,2002,1938],{"class":130},[83,2004,2005],{"class":126},"set_option",[83,2007,131],{"class":130},[83,2009,1868],{"class":126},[83,2011,140],{"class":130},[83,2013,1873],{"class":126},[83,2015,140],{"class":130},[83,2017,2018],{"class":126},"serial_port_base",[83,2020,140],{"class":130},[83,2022,2023],{"class":126},"baud_rate",[83,2025,131],{"class":130},[83,2027,2028],{"class":237},"115200",[83,2030,2031],{"class":130},"));\n",[83,2033,2034],{"class":85,"line":765},[83,2035,117],{"emptyLinePlaceholder":116},[83,2037,2038,2040,2042,2044,2047,2049,2051],{"class":85,"line":1354},[83,2039,299],{"class":126},[83,2041,302],{"class":130},[83,2043,305],{"class":89},[83,2045,2046],{"class":93}," \"SerialRobotDriver：串口打开成功\"",[83,2048,311],{"class":89},[83,2050,137],{"class":126},[83,2052,347],{"class":130},[83,2054,2055],{"class":85,"line":1368},[83,2056,117],{"emptyLinePlaceholder":116},[83,2058,2059,2062],{"class":85,"line":1373},[83,2060,2061],{"class":126},"        start_read",[83,2063,2064],{"class":130},"();\n",[83,2066,2067],{"class":85,"line":1390},[83,2068,353],{"class":130},[83,2070,2071],{"class":85,"line":1395},[83,2072,117],{"emptyLinePlaceholder":116},[83,2074,2075],{"class":85,"line":1400},[83,2076,1055],{"class":89},[83,2078,2079,2081,2084],{"class":85,"line":1409},[83,2080,897],{"class":89},[83,2082,2083],{"class":126}," start_read",[83,2085,632],{"class":130},[83,2087,2089],{"class":85,"line":2088},67,[83,2090,222],{"class":130},[83,2092,2094,2096,2098,2100,2102,2105],{"class":85,"line":2093},68,[83,2095,1921],{"class":126},[83,2097,140],{"class":130},[83,2099,1873],{"class":126},[83,2101,140],{"class":130},[83,2103,2104],{"class":126},"async_read_until",[83,2106,2107],{"class":130},"(serial_,\n",[83,2109,2111],{"class":85,"line":2110},69,[83,2112,2113],{"class":130},"                                      buffer_,\n",[83,2115,2117,2120,2123,2126],{"class":85,"line":2116},70,[83,2118,2119],{"class":93},"                                      '",[83,2121,2122],{"class":237},"\\n",[83,2124,2125],{"class":93},"'",[83,2127,2128],{"class":130},",\n",[83,2130,2132,2135,2137,2140,2142,2144,2147],{"class":85,"line":2131},71,[83,2133,2134],{"class":126},"                                      std",[83,2136,140],{"class":130},[83,2138,2139],{"class":126},"bind",[83,2141,131],{"class":130},[83,2143,146],{"class":89},[83,2145,2146],{"class":126},"SerialRobotDriver",[83,2148,2149],{"class":130},"::on_read,\n",[83,2151,2153,2156],{"class":85,"line":2152},72,[83,2154,2155],{"class":237},"                                                this",[83,2157,2128],{"class":130},[83,2159,2161,2164,2166,2169],{"class":85,"line":2160},73,[83,2162,2163],{"class":126},"                                                std",[83,2165,140],{"class":130},[83,2167,2168],{"class":126},"placeholders",[83,2170,2171],{"class":130},"::_1,\n",[83,2173,2175,2177,2179,2181],{"class":85,"line":2174},74,[83,2176,2163],{"class":126},[83,2178,140],{"class":130},[83,2180,2168],{"class":126},[83,2182,2183],{"class":130},"::_2));\n",[83,2185,2187],{"class":85,"line":2186},75,[83,2188,353],{"class":130},[83,2190,2192],{"class":85,"line":2191},76,[83,2193,117],{"emptyLinePlaceholder":116},[83,2195,2197,2199,2202,2204,2206,2209,2211,2213,2215,2218,2220,2223,2225,2227,2229,2232,2235],{"class":85,"line":2196},77,[83,2198,897],{"class":89},[83,2200,2201],{"class":126}," on_read",[83,2203,131],{"class":130},[83,2205,134],{"class":89},[83,2207,2208],{"class":126}," boost",[83,2210,140],{"class":130},[83,2212,1926],{"class":126},[83,2214,140],{"class":130},[83,2216,2217],{"class":126},"error_code",[83,2219,146],{"class":89},[83,2221,2222],{"class":149}," ec",[83,2224,1886],{"class":130},[83,2226,1067],{"class":126},[83,2228,140],{"class":130},[83,2230,2231],{"class":89},"size_t",[83,2233,2234],{"class":149}," bytes_transferred",[83,2236,153],{"class":130},[83,2238,2240],{"class":85,"line":2239},78,[83,2241,222],{"class":130},[83,2243,2245,2247],{"class":85,"line":2244},79,[83,2246,952],{"class":89},[83,2248,1951],{"class":130},[83,2250,2252],{"class":85,"line":2251},80,[83,2253,965],{"class":130},[83,2255,2257,2259,2261,2263,2266,2268,2270,2272,2274,2276,2278],{"class":85,"line":2256},81,[83,2258,1022],{"class":126},[83,2260,302],{"class":130},[83,2262,305],{"class":89},[83,2264,2265],{"class":93}," \"串口读取错误：\"",[83,2267,311],{"class":89},[83,2269,1971],{"class":130},[83,2271,1974],{"class":126},[83,2273,1977],{"class":130},[83,2275,305],{"class":89},[83,2277,137],{"class":126},[83,2279,347],{"class":130},[83,2281,2283,2285],{"class":85,"line":2282},82,[83,2284,1988],{"class":89},[83,2286,241],{"class":130},[83,2288,2290],{"class":85,"line":2289},83,[83,2291,978],{"class":130},[83,2293,2295],{"class":85,"line":2294},84,[83,2296,117],{"emptyLinePlaceholder":116},[83,2298,2300,2302,2305,2308,2310,2312],{"class":85,"line":2299},85,[83,2301,299],{"class":126},[83,2303,2304],{"class":130},"::istream ",[83,2306,2307],{"class":126},"is",[83,2309,131],{"class":130},[83,2311,146],{"class":89},[83,2313,2314],{"class":130},"buffer_);\n",[83,2316,2318,2320],{"class":85,"line":2317},86,[83,2319,299],{"class":126},[83,2321,2322],{"class":130},"::string line;\n",[83,2324,2326,2328,2330,2333],{"class":85,"line":2325},87,[83,2327,299],{"class":126},[83,2329,140],{"class":130},[83,2331,2332],{"class":126},"getline",[83,2334,2335],{"class":130},"(is, line);\n",[83,2337,2339],{"class":85,"line":2338},88,[83,2340,117],{"emptyLinePlaceholder":116},[83,2342,2344,2346,2348,2350,2353,2355],{"class":85,"line":2343},89,[83,2345,299],{"class":126},[83,2347,302],{"class":130},[83,2349,305],{"class":89},[83,2351,2352],{"class":93}," \"收到串口行，bytes = \"",[83,2354,311],{"class":89},[83,2356,2357],{"class":130}," bytes_transferred\n",[83,2359,2361,2363,2366,2368,2370,2372,2374],{"class":85,"line":2360},90,[83,2362,320],{"class":89},[83,2364,2365],{"class":93}," \"，line = \"",[83,2367,311],{"class":89},[83,2369,598],{"class":130},[83,2371,305],{"class":89},[83,2373,137],{"class":126},[83,2375,347],{"class":130},[83,2377,2379],{"class":85,"line":2378},91,[83,2380,117],{"emptyLinePlaceholder":116},[83,2382,2384,2387,2389],{"class":85,"line":2383},92,[83,2385,2386],{"class":130},"        parser_.",[83,2388,1331],{"class":126},[83,2390,174],{"class":130},[83,2392,2394],{"class":85,"line":2393},93,[83,2395,117],{"emptyLinePlaceholder":116},[83,2397,2399,2401],{"class":85,"line":2398},94,[83,2400,2061],{"class":126},[83,2402,2064],{"class":130},[83,2404,2406],{"class":85,"line":2405},95,[83,2407,353],{"class":130},[83,2409,2411],{"class":85,"line":2410},96,[83,2412,117],{"emptyLinePlaceholder":116},[83,2414,2416],{"class":85,"line":2415},97,[83,2417,1055],{"class":89},[83,2419,2421],{"class":85,"line":2420},98,[83,2422,2423],{"class":643},"    // serial_port 表示串口设备，后续读写都通过它完成。\n",[83,2425,2427,2430,2432,2434],{"class":85,"line":2426},99,[83,2428,2429],{"class":126},"    boost",[83,2431,140],{"class":130},[83,2433,1873],{"class":126},[83,2435,2436],{"class":130},"::serial_port serial_;\n",[83,2438,2440,2442,2444,2446],{"class":85,"line":2439},100,[83,2441,2429],{"class":126},[83,2443,140],{"class":130},[83,2445,1873],{"class":126},[83,2447,2448],{"class":130},"::streambuf buffer_;\n",[83,2450,2452],{"class":85,"line":2451},101,[83,2453,2454],{"class":130},"    RobotProtocolParser parser_;\n",[83,2456,2458],{"class":85,"line":2457},102,[83,2459,1273],{"class":130},[83,2461,2463],{"class":85,"line":2462},103,[83,2464,117],{"emptyLinePlaceholder":116},[83,2466,2468,2470,2472,2474,2476,2479,2481,2484,2487],{"class":85,"line":2467},104,[83,2469,626],{"class":89},[83,2471,629],{"class":126},[83,2473,131],{"class":130},[83,2475,626],{"class":89},[83,2477,2478],{"class":149}," argc",[83,2480,1886],{"class":130},[83,2482,2483],{"class":89},"char*",[83,2485,2486],{"class":149}," argv",[83,2488,2489],{"class":130},"[])\n",[83,2491,2493],{"class":85,"line":2492},105,[83,2494,159],{"class":130},[83,2496,2498],{"class":85,"line":2497},106,[83,2499,2500],{"class":643},"    // 程序从 main 函数开始执行，argc/argv 用来接收命令行参数。\n",[83,2502,2504,2506,2509,2511,2514],{"class":85,"line":2503},107,[83,2505,165],{"class":126},[83,2507,2508],{"class":130},"::string port_name ",[83,2510,234],{"class":89},[83,2512,2513],{"class":93}," \"/dev/pts/3\"",[83,2515,241],{"class":130},[83,2517,2519],{"class":85,"line":2518},108,[83,2520,117],{"emptyLinePlaceholder":116},[83,2522,2524,2526,2529,2532,2535],{"class":85,"line":2523},109,[83,2525,205],{"class":89},[83,2527,2528],{"class":130}," (argc ",[83,2530,2531],{"class":89},">=",[83,2533,2534],{"class":237}," 2",[83,2536,153],{"class":130},[83,2538,2540],{"class":85,"line":2539},110,[83,2541,222],{"class":130},[83,2543,2545,2548,2550,2553,2556],{"class":85,"line":2544},111,[83,2546,2547],{"class":130},"        port_name ",[83,2549,234],{"class":89},[83,2551,2552],{"class":130}," argv[",[83,2554,2555],{"class":237},"1",[83,2557,2558],{"class":130},"];\n",[83,2560,2562],{"class":85,"line":2561},112,[83,2563,353],{"class":130},[83,2565,2567],{"class":85,"line":2566},113,[83,2568,117],{"emptyLinePlaceholder":116},[83,2570,2572],{"class":85,"line":2571},114,[83,2573,2574],{"class":643},"    // io_context 是 Asio 的事件循环对象，异步任务需要靠它调度。\n",[83,2576,2578,2580,2582,2584],{"class":85,"line":2577},115,[83,2579,2429],{"class":126},[83,2581,140],{"class":130},[83,2583,1873],{"class":126},[83,2585,2586],{"class":130},"::io_context io;\n",[83,2588,2590,2593,2596],{"class":85,"line":2589},116,[83,2591,2592],{"class":130},"    SerialRobotDriver ",[83,2594,2595],{"class":126},"driver",[83,2597,2598],{"class":130},"(io, port_name);\n",[83,2600,2602],{"class":85,"line":2601},117,[83,2603,117],{"emptyLinePlaceholder":116},[83,2605,2607,2609,2611,2613,2616,2618,2620],{"class":85,"line":2606},118,[83,2608,165],{"class":126},[83,2610,302],{"class":130},[83,2612,305],{"class":89},[83,2614,2615],{"class":93}," \"main：开始 io.run()\"",[83,2617,311],{"class":89},[83,2619,137],{"class":126},[83,2621,347],{"class":130},[83,2623,2625],{"class":85,"line":2624},119,[83,2626,117],{"emptyLinePlaceholder":116},[83,2628,2630],{"class":85,"line":2629},120,[83,2631,2632],{"class":643},"    // 启动事件循环，前面注册的异步任务会在这里被调度执行。\n",[83,2634,2636,2639,2642],{"class":85,"line":2635},121,[83,2637,2638],{"class":130},"    io.",[83,2640,2641],{"class":126},"run",[83,2643,2064],{"class":130},[83,2645,2647],{"class":85,"line":2646},122,[83,2648,117],{"emptyLinePlaceholder":116},[83,2650,2652,2654,2656,2658,2661,2663,2665],{"class":85,"line":2651},123,[83,2653,165],{"class":126},[83,2655,302],{"class":130},[83,2657,305],{"class":89},[83,2659,2660],{"class":93}," \"main：io.run() 返回\"",[83,2662,311],{"class":89},[83,2664,137],{"class":126},[83,2666,347],{"class":130},[83,2668,2670],{"class":85,"line":2669},124,[83,2671,117],{"emptyLinePlaceholder":116},[83,2673,2675,2677,2679],{"class":85,"line":2674},125,[83,2676,757],{"class":89},[83,2678,760],{"class":237},[83,2680,241],{"class":130},[83,2682,2684],{"class":85,"line":2683},126,[83,2685,615],{"class":130},[13,2687,2688,774],{},[771,2689,773],{},[54,2691,777],{"id":2692},"编译运行-2",[13,2694,2695],{},"终端 1：创建虚拟串口。",[24,2697,2699],{"className":780,"code":2698,"language":782,"meta":30,"style":30},"socat -d -d pty,raw,echo=0 pty,raw,echo=0\n",[32,2700,2701],{"__ignoreMap":30},[83,2702,2703,2706,2709,2711,2714,2717,2719],{"class":85,"line":86},[83,2704,2705],{"class":126},"socat",[83,2707,2708],{"class":237}," -d",[83,2710,2708],{"class":237},[83,2712,2713],{"class":93}," pty,raw,echo=",[83,2715,2716],{"class":237},"0",[83,2718,2713],{"class":93},[83,2720,2721],{"class":237},"0\n",[13,2723,2724],{},"终端 2：运行程序。",[24,2726,2728],{"className":780,"code":2727,"language":782,"meta":30,"style":30},"g++ demo3_serial_robot_driver.cpp -o demo3_serial_robot_driver -std=c++17 -lboost_system -pthread\n./demo3_serial_robot_driver /dev/pts/3\n",[32,2729,2730,2751],{"__ignoreMap":30},[83,2731,2732,2734,2737,2739,2742,2745,2748],{"class":85,"line":86},[83,2733,789],{"class":126},[83,2735,2736],{"class":93}," demo3_serial_robot_driver.cpp",[83,2738,795],{"class":237},[83,2740,2741],{"class":93}," demo3_serial_robot_driver",[83,2743,2744],{"class":237}," -std=c++17",[83,2746,2747],{"class":237}," -lboost_system",[83,2749,2750],{"class":237}," -pthread\n",[83,2752,2753,2756],{"class":85,"line":97},[83,2754,2755],{"class":126},"./demo3_serial_robot_driver",[83,2757,2758],{"class":93}," /dev/pts/3\n",[13,2760,2761],{},"终端 3：发送模拟下位机数据。",[24,2763,2765],{"className":780,"code":2764,"language":782,"meta":30,"style":30},"echo \"ODOM 1.0 2.0 0.5\" > /dev/pts/4\necho \"BAT 24.2\" > /dev/pts/4\n",[32,2766,2767,2781],{"__ignoreMap":30},[83,2768,2769,2772,2775,2778],{"class":85,"line":86},[83,2770,2771],{"class":237},"echo",[83,2773,2774],{"class":93}," \"ODOM 1.0 2.0 0.5\"",[83,2776,2777],{"class":89}," >",[83,2779,2780],{"class":93}," /dev/pts/4\n",[83,2782,2783,2785,2788,2790],{"class":85,"line":97},[83,2784,2771],{"class":237},[83,2786,2787],{"class":93}," \"BAT 24.2\"",[83,2789,2777],{"class":89},[83,2791,2780],{"class":93},[54,2793,2794],{"id":2794},"运行输出与时间顺序",[13,2796,2797],{},"程序启动后输出：",[24,2799,2802],{"className":2800,"code":2801,"language":29,"meta":30},[27],"SerialRobotDriver：串口打开成功\nmain：开始 io.run()\n",[32,2803,2801],{"__ignoreMap":30},[13,2805,2806],{},"发送第一行后输出：",[24,2808,2811],{"className":2809,"code":2810,"language":29,"meta":30},[27],"收到串口行，bytes = 17，line = ODOM 1.0 2.0 0.5\n解析 ODOM：x = 1，y = 2，yaw = 0.5\n",[32,2812,2810],{"__ignoreMap":30},[13,2814,2815],{},"发送第二行后输出：",[24,2817,2820],{"className":2818,"code":2819,"language":29,"meta":30},[27],"收到串口行，bytes = 9，line = BAT 24.2\n解析 BAT：24.2 V\n",[32,2821,2819],{"__ignoreMap":30},[54,2823,818],{"id":2824},"本示例需要注意的点-2",[13,2826,2827],{},"这就是一个最小机器人串口驱动雏形。",[13,2829,2830],{},"但它还没有做：",[2832,2833,2834,2838,2841,2844,2847,2850],"ol",{},[2835,2836,2837],"li",{},"串口断开重连；",[2835,2839,2840],{},"二进制帧解析；",[2835,2842,2843],{},"CRC 校验；",[2835,2845,2846],{},"数据转 ROS2 topic；",[2835,2848,2849],{},"cmd_vel 写回下位机；",[2835,2851,2852],{},"多线程安全。",[13,2854,2855],{},"这些可以在这个结构上继续加。",[42,2857],{},[45,2859,2861],{"id":2860},"示例-4ros2-节点里使用-asio-的推荐结构","示例 4：ROS2 节点里使用 Asio 的推荐结构",[54,2863,56],{"id":2864},"程序目标-3",[13,2866,2867],{},"这个示例展示推荐结构，不要求你现在立刻完整运行。",[13,2869,2870],{},"推荐思路：",[24,2872,2875],{"className":2873,"code":2874,"language":29,"meta":30},[27],"ROS2 节点线程：负责 rclcpp::spin()\nAsio 线程：负责 io_context.run()\n串口回调：收到数据后更新成员变量或 post 给 ROS2 逻辑\nROS2 timer：定期发布 odom / battery / imu\n",[32,2876,2874],{"__ignoreMap":30},[54,2878,2879],{"id":2879},"代码骨架",[24,2881,2883],{"className":77,"code":2882,"language":79,"meta":30,"style":30},"#include \u003Cboost/asio.hpp>\n#include \u003Cfunctional>\n#include \u003Ciostream>\n#include \u003Cmemory>\n#include \u003Cstring>\n#include \u003Cthread>\n\n#include \u003Crclcpp/rclcpp.hpp>\n#include \u003Cstd_msgs/msg/string.hpp>\n\nclass RobotNode : public rclcpp::Node\n{\npublic:\n    RobotNode()\n        : Node(\"robot_node\"),\n          guard_(boost::asio::make_work_guard(io_))\n    {\n        publisher_ = this->create_publisher\u003Cstd_msgs::msg::String>(\"robot_status\", 10);\n\n        ros_timer_ = this->create_wall_timer(\n            std::chrono::seconds(1),\n            std::bind(&RobotNode::on_ros_timer, this));\n\n        asio_thread_ = std::thread(std::bind(&RobotNode::run_asio, this));\n\n        std::cout \u003C\u003C \"RobotNode：启动完成\" \u003C\u003C std::endl;\n    }\n\n    ~RobotNode()\n    {\n        guard_.reset();\n        io_.stop();\n\n        if (asio_thread_.joinable())\n        {\n            asio_thread_.join();\n        }\n    }\n\nprivate:\n    void run_asio()\n    {\n        std::cout \u003C\u003C \"Asio 线程：io.run() 开始\" \u003C\u003C std::endl;\n        // 启动事件循环，前面注册的异步任务会在这里被调度执行。\n        io_.run();\n        std::cout \u003C\u003C \"Asio 线程：io.run() 返回\" \u003C\u003C std::endl;\n    }\n\n    void on_ros_timer()\n    {\n        std_msgs::msg::String msg;\n        msg.data = \"robot alive\";\n        publisher_->publish(msg);\n\n        RCLCPP_INFO(this->get_logger(), \"publish: %s\", msg.data.c_str());\n    }\n\nprivate:\n    // io_context 是 Asio 的事件循环对象，异步任务需要靠它调度。\n    boost::asio::io_context io_;\n    boost::asio::executor_work_guard\u003Cboost::asio::io_context::executor_type> guard_;\n    // 创建子线程，让这部分代码和 main 线程并发运行。\n    std::thread asio_thread_;\n\n    rclcpp::Publisher\u003Cstd_msgs::msg::String>::SharedPtr publisher_;\n    rclcpp::TimerBase::SharedPtr ros_timer_;\n};\n\nint main(int argc, char* argv[])\n{\n    // 程序从 main 函数开始执行，argc/argv 用来接收命令行参数。\n    rclcpp::init(argc, argv);\n\n    std::shared_ptr\u003CRobotNode> node = std::make_shared\u003CRobotNode>();\n\n    rclcpp::spin(node);\n\n    rclcpp::shutdown();\n\n    return 0;\n}\n",[32,2884,2885,2891,2897,2903,2910,2916,2923,2927,2934,2941,2945,2966,2970,2974,2981,2996,3017,3021,3063,3067,3085,3105,3128,3132,3167,3171,3188,3192,3196,3203,3207,3217,3227,3231,3244,3248,3258,3262,3266,3270,3274,3283,3287,3304,3309,3317,3334,3338,3342,3351,3355,3367,3379,3390,3394,3429,3433,3437,3441,3445,3456,3487,3492,3499,3503,3529,3541,3545,3549,3569,3573,3577,3589,3593,3625,3629,3641,3645,3656,3660,3668],{"__ignoreMap":30},[83,2886,2887,2889],{"class":85,"line":86},[83,2888,90],{"class":89},[83,2890,1496],{"class":93},[83,2892,2893,2895],{"class":85,"line":97},[83,2894,90],{"class":89},[83,2896,1510],{"class":93},[83,2898,2899,2901],{"class":85,"line":105},[83,2900,90],{"class":89},[83,2902,94],{"class":93},[83,2904,2905,2907],{"class":85,"line":113},[83,2906,90],{"class":89},[83,2908,2909],{"class":93}," \u003Cmemory>\n",[83,2911,2912,2914],{"class":85,"line":120},[83,2913,90],{"class":89},[83,2915,110],{"class":93},[83,2917,2918,2920],{"class":85,"line":156},[83,2919,90],{"class":89},[83,2921,2922],{"class":93}," \u003Cthread>\n",[83,2924,2925],{"class":85,"line":162},[83,2926,117],{"emptyLinePlaceholder":116},[83,2928,2929,2931],{"class":85,"line":177},[83,2930,90],{"class":89},[83,2932,2933],{"class":93}," \u003Crclcpp/rclcpp.hpp>\n",[83,2935,2936,2938],{"class":85,"line":185},[83,2937,90],{"class":89},[83,2939,2940],{"class":93}," \u003Cstd_msgs/msg/string.hpp>\n",[83,2942,2943],{"class":85,"line":197},[83,2944,117],{"emptyLinePlaceholder":116},[83,2946,2947,2949,2952,2955,2958,2961,2963],{"class":85,"line":202},[83,2948,880],{"class":89},[83,2950,2951],{"class":126}," RobotNode",[83,2953,2954],{"class":130}," : ",[83,2956,2957],{"class":89},"public",[83,2959,2960],{"class":126}," rclcpp",[83,2962,140],{"class":130},[83,2964,2965],{"class":126},"Node\n",[83,2967,2968],{"class":85,"line":219},[83,2969,159],{"class":130},[83,2971,2972],{"class":85,"line":225},[83,2973,892],{"class":89},[83,2975,2976,2979],{"class":85,"line":244},[83,2977,2978],{"class":126},"    RobotNode",[83,2980,632],{"class":130},[83,2982,2983,2985,2988,2990,2993],{"class":85,"line":258},[83,2984,1906],{"class":130},[83,2986,2987],{"class":126},"Node",[83,2989,131],{"class":130},[83,2991,2992],{"class":93},"\"robot_node\"",[83,2994,2995],{"class":130},"),\n",[83,2997,2998,3001,3003,3005,3007,3009,3011,3014],{"class":85,"line":272},[83,2999,3000],{"class":126},"          guard_",[83,3002,131],{"class":130},[83,3004,1868],{"class":126},[83,3006,140],{"class":130},[83,3008,1873],{"class":126},[83,3010,140],{"class":130},[83,3012,3013],{"class":126},"make_work_guard",[83,3015,3016],{"class":130},"(io_))\n",[83,3018,3019],{"class":85,"line":291},[83,3020,222],{"class":130},[83,3022,3023,3026,3028,3031,3034,3037,3040,3042,3045,3048,3051,3053,3056,3058,3061],{"class":85,"line":296},[83,3024,3025],{"class":130},"        publisher_ ",[83,3027,234],{"class":89},[83,3029,3030],{"class":237}," this",[83,3032,3033],{"class":130},"->create_publisher",[83,3035,3036],{"class":89},"\u003C",[83,3038,3039],{"class":126},"std_msgs",[83,3041,140],{"class":130},[83,3043,3044],{"class":126},"msg",[83,3046,3047],{"class":130},"::String",[83,3049,3050],{"class":89},">",[83,3052,131],{"class":130},[83,3054,3055],{"class":93},"\"robot_status\"",[83,3057,1886],{"class":130},[83,3059,3060],{"class":237},"10",[83,3062,681],{"class":130},[83,3064,3065],{"class":85,"line":317},[83,3066,117],{"emptyLinePlaceholder":116},[83,3068,3069,3072,3074,3076,3079,3082],{"class":85,"line":331},[83,3070,3071],{"class":130},"        ros_timer_ ",[83,3073,234],{"class":89},[83,3075,3030],{"class":237},[83,3077,3078],{"class":130},"->",[83,3080,3081],{"class":126},"create_wall_timer",[83,3083,3084],{"class":130},"(\n",[83,3086,3087,3089,3091,3094,3096,3099,3101,3103],{"class":85,"line":350},[83,3088,1022],{"class":126},[83,3090,140],{"class":130},[83,3092,3093],{"class":126},"chrono",[83,3095,140],{"class":130},[83,3097,3098],{"class":126},"seconds",[83,3100,131],{"class":130},[83,3102,2555],{"class":237},[83,3104,2995],{"class":130},[83,3106,3107,3109,3111,3113,3115,3117,3120,3123,3126],{"class":85,"line":356},[83,3108,1022],{"class":126},[83,3110,140],{"class":130},[83,3112,2139],{"class":126},[83,3114,131],{"class":130},[83,3116,146],{"class":89},[83,3118,3119],{"class":126},"RobotNode",[83,3121,3122],{"class":130},"::on_ros_timer, ",[83,3124,3125],{"class":237},"this",[83,3127,2031],{"class":130},[83,3129,3130],{"class":85,"line":374},[83,3131,117],{"emptyLinePlaceholder":116},[83,3133,3134,3137,3139,3141,3143,3146,3148,3150,3152,3154,3156,3158,3160,3163,3165],{"class":85,"line":379},[83,3135,3136],{"class":130},"        asio_thread_ ",[83,3138,234],{"class":89},[83,3140,137],{"class":126},[83,3142,140],{"class":130},[83,3144,3145],{"class":126},"thread",[83,3147,131],{"class":130},[83,3149,1067],{"class":126},[83,3151,140],{"class":130},[83,3153,2139],{"class":126},[83,3155,131],{"class":130},[83,3157,146],{"class":89},[83,3159,3119],{"class":126},[83,3161,3162],{"class":130},"::run_asio, ",[83,3164,3125],{"class":237},[83,3166,2031],{"class":130},[83,3168,3169],{"class":85,"line":393},[83,3170,117],{"emptyLinePlaceholder":116},[83,3172,3173,3175,3177,3179,3182,3184,3186],{"class":85,"line":403},[83,3174,299],{"class":126},[83,3176,302],{"class":130},[83,3178,305],{"class":89},[83,3180,3181],{"class":93}," \"RobotNode：启动完成\"",[83,3183,311],{"class":89},[83,3185,137],{"class":126},[83,3187,347],{"class":130},[83,3189,3190],{"class":85,"line":408},[83,3191,353],{"class":130},[83,3193,3194],{"class":85,"line":435},[83,3195,117],{"emptyLinePlaceholder":116},[83,3197,3198,3201],{"class":85,"line":440},[83,3199,3200],{"class":126},"    ~RobotNode",[83,3202,632],{"class":130},[83,3204,3205],{"class":85,"line":456},[83,3206,222],{"class":130},[83,3208,3209,3212,3215],{"class":85,"line":461},[83,3210,3211],{"class":130},"        guard_.",[83,3213,3214],{"class":126},"reset",[83,3216,2064],{"class":130},[83,3218,3219,3222,3225],{"class":85,"line":475},[83,3220,3221],{"class":130},"        io_.",[83,3223,3224],{"class":126},"stop",[83,3226,2064],{"class":130},[83,3228,3229],{"class":85,"line":489},[83,3230,117],{"emptyLinePlaceholder":116},[83,3232,3233,3235,3238,3241],{"class":85,"line":503},[83,3234,952],{"class":89},[83,3236,3237],{"class":130}," (asio_thread_.",[83,3239,3240],{"class":126},"joinable",[83,3242,3243],{"class":130},"())\n",[83,3245,3246],{"class":85,"line":521},[83,3247,965],{"class":130},[83,3249,3250,3253,3256],{"class":85,"line":526},[83,3251,3252],{"class":130},"            asio_thread_.",[83,3254,3255],{"class":126},"join",[83,3257,2064],{"class":130},[83,3259,3260],{"class":85,"line":538},[83,3261,978],{"class":130},[83,3263,3264],{"class":85,"line":568},[83,3265,353],{"class":130},[83,3267,3268],{"class":85,"line":573},[83,3269,117],{"emptyLinePlaceholder":116},[83,3271,3272],{"class":85,"line":579},[83,3273,1055],{"class":89},[83,3275,3276,3278,3281],{"class":85,"line":584},[83,3277,897],{"class":89},[83,3279,3280],{"class":126}," run_asio",[83,3282,632],{"class":130},[83,3284,3285],{"class":85,"line":607},[83,3286,222],{"class":130},[83,3288,3289,3291,3293,3295,3298,3300,3302],{"class":85,"line":612},[83,3290,299],{"class":126},[83,3292,302],{"class":130},[83,3294,305],{"class":89},[83,3296,3297],{"class":93}," \"Asio 线程：io.run() 开始\"",[83,3299,311],{"class":89},[83,3301,137],{"class":126},[83,3303,347],{"class":130},[83,3305,3306],{"class":85,"line":618},[83,3307,3308],{"class":643},"        // 启动事件循环，前面注册的异步任务会在这里被调度执行。\n",[83,3310,3311,3313,3315],{"class":85,"line":623},[83,3312,3221],{"class":130},[83,3314,2641],{"class":126},[83,3316,2064],{"class":130},[83,3318,3319,3321,3323,3325,3328,3330,3332],{"class":85,"line":635},[83,3320,299],{"class":126},[83,3322,302],{"class":130},[83,3324,305],{"class":89},[83,3326,3327],{"class":93}," \"Asio 线程：io.run() 返回\"",[83,3329,311],{"class":89},[83,3331,137],{"class":126},[83,3333,347],{"class":130},[83,3335,3336],{"class":85,"line":640},[83,3337,353],{"class":130},[83,3339,3340],{"class":85,"line":647},[83,3341,117],{"emptyLinePlaceholder":116},[83,3343,3344,3346,3349],{"class":85,"line":665},[83,3345,897],{"class":89},[83,3347,3348],{"class":126}," on_ros_timer",[83,3350,632],{"class":130},[83,3352,3353],{"class":85,"line":670},[83,3354,222],{"class":130},[83,3356,3357,3360,3362,3364],{"class":85,"line":684},[83,3358,3359],{"class":126},"        std_msgs",[83,3361,140],{"class":130},[83,3363,3044],{"class":126},[83,3365,3366],{"class":130},"::String msg;\n",[83,3368,3369,3372,3374,3377],{"class":85,"line":696},[83,3370,3371],{"class":130},"        msg.data ",[83,3373,234],{"class":89},[83,3375,3376],{"class":93}," \"robot alive\"",[83,3378,241],{"class":130},[83,3380,3381,3384,3387],{"class":85,"line":708},[83,3382,3383],{"class":130},"        publisher_->",[83,3385,3386],{"class":126},"publish",[83,3388,3389],{"class":130},"(msg);\n",[83,3391,3392],{"class":85,"line":720},[83,3393,117],{"emptyLinePlaceholder":116},[83,3395,3396,3399,3401,3403,3405,3408,3411,3414,3417,3420,3423,3426],{"class":85,"line":725},[83,3397,3398],{"class":126},"        RCLCPP_INFO",[83,3400,131],{"class":130},[83,3402,3125],{"class":237},[83,3404,3078],{"class":130},[83,3406,3407],{"class":126},"get_logger",[83,3409,3410],{"class":130},"(), ",[83,3412,3413],{"class":93},"\"publish: ",[83,3415,3416],{"class":237},"%s",[83,3418,3419],{"class":93},"\"",[83,3421,3422],{"class":130},", msg.data.",[83,3424,3425],{"class":126},"c_str",[83,3427,3428],{"class":130},"());\n",[83,3430,3431],{"class":85,"line":743},[83,3432,353],{"class":130},[83,3434,3435],{"class":85,"line":748},[83,3436,117],{"emptyLinePlaceholder":116},[83,3438,3439],{"class":85,"line":754},[83,3440,1055],{"class":89},[83,3442,3443],{"class":85,"line":765},[83,3444,2574],{"class":643},[83,3446,3447,3449,3451,3453],{"class":85,"line":1354},[83,3448,2429],{"class":126},[83,3450,140],{"class":130},[83,3452,1873],{"class":126},[83,3454,3455],{"class":130},"::io_context io_;\n",[83,3457,3458,3460,3462,3464,3467,3469,3471,3473,3475,3477,3479,3482,3484],{"class":85,"line":1368},[83,3459,2429],{"class":126},[83,3461,140],{"class":130},[83,3463,1873],{"class":126},[83,3465,3466],{"class":130},"::executor_work_guard",[83,3468,3036],{"class":89},[83,3470,1868],{"class":126},[83,3472,140],{"class":130},[83,3474,1873],{"class":126},[83,3476,140],{"class":130},[83,3478,1878],{"class":126},[83,3480,3481],{"class":130},"::executor_type",[83,3483,3050],{"class":89},[83,3485,3486],{"class":130}," guard_;\n",[83,3488,3489],{"class":85,"line":1373},[83,3490,3491],{"class":643},"    // 创建子线程，让这部分代码和 main 线程并发运行。\n",[83,3493,3494,3496],{"class":85,"line":1390},[83,3495,165],{"class":126},[83,3497,3498],{"class":130},"::thread asio_thread_;\n",[83,3500,3501],{"class":85,"line":1395},[83,3502,117],{"emptyLinePlaceholder":116},[83,3504,3505,3508,3510,3513,3515,3517,3519,3521,3523,3526],{"class":85,"line":1400},[83,3506,3507],{"class":126},"    rclcpp",[83,3509,140],{"class":130},[83,3511,3512],{"class":126},"Publisher",[83,3514,3036],{"class":130},[83,3516,3039],{"class":126},[83,3518,140],{"class":130},[83,3520,3044],{"class":126},[83,3522,140],{"class":130},[83,3524,3525],{"class":126},"String",[83,3527,3528],{"class":130},">::SharedPtr publisher_;\n",[83,3530,3531,3533,3535,3538],{"class":85,"line":1409},[83,3532,3507],{"class":126},[83,3534,140],{"class":130},[83,3536,3537],{"class":126},"TimerBase",[83,3539,3540],{"class":130},"::SharedPtr ros_timer_;\n",[83,3542,3543],{"class":85,"line":2088},[83,3544,1273],{"class":130},[83,3546,3547],{"class":85,"line":2093},[83,3548,117],{"emptyLinePlaceholder":116},[83,3550,3551,3553,3555,3557,3559,3561,3563,3565,3567],{"class":85,"line":2110},[83,3552,626],{"class":89},[83,3554,629],{"class":126},[83,3556,131],{"class":130},[83,3558,626],{"class":89},[83,3560,2478],{"class":149},[83,3562,1886],{"class":130},[83,3564,2483],{"class":89},[83,3566,2486],{"class":149},[83,3568,2489],{"class":130},[83,3570,3571],{"class":85,"line":2116},[83,3572,159],{"class":130},[83,3574,3575],{"class":85,"line":2131},[83,3576,2500],{"class":643},[83,3578,3579,3581,3583,3586],{"class":85,"line":2152},[83,3580,3507],{"class":126},[83,3582,140],{"class":130},[83,3584,3585],{"class":126},"init",[83,3587,3588],{"class":130},"(argc, argv);\n",[83,3590,3591],{"class":85,"line":2160},[83,3592,117],{"emptyLinePlaceholder":116},[83,3594,3595,3597,3600,3602,3604,3606,3609,3611,3613,3615,3618,3620,3622],{"class":85,"line":2174},[83,3596,165],{"class":126},[83,3598,3599],{"class":130},"::shared_ptr",[83,3601,3036],{"class":89},[83,3603,3119],{"class":130},[83,3605,3050],{"class":89},[83,3607,3608],{"class":130}," node ",[83,3610,234],{"class":89},[83,3612,137],{"class":126},[83,3614,140],{"class":130},[83,3616,3617],{"class":126},"make_shared",[83,3619,3036],{"class":130},[83,3621,3119],{"class":126},[83,3623,3624],{"class":130},">();\n",[83,3626,3627],{"class":85,"line":2186},[83,3628,117],{"emptyLinePlaceholder":116},[83,3630,3631,3633,3635,3638],{"class":85,"line":2191},[83,3632,3507],{"class":126},[83,3634,140],{"class":130},[83,3636,3637],{"class":126},"spin",[83,3639,3640],{"class":130},"(node);\n",[83,3642,3643],{"class":85,"line":2196},[83,3644,117],{"emptyLinePlaceholder":116},[83,3646,3647,3649,3651,3654],{"class":85,"line":2239},[83,3648,3507],{"class":126},[83,3650,140],{"class":130},[83,3652,3653],{"class":126},"shutdown",[83,3655,2064],{"class":130},[83,3657,3658],{"class":85,"line":2244},[83,3659,117],{"emptyLinePlaceholder":116},[83,3661,3662,3664,3666],{"class":85,"line":2251},[83,3663,757],{"class":89},[83,3665,760],{"class":237},[83,3667,241],{"class":130},[83,3669,3670],{"class":85,"line":2256},[83,3671,615],{"class":130},[54,3673,3674],{"id":3674},"典型运行输出与时间顺序",[13,3676,3677],{},"节点启动后输出类似：",[24,3679,3682],{"className":3680,"code":3681,"language":29,"meta":30},[27],"RobotNode：启动完成\nAsio 线程：io.run() 开始\n",[32,3683,3681],{"__ignoreMap":30},[13,3685,3686],{},"之后 ROS2 timer 每 1 秒输出一次：",[24,3688,3691],{"className":3689,"code":3690,"language":29,"meta":30},[27],"[INFO] [robot_node]: publish: robot alive\n[INFO] [robot_node]: publish: robot alive\n[INFO] [robot_node]: publish: robot alive\n",[32,3692,3690],{"__ignoreMap":30},[13,3694,3695],{},"按 Ctrl+C 退出后：",[24,3697,3700],{"className":3698,"code":3699,"language":29,"meta":30},[27],"Asio 线程：io.run() 返回\n",[32,3701,3699],{"__ignoreMap":30},[54,3703,818],{"id":3704},"本示例需要注意的点-3",[13,3706,3707],{},"ROS2 里也有 timer：",[24,3709,3711],{"className":77,"code":3710,"language":79,"meta":30,"style":30},"create_wall_timer(...)\n",[32,3712,3713],{"__ignoreMap":30},[83,3714,3715,3717],{"class":85,"line":86},[83,3716,3081],{"class":126},[83,3718,3719],{"class":130},"(...)\n",[13,3721,3722],{},"Boost.Asio 里也有 timer：",[24,3724,3726],{"className":77,"code":3725,"language":79,"meta":30,"style":30},"boost::asio::steady_timer\n",[32,3727,3728],{"__ignoreMap":30},[83,3729,3730,3732,3734,3736],{"class":85,"line":86},[83,3731,1868],{"class":126},[83,3733,140],{"class":130},[83,3735,1873],{"class":126},[83,3737,3738],{"class":130},"::steady_timer\n",[13,3740,3741],{},"它们不是一回事。",[13,3743,3744],{},"一般建议：",[24,3746,3749],{"className":3747,"code":3748,"language":29,"meta":30},[27],"ROS2 周期发布 topic -> 用 ROS2 timer\n串口/TCP/UDP 超时、重连、心跳 -> 用 Asio timer\n",[32,3750,3748],{"__ignoreMap":30},[54,3752,3754],{"id":3753},"为什么不要在-ros2-回调里阻塞读串口","为什么不要在 ROS2 回调里阻塞读串口",[13,3756,3757],{},"不要这样：",[24,3759,3762],{"className":3760,"code":3761,"language":29,"meta":30},[27],"订阅 cmd_vel 的回调里\n直接 while 循环 read 串口\n",[32,3763,3761],{"__ignoreMap":30},[13,3765,3766],{},"因为 ROS2 executor 可能被你阻塞，其他 topic、timer、service 都会受影响。",[13,3768,3769],{},"更好的方式是：",[24,3771,3774],{"className":3772,"code":3773,"language":29,"meta":30},[27],"Asio 负责异步收发\nROS2 回调只把命令投递给 Asio 模块\n",[32,3775,3773],{"__ignoreMap":30},[42,3777],{},[45,3779,3781],{"id":3780},"示例-5ros2-回调里把任务投递给-asio","示例 5：ROS2 回调里把任务投递给 Asio",[54,3783,56],{"id":3784},"程序目标-4",[13,3786,3787],{},"展示一种常见结构：ROS2 收到命令后，不直接写串口，而是把写操作投递给 Asio 线程。",[54,3789,3790],{"id":3790},"代码片段",[24,3792,3794],{"className":77,"code":3793,"language":79,"meta":30,"style":30},"void on_cmd_vel(const std_msgs::msg::String::SharedPtr msg)\n{\n    std::string command = msg->data + \"\\n\";\n\n    boost::asio::post(io_,\n                      std::bind(&RobotNode::send_command_in_asio_thread,\n                                this,\n                                command));\n}\n\nvoid send_command_in_asio_thread(const std::string& command)\n{\n    std::cout \u003C\u003C \"Asio 线程中发送命令：\" \u003C\u003C command;\n\n    // 这里调用 SerialWriter::async_send(command)\n    // 或者调用你自己的串口驱动发送队列\n}\n",[32,3795,3796,3828,3832,3856,3860,3876,3894,3901,3906,3910,3914,3938,3942,3958,3962,3967,3972],{"__ignoreMap":30},[83,3797,3798,3800,3803,3805,3807,3810,3812,3814,3816,3818,3820,3823,3826],{"class":85,"line":86},[83,3799,123],{"class":89},[83,3801,3802],{"class":126}," on_cmd_vel",[83,3804,131],{"class":130},[83,3806,134],{"class":89},[83,3808,3809],{"class":126}," std_msgs",[83,3811,140],{"class":130},[83,3813,3044],{"class":126},[83,3815,140],{"class":130},[83,3817,3525],{"class":126},[83,3819,140],{"class":130},[83,3821,3822],{"class":126},"SharedPtr",[83,3824,3825],{"class":149}," msg",[83,3827,153],{"class":130},[83,3829,3830],{"class":85,"line":97},[83,3831,159],{"class":130},[83,3833,3834,3836,3839,3841,3844,3847,3850,3852,3854],{"class":85,"line":105},[83,3835,165],{"class":126},[83,3837,3838],{"class":130},"::string command ",[83,3840,234],{"class":89},[83,3842,3843],{"class":130}," msg->data ",[83,3845,3846],{"class":89},"+",[83,3848,3849],{"class":93}," \"",[83,3851,2122],{"class":237},[83,3853,3419],{"class":93},[83,3855,241],{"class":130},[83,3857,3858],{"class":85,"line":113},[83,3859,117],{"emptyLinePlaceholder":116},[83,3861,3862,3864,3866,3868,3870,3873],{"class":85,"line":120},[83,3863,2429],{"class":126},[83,3865,140],{"class":130},[83,3867,1873],{"class":126},[83,3869,140],{"class":130},[83,3871,3872],{"class":126},"post",[83,3874,3875],{"class":130},"(io_,\n",[83,3877,3878,3881,3883,3885,3887,3889,3891],{"class":85,"line":156},[83,3879,3880],{"class":126},"                      std",[83,3882,140],{"class":130},[83,3884,2139],{"class":126},[83,3886,131],{"class":130},[83,3888,146],{"class":89},[83,3890,3119],{"class":126},[83,3892,3893],{"class":130},"::send_command_in_asio_thread,\n",[83,3895,3896,3899],{"class":85,"line":162},[83,3897,3898],{"class":237},"                                this",[83,3900,2128],{"class":130},[83,3902,3903],{"class":85,"line":177},[83,3904,3905],{"class":130},"                                command));\n",[83,3907,3908],{"class":85,"line":185},[83,3909,615],{"class":130},[83,3911,3912],{"class":85,"line":197},[83,3913,117],{"emptyLinePlaceholder":116},[83,3915,3916,3918,3921,3923,3925,3927,3929,3931,3933,3936],{"class":85,"line":202},[83,3917,123],{"class":89},[83,3919,3920],{"class":126}," send_command_in_asio_thread",[83,3922,131],{"class":130},[83,3924,134],{"class":89},[83,3926,137],{"class":126},[83,3928,140],{"class":130},[83,3930,143],{"class":126},[83,3932,146],{"class":89},[83,3934,3935],{"class":149}," command",[83,3937,153],{"class":130},[83,3939,3940],{"class":85,"line":219},[83,3941,159],{"class":130},[83,3943,3944,3946,3948,3950,3953,3955],{"class":85,"line":225},[83,3945,165],{"class":126},[83,3947,302],{"class":130},[83,3949,305],{"class":89},[83,3951,3952],{"class":93}," \"Asio 线程中发送命令：\"",[83,3954,311],{"class":89},[83,3956,3957],{"class":130}," command;\n",[83,3959,3960],{"class":85,"line":244},[83,3961,117],{"emptyLinePlaceholder":116},[83,3963,3964],{"class":85,"line":258},[83,3965,3966],{"class":643},"    // 这里调用 SerialWriter::async_send(command)\n",[83,3968,3969],{"class":85,"line":272},[83,3970,3971],{"class":643},"    // 或者调用你自己的串口驱动发送队列\n",[83,3973,3974],{"class":85,"line":291},[83,3975,615],{"class":130},[54,3977,3978],{"id":3978},"典型输出与时间顺序",[13,3980,3981],{},"当 ROS2 收到一条命令：",[24,3983,3986],{"className":3984,"code":3985,"language":29,"meta":30},[27],"cmd_vel 0.2 0.0\n",[32,3987,3985],{"__ignoreMap":30},[13,3989,3990],{},"ROS2 回调线程会立刻把任务投递出去。随后 Asio 线程执行：",[24,3992,3995],{"className":3993,"code":3994,"language":29,"meta":30},[27],"Asio 线程中发送命令：cmd_vel 0.2 0.0\n",[32,3996,3994],{"__ignoreMap":30},[54,3998,818],{"id":3999},"本示例需要注意的点-4",[13,4001,4002,4005],{},[32,4003,4004],{},"boost::asio::post()"," 可以把任务安全地投递到 Asio 事件循环。",[13,4007,4008],{},"如果你的串口对象只在 Asio 线程里访问，那么可以减少很多加锁问题。",[42,4010],{},[45,4012,4013],{"id":4013},"机器人工程中的推荐分层",[13,4015,4016],{},"推荐结构：",[24,4018,4021],{"className":4019,"code":4020,"language":29,"meta":30},[27],"RobotNode                  ROS2 节点层\n├── SerialRobotDriver      串口通信层\n│   ├── async_read_until   异步读取\n│   ├── write_queue        异步写队列\n│   └── steady_timer       超时/心跳/重连\n├── RobotProtocolParser    协议解析层\n└── RobotState             数据缓存层\n",[32,4022,4020],{"__ignoreMap":30},[13,4024,4025],{},"不要写成：",[24,4027,4030],{"className":4028,"code":4029,"language":29,"meta":30},[27],"一个 node.cpp 里面塞满：\n串口打开\nread\nwrite\n协议解析\nodom 发布\ncmd_vel 订阅\nPID 控制\nTF 发布\n参数读取\n",[32,4031,4029],{"__ignoreMap":30},[13,4033,4034],{},"这样后期会非常难维护。",[42,4036],{},[45,4038,4039],{"id":4039},"常见坑总结",[54,4041,4043,4044,4047],{"id":4042},"坑-1iorun-提前返回","坑 1：",[32,4045,4046],{},"io.run()"," 提前返回",[13,4049,4050,4051,40],{},"原因通常是没有异步任务，也没有 ",[32,4052,4053],{},"work_guard",[13,4055,4056],{},"解决：",[24,4058,4060],{"className":77,"code":4059,"language":79,"meta":30,"style":30},"auto guard = boost::asio::make_work_guard(io);\n",[32,4061,4062],{"__ignoreMap":30},[83,4063,4064,4067,4070,4072,4074,4076,4078,4080,4082],{"class":85,"line":86},[83,4065,4066],{"class":89},"auto",[83,4068,4069],{"class":130}," guard ",[83,4071,234],{"class":89},[83,4073,2208],{"class":126},[83,4075,140],{"class":130},[83,4077,1873],{"class":126},[83,4079,140],{"class":130},[83,4081,3013],{"class":126},[83,4083,4084],{"class":130},"(io);\n",[54,4086,4088],{"id":4087},"坑-2回调不执行","坑 2：回调不执行",[13,4090,4091],{},"常见原因：",[2832,4093,4094,4100,4105,4108,4111],{},[2835,4095,4096,4097,4099],{},"没有调用 ",[32,4098,4046],{},"；",[2835,4101,4102,4104],{},[32,4103,4046],{}," 已经返回；",[2835,4106,4107],{},"异步对象提前析构；",[2835,4109,4110],{},"buffer 提前析构；",[2835,4112,4113,4114,4117],{},"串口没收到分隔符，例如 ",[32,4115,4116],{},"async_read_until(..., '\\n')"," 但下位机没发换行。",[54,4119,4121],{"id":4120},"坑-3类里绑定成员函数写错","坑 3：类里绑定成员函数写错",[13,4123,4124],{},"正确写法：",[24,4126,4128],{"className":77,"code":4127,"language":79,"meta":30,"style":30},"std::bind(&ClassName::callback,\n          this,\n          std::placeholders::_1,\n          std::placeholders::_2)\n",[32,4129,4130,4148,4155,4166],{"__ignoreMap":30},[83,4131,4132,4134,4136,4138,4140,4142,4145],{"class":85,"line":86},[83,4133,1067],{"class":126},[83,4135,140],{"class":130},[83,4137,2139],{"class":126},[83,4139,131],{"class":130},[83,4141,146],{"class":89},[83,4143,4144],{"class":126},"ClassName",[83,4146,4147],{"class":130},"::callback,\n",[83,4149,4150,4153],{"class":85,"line":97},[83,4151,4152],{"class":237},"          this",[83,4154,2128],{"class":130},[83,4156,4157,4160,4162,4164],{"class":85,"line":105},[83,4158,4159],{"class":126},"          std",[83,4161,140],{"class":130},[83,4163,2168],{"class":126},[83,4165,2171],{"class":130},[83,4167,4168,4170,4172,4174],{"class":85,"line":113},[83,4169,4159],{"class":126},[83,4171,140],{"class":130},[83,4173,2168],{"class":126},[83,4175,4176],{"class":130},"::_2)\n",[13,4178,4179,4180,4183],{},"如果是 ",[32,4181,4182],{},"shared_ptr"," 管理生命周期：",[24,4185,4187],{"className":77,"code":4186,"language":79,"meta":30,"style":30},"std::bind(&Session::on_read,\n          shared_from_this(),\n          std::placeholders::_1,\n          std::placeholders::_2)\n",[32,4188,4189,4206,4214,4224],{"__ignoreMap":30},[83,4190,4191,4193,4195,4197,4199,4201,4204],{"class":85,"line":86},[83,4192,1067],{"class":126},[83,4194,140],{"class":130},[83,4196,2139],{"class":126},[83,4198,131],{"class":130},[83,4200,146],{"class":89},[83,4202,4203],{"class":126},"Session",[83,4205,2149],{"class":130},[83,4207,4208,4211],{"class":85,"line":97},[83,4209,4210],{"class":126},"          shared_from_this",[83,4212,4213],{"class":130},"(),\n",[83,4215,4216,4218,4220,4222],{"class":85,"line":105},[83,4217,4159],{"class":126},[83,4219,140],{"class":130},[83,4221,2168],{"class":126},[83,4223,2171],{"class":130},[83,4225,4226,4228,4230,4232],{"class":85,"line":113},[83,4227,4159],{"class":126},[83,4229,140],{"class":130},[83,4231,2168],{"class":126},[83,4233,4176],{"class":130},[54,4235,4237],{"id":4236},"坑-4异步写时-buffer-是局部变量","坑 4：异步写时 buffer 是局部变量",[13,4239,4240],{},"错误思路：",[24,4242,4245],{"className":4243,"code":4244,"language":29,"meta":30},[27],"函数里创建 std::string\n把它交给 async_write\n函数返回后 string 销毁\n异步写还没完成\n",[32,4246,4244],{"__ignoreMap":30},[13,4248,4249],{},"推荐：",[24,4251,4254],{"className":4252,"code":4253,"language":29,"meta":30},[27],"用类成员变量\n用 shared_ptr\n用 write_queue\n",[32,4255,4253],{"__ignoreMap":30},[54,4257,4259],{"id":4258},"坑-5tcp-当成一包一包的数据","坑 5：TCP 当成一包一包的数据",[13,4261,4262],{},"TCP 是字节流，没有消息边界。机器人自定义协议必须设计分帧。",[54,4264,4266],{"id":4265},"坑-6udp-以为一定可靠","坑 6：UDP 以为一定可靠",[13,4268,4269],{},"UDP 可能丢包、乱序、重复。重要命令不要无脑依赖单次 UDP。",[42,4271],{},[45,4273,4274],{"id":4274},"你后面可以怎么继续扩展",[13,4276,4277],{},"学完这套后，可以继续做三个小项目：",[2832,4279,4280,4288,4296],{},[2835,4281,4282,4285,4287],{},[771,4283,4284],{},"串口下位机协议驱动",[17,4286],{},"\nLinux 上位机通过 Asio 串口读 STM32 发来的 odom，并发送 cmd_vel。",[2835,4289,4290,4293,4295],{},[771,4291,4292],{},"TCP 调试服务器",[17,4294],{},"\n写一个 TCP server，手机或电脑通过网络连接，发送调试命令控制小车。",[2835,4297,4298,4301,4303],{},[771,4299,4300],{},"UDP 心跳与状态广播",[17,4302],{},"\n小车每 1 秒广播一次自身状态，调试工具自动发现设备。",[13,4305,4306],{},"这三个项目和 ROS2、Nav2、ros2_control、小车底盘控制都能接上。",[4308,4309,4310],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}",{"title":30,"searchDepth":97,"depth":97,"links":4312},[4313,4321,4328,4335,4342,4348,4349,4358],{"id":47,"depth":97,"text":4314,"children":4315},"示例 1：普通 main() 中模拟机器人协议解析",[4316,4317,4318,4319,4320],{"id":56,"depth":105,"text":56},{"id":74,"depth":105,"text":74},{"id":777,"depth":105,"text":777},{"id":809,"depth":105,"text":809},{"id":818,"depth":105,"text":818},{"id":838,"depth":97,"text":839,"children":4322},[4323,4324,4325,4326,4327],{"id":842,"depth":105,"text":56},{"id":848,"depth":105,"text":74},{"id":1418,"depth":105,"text":777},{"id":1445,"depth":105,"text":809},{"id":1454,"depth":105,"text":818},{"id":1468,"depth":97,"text":1469,"children":4329},[4330,4331,4332,4333,4334],{"id":1472,"depth":105,"text":56},{"id":1484,"depth":105,"text":74},{"id":2692,"depth":105,"text":777},{"id":2794,"depth":105,"text":2794},{"id":2824,"depth":105,"text":818},{"id":2860,"depth":97,"text":2861,"children":4336},[4337,4338,4339,4340,4341],{"id":2864,"depth":105,"text":56},{"id":2879,"depth":105,"text":2879},{"id":3674,"depth":105,"text":3674},{"id":3704,"depth":105,"text":818},{"id":3753,"depth":105,"text":3754},{"id":3780,"depth":97,"text":3781,"children":4343},[4344,4345,4346,4347],{"id":3784,"depth":105,"text":56},{"id":3790,"depth":105,"text":3790},{"id":3978,"depth":105,"text":3978},{"id":3999,"depth":105,"text":818},{"id":4013,"depth":97,"text":4013},{"id":4039,"depth":97,"text":4039,"children":4350},[4351,4353,4354,4355,4356,4357],{"id":4042,"depth":105,"text":4352},"坑 1：io.run() 提前返回",{"id":4087,"depth":105,"text":4088},{"id":4120,"depth":105,"text":4121},{"id":4236,"depth":105,"text":4237},{"id":4258,"depth":105,"text":4259},{"id":4265,"depth":105,"text":4266},{"id":4274,"depth":97,"text":4274},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","19.1.6",19010600,"2023-10-05","wiki/2023-10-05-cplusplus-jiao-xue","zh-cn:2023-10-05-cplusplus-jiao-xue","/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue","Cplusplus教学","md","wiki/2023-10-05-Cplusplus教学/ch19-1-6-机器人工程写法与ROS2集成",false,null,"/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","zh-CN","zh-cn",{},{"title":5,"description":30},"qL1KGs2y--WqYb2rK7p8i7q-mQnt--97NtnwmI04pC0",[4378,4383,4388,4394,4400,4406,4412,4418,4424,4430,4436,4442,4448,4454,4460,4466,4472,4478,4484,4490,4496,4502,4508,4514,4519,4525,4531,4537,4542,4548,4554,4560,4566,4572,4578,4584,4590,4596,4602,4608,4614,4620,4626,4632,4638,4644,4650,4656,4662,4668,4674,4680,4686,4692,4698,4699,4705,4711,4717,4723,4729,4735,4741,4747,4753,4759,4765,4771,4777,4783],{"path":4379,"stem":4380,"title":4381,"date":4362,"chapter":2555,"chapterSort":4382,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch1-c-ji-chu-chu-shi","wiki/2023-10-05-Cplusplus教学/ch1-C++基础初识","C++基础初识",1000000,{"path":4384,"stem":4385,"title":4386,"date":4362,"chapter":3060,"chapterSort":4387,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch10-nei-cun-fen-qu-yu-sheng-ming-zhou-qi","wiki/2023-10-05-Cplusplus教学/ch10-内存分区与生命周期","内存分区与生命周期",10000000,{"path":4389,"stem":4390,"title":4391,"date":4362,"chapter":4392,"chapterSort":4393,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch11-yin-yong","wiki/2023-10-05-Cplusplus教学/ch11-引用","引用","11",11000000,{"path":4395,"stem":4396,"title":4397,"date":4362,"chapter":4398,"chapterSort":4399,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch12-han-shu-ti-gao-yu-ming-ming-kong-jian","wiki/2023-10-05-Cplusplus教学/ch12-函数提高与命名空间","函数提高与命名空间","12",12000000,{"path":4401,"stem":4402,"title":4403,"date":4362,"chapter":4404,"chapterSort":4405,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-1-feng-zhuang","wiki/2023-10-05-Cplusplus教学/ch13-1-封装","封装","13.1",13010000,{"path":4407,"stem":4408,"title":4409,"date":4362,"chapter":4410,"chapterSort":4411,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-2-dui-xiang-chu-shi-hua-he-qing-li","wiki/2023-10-05-Cplusplus教学/ch13-2-对象初始化和清理","对象初始化和清理","13.2",13020000,{"path":4413,"stem":4414,"title":4415,"date":4362,"chapter":4416,"chapterSort":4417,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-3-dui-xiang-mo-xing-yu-this-zhi-zhen","wiki/2023-10-05-Cplusplus教学/ch13-3-对象模型与this指针","对象模型与this指针","13.3",13030000,{"path":4419,"stem":4420,"title":4421,"date":4362,"chapter":4422,"chapterSort":4423,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-4-you-yuan-yu-yun-suan-fu-zhong-zai","wiki/2023-10-05-Cplusplus教学/ch13-4-友元与运算符重载","友元与运算符重载","13.4",13040000,{"path":4425,"stem":4426,"title":4427,"date":4362,"chapter":4428,"chapterSort":4429,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-5-ji-cheng","wiki/2023-10-05-Cplusplus教学/ch13-5-继承","继承","13.5",13050000,{"path":4431,"stem":4432,"title":4433,"date":4362,"chapter":4434,"chapterSort":4435,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-6-duo-tai","wiki/2023-10-05-Cplusplus教学/ch13-6-多态","多态","13.6",13060000,{"path":4437,"stem":4438,"title":4439,"date":4362,"chapter":4440,"chapterSort":4441,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch13-lei-he-dui-xiang","wiki/2023-10-05-Cplusplus教学/ch13-类和对象","类和对象","13",13000000,{"path":4443,"stem":4444,"title":4445,"date":4362,"chapter":4446,"chapterSort":4447,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch14-wen-jian-cao-zuo","wiki/2023-10-05-Cplusplus教学/ch14-文件操作","文件操作","14",14000000,{"path":4449,"stem":4450,"title":4451,"date":4362,"chapter":4452,"chapterSort":4453,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch15-mu-ban","wiki/2023-10-05-Cplusplus教学/ch15-模板","模板","15",15000000,{"path":4455,"stem":4456,"title":4457,"date":4362,"chapter":4458,"chapterSort":4459,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-1-stl-chu-shi","wiki/2023-10-05-Cplusplus教学/ch16-1-STL初识","STL初识","16.1",16010000,{"path":4461,"stem":4462,"title":4463,"date":4362,"chapter":4464,"chapterSort":4465,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-2-std-array","wiki/2023-10-05-Cplusplus教学/ch16-2-std-array","std::array","16.2",16020000,{"path":4467,"stem":4468,"title":4469,"date":4362,"chapter":4470,"chapterSort":4471,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-3-string-rong-qi","wiki/2023-10-05-Cplusplus教学/ch16-3-string容器","string容器","16.3",16030000,{"path":4473,"stem":4474,"title":4475,"date":4362,"chapter":4476,"chapterSort":4477,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-4-vector-yu-deque-rong-qi","wiki/2023-10-05-Cplusplus教学/ch16-4-vector与deque容器","vector与deque容器","16.4",16040000,{"path":4479,"stem":4480,"title":4481,"date":4362,"chapter":4482,"chapterSort":4483,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-5-stack-queue-list-rong-qi","wiki/2023-10-05-Cplusplus教学/ch16-5-stack-queue-list容器","stack / queue / list 容器","16.5",16050000,{"path":4485,"stem":4486,"title":4487,"date":4362,"chapter":4488,"chapterSort":4489,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-6-set-map-rong-qi","wiki/2023-10-05-Cplusplus教学/ch16-6-set-map容器","set / map 容器","16.6",16060000,{"path":4491,"stem":4492,"title":4493,"date":4362,"chapter":4494,"chapterSort":4495,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-7-han-shu-dui-xiang-yu-wei-ci","wiki/2023-10-05-Cplusplus教学/ch16-7-函数对象与谓词","函数对象与谓词","16.7",16070000,{"path":4497,"stem":4498,"title":4499,"date":4362,"chapter":4500,"chapterSort":4501,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-8-chang-yong-suan-fa","wiki/2023-10-05-Cplusplus教学/ch16-8-常用算法","常用算法","16.8",16080000,{"path":4503,"stem":4504,"title":4505,"date":4362,"chapter":4506,"chapterSort":4507,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch16-stl-ti-gao-bian-cheng","wiki/2023-10-05-Cplusplus教学/ch16-STL提高编程","STL提高编程","16",16000000,{"path":4509,"stem":4510,"title":4511,"date":4362,"chapter":4512,"chapterSort":4513,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch17-c-lei-xing-zhuan-huan","wiki/2023-10-05-Cplusplus教学/ch17-C++类型转换","C++类型转换","17",17000000,{"path":4515,"stem":4516,"title":4066,"date":4362,"chapter":4517,"chapterSort":4518,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-1-auto","wiki/2023-10-05-Cplusplus教学/ch18-1-auto","18.1",18010000,{"path":4520,"stem":4521,"title":4522,"date":4362,"chapter":4523,"chapterSort":4524,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-10-you-zhi-yin-yong-he-yi-dong-yu-yi","wiki/2023-10-05-Cplusplus教学/ch18-10-右值引用和移动语义","右值引用和移动语义","18.10",18100000,{"path":4526,"stem":4527,"title":4528,"date":4362,"chapter":4529,"chapterSort":4530,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-11-lambda-biao-da-shi","wiki/2023-10-05-Cplusplus教学/ch18-11-Lambda表达式","Lambda 表达式","18.11",18110000,{"path":4532,"stem":4533,"title":4534,"date":4362,"chapter":4535,"chapterSort":4536,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-12-std-function","wiki/2023-10-05-Cplusplus教学/ch18-12-std-function","std::function","18.12",18120000,{"path":4538,"stem":4539,"title":39,"date":4362,"chapter":4540,"chapterSort":4541,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-13-std-bind","wiki/2023-10-05-Cplusplus教学/ch18-13-std-bind","18.13",18130000,{"path":4543,"stem":4544,"title":4545,"date":4362,"chapter":4546,"chapterSort":4547,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-14-std-optional","wiki/2023-10-05-Cplusplus教学/ch18-14-std-optional","std::optional","18.14",18140000,{"path":4549,"stem":4550,"title":4551,"date":4362,"chapter":4552,"chapterSort":4553,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-15-std-variant","wiki/2023-10-05-Cplusplus教学/ch18-15-std-variant","std::variant","18.15",18150000,{"path":4555,"stem":4556,"title":4557,"date":4362,"chapter":4558,"chapterSort":4559,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-16-std-span","wiki/2023-10-05-Cplusplus教学/ch18-16-std-span","std::span","18.16",18160000,{"path":4561,"stem":4562,"title":4563,"date":4362,"chapter":4564,"chapterSort":4565,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-17-std-format-print","wiki/2023-10-05-Cplusplus教学/ch18-17-std-format-print","std::format / std::print","18.17",18170000,{"path":4567,"stem":4568,"title":4569,"date":4362,"chapter":4570,"chapterSort":4571,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-18-std-chrono","wiki/2023-10-05-Cplusplus教学/ch18-18-std-chrono","std::chrono","18.18",18180000,{"path":4573,"stem":4574,"title":4575,"date":4362,"chapter":4576,"chapterSort":4577,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-19-1-std-thread","wiki/2023-10-05-Cplusplus教学/ch18-19-1-std-thread","std::thread 与 join","18.19.1",18190100,{"path":4579,"stem":4580,"title":4581,"date":4362,"chapter":4582,"chapterSort":4583,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-19-2-mutex-lock-guard","wiki/2023-10-05-Cplusplus教学/ch18-19-2-mutex-lock-guard","mutex 与 lock_guard","18.19.2",18190200,{"path":4585,"stem":4586,"title":4587,"date":4362,"chapter":4588,"chapterSort":4589,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-19-3-std-atomic","wiki/2023-10-05-Cplusplus教学/ch18-19-3-std-atomic","std::atomic","18.19.3",18190300,{"path":4591,"stem":4592,"title":4593,"date":4362,"chapter":4594,"chapterSort":4595,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-19-4-condition-variable","wiki/2023-10-05-Cplusplus教学/ch18-19-4-condition-variable","condition_variable","18.19.4",18190400,{"path":4597,"stem":4598,"title":4599,"date":4362,"chapter":4600,"chapterSort":4601,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-19-bing-fa-bian-cheng","wiki/2023-10-05-Cplusplus教学/ch18-19-并发编程","并发编程","18.19",18190000,{"path":4603,"stem":4604,"title":4605,"date":4362,"chapter":4606,"chapterSort":4607,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-2-nullptr","wiki/2023-10-05-Cplusplus教学/ch18-2-nullptr","nullptr","18.2",18020000,{"path":4609,"stem":4610,"title":4611,"date":4362,"chapter":4612,"chapterSort":4613,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-20-std-filesystem","wiki/2023-10-05-Cplusplus教学/ch18-20-std-filesystem","std::filesystem","18.20",18200000,{"path":4615,"stem":4616,"title":4617,"date":4362,"chapter":4618,"chapterSort":4619,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-21-modules-jian-jie","wiki/2023-10-05-Cplusplus教学/ch18-21-modules简介","modules 简介","18.21",18210000,{"path":4621,"stem":4622,"title":4623,"date":4362,"chapter":4624,"chapterSort":4625,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-3-using","wiki/2023-10-05-Cplusplus教学/ch18-3-using","using","18.3",18030000,{"path":4627,"stem":4628,"title":4629,"date":4362,"chapter":4630,"chapterSort":4631,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-4-enum-class","wiki/2023-10-05-Cplusplus教学/ch18-4-enum-class","enum class","18.4",18040000,{"path":4633,"stem":4634,"title":4635,"date":4362,"chapter":4636,"chapterSort":4637,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-5-fan-wei-for-xun-huan","wiki/2023-10-05-Cplusplus教学/ch18-5-范围for循环","范围 for 循环","18.5",18050000,{"path":4639,"stem":4640,"title":4641,"date":4362,"chapter":4642,"chapterSort":4643,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-6-jie-gou-hua-bang-ding","wiki/2023-10-05-Cplusplus教学/ch18-6-结构化绑定","结构化绑定","18.6",18060000,{"path":4645,"stem":4646,"title":4647,"date":4362,"chapter":4648,"chapterSort":4649,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-7-constexpr","wiki/2023-10-05-Cplusplus教学/ch18-7-constexpr","constexpr","18.7",18070000,{"path":4651,"stem":4652,"title":4653,"date":4362,"chapter":4654,"chapterSort":4655,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-8-raii","wiki/2023-10-05-Cplusplus教学/ch18-8-RAII","RAII","18.8",18080000,{"path":4657,"stem":4658,"title":4659,"date":4362,"chapter":4660,"chapterSort":4661,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-9-zhi-neng-zhi-zhen","wiki/2023-10-05-Cplusplus教学/ch18-9-智能指针","智能指针","18.9",18090000,{"path":4663,"stem":4664,"title":4665,"date":4362,"chapter":4666,"chapterSort":4667,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch18-xian-dai-c","wiki/2023-10-05-Cplusplus教学/ch18-现代C++","现代C++","18",18000000,{"path":4669,"stem":4670,"title":4671,"date":4362,"chapter":4672,"chapterSort":4673,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-1-ding-shi-qi-yu-yi-bu-io","wiki/2023-10-05-Cplusplus教学/ch19-1-1-定时器与异步IO","定时器与异步 IO","19.1.1",19010100,{"path":4675,"stem":4676,"title":4677,"date":4362,"chapter":4678,"chapterSort":4679,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-2-boost-asio-ji-chu","wiki/2023-10-05-Cplusplus教学/ch19-1-2-Boost.Asio基础","Boost.Asio 基础","19.1.2",19010200,{"path":4681,"stem":4682,"title":4683,"date":4362,"chapter":4684,"chapterSort":4685,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-3-chuan-kou-tong-xin","wiki/2023-10-05-Cplusplus教学/ch19-1-3-串口通信","串口通信","19.1.3",19010300,{"path":4687,"stem":4688,"title":4689,"date":4362,"chapter":4690,"chapterSort":4691,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-4-tcp-tong-xin","wiki/2023-10-05-Cplusplus教学/ch19-1-4-TCP通信","TCP 通信","19.1.4",19010400,{"path":4693,"stem":4694,"title":4695,"date":4362,"chapter":4696,"chapterSort":4697,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-5-udp-tong-xin","wiki/2023-10-05-Cplusplus教学/ch19-1-5-UDP通信","UDP 通信","19.1.5",19010500,{"path":4359,"stem":4368,"title":5,"date":4362,"chapter":4360,"chapterSort":4361,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},{"path":4700,"stem":4701,"title":4702,"date":4362,"chapter":4703,"chapterSort":4704,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-boost-asio-yi-bu-io-ku","wiki/2023-10-05-Cplusplus教学/ch19-1-Boost.Asio异步IO库","Boost.Asio异步IO库","19.1",19010000,{"path":4706,"stem":4707,"title":4708,"date":4362,"chapter":4709,"chapterSort":4710,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-2-eigen-xian-xing-dai-shu-ku","wiki/2023-10-05-Cplusplus教学/ch19-2-Eigen线性代数库","Eigen线性代数库","19.2",19020000,{"path":4712,"stem":4713,"title":4714,"date":4362,"chapter":4715,"chapterSort":4716,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-3-opencv-ji-suan-ji-shi-jue-ku","wiki/2023-10-05-Cplusplus教学/ch19-3-OpenCV计算机视觉库","OpenCV计算机视觉库","19.3",19030000,{"path":4718,"stem":4719,"title":4720,"date":4362,"chapter":4721,"chapterSort":4722,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-4-pcl-dian-yun-ku","wiki/2023-10-05-Cplusplus教学/ch19-4-PCL点云库","PCL点云库","19.4",19040000,{"path":4724,"stem":4725,"title":4726,"date":4362,"chapter":4727,"chapterSort":4728,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-5-sophus-li-qun-li-dai-shu-ku","wiki/2023-10-05-Cplusplus教学/ch19-5-Sophus李群李代数库","Sophus 李群李代数库","19.5",19050000,{"path":4730,"stem":4731,"title":4732,"date":4362,"chapter":4733,"chapterSort":4734,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch19-chang-yong-ku","wiki/2023-10-05-Cplusplus教学/ch19-常用库","常用库学习","19",19000000,{"path":4736,"stem":4737,"title":4738,"date":4362,"chapter":4739,"chapterSort":4740,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch2-shu-ju-lei-xing-yu-shu-ju-cun-fang","wiki/2023-10-05-Cplusplus教学/ch2-数据类型与数据存放","数据类型与数据存放","2",2000000,{"path":4742,"stem":4743,"title":4744,"date":4362,"chapter":4745,"chapterSort":4746,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch3-shu-ru-shu-chu","wiki/2023-10-05-Cplusplus教学/ch3-输入输出","输入输出","3",3000000,{"path":4748,"stem":4749,"title":4750,"date":4362,"chapter":4751,"chapterSort":4752,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch4-yun-suan-fu","wiki/2023-10-05-Cplusplus教学/ch4-运算符","运算符","4",4000000,{"path":4754,"stem":4755,"title":4756,"date":4362,"chapter":4757,"chapterSort":4758,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch5-cheng-xu-liu-cheng-jie-gou","wiki/2023-10-05-Cplusplus教学/ch5-程序流程结构","程序流程结构","5",5000000,{"path":4760,"stem":4761,"title":4762,"date":4362,"chapter":4763,"chapterSort":4764,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch6-shu-zu","wiki/2023-10-05-Cplusplus教学/ch6-数组","数组","6",6000000,{"path":4766,"stem":4767,"title":4768,"date":4362,"chapter":4769,"chapterSort":4770,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch7-han-shu-yu-tou-wen-jian","wiki/2023-10-05-Cplusplus教学/ch7-函数与头文件","函数与头文件","7",7000000,{"path":4772,"stem":4773,"title":4774,"date":4362,"chapter":4775,"chapterSort":4776,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch8-zhi-zhen","wiki/2023-10-05-Cplusplus教学/ch8-指针","指针","8",8000000,{"path":4778,"stem":4779,"title":4780,"date":4362,"chapter":4781,"chapterSort":4782,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":4369},"/zh-cn/wiki/2023-10-05-cplusplus-jiao-xue/ch9-jie-gou-ti-yu-gong-yong-ti","wiki/2023-10-05-Cplusplus教学/ch9-结构体与共用体","结构体与共用体","9",9000000,{"path":4365,"stem":4784,"title":4785,"date":4362,"chapter":4370,"chapterSort":4786,"docKey":4364,"docRoot":4365,"docTitle":4366,"isWikiDoc":116,"isWikiIndex":116},"wiki/2023-10-05-Cplusplus教学/index","C/C++教程",0,{"variants":4788},[4789,4792,4795,4798,4801],{"path":4790,"localeSlug":4791,"i18nKey":4368},"/en-us/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","en-us",{"path":4793,"localeSlug":4794,"i18nKey":4368},"/zh-hant/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","zh-hant",{"path":4796,"localeSlug":4797,"i18nKey":4368},"/zh-hk/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","zh-hk",{"path":4799,"localeSlug":4800,"i18nKey":4368},"/zh-tw/wiki/2023-10-05-cplusplus-jiao-xue/ch19-1-6-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","zh-tw",{"path":4359,"localeSlug":4373,"i18nKey":4368},[4790,4371,4793,4371,4796,4371,4799,4371,4359,4371],1780663040098]