[{"data":1,"prerenderedAt":3633},["ShallowReactive",2],{"wiki-page-\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0350-chuan-kou-tong-xin-chuan-kou-bao-xie-yi-shi-li":3,"wiki-doc-items-2023-10-05-cplusplus-jiao-xue":3076,"wiki:author-members":3621},{"id":4,"title":5,"authors":6,"body":8,"chapterDepth":75,"chapterOrder":3064,"date":3065,"description":28,"docKey":3066,"docRoot":3067,"docTitle":3068,"extension":3069,"isWikiDoc":774,"isWikiIndex":3070,"meta":3071,"navigation":774,"path":3072,"seo":3073,"stem":3074,"wikiDepth":64,"__hash__":3075},"wiki\u002Fwiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0350-串口通信串口包协议实例.md","串口通信串口包协议实例",[7],"董佳辉",{"type":9,"value":10,"toc":3029},"minimark",[11,18,21,32,39,42,46,53,94,101,107,126,128,132,137,140,159,162,179,183,185,201,203,219,223,230,234,236,252,254,269,272,275,304,307,313,316,322,339,341,348,351,354,357,382,386,389,395,416,419,426,429,505,508,511,563,587,593,595,629,632,634,652,657,663,665,680,683,686,694,697,699,706,720,724,742,1076,1079,1138,1141,1147,1160,1163,1169,1172,1299,1305,1307,1352,1366,1372,1374,1438,1441,1443,1469,1489,1583,1600,1606,1608,1645,1648,1650,1660,1665,1671,1673,1692,1695,1697,1701,1705,1715,2033,2045,2048,2099,2103,2118,2122,2139,2167,2171,2185,2234,2241,2267,2296,2304,2318,2368,2402,2406,2423,2631,2655,2665,2691,2700,2702,2706,2709,2723,2769,2772,2781,2837,2887,2906,2909,2915,2945,2966,2990,2993,3012,3025],[12,13,14],"blockquote",{},[15,16,17],"p",{},"串口是机器人里最常见的通信方式之一：上位机 ROS2 \u002F Linux 程序通过 USB-TTL、USB-CAN 转串口、CH340、CP2102、FT232 等设备和 STM32、ESP32、下位机控制板通信。",[15,19,20],{},"本节依旧按照“先准备环境，再看怎么调用，最后运行观察结果”的顺序：",[22,23,29],"pre",{"className":24,"code":26,"language":27,"meta":28},[25],"language-text","克隆 Serial_Pack\n先学 ROS2 端串口驱动：打开、接收、发送\n再学跨平台串口包协议：注册命令、打包、解包\n最后用虚拟串口或 STM32 验证\n","text","",[30,31,26],"code",{"__ignoreMap":28},[15,33,34,35,38],{},"串口接收和 ROS2 定时器等异步回调统一使用 ",[30,36,37],{},"std::bind","。协议的命令处理函数需要明确的参数类型，直接注册普通函数或成员函数。",[40,41],"hr",{},[43,44,45],"h2",{"id":45},"获取项目",[15,47,48,49,52],{},"第一次拿到项目，先只克隆这一个仓库。下面统一以克隆到用户主目录的 ",[30,50,51],{},"~\u002FSerial_Pack"," 为例：",[22,54,58],{"className":55,"code":56,"language":57,"meta":28,"style":28},"language-bash shiki shiki-themes github-light github-dark","cd ~\ngit clone https:\u002F\u002Fgithub.com\u002Ftungchiahui\u002FSerial_Pack.git\ncd Serial_Pack\n","bash",[30,59,60,73,86],{"__ignoreMap":28},[61,62,65,69],"span",{"class":63,"line":64},"line",1,[61,66,68],{"class":67},"sj4cs","cd",[61,70,72],{"class":71},"sZZnC"," ~\n",[61,74,76,80,83],{"class":63,"line":75},2,[61,77,79],{"class":78},"sScJk","git",[61,81,82],{"class":71}," clone",[61,84,85],{"class":71}," https:\u002F\u002Fgithub.com\u002Ftungchiahui\u002FSerial_Pack.git\n",[61,87,89,91],{"class":63,"line":88},3,[61,90,68],{"class":67},[61,92,93],{"class":71}," Serial_Pack\n",[15,95,96,97,100],{},"本教程会用到以下文件，路径都从 ",[30,98,99],{},"Serial_Pack\u002F"," 算起：",[22,102,105],{"className":103,"code":104,"language":27,"meta":28},[25],"example\u002Fros2_ws\u002Fsrc\u002Fserial_comm\u002F       ROS2 演示节点\nexample\u002Fros2_ws\u002Fsrc\u002Fserial_transport\u002F  PC 端串口驱动\nexample\u002Fros2_ws\u002Fsrc\u002Fwire_protocol\u002F     不依赖 ROS2 的 C++20 串口包协议\nexample\u002Fstm32_hal\u002F                     STM32CubeMX + HAL 接入例程\n",[30,106,104],{"__ignoreMap":28},[15,108,109,110,113,114,117,118,121,122,125],{},"只需要这个仓库。PC 端使用现成的 ROS2 工作区；STM32 端从 ",[30,111,112],{},"example\u002Fstm32_hal\u002FTEST.ioc"," 用 CubeMX 生成工程，再接入同目录的 ",[30,115,116],{},"applications","、",[30,119,120],{},"bsp"," 和 ",[30,123,124],{},"cmake\u002Fuser","。",[40,127],{},[43,129,131],{"id":130},"linux-下准备asio库","Linux 下准备ASIO库",[133,134,136],"h3",{"id":135},"本库只依赖standalone-asio","本库只依赖standalone asio",[15,138,139],{},"Ubuntu \u002F Debian：",[22,141,143],{"className":55,"code":142,"language":57,"meta":28,"style":28},"sudo apt install libasio-dev\n",[30,144,145],{"__ignoreMap":28},[61,146,147,150,153,156],{"class":63,"line":64},[61,148,149],{"class":78},"sudo",[61,151,152],{"class":71}," apt",[61,154,155],{"class":71}," install",[61,157,158],{"class":71}," libasio-dev\n",[15,160,161],{},"Fedora：",[22,163,165],{"className":55,"code":164,"language":57,"meta":28,"style":28},"sudo dnf install asio-devel\n",[30,166,167],{"__ignoreMap":28},[61,168,169,171,174,176],{"class":63,"line":64},[61,170,149],{"class":78},[61,172,173],{"class":71}," dnf",[61,175,155],{"class":71},[61,177,178],{"class":71}," asio-devel\n",[133,180,182],{"id":181},"选装完整的boost库没啥必要针对于完整的程序","选装完整的boost库（没啥必要针对于完整的程序）",[15,184,139],{},[22,186,188],{"className":55,"code":187,"language":57,"meta":28,"style":28},"sudo apt install libboost-all-dev \n",[30,189,190],{"__ignoreMap":28},[61,191,192,194,196,198],{"class":63,"line":64},[61,193,149],{"class":78},[61,195,152],{"class":71},[61,197,155],{"class":71},[61,199,200],{"class":71}," libboost-all-dev\n",[15,202,161],{},[22,204,206],{"className":55,"code":205,"language":57,"meta":28,"style":28},"sudo dnf install boost-devel\n",[30,207,208],{"__ignoreMap":28},[61,209,210,212,214,216],{"class":63,"line":64},[61,211,149],{"class":78},[61,213,173],{"class":71},[61,215,155],{"class":71},[61,217,218],{"class":71}," boost-devel\n",[43,220,222],{"id":221},"linux-下准备虚拟串口测试环境","Linux 下准备虚拟串口测试环境",[15,224,225,226,229],{},"如果你手里没有真实 STM32，可以用 ",[30,227,228],{},"socat"," 创建一对虚拟串口。",[133,231,233],{"id":232},"安装-socat","安装 socat",[15,235,139],{},[22,237,239],{"className":55,"code":238,"language":57,"meta":28,"style":28},"sudo apt install socat \n",[30,240,241],{"__ignoreMap":28},[61,242,243,245,247,249],{"class":63,"line":64},[61,244,149],{"class":78},[61,246,152],{"class":71},[61,248,155],{"class":71},[61,250,251],{"class":71}," socat\n",[15,253,161],{},[22,255,257],{"className":55,"code":256,"language":57,"meta":28,"style":28},"sudo dnf install socat\n",[30,258,259],{"__ignoreMap":28},[61,260,261,263,265,267],{"class":63,"line":64},[61,262,149],{"class":78},[61,264,173],{"class":71},[61,266,155],{"class":71},[61,268,251],{"class":71},[133,270,271],{"id":271},"创建一对虚拟串口",[15,273,274],{},"新开一个终端，运行：",[22,276,278],{"className":55,"code":277,"language":57,"meta":28,"style":28},"socat -d -d \\\n  PTY,raw,echo=0,link=\u002Ftmp\u002FttyV0 \\\n  PTY,raw,echo=0,link=\u002Ftmp\u002FttyV1\n",[30,279,280,292,299],{"__ignoreMap":28},[61,281,282,284,287,289],{"class":63,"line":64},[61,283,228],{"class":78},[61,285,286],{"class":67}," -d",[61,288,286],{"class":67},[61,290,291],{"class":67}," \\\n",[61,293,294,297],{"class":63,"line":75},[61,295,296],{"class":71},"  PTY,raw,echo=0,link=\u002Ftmp\u002FttyV0",[61,298,291],{"class":67},[61,300,301],{"class":63,"line":88},[61,302,303],{"class":71},"  PTY,raw,echo=0,link=\u002Ftmp\u002FttyV1\n",[15,305,306],{},"你会看到类似输出：",[22,308,311],{"className":309,"code":310,"language":27,"meta":28},[25],"2026\u002F05\u002F24 12:00:00 socat[12345] N PTY is \u002Fdev\u002Fpts\u002F3\n2026\u002F05\u002F24 12:00:00 socat[12345] N PTY is \u002Fdev\u002Fpts\u002F4\n2026\u002F05\u002F24 12:00:00 socat[12345] N starting data transfer loop with FDs [5,5] and [7,7]\n",[30,312,310],{"__ignoreMap":28},[15,314,315],{},"这表示：",[22,317,320],{"className":318,"code":319,"language":27,"meta":28},[25],"\u002Fdev\u002Fpts\u002F3 和 \u002Fdev\u002Fpts\u002F4 是一对互通的虚拟串口\n程序写 \u002Fdev\u002Fpts\u002F3，另一个终端读 \u002Fdev\u002Fpts\u002F4 就能看到\n程序读 \u002Fdev\u002Fpts\u002F3，另一个终端写 \u002Fdev\u002Fpts\u002F4 就能发给程序\n\n同时我也给了他们别名\n\u002Fdev\u002Fpts\u002F3 和 \u002Ftmp\u002FttyV0是同一个东西\n\u002Fdev\u002Fpts\u002F4 和 \u002Ftmp\u002FttyV1是同一个东西\n",[30,321,319],{"__ignoreMap":28},[15,323,324,325,121,328,331,332,335,336,338],{},"下面示例使用 ",[30,326,327],{},"\u002Ftmp\u002FttyV0",[30,329,330],{},"\u002Ftmp\u002FttyV1","，不用猜测每次变化的 ",[30,333,334],{},"\u002Fdev\u002Fpts\u002FN","。保持这个 ",[30,337,228],{}," 终端运行。",[40,340],{},[43,342,344,345],{"id":343},"第一部分ros2-端串口驱动-serial_transport","第一部分：ROS2 端串口驱动 ",[30,346,347],{},"serial_transport",[133,349,350],{"id":350},"程序目标",[15,352,353],{},"先只关心一件事：ROS2 节点怎样打开一个串口、收到一段字节、发出一段字节。此时不要求看懂串口包的内容；下一部分才把“字节”解释为命令和字段。",[15,355,356],{},"PC 端公开给我们使用的主要是两个类型：",[358,359,360,367],"ul",{},[361,362,363,366],"li",{},[30,364,365],{},"serial_transport::Serial_Config","：保存设备名、波特率、数据位、校验位、停止位和流控。本例使用 115200 8N1、无流控。",[361,368,369,372,373,117,376,121,379,125],{},[30,370,371],{},"serial_transport::SerialTransport","：提供 ",[30,374,375],{},"start(config, 回调)",[30,377,378],{},"async_write(字节)",[30,380,381],{},"stop()",[133,383,385],{"id":384},"先看调用顺序伪代码","先看调用顺序（伪代码）",[15,387,388],{},"这里故意只写使用顺序，不展开 Asio 线程和串口驱动源码：",[22,390,393],{"className":391,"code":392,"language":27,"meta":28},[25],"ROS2 节点初始化：\n    从参数读取 port 和 baud_rate\n    填写 Serial_Config：设备名、115200、8N1、无流控\n    driver.start(config, std::bind(本节点的接收函数, this, 第一个参数))\n\n接收函数(本次收到的字节片段)：\n    把片段交给后面的协议 feed()\n\n需要发送时：\n    driver.async_write(待发送的字节)\n\n节点退出时：\n    driver.stop()\n",[30,394,392],{"__ignoreMap":28},[15,396,397,398,401,402,121,405,408,409,117,412,415],{},"这些调用对应 ",[30,399,400],{},"example\u002Fros2_ws\u002Fsrc\u002Fserial_comm\u002Fsrc\u002Fserial_comm.cpp"," 中的 ",[30,403,404],{},"serial_config_",[30,406,407],{},"serial_driver_","。示例从 ROS2 参数 ",[30,410,411],{},"port",[30,413,414],{},"baud_rate"," 读取设备名和波特率，其余配置固定为 8N1、无流控。",[133,417,418],{"id":418},"关键函数说明",[420,421,423],"h4",{"id":422},"serialtransportstart",[30,424,425],{},"SerialTransport::start()",[15,427,428],{},"函数原型：",[22,430,434],{"className":431,"code":432,"language":433,"meta":28,"style":28},"language-cpp shiki shiki-themes github-light github-dark","bool start(\n    Serial_Config config,\n    std::function\u003Cvoid(std::span\u003Cconst uint8_t>)> receive_callback);\n","cpp",[30,435,436,449,461],{"__ignoreMap":28},[61,437,438,442,445],{"class":63,"line":64},[61,439,441],{"class":440},"szBVR","bool",[61,443,444],{"class":78}," start",[61,446,448],{"class":447},"sVt8B","(\n",[61,450,451,454,458],{"class":63,"line":75},[61,452,453],{"class":78},"    Serial_Config",[61,455,457],{"class":456},"s4XuR"," config",[61,459,460],{"class":447},",\n",[61,462,463,466,469,472,475,478,481,484,486,488,490,493,496,499,502],{"class":63,"line":88},[61,464,465],{"class":78},"    std",[61,467,468],{"class":447},"::",[61,470,471],{"class":78},"function",[61,473,474],{"class":447},"\u003C",[61,476,477],{"class":440},"void",[61,479,480],{"class":447},"(",[61,482,483],{"class":78},"std",[61,485,468],{"class":447},[61,487,61],{"class":78},[61,489,474],{"class":447},[61,491,492],{"class":440},"const",[61,494,495],{"class":440}," uint8_t",[61,497,498],{"class":447},">)> ",[61,500,501],{"class":456},"receive_callback",[61,503,504],{"class":447},");\n",[15,506,507],{},"作用：启动串口驱动，尝试按配置打开设备，并开始异步接收。收到字节后调用接收回调。",[15,509,510],{},"参数：",[358,512,513,546],{},[361,514,515,518,519,522,523,526,527,530,531,534,535,538,539,542,543,125],{},[30,516,517],{},"config","：",[30,520,521],{},"Serial_Config","，串口配置，包含设备名 ",[30,524,525],{},"port_name_","、波特率 ",[30,528,529],{},"baud_rate_","、数据位 ",[30,532,533],{},"character_size_","、校验位 ",[30,536,537],{},"parity_","、停止位 ",[30,540,541],{},"stop_bits_"," 和流控 ",[30,544,545],{},"flow_control_",[361,547,548,518,550,553,554,558,559,562],{},[30,549,501],{},[30,551,552],{},"std::function\u003Cvoid(std::span\u003Cconst uint8_t>)>","，收到字节时执行的函数。它的参数是",[555,556,557],"strong",{},"本次收到的字节片段","，不是保证完整的一帧。",[30,560,561],{},"std::span"," 不拥有数据，只在本次回调中使用；需要留存时自行复制。",[15,564,565,566,568,569,572,573,125,576,578,579,582,583,586],{},"返回值：",[30,567,441],{},"。首次启动返回 ",[30,570,571],{},"true","；同一个驱动对象已经启动时返回 ",[30,574,575],{},"false",[30,577,571],{}," 只表示启动流程已提交，",[555,580,581],{},"不代表物理串口已经打开","。设备暂时不存在时驱动会尝试重连，要以终端的 ",[30,584,585],{},"串口打开成功"," 日志确认实际打开。",[420,588,590],{"id":589},"serialtransportasync_write",[30,591,592],{},"SerialTransport::async_write()",[15,594,428],{},[22,596,598],{"className":431,"code":597,"language":433,"meta":28,"style":28},"void async_write(std::span\u003Cconst uint8_t> frame);\n",[30,599,600],{"__ignoreMap":28},[61,601,602,604,607,609,611,613,615,617,619,621,624,627],{"class":63,"line":64},[61,603,477],{"class":440},[61,605,606],{"class":78}," async_write",[61,608,480],{"class":447},[61,610,483],{"class":78},[61,612,468],{"class":447},[61,614,61],{"class":78},[61,616,474],{"class":447},[61,618,492],{"class":440},[61,620,495],{"class":440},[61,622,623],{"class":447},"> ",[61,625,626],{"class":456},"frame",[61,628,504],{"class":447},[15,630,631],{},"作用：把一段字节提交给串口驱动异步发送。",[15,633,510],{},[358,635,636],{},[361,637,638,518,640,643,644,647,648,651],{},[30,639,626],{},[30,641,642],{},"std::span\u003Cconst uint8_t>","，表示待发送字节的起始位置和长度。可以传 ",[30,645,646],{},"pack()"," 返回的 ",[30,649,650],{},"std::array","；当前驱动在调用时会复制这段数据，所以局部帧数组在函数返回后可以销毁。",[15,653,565,654,656],{},[30,655,477],{},"，没有发送成功状态。这次调用不等待对端确认；如果物理串口尚未打开，提交的帧不会在重连后补发。",[420,658,660],{"id":659},"serialtransportstop",[30,661,662],{},"SerialTransport::stop()",[15,664,428],{},[22,666,668],{"className":431,"code":667,"language":433,"meta":28,"style":28},"void stop();\n",[30,669,670],{"__ignoreMap":28},[61,671,672,674,677],{"class":63,"line":64},[61,673,477],{"class":440},[61,675,676],{"class":78}," stop",[61,678,679],{"class":447},"();\n",[15,681,682],{},"作用：停止接收、关闭串口并等待驱动线程退出。",[15,684,685],{},"参数：无。",[15,687,565,688,125,690,693],{},[30,689,477],{},[30,691,692],{},"SerialTransport"," 析构时也会调用它；接收回调使用的节点对象必须活到驱动停止以后。",[15,695,696],{},"到这里，我们只知道怎样搬运字节。接下来才解决“哪些字节属于同一条命令、收到后交给谁处理”。",[40,698],{},[43,700,702,703],{"id":701},"第二部分跨平台串口包通信协议-wire_protocol","第二部分：跨平台串口包通信协议 ",[30,704,705],{},"wire_protocol",[15,707,708,710,711,117,713,117,716,719],{},[30,709,705],{}," 是纯 C++20：ROS2\u002FLinux 程序可以使用，具备 C++20 支持的 STM32 工程也可以使用。两端用同一套 ",[30,712,646],{},[30,714,715],{},"feed()",[30,717,718],{},"set_unpack_callback()","，各自用自己的串口驱动搬运字节。使用者不需要自己处理半包、粘包和校验，也不用阅读协议解析源码。",[133,721,723],{"id":722},"程序目标先不接串口验证一次打包和解包","程序目标：先不接串口，验证一次打包和解包",[15,725,726,727,125,730,732,733,735,736,738,739,518],{},"下面是",[555,728,729],{},"可以单独编译的真实 C++ 代码",[30,731,646],{}," 生成一帧，再把这一帧喂给 ",[30,734,715],{},"，观察注册的函数是否被调用。在 ",[30,737,99],{}," 根目录新建 ",[30,740,741],{},"protocol_demo.cpp",[22,743,745],{"className":431,"code":744,"language":433,"meta":28,"style":28},"#include \u003Cwire_protocol\u002Fprotocol.hpp>\n#include \u003Ccstdint>\n#include \u003Ciostream>\n\nvoid on_mode(std::uint32_t seq, std::int32_t mode)\n{\n    std::cout \u003C\u003C \"收到 mode：seq=\" \u003C\u003C seq \u003C\u003C \" mode=\" \u003C\u003C mode \u003C\u003C '\\n';\n}\n\nint main()\n{\n    wire_protocol::CallbackProtocol protocol;\n\n    \u002F\u002F 0x02 命令收到后，按 on_mode 的参数类型解包。\n    if (!protocol.set_unpack_callback(0x02, on_mode))\n    {\n        std::cerr \u003C\u003C \"注册命令失败\\n\";\n        return 1;\n    }\n\n    const std::uint32_t seq = 1;\n    const std::int32_t mode = 2;\n    const auto frame = protocol.pack(0x02, seq, mode);\n    protocol.feed(frame);\n}\n",[30,746,747,755,762,769,776,812,818,862,868,873,885,890,899,904,911,940,946,967,978,984,989,1011,1031,1059,1071],{"__ignoreMap":28},[61,748,749,752],{"class":63,"line":64},[61,750,751],{"class":440},"#include",[61,753,754],{"class":71}," \u003Cwire_protocol\u002Fprotocol.hpp>\n",[61,756,757,759],{"class":63,"line":75},[61,758,751],{"class":440},[61,760,761],{"class":71}," \u003Ccstdint>\n",[61,763,764,766],{"class":63,"line":88},[61,765,751],{"class":440},[61,767,768],{"class":71}," \u003Ciostream>\n",[61,770,772],{"class":63,"line":771},4,[61,773,775],{"emptyLinePlaceholder":774},true,"\n",[61,777,779,781,784,786,788,790,793,796,799,801,803,806,809],{"class":63,"line":778},5,[61,780,477],{"class":440},[61,782,783],{"class":78}," on_mode",[61,785,480],{"class":447},[61,787,483],{"class":78},[61,789,468],{"class":447},[61,791,792],{"class":440},"uint32_t",[61,794,795],{"class":456}," seq",[61,797,798],{"class":447},", ",[61,800,483],{"class":78},[61,802,468],{"class":447},[61,804,805],{"class":440},"int32_t",[61,807,808],{"class":456}," mode",[61,810,811],{"class":447},")\n",[61,813,815],{"class":63,"line":814},6,[61,816,817],{"class":447},"{\n",[61,819,821,823,826,829,832,835,838,840,843,845,848,850,853,856,859],{"class":63,"line":820},7,[61,822,465],{"class":78},[61,824,825],{"class":447},"::cout ",[61,827,828],{"class":440},"\u003C\u003C",[61,830,831],{"class":71}," \"收到 mode：seq=\"",[61,833,834],{"class":440}," \u003C\u003C",[61,836,837],{"class":447}," seq ",[61,839,828],{"class":440},[61,841,842],{"class":71}," \" mode=\"",[61,844,834],{"class":440},[61,846,847],{"class":447}," mode ",[61,849,828],{"class":440},[61,851,852],{"class":71}," '",[61,854,855],{"class":67},"\\n",[61,857,858],{"class":71},"'",[61,860,861],{"class":447},";\n",[61,863,865],{"class":63,"line":864},8,[61,866,867],{"class":447},"}\n",[61,869,871],{"class":63,"line":870},9,[61,872,775],{"emptyLinePlaceholder":774},[61,874,876,879,882],{"class":63,"line":875},10,[61,877,878],{"class":440},"int",[61,880,881],{"class":78}," main",[61,883,884],{"class":447},"()\n",[61,886,888],{"class":63,"line":887},11,[61,889,817],{"class":447},[61,891,893,896],{"class":63,"line":892},12,[61,894,895],{"class":78},"    wire_protocol",[61,897,898],{"class":447},"::CallbackProtocol protocol;\n",[61,900,902],{"class":63,"line":901},13,[61,903,775],{"emptyLinePlaceholder":774},[61,905,907],{"class":63,"line":906},14,[61,908,910],{"class":909},"sJ8bj","    \u002F\u002F 0x02 命令收到后，按 on_mode 的参数类型解包。\n",[61,912,914,917,920,923,926,929,931,934,937],{"class":63,"line":913},15,[61,915,916],{"class":440},"    if",[61,918,919],{"class":447}," (",[61,921,922],{"class":440},"!",[61,924,925],{"class":447},"protocol.",[61,927,928],{"class":78},"set_unpack_callback",[61,930,480],{"class":447},[61,932,933],{"class":440},"0x",[61,935,936],{"class":67},"02",[61,938,939],{"class":447},", on_mode))\n",[61,941,943],{"class":63,"line":942},16,[61,944,945],{"class":447},"    {\n",[61,947,949,952,955,957,960,962,965],{"class":63,"line":948},17,[61,950,951],{"class":78},"        std",[61,953,954],{"class":447},"::cerr ",[61,956,828],{"class":440},[61,958,959],{"class":71}," \"注册命令失败",[61,961,855],{"class":67},[61,963,964],{"class":71},"\"",[61,966,861],{"class":447},[61,968,970,973,976],{"class":63,"line":969},18,[61,971,972],{"class":440},"        return",[61,974,975],{"class":67}," 1",[61,977,861],{"class":447},[61,979,981],{"class":63,"line":980},19,[61,982,983],{"class":447},"    }\n",[61,985,987],{"class":63,"line":986},20,[61,988,775],{"emptyLinePlaceholder":774},[61,990,992,995,998,1000,1002,1004,1007,1009],{"class":63,"line":991},21,[61,993,994],{"class":440},"    const",[61,996,997],{"class":78}," std",[61,999,468],{"class":447},[61,1001,792],{"class":440},[61,1003,837],{"class":447},[61,1005,1006],{"class":440},"=",[61,1008,975],{"class":67},[61,1010,861],{"class":447},[61,1012,1014,1016,1018,1020,1022,1024,1026,1029],{"class":63,"line":1013},22,[61,1015,994],{"class":440},[61,1017,997],{"class":78},[61,1019,468],{"class":447},[61,1021,805],{"class":440},[61,1023,847],{"class":447},[61,1025,1006],{"class":440},[61,1027,1028],{"class":67}," 2",[61,1030,861],{"class":447},[61,1032,1034,1036,1039,1042,1044,1047,1050,1052,1054,1056],{"class":63,"line":1033},23,[61,1035,994],{"class":440},[61,1037,1038],{"class":440}," auto",[61,1040,1041],{"class":447}," frame ",[61,1043,1006],{"class":440},[61,1045,1046],{"class":447}," protocol.",[61,1048,1049],{"class":78},"pack",[61,1051,480],{"class":447},[61,1053,933],{"class":440},[61,1055,936],{"class":67},[61,1057,1058],{"class":447},", seq, mode);\n",[61,1060,1062,1065,1068],{"class":63,"line":1061},24,[61,1063,1064],{"class":447},"    protocol.",[61,1066,1067],{"class":78},"feed",[61,1069,1070],{"class":447},"(frame);\n",[61,1072,1074],{"class":63,"line":1073},25,[61,1075,867],{"class":447},[15,1077,1078],{},"在仓库根目录编译运行（需要支持 C++20 的编译器）：",[22,1080,1082],{"className":55,"code":1081,"language":57,"meta":28,"style":28},"cd ~\u002FSerial_Pack\ng++ -std=c++20 \\\n  -I example\u002Fros2_ws\u002Fsrc\u002Fwire_protocol\u002Finclude \\\n  protocol_demo.cpp \\\n  example\u002Fros2_ws\u002Fsrc\u002Fwire_protocol\u002Fsrc\u002Fprotocol.cpp \\\n  -o protocol_demo\n.\u002Fprotocol_demo\n",[30,1083,1084,1091,1101,1111,1118,1125,1133],{"__ignoreMap":28},[61,1085,1086,1088],{"class":63,"line":64},[61,1087,68],{"class":67},[61,1089,1090],{"class":71}," ~\u002FSerial_Pack\n",[61,1092,1093,1096,1099],{"class":63,"line":75},[61,1094,1095],{"class":78},"g++",[61,1097,1098],{"class":67}," -std=c++20",[61,1100,291],{"class":67},[61,1102,1103,1106,1109],{"class":63,"line":88},[61,1104,1105],{"class":67},"  -I",[61,1107,1108],{"class":71}," example\u002Fros2_ws\u002Fsrc\u002Fwire_protocol\u002Finclude",[61,1110,291],{"class":67},[61,1112,1113,1116],{"class":63,"line":771},[61,1114,1115],{"class":71},"  protocol_demo.cpp",[61,1117,291],{"class":67},[61,1119,1120,1123],{"class":63,"line":778},[61,1121,1122],{"class":71},"  example\u002Fros2_ws\u002Fsrc\u002Fwire_protocol\u002Fsrc\u002Fprotocol.cpp",[61,1124,291],{"class":67},[61,1126,1127,1130],{"class":63,"line":814},[61,1128,1129],{"class":67},"  -o",[61,1131,1132],{"class":71}," protocol_demo\n",[61,1134,1135],{"class":63,"line":820},[61,1136,1137],{"class":78},".\u002Fprotocol_demo\n",[15,1139,1140],{},"预期输出：",[22,1142,1145],{"className":1143,"code":1144,"language":27,"meta":28},[25],"收到 mode：seq=1 mode=2\n",[30,1146,1144],{"__ignoreMap":28},[15,1148,1149,1150,1153,1154,1156,1157,125],{},"这里的 ",[30,1151,1152],{},"feed(frame)"," 是为了先练习协议 API。真正接串口时，传给 ",[30,1155,715],{}," 的是串口每次收到的字节片段，",[555,1158,1159],{},"不要求刚好一整帧",[133,1161,418],{"id":1162},"关键函数说明-1",[420,1164,1166],{"id":1165},"callbackprotocolset_unpack_callback",[30,1167,1168],{},"CallbackProtocol::set_unpack_callback()",[15,1170,1171],{},"函数原型（有两种调用形式）：",[22,1173,1175],{"className":431,"code":1174,"language":433,"meta":28,"style":28},"template \u003Ctypename Callable>\nbool set_unpack_callback(std::uint8_t command, Callable&& callback) noexcept;\n\ntemplate \u003Ctypename Method, typename Object>\nbool set_unpack_callback(\n    std::uint8_t command, Method method, Object* object) noexcept;\n",[30,1176,1177,1194,1232,1236,1256,1264],{"__ignoreMap":28},[61,1178,1179,1182,1185,1188,1191],{"class":63,"line":64},[61,1180,1181],{"class":440},"template",[61,1183,1184],{"class":447}," \u003C",[61,1186,1187],{"class":440},"typename",[61,1189,1190],{"class":78}," Callable",[61,1192,1193],{"class":447},">\n",[61,1195,1196,1198,1201,1203,1205,1207,1210,1213,1215,1218,1221,1224,1227,1230],{"class":63,"line":75},[61,1197,441],{"class":440},[61,1199,1200],{"class":78}," set_unpack_callback",[61,1202,480],{"class":447},[61,1204,483],{"class":78},[61,1206,468],{"class":447},[61,1208,1209],{"class":440},"uint8_t",[61,1211,1212],{"class":456}," command",[61,1214,798],{"class":447},[61,1216,1217],{"class":78},"Callable",[61,1219,1220],{"class":440},"&&",[61,1222,1223],{"class":456}," callback",[61,1225,1226],{"class":447},") ",[61,1228,1229],{"class":440},"noexcept",[61,1231,861],{"class":447},[61,1233,1234],{"class":63,"line":88},[61,1235,775],{"emptyLinePlaceholder":774},[61,1237,1238,1240,1242,1244,1247,1249,1251,1254],{"class":63,"line":771},[61,1239,1181],{"class":440},[61,1241,1184],{"class":447},[61,1243,1187],{"class":440},[61,1245,1246],{"class":78}," Method",[61,1248,798],{"class":447},[61,1250,1187],{"class":440},[61,1252,1253],{"class":78}," Object",[61,1255,1193],{"class":447},[61,1257,1258,1260,1262],{"class":63,"line":778},[61,1259,441],{"class":440},[61,1261,1200],{"class":78},[61,1263,448],{"class":447},[61,1265,1266,1268,1270,1272,1274,1276,1279,1282,1284,1287,1290,1293,1295,1297],{"class":63,"line":814},[61,1267,465],{"class":78},[61,1269,468],{"class":447},[61,1271,1209],{"class":440},[61,1273,1212],{"class":456},[61,1275,798],{"class":447},[61,1277,1278],{"class":78},"Method",[61,1280,1281],{"class":456}," method",[61,1283,798],{"class":447},[61,1285,1286],{"class":78},"Object",[61,1288,1289],{"class":440},"*",[61,1291,1292],{"class":456}," object",[61,1294,1226],{"class":447},[61,1296,1229],{"class":440},[61,1298,861],{"class":447},[15,1300,1301,1302,1304],{},"作用：在开始 ",[30,1303,715],{}," 之前，为一条命令注册解包后的处理函数。收到该命令的有效帧时，协议按处理函数的参数类型取出字段并调用它。",[15,1306,510],{},[358,1308,1309,1321,1333,1342],{},[361,1310,1311,518,1314,1317,1318,125],{},[30,1312,1313],{},"command",[30,1315,1316],{},"std::uint8_t","，命令号，例如 ",[30,1319,1320],{},"0x02",[361,1322,1323,1326,1327,1329,1330,125],{},[30,1324,1325],{},"callback","：普通函数、函数指针或符合限制的可调用对象。它必须返回 ",[30,1328,477],{},"，字段参数要按值传递且类型明确，例如 ",[30,1331,1332],{},"void on_mode(std::uint32_t seq, std::int32_t mode)",[361,1334,1335,1338,1339,125],{},[30,1336,1337],{},"method","：使用成员函数形式时传入成员函数指针，例如 ",[30,1340,1341],{},"&Serial_Node::handle_set_mode",[361,1343,1344,1347,1348,1351],{},[30,1345,1346],{},"object","：使用成员函数形式时传入所属对象指针，例如 ",[30,1349,1350],{},"this","；对象在接收期间必须保持有效。",[15,1353,565,1354,1356,1357,1359,1360,1362,1363,1365],{},[30,1355,441],{},"。注册成功返回 ",[30,1358,571],{},"；命令重复、8 个回调槽位已满，或传入空函数\u002F对象指针时返回 ",[30,1361,575],{},"。不受支持的 handler 签名会在编译期报错。这里直接传函数或成员函数指针，不用 ",[30,1364,37],{}," 包装协议 handler。",[420,1367,1369],{"id":1368},"callbackprotocolpack",[30,1370,1371],{},"CallbackProtocol::pack()",[15,1373,428],{},[22,1375,1377],{"className":431,"code":1376,"language":433,"meta":28,"style":28},"template \u003CWireField... T>\nauto pack(std::uint8_t command, const T&... fields) const noexcept;\n",[30,1378,1379,1396],{"__ignoreMap":28},[61,1380,1381,1383,1385,1388,1391,1394],{"class":63,"line":64},[61,1382,1181],{"class":440},[61,1384,1184],{"class":447},[61,1386,1387],{"class":440},"WireField",[61,1389,1390],{"class":447},"... ",[61,1392,1393],{"class":78},"T",[61,1395,1193],{"class":447},[61,1397,1398,1401,1404,1406,1408,1410,1412,1414,1416,1418,1421,1424,1426,1429,1431,1433,1436],{"class":63,"line":75},[61,1399,1400],{"class":440},"auto",[61,1402,1403],{"class":78}," pack",[61,1405,480],{"class":447},[61,1407,483],{"class":78},[61,1409,468],{"class":447},[61,1411,1209],{"class":440},[61,1413,1212],{"class":456},[61,1415,798],{"class":447},[61,1417,492],{"class":440},[61,1419,1420],{"class":78}," T",[61,1422,1423],{"class":440},"&",[61,1425,1390],{"class":447},[61,1427,1428],{"class":456},"fields",[61,1430,1226],{"class":447},[61,1432,492],{"class":440},[61,1434,1435],{"class":440}," noexcept",[61,1437,861],{"class":447},[15,1439,1440],{},"作用：把命令号和字段打包成一帧可以交给串口发送的字节。",[15,1442,510],{},[358,1444,1445,1452],{},[361,1446,1447,518,1449,1451],{},[30,1448,1313],{},[30,1450,1316],{},"，要发送的命令号。",[361,1453,1454,1457,1458,1461,1462,1465,1466,125],{},[30,1455,1456],{},"fields...","：字段列表，类型由模板参数 ",[30,1459,1460],{},"T..."," 推导；类型和顺序要与接收侧的处理函数一致。本例 ",[30,1463,1464],{},"pack(0x02, seq, mode)"," 对应 ",[30,1467,1468],{},"void on_mode(uint32_t seq, int32_t mode)",[15,1470,1471,1472,1474,1475,1478,1479,1482,1483,1485,1486,518],{},"返回值：源码中写作 ",[30,1473,1400],{},"，实际类型是 ",[30,1476,1477],{},"std::array\u003Cstd::uint8_t, N>","，数组拥有帧数据；",[30,1480,1481],{},"N"," 在编译期由字段占用的字节数加 7 字节帧开销确定。例如两个 32 位整数的 ",[30,1484,1464],{}," 返回 15 字节数组。另一个示例命令是 ",[30,1487,1488],{},"0x01",[22,1490,1492],{"className":431,"code":1491,"language":433,"meta":28,"style":28},"std::uint32_t seq = 7;\nfloat vx = 1.0f;\nfloat vy = 0.0f;\nfloat wz = 0.5f;\nauto frame = protocol.pack(0x01, seq, vx, vy, wz);\n",[30,1493,1494,1511,1529,1545,1561],{"__ignoreMap":28},[61,1495,1496,1498,1500,1502,1504,1506,1509],{"class":63,"line":64},[61,1497,483],{"class":78},[61,1499,468],{"class":447},[61,1501,792],{"class":440},[61,1503,837],{"class":447},[61,1505,1006],{"class":440},[61,1507,1508],{"class":67}," 7",[61,1510,861],{"class":447},[61,1512,1513,1516,1519,1521,1524,1527],{"class":63,"line":75},[61,1514,1515],{"class":440},"float",[61,1517,1518],{"class":447}," vx ",[61,1520,1006],{"class":440},[61,1522,1523],{"class":67}," 1.0",[61,1525,1526],{"class":440},"f",[61,1528,861],{"class":447},[61,1530,1531,1533,1536,1538,1541,1543],{"class":63,"line":88},[61,1532,1515],{"class":440},[61,1534,1535],{"class":447}," vy ",[61,1537,1006],{"class":440},[61,1539,1540],{"class":67}," 0.0",[61,1542,1526],{"class":440},[61,1544,861],{"class":447},[61,1546,1547,1549,1552,1554,1557,1559],{"class":63,"line":771},[61,1548,1515],{"class":440},[61,1550,1551],{"class":447}," wz ",[61,1553,1006],{"class":440},[61,1555,1556],{"class":67}," 0.5",[61,1558,1526],{"class":440},[61,1560,861],{"class":447},[61,1562,1563,1565,1567,1569,1571,1573,1575,1577,1580],{"class":63,"line":778},[61,1564,1400],{"class":440},[61,1566,1041],{"class":447},[61,1568,1006],{"class":440},[61,1570,1046],{"class":447},[61,1572,1049],{"class":78},[61,1574,480],{"class":447},[61,1576,933],{"class":440},[61,1578,1579],{"class":67},"01",[61,1581,1582],{"class":447},", seq, vx, vy, wz);\n",[15,1584,1585,1586,1589,1590,1592,1593,1596,1597,1599],{},"对应的接收函数签名为 ",[30,1587,1588],{},"void on_cmd_vel(uint32_t seq, float vx, float vy, float wz)","。不要把 ",[30,1591,1515],{}," 写成 ",[30,1594,1595],{},"double","，也不要把 ",[30,1598,805],{}," 写成编译器宽度不明确的其他类型。两端必须约定相同命令号、字段类型和顺序。",[420,1601,1603],{"id":1602},"callbackprotocolfeed",[30,1604,1605],{},"CallbackProtocol::feed()",[15,1607,428],{},[22,1609,1611],{"className":431,"code":1610,"language":433,"meta":28,"style":28},"void feed(std::span\u003Cconst std::uint8_t> bytes);\n",[30,1612,1613],{"__ignoreMap":28},[61,1614,1615,1617,1620,1622,1624,1626,1628,1630,1632,1634,1636,1638,1640,1643],{"class":63,"line":64},[61,1616,477],{"class":440},[61,1618,1619],{"class":78}," feed",[61,1621,480],{"class":447},[61,1623,483],{"class":78},[61,1625,468],{"class":447},[61,1627,61],{"class":78},[61,1629,474],{"class":447},[61,1631,492],{"class":440},[61,1633,997],{"class":78},[61,1635,468],{"class":447},[61,1637,1209],{"class":440},[61,1639,623],{"class":447},[61,1641,1642],{"class":456},"bytes",[61,1644,504],{"class":447},[15,1646,1647],{},"作用：把串口本次收到的字节交给协议；遇到完整且有效的帧时，自动调用已注册的命令处理函数。一次调用可能没有回调，也可能触发多个回调。",[15,1649,510],{},[358,1651,1652],{},[361,1653,1654,518,1656,1659],{},[30,1655,1642],{},[30,1657,1658],{},"std::span\u003Cconst std::uint8_t>","，表示这次收到的字节片段。它可以是半帧，也可以包含几帧，不需要先凑成完整帧。",[15,1661,565,1662,1664],{},[30,1663,477],{},"。一个串口应长期保留一个协议对象，接收过程中不要重新注册回调。",[420,1666,1668],{"id":1667},"callbackprotocolreset",[30,1669,1670],{},"CallbackProtocol::reset()",[15,1672,428],{},[22,1674,1676],{"className":431,"code":1675,"language":433,"meta":28,"style":28},"void reset() noexcept;\n",[30,1677,1678],{"__ignoreMap":28},[61,1679,1680,1682,1685,1688,1690],{"class":63,"line":64},[61,1681,477],{"class":440},[61,1683,1684],{"class":78}," reset",[61,1686,1687],{"class":447},"() ",[61,1689,1229],{"class":440},[61,1691,861],{"class":447},[15,1693,1694],{},"作用：丢弃尚未收齐的残留字节，例如串口断开后不想让旧半帧影响新连接时调用；已注册的处理函数会保留。",[15,1696,685],{},[15,1698,565,1699,125],{},[30,1700,477],{},[133,1702,1704],{"id":1703},"接到-ros2-示例节点上","接到 ROS2 示例节点上",[15,1706,1707,1710,1711,1714],{},[30,1708,1709],{},"serial_comm.cpp"," 把两个库连接起来，关键的",[555,1712,1713],{},"真实代码","是：",[22,1716,1718],{"className":431,"code":1717,"language":433,"meta":28,"style":28},"\u002F\u002F Serial_Node 的成员：先声明协议，再声明驱动。\nwire_protocol::CallbackProtocol protocol_;\nserial_transport::Serial_Config serial_config_;\nserial_transport::SerialTransport serial_driver_;\n\n\u002F\u002F 构造节点时，先注册协议命令，再启动串口接收。\nif (!protocol_.set_unpack_callback(0x01, &Serial_Node::handle_cmd_vel, this))\n{\n    return;\n}\nif (!protocol_.set_unpack_callback(0x02, &Serial_Node::handle_set_mode, this))\n{\n    return;\n}\nserial_driver_.start(serial_config_,\n    std::bind(&Serial_Node::serial_receive_callback, this, std::placeholders::_1));\n\n\u002F\u002F 接收回调：只负责把收到的片段交给协议。\nvoid serial_receive_callback(std::span\u003Cconst std::uint8_t> bytes)\n{\n    protocol_.feed(bytes);\n}\n\n\u002F\u002F 需要发 0x02 命令时：先打包，再交给驱动。\nvoid send_mode(std::uint32_t seq, std::int32_t mode)\n{\n    auto frame = protocol_.pack(0x02, seq, mode);\n    serial_driver_.async_write(frame);\n}\n",[30,1719,1720,1725,1732,1739,1746,1750,1755,1790,1794,1801,1805,1836,1840,1846,1850,1861,1893,1897,1902,1933,1937,1947,1951,1955,1960,1989,1994,2017,2028],{"__ignoreMap":28},[61,1721,1722],{"class":63,"line":64},[61,1723,1724],{"class":909},"\u002F\u002F Serial_Node 的成员：先声明协议，再声明驱动。\n",[61,1726,1727,1729],{"class":63,"line":75},[61,1728,705],{"class":78},[61,1730,1731],{"class":447},"::CallbackProtocol protocol_;\n",[61,1733,1734,1736],{"class":63,"line":88},[61,1735,347],{"class":78},[61,1737,1738],{"class":447},"::Serial_Config serial_config_;\n",[61,1740,1741,1743],{"class":63,"line":771},[61,1742,347],{"class":78},[61,1744,1745],{"class":447},"::SerialTransport serial_driver_;\n",[61,1747,1748],{"class":63,"line":778},[61,1749,775],{"emptyLinePlaceholder":774},[61,1751,1752],{"class":63,"line":814},[61,1753,1754],{"class":909},"\u002F\u002F 构造节点时，先注册协议命令，再启动串口接收。\n",[61,1756,1757,1760,1762,1764,1767,1769,1771,1773,1775,1777,1779,1782,1785,1787],{"class":63,"line":820},[61,1758,1759],{"class":440},"if",[61,1761,919],{"class":447},[61,1763,922],{"class":440},[61,1765,1766],{"class":447},"protocol_.",[61,1768,928],{"class":78},[61,1770,480],{"class":447},[61,1772,933],{"class":440},[61,1774,1579],{"class":67},[61,1776,798],{"class":447},[61,1778,1423],{"class":440},[61,1780,1781],{"class":78},"Serial_Node",[61,1783,1784],{"class":447},"::handle_cmd_vel, ",[61,1786,1350],{"class":67},[61,1788,1789],{"class":447},"))\n",[61,1791,1792],{"class":63,"line":864},[61,1793,817],{"class":447},[61,1795,1796,1799],{"class":63,"line":870},[61,1797,1798],{"class":440},"    return",[61,1800,861],{"class":447},[61,1802,1803],{"class":63,"line":875},[61,1804,867],{"class":447},[61,1806,1807,1809,1811,1813,1815,1817,1819,1821,1823,1825,1827,1829,1832,1834],{"class":63,"line":887},[61,1808,1759],{"class":440},[61,1810,919],{"class":447},[61,1812,922],{"class":440},[61,1814,1766],{"class":447},[61,1816,928],{"class":78},[61,1818,480],{"class":447},[61,1820,933],{"class":440},[61,1822,936],{"class":67},[61,1824,798],{"class":447},[61,1826,1423],{"class":440},[61,1828,1781],{"class":78},[61,1830,1831],{"class":447},"::handle_set_mode, ",[61,1833,1350],{"class":67},[61,1835,1789],{"class":447},[61,1837,1838],{"class":63,"line":892},[61,1839,817],{"class":447},[61,1841,1842,1844],{"class":63,"line":901},[61,1843,1798],{"class":440},[61,1845,861],{"class":447},[61,1847,1848],{"class":63,"line":906},[61,1849,867],{"class":447},[61,1851,1852,1855,1858],{"class":63,"line":913},[61,1853,1854],{"class":447},"serial_driver_.",[61,1856,1857],{"class":78},"start",[61,1859,1860],{"class":447},"(serial_config_,\n",[61,1862,1863,1865,1867,1870,1872,1874,1876,1879,1881,1883,1885,1887,1890],{"class":63,"line":942},[61,1864,465],{"class":78},[61,1866,468],{"class":447},[61,1868,1869],{"class":78},"bind",[61,1871,480],{"class":447},[61,1873,1423],{"class":440},[61,1875,1781],{"class":78},[61,1877,1878],{"class":447},"::serial_receive_callback, ",[61,1880,1350],{"class":67},[61,1882,798],{"class":447},[61,1884,483],{"class":78},[61,1886,468],{"class":447},[61,1888,1889],{"class":78},"placeholders",[61,1891,1892],{"class":447},"::_1));\n",[61,1894,1895],{"class":63,"line":948},[61,1896,775],{"emptyLinePlaceholder":774},[61,1898,1899],{"class":63,"line":969},[61,1900,1901],{"class":909},"\u002F\u002F 接收回调：只负责把收到的片段交给协议。\n",[61,1903,1904,1906,1909,1911,1913,1915,1917,1919,1921,1923,1925,1927,1929,1931],{"class":63,"line":980},[61,1905,477],{"class":440},[61,1907,1908],{"class":78}," serial_receive_callback",[61,1910,480],{"class":447},[61,1912,483],{"class":78},[61,1914,468],{"class":447},[61,1916,61],{"class":78},[61,1918,474],{"class":447},[61,1920,492],{"class":440},[61,1922,997],{"class":78},[61,1924,468],{"class":447},[61,1926,1209],{"class":440},[61,1928,623],{"class":447},[61,1930,1642],{"class":456},[61,1932,811],{"class":447},[61,1934,1935],{"class":63,"line":986},[61,1936,817],{"class":447},[61,1938,1939,1942,1944],{"class":63,"line":991},[61,1940,1941],{"class":447},"    protocol_.",[61,1943,1067],{"class":78},[61,1945,1946],{"class":447},"(bytes);\n",[61,1948,1949],{"class":63,"line":1013},[61,1950,867],{"class":447},[61,1952,1953],{"class":63,"line":1033},[61,1954,775],{"emptyLinePlaceholder":774},[61,1956,1957],{"class":63,"line":1061},[61,1958,1959],{"class":909},"\u002F\u002F 需要发 0x02 命令时：先打包，再交给驱动。\n",[61,1961,1962,1964,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987],{"class":63,"line":1073},[61,1963,477],{"class":440},[61,1965,1966],{"class":78}," send_mode",[61,1968,480],{"class":447},[61,1970,483],{"class":78},[61,1972,468],{"class":447},[61,1974,792],{"class":440},[61,1976,795],{"class":456},[61,1978,798],{"class":447},[61,1980,483],{"class":78},[61,1982,468],{"class":447},[61,1984,805],{"class":440},[61,1986,808],{"class":456},[61,1988,811],{"class":447},[61,1990,1992],{"class":63,"line":1991},26,[61,1993,817],{"class":447},[61,1995,1997,2000,2002,2004,2007,2009,2011,2013,2015],{"class":63,"line":1996},27,[61,1998,1999],{"class":440},"    auto",[61,2001,1041],{"class":447},[61,2003,1006],{"class":440},[61,2005,2006],{"class":447}," protocol_.",[61,2008,1049],{"class":78},[61,2010,480],{"class":447},[61,2012,933],{"class":440},[61,2014,936],{"class":67},[61,2016,1058],{"class":447},[61,2018,2020,2023,2026],{"class":63,"line":2019},28,[61,2021,2022],{"class":447},"    serial_driver_.",[61,2024,2025],{"class":78},"async_write",[61,2027,1070],{"class":447},[61,2029,2031],{"class":63,"line":2030},29,[61,2032,867],{"class":447},[15,2034,2035,2036,125,2038,2041,2042,2044],{},"上面是类成员和构造函数中的使用片段，不是另一个完整节点；完整代码在仓库的 ",[30,2037,1709],{},[30,2039,2040],{},"protocol_"," 声明在 ",[30,2043,407],{}," 前面，是为了节点销毁时先停串口，再销毁协议对象。",[15,2046,2047],{},"现成示例的两条命令如下：",[2049,2050,2051,2067],"table",{},[2052,2053,2054],"thead",{},[2055,2056,2057,2061,2064],"tr",{},[2058,2059,2060],"th",{},"命令",[2058,2062,2063],{},"收发字段",[2058,2065,2066],{},"示例行为",[2068,2069,2070,2085],"tbody",{},[2055,2071,2072,2077,2082],{},[2073,2074,2075],"td",{},[30,2076,1488],{},[2073,2078,2079],{},[30,2080,2081],{},"uint32_t seq, float vx, float vy, float wz",[2073,2083,2084],{},"模拟速度，PC 周期发送并可接收",[2055,2086,2087,2091,2096],{},[2073,2088,2089],{},[30,2090,1320],{},[2073,2092,2093],{},[30,2094,2095],{},"uint32_t seq, int32_t mode",[2073,2097,2098],{},"模拟模式，PC 周期发送并可接收",[133,2100,2102],{"id":2101},"在-stm32-上使用同一份协议","在 STM32 上使用同一份协议",[15,2104,2105,2106,2109,2110,2113,2114,2117],{},"这里直接使用仓库里的 ",[30,2107,2108],{},"example\u002Fstm32_hal\u002F","，不必另找一份 STM32 工程。该目录",[555,2111,2112],{},"不是已经生成完毕的完整固件工程","：它保存了 ",[30,2115,2116],{},"TEST.ioc","、C++ 应用文件和用户 CMake 文件，先用 CubeMX 生成 HAL、FreeRTOS 和顶层构建文件。",[133,2119,2121],{"id":2120},"第一步用-cubemx-生成-stm32-工程","第一步：用 CubeMX 生成 STM32 工程",[15,2123,2124,2125,2127,2128,2131,2132,117,2135,2138],{},"打开 ",[30,2126,112],{},"，在 CubeMX 中检查目标芯片与自己的板卡是否匹配，然后在这个目录生成代码。例程的 ",[30,2129,2130],{},".ioc"," 选择了 STM32F103C8Tx、CMake 工具链、FreeRTOS CMSIS V2、USART1，以及名为 ",[30,2133,2134],{},"serialTask",[30,2136,2137],{},"ledTask"," 的任务；USART1 TX\u002FRX 分别是 PA9\u002FPA10。若你的板卡引脚不同，先在 CubeMX 中调整再生成。",[15,2140,2141,2142,117,2145,2148,2149,2152,2153,117,2156,117,2159,2162,2163,2166],{},"生成后，目录中应出现 ",[30,2143,2144],{},"Core\u002F",[30,2146,2147],{},"Drivers\u002F","、顶层 ",[30,2150,2151],{},"CMakeLists.txt"," 等文件；原有的 ",[30,2154,2155],{},"applications\u002F",[30,2157,2158],{},"bsp\u002F",[30,2160,2161],{},"cmake\u002Fuser\u002F"," 继续保留。请在 CubeMX 的设置中保留 ",[30,2164,2165],{},"USER CODE","，以免以后重新生成时丢失手动修改。",[133,2168,2170],{"id":2169},"第二步接入-c-构建文件","第二步：接入 C++ 构建文件",[15,2172,2173,2174,2177,2178,2180,2181,2184],{},"在刚生成的",[555,2175,2176],{},"顶层"," ",[30,2179,2151],{}," 中，保留 CubeMX 原有的 C 标准设置，在 ",[30,2182,2183],{},"project()"," 之前加入：",[22,2186,2190],{"className":2187,"code":2188,"language":2189,"meta":28,"style":28},"language-cmake shiki shiki-themes github-light github-dark","set(CMAKE_CXX_STANDARD 20 CACHE STRING \"C++ language standard\")\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS ON)\n","cmake",[30,2191,2192,2211,2223],{"__ignoreMap":28},[61,2193,2194,2197,2200,2203,2206,2209],{"class":63,"line":64},[61,2195,2196],{"class":440},"set",[61,2198,2199],{"class":447},"(CMAKE_CXX_STANDARD 20 ",[61,2201,2202],{"class":440},"CACHE",[61,2204,2205],{"class":440}," STRING",[61,2207,2208],{"class":71}," \"C++ language standard\"",[61,2210,811],{"class":447},[61,2212,2213,2215,2218,2221],{"class":63,"line":75},[61,2214,2196],{"class":440},[61,2216,2217],{"class":447},"(CMAKE_CXX_STANDARD_REQUIRED ",[61,2219,2220],{"class":440},"ON",[61,2222,811],{"class":447},[61,2224,2225,2227,2230,2232],{"class":63,"line":88},[61,2226,2196],{"class":440},[61,2228,2229],{"class":447},"(CMAKE_CXX_EXTENSIONS ",[61,2231,2220],{"class":440},[61,2233,811],{"class":447},[15,2235,2236,2237,2240],{},"找到已有的 CubeMX 子目录调用，在它后面",[555,2238,2239],{},"新增","用户目录调用：",[22,2242,2244],{"className":2187,"code":2243,"language":2189,"meta":28,"style":28},"add_subdirectory(cmake\u002Fstm32cubemx) # CubeMX 生成的原有行\nadd_subdirectory(cmake\u002Fuser)        # 需要新增的行\n",[30,2245,2246,2257],{"__ignoreMap":28},[61,2247,2248,2251,2254],{"class":63,"line":64},[61,2249,2250],{"class":440},"add_subdirectory",[61,2252,2253],{"class":447},"(cmake\u002Fstm32cubemx) ",[61,2255,2256],{"class":909},"# CubeMX 生成的原有行\n",[61,2258,2259,2261,2264],{"class":63,"line":75},[61,2260,2250],{"class":440},[61,2262,2263],{"class":447},"(cmake\u002Fuser)        ",[61,2265,2266],{"class":909},"# 需要新增的行\n",[15,2268,2269,2272,2273,117,2276,117,2279,2282,2283,2285,2286,2289,2290,2292,2293,125],{},[30,2270,2271],{},"cmake\u002Fuser\u002FCMakeLists.txt"," 已经把 ",[30,2274,2275],{},"applications\u002FSrc\u002Fcpp_interface.cpp",[30,2277,2278],{},"serial.cpp",[30,2280,2281],{},"protocol.cpp"," 等文件加入目标，并设置所需头文件目录；所以这里必须添加 ",[30,2284,124],{},"，只把 C++ 标准设为 20 还不够。以后如果新增 ",[30,2287,2288],{},".cpp","，也要在 ",[30,2291,2271],{}," 的源文件列表中加入它。顶层 CMake 的具体放置位置和 CubeMX 再生成时的注意事项，可对照该目录的 ",[30,2294,2295],{},"README-zh_CN.md",[133,2297,2299,2300,2303],{"id":2298},"第三步在-mainc-调用-c-入口","第三步：在 ",[30,2301,2302],{},"main.c"," 调用 C++ 入口",[15,2305,2306,2307,2310,2311,2314,2315,518],{},"在生成的 ",[30,2308,2309],{},"Core\u002FSrc\u002Fmain.c"," 中，把头文件放进 ",[30,2312,2313],{},"USER CODE BEGIN Includes","，并在外设初始化之后调用 ",[30,2316,2317],{},"cpp_main()",[22,2319,2323],{"className":2320,"code":2321,"language":2322,"meta":28,"style":28},"language-c shiki shiki-themes github-light github-dark","\u002F* USER CODE BEGIN Includes *\u002F\n#include \"cpp_interface.h\"\n\u002F* USER CODE END Includes *\u002F\n\n\u002F* main() 中，MX_USART1_UART_Init() 之后 *\u002F\n\u002F* USER CODE BEGIN 2 *\u002F\ncpp_main();\n\u002F* USER CODE END 2 *\u002F\n","c",[30,2324,2325,2330,2337,2342,2346,2351,2356,2363],{"__ignoreMap":28},[61,2326,2327],{"class":63,"line":64},[61,2328,2329],{"class":909},"\u002F* USER CODE BEGIN Includes *\u002F\n",[61,2331,2332,2334],{"class":63,"line":75},[61,2333,751],{"class":440},[61,2335,2336],{"class":71}," \"cpp_interface.h\"\n",[61,2338,2339],{"class":63,"line":88},[61,2340,2341],{"class":909},"\u002F* USER CODE END Includes *\u002F\n",[61,2343,2344],{"class":63,"line":771},[61,2345,775],{"emptyLinePlaceholder":774},[61,2347,2348],{"class":63,"line":778},[61,2349,2350],{"class":909},"\u002F* main() 中，MX_USART1_UART_Init() 之后 *\u002F\n",[61,2352,2353],{"class":63,"line":814},[61,2354,2355],{"class":909},"\u002F* USER CODE BEGIN 2 *\u002F\n",[61,2357,2358,2361],{"class":63,"line":820},[61,2359,2360],{"class":78},"cpp_main",[61,2362,679],{"class":447},[61,2364,2365],{"class":63,"line":864},[61,2366,2367],{"class":909},"\u002F* USER CODE END 2 *\u002F\n",[15,2369,2370,2373,2374,2377,2378,2380,2381,2384,2385,518,2388,2390,2391,2394,2395,2398,2399,2401],{},[30,2371,2372],{},"cpp_interface.h"," 已通过 ",[30,2375,2376],{},"extern \"C\""," 暴露 ",[30,2379,2317],{},"，因此 C 文件可以直接调用。例程里 ",[30,2382,2383],{},"isRTOS"," 为 ",[30,2386,2387],{},"1",[30,2389,2317],{}," 在 FreeRTOS 模式下不会进入裸机死循环；串口逻辑由 ",[30,2392,2393],{},"applications\u002FSrc\u002Fserial.cpp"," 定义的 ",[30,2396,2397],{},"StartSerialTask"," 执行。",[30,2400,2130],{}," 中已经配置了同名的 FreeRTOS 任务，不需要自己再建一个。",[133,2403,2405],{"id":2404},"第四步看例程怎样调用协议-api","第四步：看例程怎样调用协议 API",[15,2407,2408,2409,2412,2413,2416,2417,2419,2420,2422],{},"STM32 端的 ",[30,2410,2411],{},"applications\u002FInc\u002Fprotocol.hpp"," 与 ROS2 端是同一套协议接口；",[30,2414,2415],{},"applications\u002FSrc\u002Fprotocol.cpp"," 已由 ",[30,2418,2271],{}," 加入编译。下面摘出 ",[30,2421,2393],{}," 中的关键调用，分别位于任务、UART 接收完成回调和发送循环中：",[22,2424,2426],{"className":431,"code":2425,"language":433,"meta":28,"style":28},"wire_protocol::CallbackProtocol protocol_;\nstd::array\u003Cuint8_t, 1> rx_buffer;\n\n\u002F\u002F StartSerialTask 中：先注册，再启动串口接收。\nif (!protocol_.set_unpack_callback(0x01, cmd_vel_callback) ||\n    !protocol_.set_unpack_callback(0x02, set_mode_callback))\n{\n    Error_Handler();\n}\nHAL_UART_Receive_IT(&huart1, rx_buffer.data(), rx_buffer.size());\n\n\u002F\u002F HAL_UART_RxCpltCallback 中：收到一个字节就交给协议，再继续接收。\nprotocol_.feed(rx_buffer);\nHAL_UART_Receive_IT(&huart1, rx_buffer.data(), rx_buffer.size());\n\n\u002F\u002F StartSerialTask 的循环中：打包 0x01，再用 HAL 串口发送。\nauto frame = protocol_.pack(0x01, seq, vx, vy, wz);\nHAL_UART_Transmit(&huart1, frame.data(), frame.size(), osWaitForever);\n",[30,2427,2428,2434,2454,2458,2463,2487,2505,2509,2516,2520,2544,2548,2553,2562,2580,2584,2589,2609],{"__ignoreMap":28},[61,2429,2430,2432],{"class":63,"line":64},[61,2431,705],{"class":78},[61,2433,1731],{"class":447},[61,2435,2436,2438,2441,2444,2446,2448,2451],{"class":63,"line":75},[61,2437,483],{"class":78},[61,2439,2440],{"class":447},"::array",[61,2442,2443],{"class":440},"\u003Cuint8_t",[61,2445,798],{"class":447},[61,2447,2387],{"class":67},[61,2449,2450],{"class":440},">",[61,2452,2453],{"class":447}," rx_buffer;\n",[61,2455,2456],{"class":63,"line":88},[61,2457,775],{"emptyLinePlaceholder":774},[61,2459,2460],{"class":63,"line":771},[61,2461,2462],{"class":909},"\u002F\u002F StartSerialTask 中：先注册，再启动串口接收。\n",[61,2464,2465,2467,2469,2471,2473,2475,2477,2479,2481,2484],{"class":63,"line":778},[61,2466,1759],{"class":440},[61,2468,919],{"class":447},[61,2470,922],{"class":440},[61,2472,1766],{"class":447},[61,2474,928],{"class":78},[61,2476,480],{"class":447},[61,2478,933],{"class":440},[61,2480,1579],{"class":67},[61,2482,2483],{"class":447},", cmd_vel_callback) ",[61,2485,2486],{"class":440},"||\n",[61,2488,2489,2492,2494,2496,2498,2500,2502],{"class":63,"line":814},[61,2490,2491],{"class":440},"    !",[61,2493,1766],{"class":447},[61,2495,928],{"class":78},[61,2497,480],{"class":447},[61,2499,933],{"class":440},[61,2501,936],{"class":67},[61,2503,2504],{"class":447},", set_mode_callback))\n",[61,2506,2507],{"class":63,"line":820},[61,2508,817],{"class":447},[61,2510,2511,2514],{"class":63,"line":864},[61,2512,2513],{"class":78},"    Error_Handler",[61,2515,679],{"class":447},[61,2517,2518],{"class":63,"line":870},[61,2519,867],{"class":447},[61,2521,2522,2525,2527,2529,2532,2535,2538,2541],{"class":63,"line":875},[61,2523,2524],{"class":78},"HAL_UART_Receive_IT",[61,2526,480],{"class":447},[61,2528,1423],{"class":440},[61,2530,2531],{"class":447},"huart1, rx_buffer.",[61,2533,2534],{"class":78},"data",[61,2536,2537],{"class":447},"(), rx_buffer.",[61,2539,2540],{"class":78},"size",[61,2542,2543],{"class":447},"());\n",[61,2545,2546],{"class":63,"line":887},[61,2547,775],{"emptyLinePlaceholder":774},[61,2549,2550],{"class":63,"line":892},[61,2551,2552],{"class":909},"\u002F\u002F HAL_UART_RxCpltCallback 中：收到一个字节就交给协议，再继续接收。\n",[61,2554,2555,2557,2559],{"class":63,"line":901},[61,2556,1766],{"class":447},[61,2558,1067],{"class":78},[61,2560,2561],{"class":447},"(rx_buffer);\n",[61,2563,2564,2566,2568,2570,2572,2574,2576,2578],{"class":63,"line":906},[61,2565,2524],{"class":78},[61,2567,480],{"class":447},[61,2569,1423],{"class":440},[61,2571,2531],{"class":447},[61,2573,2534],{"class":78},[61,2575,2537],{"class":447},[61,2577,2540],{"class":78},[61,2579,2543],{"class":447},[61,2581,2582],{"class":63,"line":913},[61,2583,775],{"emptyLinePlaceholder":774},[61,2585,2586],{"class":63,"line":942},[61,2587,2588],{"class":909},"\u002F\u002F StartSerialTask 的循环中：打包 0x01，再用 HAL 串口发送。\n",[61,2590,2591,2593,2595,2597,2599,2601,2603,2605,2607],{"class":63,"line":948},[61,2592,1400],{"class":440},[61,2594,1041],{"class":447},[61,2596,1006],{"class":440},[61,2598,2006],{"class":447},[61,2600,1049],{"class":78},[61,2602,480],{"class":447},[61,2604,933],{"class":440},[61,2606,1579],{"class":67},[61,2608,1582],{"class":447},[61,2610,2611,2614,2616,2618,2621,2623,2626,2628],{"class":63,"line":969},[61,2612,2613],{"class":78},"HAL_UART_Transmit",[61,2615,480],{"class":447},[61,2617,1423],{"class":440},[61,2619,2620],{"class":447},"huart1, frame.",[61,2622,2534],{"class":78},[61,2624,2625],{"class":447},"(), frame.",[61,2627,2540],{"class":78},[61,2629,2630],{"class":447},"(), osWaitForever);\n",[15,2632,2633,2634,117,2636,2638,2639,117,2642,2645,2646,2648,2649,2651,2652,2654],{},"这些是例程中不同函数的使用片段，不是要粘贴到同一个函数里的完整程序。STM32 端收到 ",[30,2635,1488],{},[30,2637,1320],{}," 后，回调分别更新 ",[30,2640,2641],{},"cmd_vel",[30,2643,2644],{},"set_mode","；它自身周期性发送 ",[30,2647,1488],{},"，没有周期性回发 ",[30,2650,1320],{},"。例程接收回调在中断中调用 ",[30,2653,715],{},"，自己的业务回调应保持简短；如果改为多个任务或中断共同访问同一个协议对象，要把协议操作集中到一个任务中。",[15,2656,2657,2658,2661,2662,2664],{},"最后，用已安装的 ARM 工具链和 Ninja 构建 CubeMX 生成的工程。如果生成了 ",[30,2659,2660],{},"CMakePresets.json","，可以在 ",[30,2663,2108],{}," 下运行：",[22,2666,2668],{"className":55,"code":2667,"language":57,"meta":28,"style":28},"cmake --preset Debug\ncmake --build --preset Debug\n",[30,2669,2670,2680],{"__ignoreMap":28},[61,2671,2672,2674,2677],{"class":63,"line":64},[61,2673,2189],{"class":78},[61,2675,2676],{"class":67}," --preset",[61,2678,2679],{"class":71}," Debug\n",[61,2681,2682,2684,2687,2689],{"class":63,"line":75},[61,2683,2189],{"class":78},[61,2685,2686],{"class":67}," --build",[61,2688,2676],{"class":67},[61,2690,2679],{"class":71},[15,2692,2693,2694,2696,2697,125],{},"再用你的调试器或下载器把生成的固件烧录到板卡。接线时板卡 PA9\u002FTX 接 USB-TTL 的 RX，PA10\u002FRX 接 USB-TTL 的 TX，并共地；两端统一为 115200 8N1、无流控。PC 端把 ",[30,2695,411],{}," 换成实际设备名，例如 ",[30,2698,2699],{},"\u002Fdev\u002FttyUSB0",[40,2701],{},[43,2703,2705],{"id":2704},"编译运行-ros2-示例观察收发结果","编译运行 ROS2 示例，观察收发结果",[133,2707,2708],{"id":2708},"编译",[15,2710,2711,2712,2715,2716,2719,2720,518],{},"安装好 ROS2、",[30,2713,2714],{},"colcon"," 和 standalone Asio 开发头文件（Ubuntu\u002FDebian 包名 ",[30,2717,2718],{},"libasio-dev","）。以下以 ROS2 humble 为例；如果你安装了其他发行版，替换 ",[30,2721,2722],{},"humble",[22,2724,2726],{"className":55,"code":2725,"language":57,"meta":28,"style":28},"cd ~\u002FSerial_Pack\u002Fexample\u002Fros2_ws\nsource \u002Fopt\u002Fros\u002Fhumble\u002Fsetup.bash\ncolcon build --packages-select wire_protocol serial_transport serial_comm\nsource install\u002Fsetup.bash\n",[30,2727,2728,2735,2743,2762],{"__ignoreMap":28},[61,2729,2730,2732],{"class":63,"line":64},[61,2731,68],{"class":67},[61,2733,2734],{"class":71}," ~\u002FSerial_Pack\u002Fexample\u002Fros2_ws\n",[61,2736,2737,2740],{"class":63,"line":75},[61,2738,2739],{"class":67},"source",[61,2741,2742],{"class":71}," \u002Fopt\u002Fros\u002Fhumble\u002Fsetup.bash\n",[61,2744,2745,2747,2750,2753,2756,2759],{"class":63,"line":88},[61,2746,2714],{"class":78},[61,2748,2749],{"class":71}," build",[61,2751,2752],{"class":67}," --packages-select",[61,2754,2755],{"class":71}," wire_protocol",[61,2757,2758],{"class":71}," serial_transport",[61,2760,2761],{"class":71}," serial_comm\n",[61,2763,2764,2766],{"class":63,"line":771},[61,2765,2739],{"class":67},[61,2767,2768],{"class":71}," install\u002Fsetup.bash\n",[133,2770,2771],{"id":2771},"用虚拟串口运行",[15,2773,2774,2775,2777,2778,518],{},"保持前面的 ",[30,2776,228],{}," 终端运行。在两个新终端分别执行；",[555,2779,2780],{},"每个新终端都要进入工作区并加载环境",[22,2782,2784],{"className":55,"code":2783,"language":57,"meta":28,"style":28},"# 终端 2：打开 \u002Ftmp\u002FttyV0\ncd ~\u002FSerial_Pack\u002Fexample\u002Fros2_ws\nsource \u002Fopt\u002Fros\u002Fhumble\u002Fsetup.bash\nsource install\u002Fsetup.bash\nros2 run serial_comm serial_comm --ros-args -r __node:=serial_a -p port:=\u002Ftmp\u002FttyV0\n",[30,2785,2786,2791,2797,2803,2809],{"__ignoreMap":28},[61,2787,2788],{"class":63,"line":64},[61,2789,2790],{"class":909},"# 终端 2：打开 \u002Ftmp\u002FttyV0\n",[61,2792,2793,2795],{"class":63,"line":75},[61,2794,68],{"class":67},[61,2796,2734],{"class":71},[61,2798,2799,2801],{"class":63,"line":88},[61,2800,2739],{"class":67},[61,2802,2742],{"class":71},[61,2804,2805,2807],{"class":63,"line":771},[61,2806,2739],{"class":67},[61,2808,2768],{"class":71},[61,2810,2811,2814,2817,2820,2822,2825,2828,2831,2834],{"class":63,"line":778},[61,2812,2813],{"class":78},"ros2",[61,2815,2816],{"class":71}," run",[61,2818,2819],{"class":71}," serial_comm",[61,2821,2819],{"class":71},[61,2823,2824],{"class":67}," --ros-args",[61,2826,2827],{"class":67}," -r",[61,2829,2830],{"class":71}," __node:=serial_a",[61,2832,2833],{"class":67}," -p",[61,2835,2836],{"class":71}," port:=\u002Ftmp\u002FttyV0\n",[22,2838,2840],{"className":55,"code":2839,"language":57,"meta":28,"style":28},"# 终端 3：打开 \u002Ftmp\u002FttyV1\ncd ~\u002FSerial_Pack\u002Fexample\u002Fros2_ws\nsource \u002Fopt\u002Fros\u002Fhumble\u002Fsetup.bash\nsource install\u002Fsetup.bash\nros2 run serial_comm serial_comm --ros-args -r __node:=serial_b -p port:=\u002Ftmp\u002FttyV1\n",[30,2841,2842,2847,2853,2859,2865],{"__ignoreMap":28},[61,2843,2844],{"class":63,"line":64},[61,2845,2846],{"class":909},"# 终端 3：打开 \u002Ftmp\u002FttyV1\n",[61,2848,2849,2851],{"class":63,"line":75},[61,2850,68],{"class":67},[61,2852,2734],{"class":71},[61,2854,2855,2857],{"class":63,"line":88},[61,2856,2739],{"class":67},[61,2858,2742],{"class":71},[61,2860,2861,2863],{"class":63,"line":771},[61,2862,2739],{"class":67},[61,2864,2768],{"class":71},[61,2866,2867,2869,2871,2873,2875,2877,2879,2882,2884],{"class":63,"line":778},[61,2868,2813],{"class":78},[61,2870,2816],{"class":71},[61,2872,2819],{"class":71},[61,2874,2819],{"class":71},[61,2876,2824],{"class":67},[61,2878,2827],{"class":67},[61,2880,2881],{"class":71}," __node:=serial_b",[61,2883,2833],{"class":67},[61,2885,2886],{"class":71}," port:=\u002Ftmp\u002FttyV1\n",[15,2888,2889,2890,2892,2893,121,2896,2899,2900,2902,2903,125],{},"两个节点都运行时，每个节点发出的串口包会到达另一端。正常情况下，终端先出现 ",[30,2891,585],{},"，随后出现 ",[30,2894,2895],{},"[RX cmd_vel]",[30,2897,2898],{},"[RX set_mode]","。数值和出现时间会随运行情况变化。结束时在两个节点和 ",[30,2901,228],{}," 终端按 ",[30,2904,2905],{},"Ctrl+C",[133,2907,2908],{"id":2908},"用真实串口运行",[15,2910,2911,2912,2914],{},"按 ",[30,2913,2108],{}," 的步骤生成、构建并烧录 STM32 固件后，PC 端只运行一个节点：",[22,2916,2918],{"className":55,"code":2917,"language":57,"meta":28,"style":28},"ros2 run serial_comm serial_comm --ros-args -p port:=\u002Fdev\u002FttyUSB0 -p baud_rate:=115200\n",[30,2919,2920],{"__ignoreMap":28},[61,2921,2922,2924,2926,2928,2930,2932,2934,2937,2939,2942],{"class":63,"line":64},[61,2923,2813],{"class":78},[61,2925,2816],{"class":71},[61,2927,2819],{"class":71},[61,2929,2819],{"class":71},[61,2931,2824],{"class":67},[61,2933,2833],{"class":67},[61,2935,2936],{"class":71}," port:=\u002Fdev\u002FttyUSB0",[61,2938,2833],{"class":67},[61,2940,2941],{"class":71}," baud_rate:=",[61,2943,2944],{"class":67},"115200\n",[15,2946,2947,2948,2950,2951,2954,2955,2957,2958,2961,2962,2965],{},"把 ",[30,2949,2699],{}," 改为自己的设备名；也可以修改 ",[30,2952,2953],{},"serial_comm\u002Fconfig\u002Fserial_comm.yaml"," 的 ",[30,2956,411],{}," 后使用 ",[30,2959,2960],{},"ros2 launch serial_comm serial_comm.launch.py","。如果没有使用 ",[30,2963,2964],{},"colcon build --symlink-install","，修改配置后要重新构建，安装目录中的配置才会更新。",[15,2967,2968,2969,2971,2972,2974,2975,121,2977,2979,2980,121,2982,2984,2985,2987,2988,125],{},"板卡例程会发送 ",[30,2970,1488],{},"，所以 PC 应看到 ",[30,2973,2895],{},"。PC 发出的 ",[30,2976,1488],{},[30,2978,1320],{}," 会分别更新板卡端的 ",[30,2981,2641],{},[30,2983,2644],{},"，可用调试器观察；因为板卡例程不发送 ",[30,2986,1320],{},"，PC 不会因此看到 ",[30,2989,2898],{},[133,2991,2992],{"id":2992},"没有收到预期结果时",[358,2994,2995,3001,3004],{},[361,2996,2997,3000],{},[30,2998,2999],{},"串口打开失败","：检查设备名、USB-TTL 是否插好、当前用户是否有串口权限。",[361,3002,3003],{},"已打开串口但没有协议回调：检查 TX\u002FRX 是否交叉、是否共地、两端波特率是否一致，以及命令号和字段类型是否对应。",[361,3005,3006,3008,3009,3011],{},[30,3007,718],{}," 返回 ",[30,3010,575],{},"：检查命令是否重复、是否超过 8 个回调，或是否传入空函数。",[15,3013,3014,3015],{},"本节最重要的调用关系就是：",[555,3016,3017,3018,3020,3021,121,3023,125],{},"ROS2\u002FSTM32 各自的串口驱动负责收发字节；两端用同一份 ",[30,3019,705],{}," 负责 ",[30,3022,646],{},[30,3024,715],{},[3026,3027,3028],"style",{},"html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":28,"searchDepth":75,"depth":75,"links":3030},[3031,3032,3036,3040,3046,3058],{"id":45,"depth":75,"text":45},{"id":130,"depth":75,"text":131,"children":3033},[3034,3035],{"id":135,"depth":88,"text":136},{"id":181,"depth":88,"text":182},{"id":221,"depth":75,"text":222,"children":3037},[3038,3039],{"id":232,"depth":88,"text":233},{"id":271,"depth":88,"text":271},{"id":343,"depth":75,"text":3041,"children":3042},"第一部分：ROS2 端串口驱动 serial_transport",[3043,3044,3045],{"id":350,"depth":88,"text":350},{"id":384,"depth":88,"text":385},{"id":418,"depth":88,"text":418},{"id":701,"depth":75,"text":3047,"children":3048},"第二部分：跨平台串口包通信协议 wire_protocol",[3049,3050,3051,3052,3053,3054,3055,3057],{"id":722,"depth":88,"text":723},{"id":1162,"depth":88,"text":418},{"id":1703,"depth":88,"text":1704},{"id":2101,"depth":88,"text":2102},{"id":2120,"depth":88,"text":2121},{"id":2169,"depth":88,"text":2170},{"id":2298,"depth":88,"text":3056},"第三步：在 main.c 调用 C++ 入口",{"id":2404,"depth":88,"text":2405},{"id":2704,"depth":75,"text":2705,"children":3059},[3060,3061,3062,3063],{"id":2708,"depth":88,"text":2708},{"id":2771,"depth":88,"text":2771},{"id":2908,"depth":88,"text":2908},{"id":2992,"depth":88,"text":2992},"2100-0100-0350","2023-10-05","2023-10-05-cplusplus-jiao-xue","\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue","Cplusplus教学","md",false,{},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0350-chuan-kou-tong-xin-chuan-kou-bao-xie-yi-shi-li",{"title":5,"description":28},"wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0350-串口通信串口包协议实例","VLoI_eHPF7nMXtPS4-km6qXFtW6PNLHDdDR4Zg62htA",[3077,3084,3090,3096,3102,3108,3114,3120,3126,3132,3138,3144,3150,3156,3162,3168,3174,3180,3186,3192,3198,3204,3210,3216,3222,3228,3234,3239,3245,3251,3257,3263,3269,3275,3281,3287,3293,3299,3305,3311,3316,3322,3328,3334,3340,3346,3352,3358,3364,3370,3376,3382,3387,3393,3399,3404,3410,3416,3422,3428,3434,3440,3446,3452,3458,3464,3470,3476,3482,3488,3494,3500,3506,3512,3518,3524,3526,3532,3538,3544,3550,3556,3562,3568,3574,3580,3586,3592,3598,3604,3610,3616],{"path":3078,"stem":3079,"title":3080,"authors":3081,"date":3065,"chapterOrder":3082,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0100-c-kai-fa-huan-jing-da-jian-yu-ce-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F0100-C++开发环境搭建与测试","C++开发环境搭建与测试",[7],"0100",0,{"path":3085,"stem":3086,"title":3087,"authors":3088,"date":3065,"chapterOrder":3089,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0200-c-ji-chu-chu-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F0200-C++基础初识","C++基础初识",[7],"0200",{"path":3091,"stem":3092,"title":3093,"authors":3094,"date":3065,"chapterOrder":3095,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0300-shu-ju-lei-xing-yu-shu-ju-cun-fang","wiki\u002F2023-10-05-Cplusplus教学\u002F0300-数据类型与数据存放","数据类型与数据存放",[7],"0300",{"path":3097,"stem":3098,"title":3099,"authors":3100,"date":3065,"chapterOrder":3101,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0400-shu-ru-shu-chu","wiki\u002F2023-10-05-Cplusplus教学\u002F0400-输入输出","输入输出",[7],"0400",{"path":3103,"stem":3104,"title":3105,"authors":3106,"date":3065,"chapterOrder":3107,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0500-yun-suan-fu","wiki\u002F2023-10-05-Cplusplus教学\u002F0500-运算符","运算符",[7],"0500",{"path":3109,"stem":3110,"title":3111,"authors":3112,"date":3065,"chapterOrder":3113,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0600-cheng-xu-liu-cheng-jie-gou","wiki\u002F2023-10-05-Cplusplus教学\u002F0600-程序流程结构","程序流程结构",[7],"0600",{"path":3115,"stem":3116,"title":3117,"authors":3118,"date":3065,"chapterOrder":3119,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0700-shu-zu","wiki\u002F2023-10-05-Cplusplus教学\u002F0700-数组","数组",[7],"0700",{"path":3121,"stem":3122,"title":3123,"authors":3124,"date":3065,"chapterOrder":3125,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0800-han-shu-yu-tou-wen-jian","wiki\u002F2023-10-05-Cplusplus教学\u002F0800-函数与头文件","函数与头文件",[7],"0800",{"path":3127,"stem":3128,"title":3129,"authors":3130,"date":3065,"chapterOrder":3131,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0900-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F0900-指针","指针",[7],"0900",{"path":3133,"stem":3134,"title":3135,"authors":3136,"date":3065,"chapterOrder":3137,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1000-jie-gou-ti-yu-gong-yong-ti","wiki\u002F2023-10-05-Cplusplus教学\u002F1000-结构体与共用体","结构体与共用体",[7],"1000",{"path":3139,"stem":3140,"title":3141,"authors":3142,"date":3065,"chapterOrder":3143,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1100-nei-cun-fen-qu-yu-sheng-ming-zhou-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1100-内存分区与生命周期","内存分区与生命周期",[7],"1100",{"path":3145,"stem":3146,"title":3147,"authors":3148,"date":3065,"chapterOrder":3149,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1200-yin-yong","wiki\u002F2023-10-05-Cplusplus教学\u002F1200-引用","引用",[7],"1200",{"path":3151,"stem":3152,"title":3153,"authors":3154,"date":3065,"chapterOrder":3155,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1300-han-shu-ti-gao","wiki\u002F2023-10-05-Cplusplus教学\u002F1300-函数提高","函数提高",[7],"1300",{"path":3157,"stem":3158,"title":3159,"authors":3160,"date":3065,"chapterOrder":3161,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1350-ming-ming-kong-jian","wiki\u002F2023-10-05-Cplusplus教学\u002F1350-命名空间","命名空间",[7],"1350",{"path":3163,"stem":3164,"title":3165,"authors":3166,"date":3065,"chapterOrder":3167,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0100-feng-zhuang","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0100-封装","封装",[7],"1400-0100",{"path":3169,"stem":3170,"title":3171,"authors":3172,"date":3065,"chapterOrder":3173,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0200-dui-xiang-chu-shi-hua-he-qing-li","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0200-对象初始化和清理","对象初始化和清理",[7],"1400-0200",{"path":3175,"stem":3176,"title":3177,"authors":3178,"date":3065,"chapterOrder":3179,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0300-dui-xiang-mo-xing-yu-this-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0300-对象模型与this指针","对象模型与this指针",[7],"1400-0300",{"path":3181,"stem":3182,"title":3183,"authors":3184,"date":3065,"chapterOrder":3185,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0400-you-yuan-yu-yun-suan-fu-zhong-zai","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0400-友元与运算符重载","友元与运算符重载",[7],"1400-0400",{"path":3187,"stem":3188,"title":3189,"authors":3190,"date":3065,"chapterOrder":3191,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0500-ji-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0500-继承","继承",[7],"1400-0500",{"path":3193,"stem":3194,"title":3195,"authors":3196,"date":3065,"chapterOrder":3197,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0600-duo-tai","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0600-多态","多态",[7],"1400-0600",{"path":3199,"stem":3200,"title":3201,"authors":3202,"date":3065,"chapterOrder":3203,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-lei-he-dui-xiang","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-类和对象","类和对象",[7],"1400",{"path":3205,"stem":3206,"title":3207,"authors":3208,"date":3065,"chapterOrder":3209,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1450-han-shu-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F1450-函数指针","函数指针",[7],"1450",{"path":3211,"stem":3212,"title":3213,"authors":3214,"date":3065,"chapterOrder":3215,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1500-wen-jian-cao-zuo","wiki\u002F2023-10-05-Cplusplus教学\u002F1500-文件操作","文件操作",[7],"1500",{"path":3217,"stem":3218,"title":3219,"authors":3220,"date":3065,"chapterOrder":3221,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1600-yi-chang-chu-li-yu-cuo-wu-ma","wiki\u002F2023-10-05-Cplusplus教学\u002F1600-异常处理与错误码","异常处理",[7],"1600",{"path":3223,"stem":3224,"title":3225,"authors":3226,"date":3065,"chapterOrder":3227,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1700-mu-ban","wiki\u002F2023-10-05-Cplusplus教学\u002F1700-模板","模板",[7],"1700",{"path":3229,"stem":3230,"title":3231,"authors":3232,"date":3065,"chapterOrder":3233,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0100-stl-chu-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0100-STL初识","STL初识",[7],"1800-0100",{"path":3235,"stem":3236,"title":650,"authors":3237,"date":3065,"chapterOrder":3238,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0200-std-array","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0200-std-array",[7],"1800-0200",{"path":3240,"stem":3241,"title":3242,"authors":3243,"date":3065,"chapterOrder":3244,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0300-string-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0300-string容器","string容器",[7],"1800-0300",{"path":3246,"stem":3247,"title":3248,"authors":3249,"date":3065,"chapterOrder":3250,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0400-vector-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0400-vector容器","vector 容器",[7],"1800-0400",{"path":3252,"stem":3253,"title":3254,"authors":3255,"date":3065,"chapterOrder":3256,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0500-deque-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0500-deque容器","deque 容器",[7],"1800-0500",{"path":3258,"stem":3259,"title":3260,"authors":3261,"date":3065,"chapterOrder":3262,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0600-stack-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0600-stack容器","stack 容器",[7],"1800-0600",{"path":3264,"stem":3265,"title":3266,"authors":3267,"date":3065,"chapterOrder":3268,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0700-queue-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0700-queue容器","queue 容器",[7],"1800-0700",{"path":3270,"stem":3271,"title":3272,"authors":3273,"date":3065,"chapterOrder":3274,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0800-list-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0800-list容器","list 容器",[7],"1800-0800",{"path":3276,"stem":3277,"title":3278,"authors":3279,"date":3065,"chapterOrder":3280,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0900-set-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0900-set容器","set \u002F multiset 容器",[7],"1800-0900",{"path":3282,"stem":3283,"title":3284,"authors":3285,"date":3065,"chapterOrder":3286,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1000-map-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1000-map容器","map \u002F multimap 容器",[7],"1800-1000",{"path":3288,"stem":3289,"title":3290,"authors":3291,"date":3065,"chapterOrder":3292,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1100-han-shu-dui-xiang-yu-wei-ci","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1100-函数对象与谓词","函数对象与谓词",[7],"1800-1100",{"path":3294,"stem":3295,"title":3296,"authors":3297,"date":3065,"chapterOrder":3298,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1200-chang-yong-suan-fa","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1200-常用算法","常用算法",[7],"1800-1200",{"path":3300,"stem":3301,"title":3302,"authors":3303,"date":3065,"chapterOrder":3304,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-stl-ti-gao-bian-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-STL提高编程","STL提高编程",[7],"1800",{"path":3306,"stem":3307,"title":3308,"authors":3309,"date":3065,"chapterOrder":3310,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1900-c-lei-xing-zhuan-huan","wiki\u002F2023-10-05-Cplusplus教学\u002F1900-C++类型转换","C++类型转换",[7],"1900",{"path":3312,"stem":3313,"title":1400,"authors":3314,"date":3065,"chapterOrder":3315,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0100-auto","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0100-auto",[7],"2000-0100",{"path":3317,"stem":3318,"title":3319,"authors":3320,"date":3065,"chapterOrder":3321,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0200-nullptr","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0200-nullptr","nullptr",[7],"2000-0200",{"path":3323,"stem":3324,"title":3325,"authors":3326,"date":3065,"chapterOrder":3327,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0300-using","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0300-using","using",[7],"2000-0300",{"path":3329,"stem":3330,"title":3331,"authors":3332,"date":3065,"chapterOrder":3333,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0400-enum-class","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0400-enum-class","enum class",[7],"2000-0400",{"path":3335,"stem":3336,"title":3337,"authors":3338,"date":3065,"chapterOrder":3339,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0500-fan-wei-for-xun-huan","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0500-范围for循环","范围 for 循环",[7],"2000-0500",{"path":3341,"stem":3342,"title":3343,"authors":3344,"date":3065,"chapterOrder":3345,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0600-jie-gou-hua-bang-ding","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0600-结构化绑定","结构化绑定",[7],"2000-0600",{"path":3347,"stem":3348,"title":3349,"authors":3350,"date":3065,"chapterOrder":3351,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0700-constexpr","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0700-constexpr","constexpr",[7],"2000-0700",{"path":3353,"stem":3354,"title":3355,"authors":3356,"date":3065,"chapterOrder":3357,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0800-raii","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0800-RAII","RAII",[7],"2000-0800",{"path":3359,"stem":3360,"title":3361,"authors":3362,"date":3065,"chapterOrder":3363,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0900-zhi-neng-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0900-智能指针","智能指针",[7],"2000-0900",{"path":3365,"stem":3366,"title":3367,"authors":3368,"date":3065,"chapterOrder":3369,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1000-you-zhi-yin-yong-he-yi-dong-yu-yi","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1000-右值引用和移动语义","右值引用和移动语义",[7],"2000-1000",{"path":3371,"stem":3372,"title":3373,"authors":3374,"date":3065,"chapterOrder":3375,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1100-lambda-biao-da-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1100-Lambda表达式","Lambda 表达式",[7],"2000-1100",{"path":3377,"stem":3378,"title":3379,"authors":3380,"date":3065,"chapterOrder":3381,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1200-std-function","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1200-std-function","std::function",[7],"2000-1200",{"path":3383,"stem":3384,"title":37,"authors":3385,"date":3065,"chapterOrder":3386,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1300-std-bind","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1300-std-bind",[7],"2000-1300",{"path":3388,"stem":3389,"title":3390,"authors":3391,"date":3065,"chapterOrder":3392,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1400-std-optional","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1400-std-optional","std::optional",[7],"2000-1400",{"path":3394,"stem":3395,"title":3396,"authors":3397,"date":3065,"chapterOrder":3398,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1500-std-variant","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1500-std-variant","std::variant",[7],"2000-1500",{"path":3400,"stem":3401,"title":561,"authors":3402,"date":3065,"chapterOrder":3403,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1600-std-span","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1600-std-span",[7],"2000-1600",{"path":3405,"stem":3406,"title":3407,"authors":3408,"date":3065,"chapterOrder":3409,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1700-std-format-print","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1700-std-format-print","std::format \u002F std::print",[7],"2000-1700",{"path":3411,"stem":3412,"title":3413,"authors":3414,"date":3065,"chapterOrder":3415,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1800-std-chrono","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1800-std-chrono","std::chrono",[7],"2000-1800",{"path":3417,"stem":3418,"title":3419,"authors":3420,"date":3065,"chapterOrder":3421,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-0100-bing-fa-shi-shen-me","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-0100-并发是什么","并发是什么",[7],"2000-1900-0100",{"path":3423,"stem":3424,"title":3425,"authors":3426,"date":3065,"chapterOrder":3427,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-0200-bing-xing-shi-shen-me","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-0200-并行是什么","并行是什么",[7],"2000-1900-0200",{"path":3429,"stem":3430,"title":3431,"authors":3432,"date":3065,"chapterOrder":3433,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-0300-xie-cheng-shi-shen-me","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-0300-协程是什么","协程是什么",[7],"2000-1900-0300",{"path":3435,"stem":3436,"title":3437,"authors":3438,"date":3065,"chapterOrder":3439,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1000-bing-fa-bian-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1000-并发编程","并发编程",[7],"2000-1900-1000",{"path":3441,"stem":3442,"title":3443,"authors":3444,"date":3065,"chapterOrder":3445,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1100-std-thread","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1100-std-thread","std::thread",[7],"2000-1900-1100",{"path":3447,"stem":3448,"title":3449,"authors":3450,"date":3065,"chapterOrder":3451,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1200-mutex-lock-guard","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1200-mutex-lock-guard","mutex 与 RAII 锁",[7],"2000-1900-1200",{"path":3453,"stem":3454,"title":3455,"authors":3456,"date":3065,"chapterOrder":3457,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1300-std-atomic","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1300-std-atomic","std::atomic",[7],"2000-1900-1300",{"path":3459,"stem":3460,"title":3461,"authors":3462,"date":3065,"chapterOrder":3463,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1400-condition-variable","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1400-condition-variable","condition_variable",[7],"2000-1900-1400",{"path":3465,"stem":3466,"title":3467,"authors":3468,"date":3065,"chapterOrder":3469,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1500-future-async-promise","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1500-future-async-promise","future、async、promise 与 packaged_task",[7],"2000-1900-1500",{"path":3471,"stem":3472,"title":3473,"authors":3474,"date":3065,"chapterOrder":3475,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1600-jthread-stop-token","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1600-jthread-stop-token","jthread 与 stop_token",[7],"2000-1900-1600",{"path":3477,"stem":3478,"title":3479,"authors":3480,"date":3065,"chapterOrder":3481,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1700-semaphore-latch-barrier","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1700-semaphore-latch-barrier","semaphore、latch 与 barrier",[7],"2000-1900-1700",{"path":3483,"stem":3484,"title":3485,"authors":3486,"date":3065,"chapterOrder":3487,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-bing-fa-bing-xing-yu-xie-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-并发并行与协程","并发、并行与协程",[7],"2000-1900",{"path":3489,"stem":3490,"title":3491,"authors":3492,"date":3065,"chapterOrder":3493,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-2000-std-filesystem","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-2000-std-filesystem","std::filesystem",[7],"2000-2000",{"path":3495,"stem":3496,"title":3497,"authors":3498,"date":3065,"chapterOrder":3499,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-2100-modules-jian-jie","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-2100-modules简介","modules 简介",[7],"2000-2100",{"path":3501,"stem":3502,"title":3503,"authors":3504,"date":3065,"chapterOrder":3505,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-xian-dai-c","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-现代C++","现代C++",[7],"2000",{"path":3507,"stem":3508,"title":3509,"authors":3510,"date":3065,"chapterOrder":3511,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0100-ding-shi-qi-yu-yi-bu-io","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0100-定时器与异步IO","定时器与异步 IO",[7],"2100-0100-0100",{"path":3513,"stem":3514,"title":3515,"authors":3516,"date":3065,"chapterOrder":3517,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0200-boost-asio-ji-chu","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0200-Boost.Asio基础","Boost.Asio 基础",[7],"2100-0100-0200",{"path":3519,"stem":3520,"title":3521,"authors":3522,"date":3065,"chapterOrder":3523,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0300-chuan-kou-tong-xin","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0300-串口通信","串口通信教学",[7],"2100-0100-0300",{"path":3072,"stem":3074,"title":5,"authors":3525,"date":3065,"chapterOrder":3064,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},[7],{"path":3527,"stem":3528,"title":3529,"authors":3530,"date":3065,"chapterOrder":3531,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0400-tcp-tong-xin","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0400-TCP通信","TCP 通信",[7],"2100-0100-0400",{"path":3533,"stem":3534,"title":3535,"authors":3536,"date":3065,"chapterOrder":3537,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0500-udp-tong-xin","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0500-UDP通信","UDP 通信",[7],"2100-0100-0500",{"path":3539,"stem":3540,"title":3541,"authors":3542,"date":3065,"chapterOrder":3543,"chapterDepth":75,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0600-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0600-机器人工程写法与ROS2集成","机器人工程写法与 ROS2 集成",[7],"2100-0100-0600",{"path":3545,"stem":3546,"title":3547,"authors":3548,"date":3065,"chapterOrder":3549,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-boost-asio-yi-bu-io-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-Boost.Asio异步IO库","Boost.Asio异步IO库",[7],"2100-0100",{"path":3551,"stem":3552,"title":3553,"authors":3554,"date":3065,"chapterOrder":3555,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0200-eigen-xian-xing-dai-shu-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0200-Eigen线性代数库","Eigen线性代数库",[7],"2100-0200",{"path":3557,"stem":3558,"title":3559,"authors":3560,"date":3065,"chapterOrder":3561,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0300-opencv-ji-suan-ji-shi-jue-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0300-OpenCV计算机视觉库","OpenCV计算机视觉库",[7],"2100-0300",{"path":3563,"stem":3564,"title":3565,"authors":3566,"date":3065,"chapterOrder":3567,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0400-pcl-dian-yun-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0400-PCL点云库","PCL点云库",[7],"2100-0400",{"path":3569,"stem":3570,"title":3571,"authors":3572,"date":3065,"chapterOrder":3573,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0500-sophus-li-qun-li-dai-shu-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0500-Sophus李群李代数库","Sophus 李群李代数库",[7],"2100-0500",{"path":3575,"stem":3576,"title":3577,"authors":3578,"date":3065,"chapterOrder":3579,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-chang-yong-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-常用库","常用库学习",[7],"2100",{"path":3581,"stem":3582,"title":3583,"authors":3584,"date":3065,"chapterOrder":3585,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0100-cmakepresets-yu-gou-jian-an-zhuang","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0100-CMakePresets与构建安装","CMakePresets与构建安装",[7],"2200-0100",{"path":3587,"stem":3588,"title":3589,"authors":3590,"date":3065,"chapterOrder":3591,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0200-ding-ceng-cmake-yu-gong-gong-bian-yi-xuan-xiang","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0200-顶层CMake与公共编译选项","顶层CMake与公共编译选项",[7],"2200-0200",{"path":3593,"stem":3594,"title":3595,"authors":3596,"date":3065,"chapterOrder":3597,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0300-src-yu-lib-mo-kuai-cmake-xiang-jie","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0300-src与lib模块CMake详解","src与lib模块CMake详解",[7],"2200-0300",{"path":3599,"stem":3600,"title":3601,"authors":3602,"date":3065,"chapterOrder":3603,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0400-di-san-fang-ku-yi-lai-xie-fa","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0400-第三方库依赖写法","第三方库依赖写法",[7],"2200-0400",{"path":3605,"stem":3606,"title":3607,"authors":3608,"date":3065,"chapterOrder":3609,"chapterDepth":64,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0500-mu-ban-kuo-zhan-yu-chang-jian-wen-ti","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0500-模板扩展与常见问题","模板扩展与常见问题",[7],"2200-0500",{"path":3611,"stem":3612,"title":3613,"authors":3614,"date":3065,"chapterOrder":3615,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":3070},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-cmake-gong-cheng-mu-ban","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-CMake工程模板","CMake工程模板",[7],"2200",{"path":3067,"stem":3617,"title":3618,"authors":3619,"date":3065,"chapterOrder":3620,"chapterDepth":3083,"docKey":3066,"docRoot":3067,"docTitle":3068,"isWikiDoc":774,"isWikiIndex":774},"wiki\u002F2023-10-05-Cplusplus教学\u002Findex","C\u002FC++教程",[7],null,[3622,3624,3627,3630],{"name":7,"image":3623},"\u002Fimages\u002Fmember_photo\u002F董佳辉.jpg",{"name":3625,"image":3626},"宫金良","\u002Fimages\u002Fmember_photo\u002F宫金良.jpg",{"name":3628,"image":3629},"巩丽","\u002Fimages\u002Fmember_photo\u002F巩丽.png",{"name":3631,"image":3632},"张彦斐","\u002Fimages\u002Fmember_photo\u002F张彦斐.jpg",1792431899938]