[{"data":1,"prerenderedAt":8641},["ShallowReactive",2],{"wiki-page-/zh-cn/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control":3,"wiki-doc-items-/zh-cn/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control":8276,"language-switcher-data-/zh-cn/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control":8625,"wiki-i18n-paths-/zh-cn/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control":8640},{"id":4,"title":5,"body":6,"canonicalPath":8258,"chapter":8259,"chapterSort":8260,"date":8261,"description":82,"docI18nKey":8262,"docKey":8263,"docRoot":8264,"docTitle":8265,"extension":8266,"i18nKey":8267,"isBlogPost":8268,"isWikiDoc":1568,"isWikiIndex":8268,"layout":8269,"legacyPath":8270,"locale":8271,"localeSlug":8272,"meta":8273,"navigation":1568,"path":8258,"seo":8274,"sourcePath":8270,"sourceStem":8267,"stem":8267,"wikiDepth":89,"__hash__":8275},"content/wiki/2023-12-30-ros2-tutorial/ch15-ROS2_Control.md","ROS2_Control",{"type":7,"value":8,"toc":8242},"minimark",[9,14,19,26,33,63,73,76,93,96,220,224,230,254,260,279,282,286,289,292,325,328,406,421,424,437,440,443,472,475,505,508,530,533,601,604,901,923,926,948,951,991,996,1013,1016,1049,1052,1082,1095,1099,1103,1106,1120,1123,1132,1135,1206,1209,1295,1308,1311,1316,1324,1327,1382,1385,1407,1410,1413,1466,1469,1475,1486,1490,1493,1500,1503,1631,1637,1698,1702,1708,1717,1720,1729,1732,1735,1940,1943,1968,1975,1982,2077,2088,2092,2096,2101,2117,2124,2197,2202,2292,2296,2299,2454,2461,2467,2470,2472,2477,2509,2520,2544,2547,2644,2647,2652,2689,2691,2733,2737,2746,2749,2764,2767,2784,2787,2801,2804,2829,2832,2846,2849,2875,2878,2894,2897,2911,2914,2918,2923,2932,2934,2942,2945,2993,2996,3042,3048,3052,3057,3066,3068,3076,3079,3103,3106,3140,3146,3155,3158,3223,3226,3344,3347,3424,3427,3481,3495,3499,3504,3513,3515,3524,3527,3571,3574,3655,3658,3728,3741,3745,3750,3759,3762,3777,3780,3783,3830,3833,3855,3859,3866,4213,4239,4242,4253,4256,4260,4264,4468,4472,4668,4671,4674,4708,4711,4765,4768,4771,4774,4794,4803,4807,4810,4903,4913,4965,4970,4973,4976,5717,5720,5758,5761,5852,5855,5907,5910,5913,5970,5973,5976,5979,6056,6059,6063,6066,6078,6081,6163,6168,6188,6198,6201,6207,6255,6258,6641,6644,6648,6651,6748,6751,6808,6812,6815,6971,6974,7058,7061,7127,7130,7133,7175,7178,7184,7187,7199,7202,7206,7213,7216,7235,7238,7257,7267,7273,7282,7285,7294,7300,7499,7502,7569,7575,7659,7662,7671,7674,7737,7740,7765,7768,7782,7785,7788,7791,7794,7806,7809,7833,7845,7848,7851,7876,7879,7896,7902,7905,7939,7941,7963,7969,7976,7992,7995,8004,8007,8043,8046,8048,8073,8077,8086,8111,8114,8117,8165,8168,8171,8238],[10,11,13],"h3",{"id":12},"ros2-control-概述","ROS2 Control 概述",[15,16,18],"h4",{"id":17},"ros2-control-简述","ROS2 Control 简述",[20,21,22],"p",{},[23,24,25],"strong",{},"概念",[20,27,28,32],{},[29,30,31],"code",{},"ros2_control"," 是 ROS 2 中用于机器人控制的标准框架。它的核心思想是把机器人控制拆成三层：",[34,35,36,47,57],"ul",{},[37,38,39,42,43,46],"li",{},[23,40,41],{},"控制器（Controller）"," ：负责控制算法。例如差速底盘控制器把 ",[29,44,45],{},"cmd_vel"," 转成左右轮速度，轨迹控制器把关节轨迹转成关节命令。",[37,48,49,52,53,56],{},[23,50,51],{},"控制器管理器（Controller Manager）"," ：负责加载、配置、激活、停止控制器，并在固定频率下执行 ",[29,54,55],{},"read -> update -> write"," 控制循环。",[37,58,59,62],{},[23,60,61],{},"硬件接口（Hardware Interface）"," ：负责和真实硬件、仿真硬件或模拟硬件通信，让控制器不用关心底层串口、CAN、EtherCAT、Gazebo 或自定义驱动。",[20,64,65,66,68,69,72],{},"简单说，",[29,67,31],{}," 是 ROS 2 中“控制器”和“硬件”之间的标准插座。控制器只面对标准接口，硬件只暴露标准接口，中间由 ",[29,70,71],{},"controller_manager"," 管理。",[20,74,75],{},"在 Ubuntu 24.04 + ROS 2 Jazzy 中，ROS 2 的默认安装路径是：",[77,78,83],"pre",{"className":79,"code":80,"language":81,"meta":82,"style":82},"language-bash shiki shiki-themes github-light github-dark","/opt/ros/jazzy\n","bash","",[29,84,85],{"__ignoreMap":82},[86,87,90],"span",{"class":88,"line":89},"line",1,[86,91,80],{"class":92},"sScJk",[20,94,95],{},"常见 ros2_control 包路径如下：",[97,98,99,116],"table",{},[100,101,102],"thead",{},[103,104,105,110,113],"tr",{},[106,107,109],"th",{"align":108},"left","软件包",[106,111,112],{"align":108},"路径",[106,114,115],{"align":108},"说明",[117,118,119,134,156,171,186,201],"tbody",{},[103,120,121,126,131],{},[122,123,124],"td",{"align":108},[29,125,31],{},[122,127,128],{"align":108},[29,129,130],{},"/opt/ros/jazzy/share/ros2_control",[122,132,133],{"align":108},"ros2_control 元包",[103,135,136,140,145],{},[122,137,138],{"align":108},[29,139,71],{},[122,141,142],{"align":108},[29,143,144],{},"/opt/ros/jazzy/share/controller_manager",[122,146,147,148,151,152,155],{"align":108},"控制器管理器，提供 ",[29,149,150],{},"ros2_control_node","、",[29,153,154],{},"spawner"," 等工具",[103,157,158,163,168],{},[122,159,160],{"align":108},[29,161,162],{},"controller_interface",[122,164,165],{"align":108},[29,166,167],{},"/opt/ros/jazzy/share/controller_interface",[122,169,170],{"align":108},"自定义控制器要继承的基础接口",[103,172,173,178,183],{},[122,174,175],{"align":108},[29,176,177],{},"hardware_interface",[122,179,180],{"align":108},[29,181,182],{},"/opt/ros/jazzy/share/hardware_interface",[122,184,185],{"align":108},"自定义硬件接口要继承的基础接口",[103,187,188,193,198],{},[122,189,190],{"align":108},[29,191,192],{},"ros2_controllers",[122,194,195],{"align":108},[29,196,197],{},"/opt/ros/jazzy/share/ros2_controllers",[122,199,200],{"align":108},"官方常用控制器元包",[103,202,203,208,213],{},[122,204,205],{"align":108},[29,206,207],{},"ros2controlcli",[122,209,210],{"align":108},[29,211,212],{},"/opt/ros/jazzy/share/ros2controlcli",[122,214,215,216,219],{"align":108},"提供 ",[29,217,218],{},"ros2 control ..."," 命令",[15,221,223],{"id":222},"ros2-control-解决了什么问题","ROS2 Control 解决了什么问题",[20,225,226,227,229],{},"如果不用 ",[29,228,31],{},"，机器人控制程序很容易写成这样：",[34,231,232,239,242,245,248,251],{},[37,233,234,235,238],{},"节点直接订阅 ",[29,236,237],{},"/cmd_vel","；",[37,240,241],{},"节点自己计算左右轮速度；",[37,243,244],{},"节点自己读编码器；",[37,246,247],{},"节点自己写串口或 CAN；",[37,249,250],{},"换硬件时整套控制节点都要改；",[37,252,253],{},"从仿真迁移到真机时还要再改一遍。",[20,255,256,257,259],{},"使用 ",[29,258,31],{}," 后，推荐的结构是：",[34,261,262,265,268,271,274],{},[37,263,264],{},"控制器负责算法，例如差速运动学、机械臂轨迹插值、PID、夹爪控制。",[37,266,267],{},"硬件接口负责通信，例如串口协议、CAN 报文、驱动板寄存器、Gazebo 插件。",[37,269,270],{},"URDF 负责声明机器人有哪些 joint、command interface 和 state interface。",[37,272,273],{},"YAML 负责声明加载哪些控制器以及控制器参数。",[37,275,276,278],{},[29,277,71],{}," 负责把它们组合起来。",[20,280,281],{},"这样做的好处是：控制器可以复用，硬件可以替换，仿真和真机可以共用大部分上层配置。",[10,283,285],{"id":284},"ros2-control-安装","ROS2 Control 安装",[15,287,288],{"id":288},"基础安装",[20,290,291],{},"Ubuntu 24.04 使用 ROS 2 Jazzy 时，安装命令如下：",[77,293,295],{"className":79,"code":294,"language":81,"meta":82,"style":82},"sudo apt update\nsudo apt install ros-jazzy-ros2-control ros-jazzy-ros2-controllers\n",[29,296,297,309],{"__ignoreMap":82},[86,298,299,302,306],{"class":88,"line":89},[86,300,301],{"class":92},"sudo",[86,303,305],{"class":304},"sZZnC"," apt",[86,307,308],{"class":304}," update\n",[86,310,312,314,316,319,322],{"class":88,"line":311},2,[86,313,301],{"class":92},[86,315,305],{"class":304},[86,317,318],{"class":304}," install",[86,320,321],{"class":304}," ros-jazzy-ros2-control",[86,323,324],{"class":304}," ros-jazzy-ros2-controllers\n",[20,326,327],{},"这两个包已经能满足大多数真机控制、控制器开发和基础学习。如果想把本章的调试工具、Gazebo Sim 示例、测试辅助包也装齐，建议继续安装：",[77,329,331],{"className":79,"code":330,"language":81,"meta":82,"style":82},"sudo apt install \\\n  ros-jazzy-gz-ros2-control \\\n  ros-jazzy-gz-ros2-control-demos \\\n  ros-jazzy-rqt-controller-manager \\\n  ros-jazzy-rqt-joint-trajectory-controller \\\n  ros-jazzy-ros2-controllers-test-nodes \\\n  ros-jazzy-hardware-interface-testing \\\n  ros-jazzy-joint-state-topic-hardware-interface \\\n  ros-jazzy-battery-state-broadcaster\n",[29,332,333,345,352,360,368,376,384,392,400],{"__ignoreMap":82},[86,334,335,337,339,341],{"class":88,"line":89},[86,336,301],{"class":92},[86,338,305],{"class":304},[86,340,318],{"class":304},[86,342,344],{"class":343},"sj4cs"," \\\n",[86,346,347,350],{"class":88,"line":311},[86,348,349],{"class":304},"  ros-jazzy-gz-ros2-control",[86,351,344],{"class":343},[86,353,355,358],{"class":88,"line":354},3,[86,356,357],{"class":304},"  ros-jazzy-gz-ros2-control-demos",[86,359,344],{"class":343},[86,361,363,366],{"class":88,"line":362},4,[86,364,365],{"class":304},"  ros-jazzy-rqt-controller-manager",[86,367,344],{"class":343},[86,369,371,374],{"class":88,"line":370},5,[86,372,373],{"class":304},"  ros-jazzy-rqt-joint-trajectory-controller",[86,375,344],{"class":343},[86,377,379,382],{"class":88,"line":378},6,[86,380,381],{"class":304},"  ros-jazzy-ros2-controllers-test-nodes",[86,383,344],{"class":343},[86,385,387,390],{"class":88,"line":386},7,[86,388,389],{"class":304},"  ros-jazzy-hardware-interface-testing",[86,391,344],{"class":343},[86,393,395,398],{"class":88,"line":394},8,[86,396,397],{"class":304},"  ros-jazzy-joint-state-topic-hardware-interface",[86,399,344],{"class":343},[86,401,403],{"class":88,"line":402},9,[86,404,405],{"class":304},"  ros-jazzy-battery-state-broadcaster\n",[20,407,408,409,412,413,416,417,420],{},"其中 ",[29,410,411],{},"ros-jazzy-gz-ros2-control"," 是 Gazebo Sim 接入 ros2_control 的核心包，",[29,414,415],{},"ros-jazzy-gz-ros2-control-demos"," 提供官方可运行示例，两个 ",[29,418,419],{},"rqt"," 包提供图形化控制器管理和轨迹发送工具。后面几个包不是写普通机器人必需的，但对阅读示例、测试硬件接口、学习 broadcaster 很有帮助。",[20,422,423],{},"每次打开新终端后，都需要 source ROS 2 环境：",[77,425,427],{"className":79,"code":426,"language":81,"meta":82,"style":82},"source /opt/ros/jazzy/setup.bash\n",[29,428,429],{"__ignoreMap":82},[86,430,431,434],{"class":88,"line":89},[86,432,433],{"class":343},"source",[86,435,436],{"class":304}," /opt/ros/jazzy/setup.bash\n",[15,438,439],{"id":439},"验证安装",[20,441,442],{},"可以查看基础包：",[77,444,446],{"className":79,"code":445,"language":81,"meta":82,"style":82},"ros2 pkg list | grep -E '^(ros2_control|controller_manager|hardware_interface|controller_interface|ros2controlcli)$'\n",[29,447,448],{"__ignoreMap":82},[86,449,450,453,456,459,463,466,469],{"class":88,"line":89},[86,451,452],{"class":92},"ros2",[86,454,455],{"class":304}," pkg",[86,457,458],{"class":304}," list",[86,460,462],{"class":461},"szBVR"," |",[86,464,465],{"class":92}," grep",[86,467,468],{"class":343}," -E",[86,470,471],{"class":304}," '^(ros2_control|controller_manager|hardware_interface|controller_interface|ros2controlcli)$'\n",[20,473,474],{},"正常情况下可以看到：",[77,476,478],{"className":79,"code":477,"language":81,"meta":82,"style":82},"controller_interface\ncontroller_manager\nhardware_interface\nros2_control\nros2controlcli\n",[29,479,480,485,490,495,500],{"__ignoreMap":82},[86,481,482],{"class":88,"line":89},[86,483,484],{"class":92},"controller_interface\n",[86,486,487],{"class":88,"line":311},[86,488,489],{"class":92},"controller_manager\n",[86,491,492],{"class":88,"line":354},[86,493,494],{"class":92},"hardware_interface\n",[86,496,497],{"class":88,"line":362},[86,498,499],{"class":92},"ros2_control\n",[86,501,502],{"class":88,"line":370},[86,503,504],{"class":92},"ros2controlcli\n",[20,506,507],{},"可以查看常用控制器：",[77,509,511],{"className":79,"code":510,"language":81,"meta":82,"style":82},"ros2 pkg list | grep -E '^(joint_state_broadcaster|diff_drive_controller|joint_trajectory_controller|forward_command_controller|position_controllers|velocity_controllers|effort_controllers|pid_controller|mecanum_drive_controller|ackermann_steering_controller|tricycle_controller|omni_wheel_drive_controller)$'\n",[29,512,513],{"__ignoreMap":82},[86,514,515,517,519,521,523,525,527],{"class":88,"line":89},[86,516,452],{"class":92},[86,518,455],{"class":304},[86,520,458],{"class":304},[86,522,462],{"class":461},[86,524,465],{"class":92},[86,526,468],{"class":343},[86,528,529],{"class":304}," '^(joint_state_broadcaster|diff_drive_controller|joint_trajectory_controller|forward_command_controller|position_controllers|velocity_controllers|effort_controllers|pid_controller|mecanum_drive_controller|ackermann_steering_controller|tricycle_controller|omni_wheel_drive_controller)$'\n",[20,531,532],{},"常用输出包括：",[77,534,536],{"className":79,"code":535,"language":81,"meta":82,"style":82},"ackermann_steering_controller\ndiff_drive_controller\neffort_controllers\nforward_command_controller\njoint_state_broadcaster\njoint_trajectory_controller\nmecanum_drive_controller\nomni_wheel_drive_controller\npid_controller\nposition_controllers\ntricycle_controller\nvelocity_controllers\n",[29,537,538,543,548,553,558,563,568,573,578,583,589,595],{"__ignoreMap":82},[86,539,540],{"class":88,"line":89},[86,541,542],{"class":92},"ackermann_steering_controller\n",[86,544,545],{"class":88,"line":311},[86,546,547],{"class":92},"diff_drive_controller\n",[86,549,550],{"class":88,"line":354},[86,551,552],{"class":92},"effort_controllers\n",[86,554,555],{"class":88,"line":362},[86,556,557],{"class":92},"forward_command_controller\n",[86,559,560],{"class":88,"line":370},[86,561,562],{"class":92},"joint_state_broadcaster\n",[86,564,565],{"class":88,"line":378},[86,566,567],{"class":92},"joint_trajectory_controller\n",[86,569,570],{"class":88,"line":386},[86,571,572],{"class":92},"mecanum_drive_controller\n",[86,574,575],{"class":88,"line":394},[86,576,577],{"class":92},"omni_wheel_drive_controller\n",[86,579,580],{"class":88,"line":402},[86,581,582],{"class":92},"pid_controller\n",[86,584,586],{"class":88,"line":585},10,[86,587,588],{"class":92},"position_controllers\n",[86,590,592],{"class":88,"line":591},11,[86,593,594],{"class":92},"tricycle_controller\n",[86,596,598],{"class":88,"line":597},12,[86,599,600],{"class":92},"velocity_controllers\n",[20,602,603],{},"这些包名第一次看会比较抽象，可以先按下面这样理解：",[97,605,606,625],{},[100,607,608],{},[103,609,610,613,616,619,622],{},[106,611,612],{"align":108},"包名",[106,614,615],{"align":108},"中文理解",[106,617,618],{"align":108},"主要用途",[106,620,621],{"align":108},"典型输入",[106,623,624],{"align":108},"典型输出",[117,626,627,663,688,720,741,763,783,803,822,843,862,881],{},[103,628,629,634,637,647,660],{},[122,630,631],{"align":108},[29,632,633],{},"joint_state_broadcaster",[122,635,636],{"align":108},"关节状态发布器",[122,638,639,640,643,644],{"align":108},"读取硬件 state interfaces，发布 ",[29,641,642],{},"/joint_states"," 和 ",[29,645,646],{},"/dynamic_joint_states",[122,648,649,650,151,653,151,656,659],{"align":108},"硬件中的 ",[29,651,652],{},"position",[29,654,655],{},"velocity",[29,657,658],{},"effort"," 等状态接口",[122,661,662],{"align":108},"ROS 话题，不写硬件命令",[103,664,665,670,673,676,682],{},[122,666,667],{"align":108},[29,668,669],{},"diff_drive_controller",[122,671,672],{"align":108},"差速底盘控制器",[122,674,675],{"align":108},"控制两侧轮子差速运动的小车",[122,677,678,681],{"align":108},[29,679,680],{},"geometry_msgs/msg/TwistStamped"," 速度命令",[122,683,684,685,687],{"align":108},"左右轮 ",[29,686,655],{}," command interface，另发布 odom",[103,689,690,695,698,701,711],{},[122,691,692],{"align":108},[29,693,694],{},"joint_trajectory_controller",[122,696,697],{"align":108},"关节轨迹控制器",[122,699,700],{"align":108},"执行机械臂、云台、多关节机构的关节空间轨迹",[122,702,703,706,707,710],{"align":108},[29,704,705],{},"trajectory_msgs/msg/JointTrajectory"," 或 ",[29,708,709],{},"FollowJointTrajectory"," action",[122,712,713,714,151,716,706,718,219],{"align":108},"一组关节的 ",[29,715,652],{},[29,717,655],{},[29,719,658],{},[103,721,722,727,730,733,738],{},[122,723,724],{"align":108},[29,725,726],{},"forward_command_controller",[122,728,729],{"align":108},"命令转发控制器",[122,731,732],{"align":108},"把收到的数组命令直接转发到硬件接口，适合测试硬件",[122,734,735],{"align":108},[29,736,737],{},"std_msgs/msg/Float64MultiArray",[122,739,740],{"align":108},"指定 joint 的指定 command interface",[103,742,743,748,751,754,757],{},[122,744,745],{"align":108},[29,746,747],{},"position_controllers",[122,749,750],{"align":108},"位置命令组控制器",[122,752,753],{"align":108},"对多个关节直接下发位置目标",[122,755,756],{"align":108},"位置数组",[122,758,759,760,762],{"align":108},"多个 joint 的 ",[29,761,652],{}," command interface",[103,764,765,770,773,776,779],{},[122,766,767],{"align":108},[29,768,769],{},"velocity_controllers",[122,771,772],{"align":108},"速度命令组控制器",[122,774,775],{"align":108},"对多个关节直接下发速度目标",[122,777,778],{"align":108},"速度数组",[122,780,759,781,762],{"align":108},[29,782,655],{},[103,784,785,790,793,796,799],{},[122,786,787],{"align":108},[29,788,789],{},"effort_controllers",[122,791,792],{"align":108},"力/力矩命令组控制器",[122,794,795],{"align":108},"对多个关节直接下发 effort 目标",[122,797,798],{"align":108},"effort 数组",[122,800,759,801,762],{"align":108},[29,802,658],{},[103,804,805,810,813,816,819],{},[122,806,807],{"align":108},[29,808,809],{},"pid_controller",[122,811,812],{"align":108},"PID 控制器",[122,814,815],{"align":108},"在 ros2_control 链路中做 PID 闭环控制",[122,817,818],{"align":108},"reference interface 或控制器链输入",[122,820,821],{"align":108},"PID 计算后的 command interface",[103,823,824,829,832,835,838],{},[122,825,826],{"align":108},[29,827,828],{},"mecanum_drive_controller",[122,830,831],{"align":108},"麦克纳姆轮底盘控制器",[122,833,834],{"align":108},"控制四轮麦克纳姆底盘，实现前后、横移、旋转",[122,836,837],{"align":108},"通常是底盘速度命令",[122,839,840,841,762],{"align":108},"四个麦克纳姆轮的 ",[29,842,655],{},[103,844,845,850,853,856,859],{},[122,846,847],{"align":108},[29,848,849],{},"ackermann_steering_controller",[122,851,852],{"align":108},"阿克曼转向控制器",[122,854,855],{"align":108},"控制汽车式前轮转向、后轮驱动结构",[122,857,858],{"align":108},"底盘速度/转向相关命令",[122,860,861],{"align":108},"转向关节位置命令和驱动轮速度命令",[103,863,864,869,872,875,878],{},[122,865,866],{"align":108},[29,867,868],{},"tricycle_controller",[122,870,871],{"align":108},"三轮车控制器",[122,873,874],{"align":108},"控制一个转向轮加驱动轮的三轮底盘",[122,876,877],{"align":108},"底盘速度命令",[122,879,880],{"align":108},"转向关节和驱动轮命令",[103,882,883,888,891,894,896],{},[122,884,885],{"align":108},[29,886,887],{},"omni_wheel_drive_controller",[122,889,890],{"align":108},"全向轮底盘控制器",[122,892,893],{"align":108},"控制三个或更多全向轮组成的全向底盘",[122,895,877],{"align":108},[122,897,898,899,762],{"align":108},"多个全向轮的 ",[29,900,655],{},[20,902,903,904,906,907,151,909,151,911,913,914,151,916,151,918,643,920,922],{},"这里有一个很重要的区别：",[29,905,633],{}," 是 broadcaster，它只发布状态，不负责让机器人动；",[29,908,669],{},[29,910,694],{},[29,912,828],{}," 这类才是 controller，会占用 command interface 并向硬件写命令。",[29,915,747],{},[29,917,769],{},[29,919,789],{},[29,921,726],{}," 更像“直接转发命令”的工具，适合调试硬件接口，但不会替你计算复杂运动学。",[20,924,925],{},"如果安装了 Gazebo Sim 和调试辅助包，还可以检查：",[77,927,929],{"className":79,"code":928,"language":81,"meta":82,"style":82},"ros2 pkg list | grep -E '^(gz_ros2_control|gz_ros2_control_demos|rqt_controller_manager|rqt_joint_trajectory_controller|hardware_interface_testing|joint_state_topic_hardware_interface|battery_state_broadcaster)$'\n",[29,930,931],{"__ignoreMap":82},[86,932,933,935,937,939,941,943,945],{"class":88,"line":89},[86,934,452],{"class":92},[86,936,455],{"class":304},[86,938,458],{"class":304},[86,940,462],{"class":461},[86,942,465],{"class":92},[86,944,468],{"class":343},[86,946,947],{"class":304}," '^(gz_ros2_control|gz_ros2_control_demos|rqt_controller_manager|rqt_joint_trajectory_controller|hardware_interface_testing|joint_state_topic_hardware_interface|battery_state_broadcaster)$'\n",[20,949,950],{},"正常会看到：",[77,952,954],{"className":79,"code":953,"language":81,"meta":82,"style":82},"battery_state_broadcaster\ngz_ros2_control\ngz_ros2_control_demos\nhardware_interface_testing\njoint_state_topic_hardware_interface\nrqt_controller_manager\nrqt_joint_trajectory_controller\n",[29,955,956,961,966,971,976,981,986],{"__ignoreMap":82},[86,957,958],{"class":88,"line":89},[86,959,960],{"class":92},"battery_state_broadcaster\n",[86,962,963],{"class":88,"line":311},[86,964,965],{"class":92},"gz_ros2_control\n",[86,967,968],{"class":88,"line":354},[86,969,970],{"class":92},"gz_ros2_control_demos\n",[86,972,973],{"class":88,"line":362},[86,974,975],{"class":92},"hardware_interface_testing\n",[86,977,978],{"class":88,"line":370},[86,979,980],{"class":92},"joint_state_topic_hardware_interface\n",[86,982,983],{"class":88,"line":378},[86,984,985],{"class":92},"rqt_controller_manager\n",[86,987,988],{"class":88,"line":386},[86,989,990],{"class":92},"rqt_joint_trajectory_controller\n",[20,992,993,995],{},[29,994,71],{}," 包提供了四个常用可执行文件：",[77,997,999],{"className":79,"code":998,"language":81,"meta":82,"style":82},"ros2 pkg executables controller_manager\n",[29,1000,1001],{"__ignoreMap":82},[86,1002,1003,1005,1007,1010],{"class":88,"line":89},[86,1004,452],{"class":92},[86,1006,455],{"class":304},[86,1008,1009],{"class":304}," executables",[86,1011,1012],{"class":304}," controller_manager\n",[20,1014,1015],{},"输出应包含：",[77,1017,1019],{"className":79,"code":1018,"language":81,"meta":82,"style":82},"controller_manager hardware_spawner\ncontroller_manager ros2_control_node\ncontroller_manager spawner\ncontroller_manager unspawner\n",[29,1020,1021,1028,1035,1042],{"__ignoreMap":82},[86,1022,1023,1025],{"class":88,"line":89},[86,1024,71],{"class":92},[86,1026,1027],{"class":304}," hardware_spawner\n",[86,1029,1030,1032],{"class":88,"line":311},[86,1031,71],{"class":92},[86,1033,1034],{"class":304}," ros2_control_node\n",[86,1036,1037,1039],{"class":88,"line":354},[86,1038,71],{"class":92},[86,1040,1041],{"class":304}," spawner\n",[86,1043,1044,1046],{"class":88,"line":362},[86,1045,71],{"class":92},[86,1047,1048],{"class":304}," unspawner\n",[20,1050,1051],{},"图形化调试工具可以这样启动：",[77,1053,1055],{"className":79,"code":1054,"language":81,"meta":82,"style":82},"ros2 run rqt_controller_manager rqt_controller_manager\nros2 run rqt_joint_trajectory_controller rqt_joint_trajectory_controller\n",[29,1056,1057,1070],{"__ignoreMap":82},[86,1058,1059,1061,1064,1067],{"class":88,"line":89},[86,1060,452],{"class":92},[86,1062,1063],{"class":304}," run",[86,1065,1066],{"class":304}," rqt_controller_manager",[86,1068,1069],{"class":304}," rqt_controller_manager\n",[86,1071,1072,1074,1076,1079],{"class":88,"line":311},[86,1073,452],{"class":92},[86,1075,1063],{"class":304},[86,1077,1078],{"class":304}," rqt_joint_trajectory_controller",[86,1080,1081],{"class":304}," rqt_joint_trajectory_controller\n",[20,1083,1084,1087,1088,1091,1092,1094],{},[29,1085,1086],{},"rqt_controller_manager"," 适合查看、加载、配置、激活和停止控制器；",[29,1089,1090],{},"rqt_joint_trajectory_controller"," 适合给 ",[29,1093,694],{}," 手动发送简单关节轨迹。",[10,1096,1098],{"id":1097},"ros2-control-核心概念","ROS2 Control 核心概念",[15,1100,1102],{"id":1101},"command-interface-和-state-interface","command interface 和 state interface",[20,1104,1105],{},"学习 ros2_control 最重要的是理解接口。",[34,1107,1108,1114],{},[37,1109,1110,1113],{},[23,1111,1112],{},"command interface"," ：控制器写入的目标值。例如电机目标速度、关节目标位置、关节目标力矩。",[37,1115,1116,1119],{},[23,1117,1118],{},"state interface"," ：硬件读回来的状态值。例如编码器位置、当前速度、力矩、电流、温度。",[20,1121,1122],{},"Jazzy 中标准接口名定义在：",[77,1124,1126],{"className":79,"code":1125,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/include/hardware_interface/hardware_interface/types/hardware_interface_type_values.hpp\n",[29,1127,1128],{"__ignoreMap":82},[86,1129,1130],{"class":88,"line":89},[86,1131,1125],{"class":92},[20,1133,1134],{},"常用接口如下：",[97,1136,1137,1147],{},[100,1138,1139],{},[103,1140,1141,1144],{},[106,1142,1143],{"align":108},"接口名",[106,1145,1146],{"align":108},"含义",[117,1148,1149,1158,1167,1176,1186,1196],{},[103,1150,1151,1155],{},[122,1152,1153],{"align":108},[29,1154,652],{},[122,1156,1157],{"align":108},"位置，常用于关节角度或直线位移",[103,1159,1160,1164],{},[122,1161,1162],{"align":108},[29,1163,655],{},[122,1165,1166],{"align":108},"速度，常用于轮速或关节速度",[103,1168,1169,1173],{},[122,1170,1171],{"align":108},[29,1172,658],{},[122,1174,1175],{"align":108},"力或力矩，常用于力矩控制",[103,1177,1178,1183],{},[122,1179,1180],{"align":108},[29,1181,1182],{},"acceleration",[122,1184,1185],{"align":108},"加速度",[103,1187,1188,1193],{},[122,1189,1190],{"align":108},[29,1191,1192],{},"current",[122,1194,1195],{"align":108},"电流",[103,1197,1198,1203],{},[122,1199,1200],{"align":108},[29,1201,1202],{},"temperature",[122,1204,1205],{"align":108},"温度",[20,1207,1208],{},"例如一个轮子关节可以这样声明：",[77,1210,1214],{"className":1211,"code":1212,"language":1213,"meta":82,"style":82},"language-xml shiki shiki-themes github-light github-dark","\u003Cjoint name=\"left_wheel_joint\">\n  \u003Ccommand_interface name=\"velocity\"/>\n  \u003Cstate_interface name=\"position\"/>\n  \u003Cstate_interface name=\"velocity\"/>\n\u003C/joint>\n","xml",[29,1215,1216,1238,1256,1272,1286],{"__ignoreMap":82},[86,1217,1218,1222,1226,1229,1232,1235],{"class":88,"line":89},[86,1219,1221],{"class":1220},"sVt8B","\u003C",[86,1223,1225],{"class":1224},"s9eBZ","joint",[86,1227,1228],{"class":92}," name",[86,1230,1231],{"class":1220},"=",[86,1233,1234],{"class":304},"\"left_wheel_joint\"",[86,1236,1237],{"class":1220},">\n",[86,1239,1240,1243,1246,1248,1250,1253],{"class":88,"line":311},[86,1241,1242],{"class":1220},"  \u003C",[86,1244,1245],{"class":1224},"command_interface",[86,1247,1228],{"class":92},[86,1249,1231],{"class":1220},[86,1251,1252],{"class":304},"\"velocity\"",[86,1254,1255],{"class":1220},"/>\n",[86,1257,1258,1260,1263,1265,1267,1270],{"class":88,"line":354},[86,1259,1242],{"class":1220},[86,1261,1262],{"class":1224},"state_interface",[86,1264,1228],{"class":92},[86,1266,1231],{"class":1220},[86,1268,1269],{"class":304},"\"position\"",[86,1271,1255],{"class":1220},[86,1273,1274,1276,1278,1280,1282,1284],{"class":88,"line":362},[86,1275,1242],{"class":1220},[86,1277,1262],{"class":1224},[86,1279,1228],{"class":92},[86,1281,1231],{"class":1220},[86,1283,1252],{"class":304},[86,1285,1255],{"class":1220},[86,1287,1288,1291,1293],{"class":88,"line":370},[86,1289,1290],{"class":1220},"\u003C/",[86,1292,1225],{"class":1224},[86,1294,1237],{"class":1220},[20,1296,1297,1298,1301,1302,643,1305,1307],{},"含义是：控制器可以给 ",[29,1299,1300],{},"left_wheel_joint/velocity"," 写速度命令；硬件接口要能读回 ",[29,1303,1304],{},"left_wheel_joint/position",[29,1306,1300],{},"。",[15,1309,1310],{"id":1310},"控制循环",[20,1312,1313,1315],{},[29,1314,71],{}," 的核心循环是：",[77,1317,1322],{"className":1318,"code":1320,"language":1321,"meta":82},[1319],"language-text","read() -> update() -> write()\n","text",[29,1323,1320],{"__ignoreMap":82},[20,1325,1326],{},"具体含义如下：",[97,1328,1329,1342],{},[100,1330,1331],{},[103,1332,1333,1336,1339],{},[106,1334,1335],{"align":108},"阶段",[106,1337,1338],{"align":108},"执行者",[106,1340,1341],{"align":108},"作用",[117,1343,1344,1357,1370],{},[103,1345,1346,1351,1354],{},[122,1347,1348],{"align":108},[29,1349,1350],{},"read()",[122,1352,1353],{"align":108},"硬件接口",[122,1355,1356],{"align":108},"从电机、编码器、传感器或仿真器读取状态",[103,1358,1359,1364,1367],{},[122,1360,1361],{"align":108},[29,1362,1363],{},"update()",[122,1365,1366],{"align":108},"控制器",[122,1368,1369],{"align":108},"根据状态和目标计算新的命令",[103,1371,1372,1377,1379],{},[122,1373,1374],{"align":108},[29,1375,1376],{},"write()",[122,1378,1353],{"align":108},[122,1380,1381],{"align":108},"把命令写给电机驱动器、仿真器或底层硬件",[20,1383,1384],{},"例如差速底盘：",[34,1386,1387,1392,1402],{},[37,1388,1389,1391],{},[29,1390,1350],{}," 读取左右轮编码器，更新左右轮 position/velocity。",[37,1393,1394,1397,1398,1401],{},[29,1395,1396],{},"diff_drive_controller.update()"," 根据 ",[29,1399,1400],{},"/diff_drive_controller/cmd_vel"," 和轮子反馈计算左右轮目标速度，并发布里程计。",[37,1403,1404,1406],{},[29,1405,1376],{}," 把左右轮目标速度写到电机驱动板。",[15,1408,1409],{"id":1409},"生命周期状态",[20,1411,1412],{},"ros2_control 的控制器和硬件组件都有生命周期。常见状态如下：",[97,1414,1415,1424],{},[100,1416,1417],{},[103,1418,1419,1422],{},[106,1420,1421],{"align":108},"状态",[106,1423,1146],{"align":108},[117,1425,1426,1436,1446,1456],{},[103,1427,1428,1433],{},[122,1429,1430],{"align":108},[29,1431,1432],{},"unconfigured",[122,1434,1435],{"align":108},"已加载，但还没有配置",[103,1437,1438,1443],{},[122,1439,1440],{"align":108},[29,1441,1442],{},"inactive",[122,1444,1445],{"align":108},"已配置，但不参与控制循环输出",[103,1447,1448,1453],{},[122,1449,1450],{"align":108},[29,1451,1452],{},"active",[122,1454,1455],{"align":108},"已激活，参与控制循环",[103,1457,1458,1463],{},[122,1459,1460],{"align":108},[29,1461,1462],{},"finalized",[122,1464,1465],{"align":108},"已结束",[20,1467,1468],{},"常用流程是：",[77,1470,1473],{"className":1471,"code":1472,"language":1321,"meta":82},[1319],"load -> configure -> activate\n",[29,1474,1472],{"__ignoreMap":82},[20,1476,1477,1479,1480,706,1483,1307],{},[29,1478,154],{}," 默认会帮控制器完成加载、配置和激活。如果只想加载不激活，可以使用 ",[29,1481,1482],{},"--load-only",[29,1484,1485],{},"--inactive",[10,1487,1489],{"id":1488},"urdf-中的-ros2_control-标签","URDF 中的 ros2_control 标签",[15,1491,1492],{"id":1492},"基本结构",[20,1494,1495,1496,1499],{},"ros2_control 的硬件描述写在 URDF 或 xacro 的 ",[29,1497,1498],{},"\u003Cros2_control>"," 标签中。",[20,1501,1502],{},"最小结构如下：",[77,1504,1506],{"className":1211,"code":1505,"language":1213,"meta":82,"style":82},"\u003Cros2_control name=\"MyRobotSystem\" type=\"system\">\n  \u003Chardware>\n    \u003Cplugin>硬件插件名\u003C/plugin>\n  \u003C/hardware>\n\n  \u003Cjoint name=\"关节名\">\n    \u003Ccommand_interface name=\"命令接口名\"/>\n    \u003Cstate_interface name=\"状态接口名\"/>\n  \u003C/joint>\n\u003C/ros2_control>\n",[29,1507,1508,1531,1540,1555,1564,1570,1585,1600,1615,1623],{"__ignoreMap":82},[86,1509,1510,1512,1514,1516,1518,1521,1524,1526,1529],{"class":88,"line":89},[86,1511,1221],{"class":1220},[86,1513,31],{"class":1224},[86,1515,1228],{"class":92},[86,1517,1231],{"class":1220},[86,1519,1520],{"class":304},"\"MyRobotSystem\"",[86,1522,1523],{"class":92}," type",[86,1525,1231],{"class":1220},[86,1527,1528],{"class":304},"\"system\"",[86,1530,1237],{"class":1220},[86,1532,1533,1535,1538],{"class":88,"line":311},[86,1534,1242],{"class":1220},[86,1536,1537],{"class":1224},"hardware",[86,1539,1237],{"class":1220},[86,1541,1542,1545,1548,1551,1553],{"class":88,"line":354},[86,1543,1544],{"class":1220},"    \u003C",[86,1546,1547],{"class":1224},"plugin",[86,1549,1550],{"class":1220},">硬件插件名\u003C/",[86,1552,1547],{"class":1224},[86,1554,1237],{"class":1220},[86,1556,1557,1560,1562],{"class":88,"line":362},[86,1558,1559],{"class":1220},"  \u003C/",[86,1561,1537],{"class":1224},[86,1563,1237],{"class":1220},[86,1565,1566],{"class":88,"line":370},[86,1567,1569],{"emptyLinePlaceholder":1568},true,"\n",[86,1571,1572,1574,1576,1578,1580,1583],{"class":88,"line":378},[86,1573,1242],{"class":1220},[86,1575,1225],{"class":1224},[86,1577,1228],{"class":92},[86,1579,1231],{"class":1220},[86,1581,1582],{"class":304},"\"关节名\"",[86,1584,1237],{"class":1220},[86,1586,1587,1589,1591,1593,1595,1598],{"class":88,"line":386},[86,1588,1544],{"class":1220},[86,1590,1245],{"class":1224},[86,1592,1228],{"class":92},[86,1594,1231],{"class":1220},[86,1596,1597],{"class":304},"\"命令接口名\"",[86,1599,1255],{"class":1220},[86,1601,1602,1604,1606,1608,1610,1613],{"class":88,"line":394},[86,1603,1544],{"class":1220},[86,1605,1262],{"class":1224},[86,1607,1228],{"class":92},[86,1609,1231],{"class":1220},[86,1611,1612],{"class":304},"\"状态接口名\"",[86,1614,1255],{"class":1220},[86,1616,1617,1619,1621],{"class":88,"line":402},[86,1618,1559],{"class":1220},[86,1620,1225],{"class":1224},[86,1622,1237],{"class":1220},[86,1624,1625,1627,1629],{"class":88,"line":585},[86,1626,1290],{"class":1220},[86,1628,31],{"class":1224},[86,1630,1237],{"class":1220},[20,1632,1633,1636],{},[29,1634,1635],{},"type"," 有三种常用取值：",[97,1638,1639,1651],{},[100,1640,1641],{},[103,1642,1643,1645,1648],{},[106,1644,1635],{"align":108},[106,1646,1647],{"align":108},"对应 C++ 基类",[106,1649,1650],{"align":108},"适用对象",[117,1652,1653,1668,1683],{},[103,1654,1655,1660,1665],{},[122,1656,1657],{"align":108},[29,1658,1659],{},"system",[122,1661,1662],{"align":108},[29,1663,1664],{},"hardware_interface::SystemInterface",[122,1666,1667],{"align":108},"多关节机器人、底盘、机械臂",[103,1669,1670,1675,1680],{},[122,1671,1672],{"align":108},[29,1673,1674],{},"sensor",[122,1676,1677],{"align":108},[29,1678,1679],{},"hardware_interface::SensorInterface",[122,1681,1682],{"align":108},"只读传感器",[103,1684,1685,1690,1695],{},[122,1686,1687],{"align":108},[29,1688,1689],{},"actuator",[122,1691,1692],{"align":108},[29,1693,1694],{},"hardware_interface::ActuatorInterface",[122,1696,1697],{"align":108},"单个执行器",[15,1699,1701],{"id":1700},"使用-mock_components-测试","使用 mock_components 测试",[20,1703,1704,1705,1707],{},"Jazzy 的 ",[29,1706,177],{}," 包提供了一个测试用 mock 硬件插件：",[77,1709,1711],{"className":79,"code":1710,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/hardware_interface/mock_components_plugin_description.xml\n",[29,1712,1713],{"__ignoreMap":82},[86,1714,1715],{"class":88,"line":89},[86,1716,1710],{"class":92},[20,1718,1719],{},"插件名是：",[77,1721,1723],{"className":79,"code":1722,"language":81,"meta":82,"style":82},"mock_components/GenericSystem\n",[29,1724,1725],{"__ignoreMap":82},[86,1726,1727],{"class":88,"line":89},[86,1728,1722],{"class":92},[20,1730,1731],{},"可以用它先验证控制器配置，而不用立即接真实硬件。",[20,1733,1734],{},"差速底盘示例：",[77,1736,1738],{"className":1211,"code":1737,"language":1213,"meta":82,"style":82},"\u003Cros2_control name=\"DiffBotSystem\" type=\"system\">\n  \u003Chardware>\n    \u003Cplugin>mock_components/GenericSystem\u003C/plugin>\n  \u003C/hardware>\n\n  \u003Cjoint name=\"left_wheel_joint\">\n    \u003Ccommand_interface name=\"velocity\"/>\n    \u003Cstate_interface name=\"position\"/>\n    \u003Cstate_interface name=\"velocity\"/>\n  \u003C/joint>\n\n  \u003Cjoint name=\"right_wheel_joint\">\n    \u003Ccommand_interface name=\"velocity\"/>\n    \u003Cstate_interface name=\"position\"/>\n    \u003Cstate_interface name=\"velocity\"/>\n  \u003C/joint>\n\u003C/ros2_control>\n",[29,1739,1740,1761,1769,1782,1790,1794,1808,1822,1836,1850,1858,1862,1877,1892,1907,1922,1931],{"__ignoreMap":82},[86,1741,1742,1744,1746,1748,1750,1753,1755,1757,1759],{"class":88,"line":89},[86,1743,1221],{"class":1220},[86,1745,31],{"class":1224},[86,1747,1228],{"class":92},[86,1749,1231],{"class":1220},[86,1751,1752],{"class":304},"\"DiffBotSystem\"",[86,1754,1523],{"class":92},[86,1756,1231],{"class":1220},[86,1758,1528],{"class":304},[86,1760,1237],{"class":1220},[86,1762,1763,1765,1767],{"class":88,"line":311},[86,1764,1242],{"class":1220},[86,1766,1537],{"class":1224},[86,1768,1237],{"class":1220},[86,1770,1771,1773,1775,1778,1780],{"class":88,"line":354},[86,1772,1544],{"class":1220},[86,1774,1547],{"class":1224},[86,1776,1777],{"class":1220},">mock_components/GenericSystem\u003C/",[86,1779,1547],{"class":1224},[86,1781,1237],{"class":1220},[86,1783,1784,1786,1788],{"class":88,"line":362},[86,1785,1559],{"class":1220},[86,1787,1537],{"class":1224},[86,1789,1237],{"class":1220},[86,1791,1792],{"class":88,"line":370},[86,1793,1569],{"emptyLinePlaceholder":1568},[86,1795,1796,1798,1800,1802,1804,1806],{"class":88,"line":378},[86,1797,1242],{"class":1220},[86,1799,1225],{"class":1224},[86,1801,1228],{"class":92},[86,1803,1231],{"class":1220},[86,1805,1234],{"class":304},[86,1807,1237],{"class":1220},[86,1809,1810,1812,1814,1816,1818,1820],{"class":88,"line":386},[86,1811,1544],{"class":1220},[86,1813,1245],{"class":1224},[86,1815,1228],{"class":92},[86,1817,1231],{"class":1220},[86,1819,1252],{"class":304},[86,1821,1255],{"class":1220},[86,1823,1824,1826,1828,1830,1832,1834],{"class":88,"line":394},[86,1825,1544],{"class":1220},[86,1827,1262],{"class":1224},[86,1829,1228],{"class":92},[86,1831,1231],{"class":1220},[86,1833,1269],{"class":304},[86,1835,1255],{"class":1220},[86,1837,1838,1840,1842,1844,1846,1848],{"class":88,"line":402},[86,1839,1544],{"class":1220},[86,1841,1262],{"class":1224},[86,1843,1228],{"class":92},[86,1845,1231],{"class":1220},[86,1847,1252],{"class":304},[86,1849,1255],{"class":1220},[86,1851,1852,1854,1856],{"class":88,"line":585},[86,1853,1559],{"class":1220},[86,1855,1225],{"class":1224},[86,1857,1237],{"class":1220},[86,1859,1860],{"class":88,"line":591},[86,1861,1569],{"emptyLinePlaceholder":1568},[86,1863,1864,1866,1868,1870,1872,1875],{"class":88,"line":597},[86,1865,1242],{"class":1220},[86,1867,1225],{"class":1224},[86,1869,1228],{"class":92},[86,1871,1231],{"class":1220},[86,1873,1874],{"class":304},"\"right_wheel_joint\"",[86,1876,1237],{"class":1220},[86,1878,1880,1882,1884,1886,1888,1890],{"class":88,"line":1879},13,[86,1881,1544],{"class":1220},[86,1883,1245],{"class":1224},[86,1885,1228],{"class":92},[86,1887,1231],{"class":1220},[86,1889,1252],{"class":304},[86,1891,1255],{"class":1220},[86,1893,1895,1897,1899,1901,1903,1905],{"class":88,"line":1894},14,[86,1896,1544],{"class":1220},[86,1898,1262],{"class":1224},[86,1900,1228],{"class":92},[86,1902,1231],{"class":1220},[86,1904,1269],{"class":304},[86,1906,1255],{"class":1220},[86,1908,1910,1912,1914,1916,1918,1920],{"class":88,"line":1909},15,[86,1911,1544],{"class":1220},[86,1913,1262],{"class":1224},[86,1915,1228],{"class":92},[86,1917,1231],{"class":1220},[86,1919,1252],{"class":304},[86,1921,1255],{"class":1220},[86,1923,1925,1927,1929],{"class":88,"line":1924},16,[86,1926,1559],{"class":1220},[86,1928,1225],{"class":1224},[86,1930,1237],{"class":1220},[86,1932,1934,1936,1938],{"class":88,"line":1933},17,[86,1935,1290],{"class":1220},[86,1937,31],{"class":1224},[86,1939,1237],{"class":1220},[20,1941,1942],{},"注意三点：",[34,1944,1945,1954,1957],{},[37,1946,1947,643,1950,1953],{},[29,1948,1949],{},"left_wheel_joint",[29,1951,1952],{},"right_wheel_joint"," 必须是 URDF 中真实存在的关节名。",[37,1955,1956],{},"控制器 YAML 里的关节名必须和 URDF 完全一致。",[37,1958,1959,1961,1962,1964,1965,1967],{},[29,1960,669],{}," 输出轮子 ",[29,1963,655],{}," 命令，所以轮关节必须有 ",[29,1966,655],{}," command interface。",[15,1969,1971,1974],{"id":1970},"param-传硬件参数",[29,1972,1973],{},"\u003Cparam>"," 传硬件参数",[20,1976,1977,1978,1981],{},"真实硬件通常需要串口名、波特率、CAN 设备名、减速比等参数，可以写在 ",[29,1979,1980],{},"\u003Chardware>"," 中：",[77,1983,1985],{"className":1211,"code":1984,"language":1213,"meta":82,"style":82},"\u003Chardware>\n  \u003Cplugin>my_robot_hardware/MyRobotSystem\u003C/plugin>\n  \u003Cparam name=\"serial_port\">/dev/ttyUSB0\u003C/param>\n  \u003Cparam name=\"baud_rate\">115200\u003C/param>\n  \u003Cparam name=\"gear_ratio\">30.0\u003C/param>\n\u003C/hardware>\n",[29,1986,1987,1995,2008,2029,2049,2069],{"__ignoreMap":82},[86,1988,1989,1991,1993],{"class":88,"line":89},[86,1990,1221],{"class":1220},[86,1992,1537],{"class":1224},[86,1994,1237],{"class":1220},[86,1996,1997,1999,2001,2004,2006],{"class":88,"line":311},[86,1998,1242],{"class":1220},[86,2000,1547],{"class":1224},[86,2002,2003],{"class":1220},">my_robot_hardware/MyRobotSystem\u003C/",[86,2005,1547],{"class":1224},[86,2007,1237],{"class":1220},[86,2009,2010,2012,2015,2017,2019,2022,2025,2027],{"class":88,"line":354},[86,2011,1242],{"class":1220},[86,2013,2014],{"class":1224},"param",[86,2016,1228],{"class":92},[86,2018,1231],{"class":1220},[86,2020,2021],{"class":304},"\"serial_port\"",[86,2023,2024],{"class":1220},">/dev/ttyUSB0\u003C/",[86,2026,2014],{"class":1224},[86,2028,1237],{"class":1220},[86,2030,2031,2033,2035,2037,2039,2042,2045,2047],{"class":88,"line":362},[86,2032,1242],{"class":1220},[86,2034,2014],{"class":1224},[86,2036,1228],{"class":92},[86,2038,1231],{"class":1220},[86,2040,2041],{"class":304},"\"baud_rate\"",[86,2043,2044],{"class":1220},">115200\u003C/",[86,2046,2014],{"class":1224},[86,2048,1237],{"class":1220},[86,2050,2051,2053,2055,2057,2059,2062,2065,2067],{"class":88,"line":370},[86,2052,1242],{"class":1220},[86,2054,2014],{"class":1224},[86,2056,1228],{"class":92},[86,2058,1231],{"class":1220},[86,2060,2061],{"class":304},"\"gear_ratio\"",[86,2063,2064],{"class":1220},">30.0\u003C/",[86,2066,2014],{"class":1224},[86,2068,1237],{"class":1220},[86,2070,2071,2073,2075],{"class":88,"line":378},[86,2072,1290],{"class":1220},[86,2074,1537],{"class":1224},[86,2076,1237],{"class":1220},[20,2078,2079,2080,2083,2084,2087],{},"在自定义硬件接口的 ",[29,2081,2082],{},"on_init(const hardware_interface::HardwareInfo & info)"," 中，可以通过 ",[29,2085,2086],{},"info.hardware_parameters"," 读取这些参数。",[10,2089,2091],{"id":2090},"controller-manager","Controller Manager",[15,2093,2095],{"id":2094},"controller_manager-配置","controller_manager 配置",[20,2097,2098,2100],{},[29,2099,71],{}," 是 ros2_control 的核心节点。它的默认可执行文件是：",[77,2102,2104],{"className":79,"code":2103,"language":81,"meta":82,"style":82},"ros2 run controller_manager ros2_control_node\n",[29,2105,2106],{"__ignoreMap":82},[86,2107,2108,2110,2112,2115],{"class":88,"line":89},[86,2109,452],{"class":92},[86,2111,1063],{"class":304},[86,2113,2114],{"class":304}," controller_manager",[86,2116,1034],{"class":304},[20,2118,2119,2120,2123],{},"在实际工程中通常通过 launch 启动。控制器配置一般写在 YAML 文件中，例如 ",[29,2121,2122],{},"config/controllers.yaml","：",[77,2125,2129],{"className":2126,"code":2127,"language":2128,"meta":82,"style":82},"language-yaml shiki shiki-themes github-light github-dark","controller_manager:\n  ros__parameters:\n    update_rate: 100\n\n    joint_state_broadcaster:\n      type: joint_state_broadcaster/JointStateBroadcaster\n\n    diff_drive_controller:\n      type: diff_drive_controller/DiffDriveController\n","yaml",[29,2130,2131,2138,2145,2156,2160,2167,2177,2181,2188],{"__ignoreMap":82},[86,2132,2133,2135],{"class":88,"line":89},[86,2134,71],{"class":1224},[86,2136,2137],{"class":1220},":\n",[86,2139,2140,2143],{"class":88,"line":311},[86,2141,2142],{"class":1224},"  ros__parameters",[86,2144,2137],{"class":1220},[86,2146,2147,2150,2153],{"class":88,"line":354},[86,2148,2149],{"class":1224},"    update_rate",[86,2151,2152],{"class":1220},": ",[86,2154,2155],{"class":343},"100\n",[86,2157,2158],{"class":88,"line":362},[86,2159,1569],{"emptyLinePlaceholder":1568},[86,2161,2162,2165],{"class":88,"line":370},[86,2163,2164],{"class":1224},"    joint_state_broadcaster",[86,2166,2137],{"class":1220},[86,2168,2169,2172,2174],{"class":88,"line":378},[86,2170,2171],{"class":1224},"      type",[86,2173,2152],{"class":1220},[86,2175,2176],{"class":304},"joint_state_broadcaster/JointStateBroadcaster\n",[86,2178,2179],{"class":88,"line":386},[86,2180,1569],{"emptyLinePlaceholder":1568},[86,2182,2183,2186],{"class":88,"line":394},[86,2184,2185],{"class":1224},"    diff_drive_controller",[86,2187,2137],{"class":1220},[86,2189,2190,2192,2194],{"class":88,"line":402},[86,2191,2171],{"class":1224},[86,2193,2152],{"class":1220},[86,2195,2196],{"class":304},"diff_drive_controller/DiffDriveController\n",[20,2198,2199,2201],{},[29,2200,71],{}," 常用参数：",[97,2203,2204,2216],{},[100,2205,2206],{},[103,2207,2208,2211,2214],{},[106,2209,2210],{"align":108},"参数",[106,2212,2213],{"align":108},"默认值",[106,2215,115],{"align":108},[117,2217,2218,2233,2248,2263,2278],{},[103,2219,2220,2225,2230],{},[122,2221,2222],{"align":108},[29,2223,2224],{},"update_rate",[122,2226,2227],{"align":108},[29,2228,2229],{},"100",[122,2231,2232],{"align":108},"控制循环频率，单位 Hz",[103,2234,2235,2240,2245],{},[122,2236,2237],{"align":108},[29,2238,2239],{},"enforce_command_limits",[122,2241,2242],{"align":108},[29,2243,2244],{},"false",[122,2246,2247],{"align":108},"是否根据机器人描述中的关节限制约束命令",[103,2249,2250,2255,2260],{},[122,2251,2252],{"align":108},[29,2253,2254],{},"handle_exceptions",[122,2256,2257],{"align":108},[29,2258,2259],{},"true",[122,2261,2262],{"align":108},"是否捕获控制器和硬件组件操作中的异常",[103,2264,2265,2270,2275],{},[122,2266,2267],{"align":108},[29,2268,2269],{},"hardware_components_initial_state.unconfigured",[122,2271,2272],{"align":108},[29,2273,2274],{},"[]",[122,2276,2277],{"align":108},"指定启动后停留在 unconfigured 的硬件组件",[103,2279,2280,2285,2289],{},[122,2281,2282],{"align":108},[29,2283,2284],{},"hardware_components_initial_state.inactive",[122,2286,2287],{"align":108},[29,2288,2274],{},[122,2290,2291],{"align":108},"指定启动后停留在 inactive 的硬件组件",[15,2293,2295],{"id":2294},"launch-启动方式","launch 启动方式",[20,2297,2298],{},"典型 launch 写法如下：",[77,2300,2304],{"className":2301,"code":2302,"language":2303,"meta":82,"style":82},"language-python shiki shiki-themes github-light github-dark","from launch import LaunchDescription\nfrom launch_ros.actions import Node\n\n\ndef generate_launch_description():\n    control_node = Node(\n        package=\"controller_manager\",\n        executable=\"ros2_control_node\",\n        parameters=[\n            {\"robot_description\": \"\u003C这里传入完整 URDF 字符串>\"},\n            \"config/controllers.yaml\",\n        ],\n        output=\"both\",\n    )\n\n    return LaunchDescription([control_node])\n","python",[29,2305,2306,2320,2332,2336,2340,2351,2361,2375,2387,2397,2413,2420,2425,2437,2442,2446],{"__ignoreMap":82},[86,2307,2308,2311,2314,2317],{"class":88,"line":89},[86,2309,2310],{"class":461},"from",[86,2312,2313],{"class":1220}," launch ",[86,2315,2316],{"class":461},"import",[86,2318,2319],{"class":1220}," LaunchDescription\n",[86,2321,2322,2324,2327,2329],{"class":88,"line":311},[86,2323,2310],{"class":461},[86,2325,2326],{"class":1220}," launch_ros.actions ",[86,2328,2316],{"class":461},[86,2330,2331],{"class":1220}," Node\n",[86,2333,2334],{"class":88,"line":354},[86,2335,1569],{"emptyLinePlaceholder":1568},[86,2337,2338],{"class":88,"line":362},[86,2339,1569],{"emptyLinePlaceholder":1568},[86,2341,2342,2345,2348],{"class":88,"line":370},[86,2343,2344],{"class":461},"def",[86,2346,2347],{"class":92}," generate_launch_description",[86,2349,2350],{"class":1220},"():\n",[86,2352,2353,2356,2358],{"class":88,"line":378},[86,2354,2355],{"class":1220},"    control_node ",[86,2357,1231],{"class":461},[86,2359,2360],{"class":1220}," Node(\n",[86,2362,2363,2367,2369,2372],{"class":88,"line":386},[86,2364,2366],{"class":2365},"s4XuR","        package",[86,2368,1231],{"class":461},[86,2370,2371],{"class":304},"\"controller_manager\"",[86,2373,2374],{"class":1220},",\n",[86,2376,2377,2380,2382,2385],{"class":88,"line":394},[86,2378,2379],{"class":2365},"        executable",[86,2381,1231],{"class":461},[86,2383,2384],{"class":304},"\"ros2_control_node\"",[86,2386,2374],{"class":1220},[86,2388,2389,2392,2394],{"class":88,"line":402},[86,2390,2391],{"class":2365},"        parameters",[86,2393,1231],{"class":461},[86,2395,2396],{"class":1220},"[\n",[86,2398,2399,2402,2405,2407,2410],{"class":88,"line":585},[86,2400,2401],{"class":1220},"            {",[86,2403,2404],{"class":304},"\"robot_description\"",[86,2406,2152],{"class":1220},[86,2408,2409],{"class":304},"\"\u003C这里传入完整 URDF 字符串>\"",[86,2411,2412],{"class":1220},"},\n",[86,2414,2415,2418],{"class":88,"line":591},[86,2416,2417],{"class":304},"            \"config/controllers.yaml\"",[86,2419,2374],{"class":1220},[86,2421,2422],{"class":88,"line":597},[86,2423,2424],{"class":1220},"        ],\n",[86,2426,2427,2430,2432,2435],{"class":88,"line":1879},[86,2428,2429],{"class":2365},"        output",[86,2431,1231],{"class":461},[86,2433,2434],{"class":304},"\"both\"",[86,2436,2374],{"class":1220},[86,2438,2439],{"class":88,"line":1894},[86,2440,2441],{"class":1220},"    )\n",[86,2443,2444],{"class":88,"line":1909},[86,2445,1569],{"emptyLinePlaceholder":1568},[86,2447,2448,2451],{"class":88,"line":1924},[86,2449,2450],{"class":461},"    return",[86,2452,2453],{"class":1220}," LaunchDescription([control_node])\n",[20,2455,2456,2457,2460],{},"实际项目中，",[29,2458,2459],{},"robot_description"," 通常由 xacro 生成。一般流程是：",[77,2462,2465],{"className":2463,"code":2464,"language":1321,"meta":82},[1319],"xacro 文件 -> robot_description 参数 -> robot_state_publisher\n                                  -> ros2_control_node\n",[29,2466,2464],{"__ignoreMap":82},[10,2468,2469],{"id":2469},"控制器加载与调试命令",[15,2471,154],{"id":154},[20,2473,2474,2476],{},[29,2475,154],{}," 用于加载控制器。默认会完成加载、配置、激活三步：",[77,2478,2480],{"className":79,"code":2479,"language":81,"meta":82,"style":82},"ros2 run controller_manager spawner joint_state_broadcaster\nros2 run controller_manager spawner diff_drive_controller\n",[29,2481,2482,2496],{"__ignoreMap":82},[86,2483,2484,2486,2488,2490,2493],{"class":88,"line":89},[86,2485,452],{"class":92},[86,2487,1063],{"class":304},[86,2489,2114],{"class":304},[86,2491,2492],{"class":304}," spawner",[86,2494,2495],{"class":304}," joint_state_broadcaster\n",[86,2497,2498,2500,2502,2504,2506],{"class":88,"line":311},[86,2499,452],{"class":92},[86,2501,1063],{"class":304},[86,2503,2114],{"class":304},[86,2505,2492],{"class":304},[86,2507,2508],{"class":304}," diff_drive_controller\n",[20,2510,2511,2512,2515,2516,2519],{},"如果 controller manager 名字不是默认的 ",[29,2513,2514],{},"/controller_manager","，可以用 ",[29,2517,2518],{},"-c"," 指定：",[77,2521,2523],{"className":79,"code":2522,"language":81,"meta":82,"style":82},"ros2 run controller_manager spawner joint_state_broadcaster -c /controller_manager\n",[29,2524,2525],{"__ignoreMap":82},[86,2526,2527,2529,2531,2533,2535,2538,2541],{"class":88,"line":89},[86,2528,452],{"class":92},[86,2530,1063],{"class":304},[86,2532,2114],{"class":304},[86,2534,2492],{"class":304},[86,2536,2537],{"class":304}," joint_state_broadcaster",[86,2539,2540],{"class":343}," -c",[86,2542,2543],{"class":304}," /controller_manager\n",[20,2545,2546],{},"常用选项如下：",[97,2548,2549,2558],{},[100,2550,2551],{},[103,2552,2553,2556],{},[106,2554,2555],{"align":108},"选项",[106,2557,1341],{"align":108},[117,2559,2560,2573,2586,2595,2604,2614,2624,2634],{},[103,2561,2562,2570],{},[122,2563,2564,2566,2567],{"align":108},[29,2565,2518],{}," / ",[29,2568,2569],{},"--controller-manager",[122,2571,2572],{"align":108},"指定 controller manager 节点名",[103,2574,2575,2583],{},[122,2576,2577,2566,2580],{"align":108},[29,2578,2579],{},"-p",[29,2581,2582],{},"--param-file",[122,2584,2585],{"align":108},"给控制器加载参数文件",[103,2587,2588,2592],{},[122,2589,2590],{"align":108},[29,2591,1482],{},[122,2593,2594],{"align":108},"只加载，不配置、不激活",[103,2596,2597,2601],{},[122,2598,2599],{"align":108},[29,2600,1485],{},[122,2602,2603],{"align":108},"加载并配置，但不激活",[103,2605,2606,2611],{},[122,2607,2608],{"align":108},[29,2609,2610],{},"--activate-as-group",[122,2612,2613],{"align":108},"一组控制器一起激活，适合链式控制器",[103,2615,2616,2621],{},[122,2617,2618],{"align":108},[29,2619,2620],{},"--controller-manager-timeout",[122,2622,2623],{"align":108},"等待 controller manager 服务的超时时间",[103,2625,2626,2631],{},[122,2627,2628],{"align":108},[29,2629,2630],{},"--switch-timeout",[122,2632,2633],{"align":108},"等待控制器切换完成的超时时间",[103,2635,2636,2641],{},[122,2637,2638],{"align":108},[29,2639,2640],{},"--unload-on-kill",[122,2642,2643],{"align":108},"spawner 退出时卸载控制器",[15,2645,2646],{"id":2646},"hardware_spawner",[20,2648,2649,2651],{},[29,2650,2646],{}," 用于配置或激活硬件组件：",[77,2653,2655],{"className":79,"code":2654,"language":81,"meta":82,"style":82},"ros2 run controller_manager hardware_spawner DiffBotSystem --configure\nros2 run controller_manager hardware_spawner DiffBotSystem --activate\n",[29,2656,2657,2674],{"__ignoreMap":82},[86,2658,2659,2661,2663,2665,2668,2671],{"class":88,"line":89},[86,2660,452],{"class":92},[86,2662,1063],{"class":304},[86,2664,2114],{"class":304},[86,2666,2667],{"class":304}," hardware_spawner",[86,2669,2670],{"class":304}," DiffBotSystem",[86,2672,2673],{"class":343}," --configure\n",[86,2675,2676,2678,2680,2682,2684,2686],{"class":88,"line":311},[86,2677,452],{"class":92},[86,2679,1063],{"class":304},[86,2681,2114],{"class":304},[86,2683,2667],{"class":304},[86,2685,2670],{"class":304},[86,2687,2688],{"class":343}," --activate\n",[20,2690,2546],{},[97,2692,2693,2701],{},[100,2694,2695],{},[103,2696,2697,2699],{},[106,2698,2555],{"align":108},[106,2700,1341],{"align":108},[117,2702,2703,2713,2723],{},[103,2704,2705,2710],{},[122,2706,2707],{"align":108},[29,2708,2709],{},"--configure",[122,2711,2712],{"align":108},"把硬件组件配置到 inactive",[103,2714,2715,2720],{},[122,2716,2717],{"align":108},[29,2718,2719],{},"--activate",[122,2721,2722],{"align":108},"配置并激活硬件组件",[103,2724,2725,2731],{},[122,2726,2727,2566,2729],{"align":108},[29,2728,2518],{},[29,2730,2569],{},[122,2732,2572],{"align":108},[15,2734,2736],{"id":2735},"ros2-control-命令","ros2 control 命令",[20,2738,2739,2741,2742,2745],{},[29,2740,207],{}," 提供了 ",[29,2743,2744],{},"ros2 control"," 命令。",[20,2747,2748],{},"查看控制器：",[77,2750,2752],{"className":79,"code":2751,"language":81,"meta":82,"style":82},"ros2 control list_controllers\n",[29,2753,2754],{"__ignoreMap":82},[86,2755,2756,2758,2761],{"class":88,"line":89},[86,2757,452],{"class":92},[86,2759,2760],{"class":304}," control",[86,2762,2763],{"class":304}," list_controllers\n",[20,2765,2766],{},"查看控制器详细接口：",[77,2768,2770],{"className":79,"code":2769,"language":81,"meta":82,"style":82},"ros2 control list_controllers --verbose\n",[29,2771,2772],{"__ignoreMap":82},[86,2773,2774,2776,2778,2781],{"class":88,"line":89},[86,2775,452],{"class":92},[86,2777,2760],{"class":304},[86,2779,2780],{"class":304}," list_controllers",[86,2782,2783],{"class":343}," --verbose\n",[20,2785,2786],{},"查看硬件组件：",[77,2788,2790],{"className":79,"code":2789,"language":81,"meta":82,"style":82},"ros2 control list_hardware_components\n",[29,2791,2792],{"__ignoreMap":82},[86,2793,2794,2796,2798],{"class":88,"line":89},[86,2795,452],{"class":92},[86,2797,2760],{"class":304},[86,2799,2800],{"class":304}," list_hardware_components\n",[20,2802,2803],{},"查看硬件接口：",[77,2805,2807],{"className":79,"code":2806,"language":81,"meta":82,"style":82},"ros2 control list_hardware_interfaces\nros2 control list_hardware_interfaces --verbose\n",[29,2808,2809,2818],{"__ignoreMap":82},[86,2810,2811,2813,2815],{"class":88,"line":89},[86,2812,452],{"class":92},[86,2814,2760],{"class":304},[86,2816,2817],{"class":304}," list_hardware_interfaces\n",[86,2819,2820,2822,2824,2827],{"class":88,"line":311},[86,2821,452],{"class":92},[86,2823,2760],{"class":304},[86,2825,2826],{"class":304}," list_hardware_interfaces",[86,2828,2783],{"class":343},[20,2830,2831],{},"查看可用控制器类型：",[77,2833,2835],{"className":79,"code":2834,"language":81,"meta":82,"style":82},"ros2 control list_controller_types\n",[29,2836,2837],{"__ignoreMap":82},[86,2838,2839,2841,2843],{"class":88,"line":89},[86,2840,452],{"class":92},[86,2842,2760],{"class":304},[86,2844,2845],{"class":304}," list_controller_types\n",[20,2847,2848],{},"切换控制器：",[77,2850,2852],{"className":79,"code":2851,"language":81,"meta":82,"style":82},"ros2 control switch_controllers --activate diff_drive_controller --deactivate old_controller\n",[29,2853,2854],{"__ignoreMap":82},[86,2855,2856,2858,2860,2863,2866,2869,2872],{"class":88,"line":89},[86,2857,452],{"class":92},[86,2859,2760],{"class":304},[86,2861,2862],{"class":304}," switch_controllers",[86,2864,2865],{"class":343}," --activate",[86,2867,2868],{"class":304}," diff_drive_controller",[86,2870,2871],{"class":343}," --deactivate",[86,2873,2874],{"class":304}," old_controller\n",[20,2876,2877],{},"卸载控制器：",[77,2879,2881],{"className":79,"code":2880,"language":81,"meta":82,"style":82},"ros2 control unload_controller diff_drive_controller\n",[29,2882,2883],{"__ignoreMap":82},[86,2884,2885,2887,2889,2892],{"class":88,"line":89},[86,2886,452],{"class":92},[86,2888,2760],{"class":304},[86,2890,2891],{"class":304}," unload_controller",[86,2893,2508],{"class":304},[20,2895,2896],{},"生成链式控制器图：",[77,2898,2900],{"className":79,"code":2899,"language":81,"meta":82,"style":82},"ros2 control view_controller_chains\n",[29,2901,2902],{"__ignoreMap":82},[86,2903,2904,2906,2908],{"class":88,"line":89},[86,2905,452],{"class":92},[86,2907,2760],{"class":304},[86,2909,2910],{"class":304}," view_controller_chains\n",[10,2912,2913],{"id":2913},"常用控制器",[15,2915,2917],{"id":2916},"joint-state-broadcaster","Joint State Broadcaster",[20,2919,2920,2922],{},[29,2921,633],{}," 用于发布机器人关节状态。插件描述文件是：",[77,2924,2926],{"className":79,"code":2925,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/joint_state_broadcaster/joint_state_plugin.xml\n",[29,2927,2928],{"__ignoreMap":82},[86,2929,2930],{"class":88,"line":89},[86,2931,2925],{"class":92},[20,2933,1719],{},[77,2935,2936],{"className":79,"code":2176,"language":81,"meta":82,"style":82},[29,2937,2938],{"__ignoreMap":82},[86,2939,2940],{"class":88,"line":89},[86,2941,2176],{"class":92},[20,2943,2944],{},"它会读取 state interfaces，并发布：",[97,2946,2947,2959],{},[100,2948,2949],{},[103,2950,2951,2954,2957],{},[106,2952,2953],{"align":108},"话题",[106,2955,2956],{"align":108},"类型",[106,2958,115],{"align":108},[117,2960,2961,2979],{},[103,2962,2963,2967,2972],{},[122,2964,2965],{"align":108},[29,2966,642],{},[122,2968,2969],{"align":108},[29,2970,2971],{},"sensor_msgs/msg/JointState",[122,2973,2974,2975,2978],{"align":108},"标准关节状态，常被 ",[29,2976,2977],{},"robot_state_publisher"," 和 RViz 使用",[103,2980,2981,2985,2990],{},[122,2982,2983],{"align":108},[29,2984,646],{},[122,2986,2987],{"align":108},[29,2988,2989],{},"control_msgs/msg/DynamicJointState",[122,2991,2992],{"align":108},"发布所有可用 state interfaces，包括自定义接口",[20,2994,2995],{},"常用配置：",[77,2997,2999],{"className":2126,"code":2998,"language":2128,"meta":82,"style":82},"joint_state_broadcaster:\n  ros__parameters:\n    use_local_topics: false\n    use_urdf_to_filter: true\n    publish_dynamic_joint_states: true\n",[29,3000,3001,3007,3013,3023,3033],{"__ignoreMap":82},[86,3002,3003,3005],{"class":88,"line":89},[86,3004,633],{"class":1224},[86,3006,2137],{"class":1220},[86,3008,3009,3011],{"class":88,"line":311},[86,3010,2142],{"class":1224},[86,3012,2137],{"class":1220},[86,3014,3015,3018,3020],{"class":88,"line":354},[86,3016,3017],{"class":1224},"    use_local_topics",[86,3019,2152],{"class":1220},[86,3021,3022],{"class":343},"false\n",[86,3024,3025,3028,3030],{"class":88,"line":362},[86,3026,3027],{"class":1224},"    use_urdf_to_filter",[86,3029,2152],{"class":1220},[86,3031,3032],{"class":343},"true\n",[86,3034,3035,3038,3040],{"class":88,"line":370},[86,3036,3037],{"class":1224},"    publish_dynamic_joint_states",[86,3039,2152],{"class":1220},[86,3041,3032],{"class":343},[20,3043,3044,3045,3047],{},"建议几乎所有机器人都启动 ",[29,3046,633],{},"，否则 RViz 中机器人模型通常不会动。",[15,3049,3051],{"id":3050},"diff-drive-controller","Diff Drive Controller",[20,3053,3054,3056],{},[29,3055,669],{}," 用于差速移动机器人。插件描述文件是：",[77,3058,3060],{"className":79,"code":3059,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/diff_drive_controller/diff_drive_plugin.xml\n",[29,3061,3062],{"__ignoreMap":82},[86,3063,3064],{"class":88,"line":89},[86,3065,3059],{"class":92},[20,3067,1719],{},[77,3069,3070],{"className":79,"code":2196,"language":81,"meta":82,"style":82},[29,3071,3072],{"__ignoreMap":82},[86,3073,3074],{"class":88,"line":89},[86,3075,2196],{"class":92},[20,3077,3078],{},"它的作用是：",[34,3080,3081,3084,3087,3093,3096],{},[37,3082,3083],{},"订阅速度命令；",[37,3085,3086],{},"根据差速运动学计算左右轮目标速度；",[37,3088,3089,3090,3092],{},"向左右轮关节的 ",[29,3091,655],{}," command interface 写命令；",[37,3094,3095],{},"根据左右轮反馈计算里程计；",[37,3097,3098,3099,3102],{},"可选发布 ",[29,3100,3101],{},"odom -> base_link"," TF。",[20,3104,3105],{},"订阅话题：",[97,3107,3108,3118],{},[100,3109,3110],{},[103,3111,3112,3114,3116],{},[106,3113,2953],{"align":108},[106,3115,2956],{"align":108},[106,3117,115],{"align":108},[117,3119,3120],{},[103,3121,3122,3127,3131],{},[122,3123,3124],{"align":108},[29,3125,3126],{},"~/cmd_vel",[122,3128,3129],{"align":108},[29,3130,680],{},[122,3132,3133,3134,643,3137],{"align":108},"速度命令，使用 ",[29,3135,3136],{},"linear.x",[29,3138,3139],{},"angular.z",[20,3141,3142,3143,3145],{},"如果控制器名是 ",[29,3144,669],{},"，默认命令话题一般是：",[77,3147,3149],{"className":79,"code":3148,"language":81,"meta":82,"style":82},"/diff_drive_controller/cmd_vel\n",[29,3150,3151],{"__ignoreMap":82},[86,3152,3153],{"class":88,"line":89},[86,3154,3148],{"class":92},[20,3156,3157],{},"发布话题：",[97,3159,3160,3170],{},[100,3161,3162],{},[103,3163,3164,3166,3168],{},[106,3165,2953],{"align":108},[106,3167,2956],{"align":108},[106,3169,115],{"align":108},[117,3171,3172,3187,3206],{},[103,3173,3174,3179,3184],{},[122,3175,3176],{"align":108},[29,3177,3178],{},"~/odom",[122,3180,3181],{"align":108},[29,3182,3183],{},"nav_msgs/msg/Odometry",[122,3185,3186],{"align":108},"里程计",[103,3188,3189,3194,3199],{},[122,3190,3191],{"align":108},[29,3192,3193],{},"/tf",[122,3195,3196],{"align":108},[29,3197,3198],{},"tf2_msgs/msg/TFMessage",[122,3200,3201,3202,3205],{"align":108},"当 ",[29,3203,3204],{},"enable_odom_tf=true"," 时发布",[103,3207,3208,3213,3217],{},[122,3209,3210],{"align":108},[29,3211,3212],{},"~/cmd_vel_out",[122,3214,3215],{"align":108},[29,3216,680],{},[122,3218,3201,3219,3222],{"align":108},[29,3220,3221],{},"publish_limited_velocity=true"," 时发布限幅后的速度",[20,3224,3225],{},"最小配置：",[77,3227,3229],{"className":2126,"code":3228,"language":2128,"meta":82,"style":82},"diff_drive_controller:\n  ros__parameters:\n    left_wheel_names: [\"left_wheel_joint\"]\n    right_wheel_names: [\"right_wheel_joint\"]\n    wheel_separation: 0.40\n    wheel_radius: 0.05\n    odom_frame_id: odom\n    base_frame_id: base_link\n    position_feedback: true\n    open_loop: false\n    enable_odom_tf: true\n    publish_rate: 50.0\n",[29,3230,3231,3237,3243,3256,3267,3277,3287,3297,3307,3316,3325,3334],{"__ignoreMap":82},[86,3232,3233,3235],{"class":88,"line":89},[86,3234,669],{"class":1224},[86,3236,2137],{"class":1220},[86,3238,3239,3241],{"class":88,"line":311},[86,3240,2142],{"class":1224},[86,3242,2137],{"class":1220},[86,3244,3245,3248,3251,3253],{"class":88,"line":354},[86,3246,3247],{"class":1224},"    left_wheel_names",[86,3249,3250],{"class":1220},": [",[86,3252,1234],{"class":304},[86,3254,3255],{"class":1220},"]\n",[86,3257,3258,3261,3263,3265],{"class":88,"line":362},[86,3259,3260],{"class":1224},"    right_wheel_names",[86,3262,3250],{"class":1220},[86,3264,1874],{"class":304},[86,3266,3255],{"class":1220},[86,3268,3269,3272,3274],{"class":88,"line":370},[86,3270,3271],{"class":1224},"    wheel_separation",[86,3273,2152],{"class":1220},[86,3275,3276],{"class":343},"0.40\n",[86,3278,3279,3282,3284],{"class":88,"line":378},[86,3280,3281],{"class":1224},"    wheel_radius",[86,3283,2152],{"class":1220},[86,3285,3286],{"class":343},"0.05\n",[86,3288,3289,3292,3294],{"class":88,"line":386},[86,3290,3291],{"class":1224},"    odom_frame_id",[86,3293,2152],{"class":1220},[86,3295,3296],{"class":304},"odom\n",[86,3298,3299,3302,3304],{"class":88,"line":394},[86,3300,3301],{"class":1224},"    base_frame_id",[86,3303,2152],{"class":1220},[86,3305,3306],{"class":304},"base_link\n",[86,3308,3309,3312,3314],{"class":88,"line":402},[86,3310,3311],{"class":1224},"    position_feedback",[86,3313,2152],{"class":1220},[86,3315,3032],{"class":343},[86,3317,3318,3321,3323],{"class":88,"line":585},[86,3319,3320],{"class":1224},"    open_loop",[86,3322,2152],{"class":1220},[86,3324,3022],{"class":343},[86,3326,3327,3330,3332],{"class":88,"line":591},[86,3328,3329],{"class":1224},"    enable_odom_tf",[86,3331,2152],{"class":1220},[86,3333,3032],{"class":343},[86,3335,3336,3339,3341],{"class":88,"line":597},[86,3337,3338],{"class":1224},"    publish_rate",[86,3340,2152],{"class":1220},[86,3342,3343],{"class":343},"50.0\n",[20,3345,3346],{},"如果只是用 mock 硬件学习，可以先用开环：",[77,3348,3350],{"className":2126,"code":3349,"language":2128,"meta":82,"style":82},"diff_drive_controller:\n  ros__parameters:\n    left_wheel_names: [\"left_wheel_joint\"]\n    right_wheel_names: [\"right_wheel_joint\"]\n    wheel_separation: 0.40\n    wheel_radius: 0.05\n    position_feedback: false\n    open_loop: true\n    enable_odom_tf: true\n",[29,3351,3352,3358,3364,3374,3384,3392,3400,3408,3416],{"__ignoreMap":82},[86,3353,3354,3356],{"class":88,"line":89},[86,3355,669],{"class":1224},[86,3357,2137],{"class":1220},[86,3359,3360,3362],{"class":88,"line":311},[86,3361,2142],{"class":1224},[86,3363,2137],{"class":1220},[86,3365,3366,3368,3370,3372],{"class":88,"line":354},[86,3367,3247],{"class":1224},[86,3369,3250],{"class":1220},[86,3371,1234],{"class":304},[86,3373,3255],{"class":1220},[86,3375,3376,3378,3380,3382],{"class":88,"line":362},[86,3377,3260],{"class":1224},[86,3379,3250],{"class":1220},[86,3381,1874],{"class":304},[86,3383,3255],{"class":1220},[86,3385,3386,3388,3390],{"class":88,"line":370},[86,3387,3271],{"class":1224},[86,3389,2152],{"class":1220},[86,3391,3276],{"class":343},[86,3393,3394,3396,3398],{"class":88,"line":378},[86,3395,3281],{"class":1224},[86,3397,2152],{"class":1220},[86,3399,3286],{"class":343},[86,3401,3402,3404,3406],{"class":88,"line":386},[86,3403,3311],{"class":1224},[86,3405,2152],{"class":1220},[86,3407,3022],{"class":343},[86,3409,3410,3412,3414],{"class":88,"line":394},[86,3411,3320],{"class":1224},[86,3413,2152],{"class":1220},[86,3415,3032],{"class":343},[86,3417,3418,3420,3422],{"class":88,"line":402},[86,3419,3329],{"class":1224},[86,3421,2152],{"class":1220},[86,3423,3032],{"class":343},[20,3425,3426],{},"发送速度命令：",[77,3428,3430],{"className":79,"code":3429,"language":81,"meta":82,"style":82},"ros2 topic pub /diff_drive_controller/cmd_vel geometry_msgs/msg/TwistStamped \"{\n  header: {frame_id: base_link},\n  twist: {\n    linear: {x: 0.2, y: 0.0, z: 0.0},\n    angular: {x: 0.0, y: 0.0, z: 0.5}\n  }\n}\"\n",[29,3431,3432,3451,3456,3461,3466,3471,3476],{"__ignoreMap":82},[86,3433,3434,3436,3439,3442,3445,3448],{"class":88,"line":89},[86,3435,452],{"class":92},[86,3437,3438],{"class":304}," topic",[86,3440,3441],{"class":304}," pub",[86,3443,3444],{"class":304}," /diff_drive_controller/cmd_vel",[86,3446,3447],{"class":304}," geometry_msgs/msg/TwistStamped",[86,3449,3450],{"class":304}," \"{\n",[86,3452,3453],{"class":88,"line":311},[86,3454,3455],{"class":304},"  header: {frame_id: base_link},\n",[86,3457,3458],{"class":88,"line":354},[86,3459,3460],{"class":304},"  twist: {\n",[86,3462,3463],{"class":88,"line":362},[86,3464,3465],{"class":304},"    linear: {x: 0.2, y: 0.0, z: 0.0},\n",[86,3467,3468],{"class":88,"line":370},[86,3469,3470],{"class":304},"    angular: {x: 0.0, y: 0.0, z: 0.5}\n",[86,3472,3473],{"class":88,"line":378},[86,3474,3475],{"class":304},"  }\n",[86,3477,3478],{"class":88,"line":386},[86,3479,3480],{"class":304},"}\"\n",[20,3482,3483,3484,3486,3487,3490,3491,3494],{},"注意：Jazzy 的 ",[29,3485,669],{}," 使用 ",[29,3488,3489],{},"TwistStamped","。如果旧教程使用 ",[29,3492,3493],{},"geometry_msgs/msg/Twist","，在 Jazzy 中可能无法直接工作。",[15,3496,3498],{"id":3497},"joint-trajectory-controller","Joint Trajectory Controller",[20,3500,3501,3503],{},[29,3502,694],{}," 常用于机械臂、云台、多关节机器人。插件描述文件是：",[77,3505,3507],{"className":79,"code":3506,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/joint_trajectory_controller/joint_trajectory_plugin.xml\n",[29,3508,3509],{"__ignoreMap":82},[86,3510,3511],{"class":88,"line":89},[86,3512,3506],{"class":92},[20,3514,1719],{},[77,3516,3518],{"className":79,"code":3517,"language":81,"meta":82,"style":82},"joint_trajectory_controller/JointTrajectoryController\n",[29,3519,3520],{"__ignoreMap":82},[86,3521,3522],{"class":88,"line":89},[86,3523,3517],{"class":92},[20,3525,3526],{},"它执行的是关节空间轨迹。常见输入是：",[97,3528,3529,3540],{},[100,3530,3531],{},[103,3532,3533,3536,3538],{},[106,3534,3535],{"align":108},"输入",[106,3537,2956],{"align":108},[106,3539,115],{"align":108},[117,3541,3542,3556],{},[103,3543,3544,3549,3553],{},[122,3545,3546],{"align":108},[29,3547,3548],{},"~/joint_trajectory",[122,3550,3551],{"align":108},[29,3552,705],{},[122,3554,3555],{"align":108},"轨迹话题",[103,3557,3558,3563,3568],{},[122,3559,3560],{"align":108},[29,3561,3562],{},"~/follow_joint_trajectory",[122,3564,3565],{"align":108},[29,3566,3567],{},"control_msgs/action/FollowJointTrajectory",[122,3569,3570],{"align":108},"标准轨迹 action，MoveIt2 常用",[20,3572,3573],{},"常见配置：",[77,3575,3577],{"className":2126,"code":3576,"language":2128,"meta":82,"style":82},"arm_controller:\n  ros__parameters:\n    joints:\n      - joint1\n      - joint2\n      - joint3\n    command_interfaces:\n      - position\n    state_interfaces:\n      - position\n      - velocity\n",[29,3578,3579,3586,3592,3599,3607,3614,3621,3628,3635,3642,3648],{"__ignoreMap":82},[86,3580,3581,3584],{"class":88,"line":89},[86,3582,3583],{"class":1224},"arm_controller",[86,3585,2137],{"class":1220},[86,3587,3588,3590],{"class":88,"line":311},[86,3589,2142],{"class":1224},[86,3591,2137],{"class":1220},[86,3593,3594,3597],{"class":88,"line":354},[86,3595,3596],{"class":1224},"    joints",[86,3598,2137],{"class":1220},[86,3600,3601,3604],{"class":88,"line":362},[86,3602,3603],{"class":1220},"      - ",[86,3605,3606],{"class":304},"joint1\n",[86,3608,3609,3611],{"class":88,"line":370},[86,3610,3603],{"class":1220},[86,3612,3613],{"class":304},"joint2\n",[86,3615,3616,3618],{"class":88,"line":378},[86,3617,3603],{"class":1220},[86,3619,3620],{"class":304},"joint3\n",[86,3622,3623,3626],{"class":88,"line":386},[86,3624,3625],{"class":1224},"    command_interfaces",[86,3627,2137],{"class":1220},[86,3629,3630,3632],{"class":88,"line":394},[86,3631,3603],{"class":1220},[86,3633,3634],{"class":304},"position\n",[86,3636,3637,3640],{"class":88,"line":402},[86,3638,3639],{"class":1224},"    state_interfaces",[86,3641,2137],{"class":1220},[86,3643,3644,3646],{"class":88,"line":585},[86,3645,3603],{"class":1220},[86,3647,3634],{"class":304},[86,3649,3650,3652],{"class":88,"line":591},[86,3651,3603],{"class":1220},[86,3653,3654],{"class":304},"velocity\n",[20,3656,3657],{},"URDF 中对应 joint 至少要有：",[77,3659,3661],{"className":1211,"code":3660,"language":1213,"meta":82,"style":82},"\u003Cjoint name=\"joint1\">\n  \u003Ccommand_interface name=\"position\"/>\n  \u003Cstate_interface name=\"position\"/>\n  \u003Cstate_interface name=\"velocity\"/>\n\u003C/joint>\n",[29,3662,3663,3678,3692,3706,3720],{"__ignoreMap":82},[86,3664,3665,3667,3669,3671,3673,3676],{"class":88,"line":89},[86,3666,1221],{"class":1220},[86,3668,1225],{"class":1224},[86,3670,1228],{"class":92},[86,3672,1231],{"class":1220},[86,3674,3675],{"class":304},"\"joint1\"",[86,3677,1237],{"class":1220},[86,3679,3680,3682,3684,3686,3688,3690],{"class":88,"line":311},[86,3681,1242],{"class":1220},[86,3683,1245],{"class":1224},[86,3685,1228],{"class":92},[86,3687,1231],{"class":1220},[86,3689,1269],{"class":304},[86,3691,1255],{"class":1220},[86,3693,3694,3696,3698,3700,3702,3704],{"class":88,"line":354},[86,3695,1242],{"class":1220},[86,3697,1262],{"class":1224},[86,3699,1228],{"class":92},[86,3701,1231],{"class":1220},[86,3703,1269],{"class":304},[86,3705,1255],{"class":1220},[86,3707,3708,3710,3712,3714,3716,3718],{"class":88,"line":362},[86,3709,1242],{"class":1220},[86,3711,1262],{"class":1224},[86,3713,1228],{"class":92},[86,3715,1231],{"class":1220},[86,3717,1252],{"class":304},[86,3719,1255],{"class":1220},[86,3721,3722,3724,3726],{"class":88,"line":370},[86,3723,1290],{"class":1220},[86,3725,1225],{"class":1224},[86,3727,1237],{"class":1220},[20,3729,3730,3731,3734,3735,3737,3738,3740],{},"如果机器人底层只接收速度命令，可以把 ",[29,3732,3733],{},"command_interfaces"," 改成 ",[29,3736,655],{},"。如果底层是力矩控制，可以使用 ",[29,3739,658],{},"，但这要求硬件接口和控制器参数都匹配。",[15,3742,3744],{"id":3743},"forward-command-controller","Forward Command Controller",[20,3746,3747,3749],{},[29,3748,726],{}," 是一个很适合初学者调试硬件接口的控制器。插件描述文件是：",[77,3751,3753],{"className":79,"code":3752,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/forward_command_controller/forward_command_plugin.xml\n",[29,3754,3755],{"__ignoreMap":82},[86,3756,3757],{"class":88,"line":89},[86,3758,3752],{"class":92},[20,3760,3761],{},"插件名包括：",[77,3763,3765],{"className":79,"code":3764,"language":81,"meta":82,"style":82},"forward_command_controller/ForwardCommandController\nforward_command_controller/MultiInterfaceForwardCommandController\n",[29,3766,3767,3772],{"__ignoreMap":82},[86,3768,3769],{"class":88,"line":89},[86,3770,3771],{"class":92},"forward_command_controller/ForwardCommandController\n",[86,3773,3774],{"class":88,"line":311},[86,3775,3776],{"class":92},"forward_command_controller/MultiInterfaceForwardCommandController\n",[20,3778,3779],{},"它不会做复杂控制，只是把收到的数组命令直接写到指定 joint 的指定 command interface。",[20,3781,3782],{},"示例配置：",[77,3784,3786],{"className":2126,"code":3785,"language":2128,"meta":82,"style":82},"forward_velocity_controller:\n  ros__parameters:\n    joints:\n      - left_wheel_joint\n      - right_wheel_joint\n    interface_name: velocity\n",[29,3787,3788,3795,3801,3807,3814,3821],{"__ignoreMap":82},[86,3789,3790,3793],{"class":88,"line":89},[86,3791,3792],{"class":1224},"forward_velocity_controller",[86,3794,2137],{"class":1220},[86,3796,3797,3799],{"class":88,"line":311},[86,3798,2142],{"class":1224},[86,3800,2137],{"class":1220},[86,3802,3803,3805],{"class":88,"line":354},[86,3804,3596],{"class":1224},[86,3806,2137],{"class":1220},[86,3808,3809,3811],{"class":88,"line":362},[86,3810,3603],{"class":1220},[86,3812,3813],{"class":304},"left_wheel_joint\n",[86,3815,3816,3818],{"class":88,"line":370},[86,3817,3603],{"class":1220},[86,3819,3820],{"class":304},"right_wheel_joint\n",[86,3822,3823,3826,3828],{"class":88,"line":378},[86,3824,3825],{"class":1224},"    interface_name",[86,3827,2152],{"class":1220},[86,3829,3654],{"class":304},[20,3831,3832],{},"对于刚写完硬件接口的人来说，推荐先用 forward controller 测试：",[34,3834,3835,3841,3846,3849],{},[37,3836,3837,3840],{},[29,3838,3839],{},"ros2 control list_hardware_interfaces"," 确认接口存在；",[37,3842,3843,3844,238],{},"启动 ",[29,3845,3792],{},[37,3847,3848],{},"发送简单速度数组；",[37,3850,3851,3852,3854],{},"看硬件接口的 ",[29,3853,1376],{}," 是否收到命令。",[15,3856,3858],{"id":3857},"ros2_controllers-中的其他控制器","ros2_controllers 中的其他控制器",[20,3860,3861,3862,3865],{},"安装 ",[29,3863,3864],{},"ros-jazzy-ros2-controllers"," 后，还会带很多控制器。常见类型如下：",[97,3867,3868,3883],{},[100,3869,3870],{},[103,3871,3872,3874,3877,3880],{},[106,3873,1366],{"align":108},[106,3875,3876],{"align":108},"插件名",[106,3878,3879],{"align":108},"用途",[106,3881,3882],{"align":108},"常见硬件接口",[117,3884,3885,3908,3935,3952,3969,3986,4006,4022,4041,4062,4080,4095,4113,4133,4149,4165,4181,4197],{},[103,3886,3887,3890,3895,3898],{},[122,3888,3889],{"align":108},"差速底盘",[122,3891,3892],{"align":108},[29,3893,3894],{},"diff_drive_controller/DiffDriveController",[122,3896,3897],{"align":108},"双轮或两侧多轮差速机器人，负责从底盘速度计算左右轮速度，并发布里程计",[122,3899,3900,3901,3903,3904,3907],{"align":108},"轮关节 ",[29,3902,655],{}," 命令，轮关节 ",[29,3905,3906],{},"position/velocity"," 状态",[103,3909,3910,3913,3918,3923],{},[122,3911,3912],{"align":108},"机械臂轨迹",[122,3914,3915],{"align":108},[29,3916,3917],{},"joint_trajectory_controller/JointTrajectoryController",[122,3919,3920,3921],{"align":108},"多关节轨迹执行，MoveIt2 常用它执行 ",[29,3922,709],{},[122,3924,3925,3926,151,3928,706,3930,3932,3933,3907],{"align":108},"关节 ",[29,3927,652],{},[29,3929,655],{},[29,3931,658],{}," 命令，通常读取 ",[29,3934,3906],{},[103,3936,3937,3940,3945,3948],{},[122,3938,3939],{"align":108},"位置组控制",[122,3941,3942],{"align":108},[29,3943,3944],{},"position_controllers/JointGroupPositionController",[122,3946,3947],{"align":108},"直接把位置数组写给多个关节，不做轨迹规划和运动学",[122,3949,759,3950,219],{"align":108},[29,3951,652],{},[103,3953,3954,3957,3962,3965],{},[122,3955,3956],{"align":108},"速度组控制",[122,3958,3959],{"align":108},[29,3960,3961],{},"velocity_controllers/JointGroupVelocityController",[122,3963,3964],{"align":108},"直接把速度数组写给多个关节，常用于轮子或速度型执行器测试",[122,3966,759,3967,219],{"align":108},[29,3968,655],{},[103,3970,3971,3974,3979,3982],{},[122,3972,3973],{"align":108},"力矩组控制",[122,3975,3976],{"align":108},[29,3977,3978],{},"effort_controllers/JointGroupEffortController",[122,3980,3981],{"align":108},"直接把 effort 数组写给多个关节，适合力矩控制或简单测试",[122,3983,759,3984,219],{"align":108},[29,3985,658],{},[103,3987,3988,3991,3996,3999],{},[122,3989,3990],{"align":108},"命令转发",[122,3992,3993],{"align":108},[29,3994,3995],{},"forward_command_controller/ForwardCommandController",[122,3997,3998],{"align":108},"通用命令转发器，可以选择某一种 command interface",[122,4000,4001,4002,4005],{"align":108},"由 ",[29,4003,4004],{},"interface_name"," 参数决定",[103,4007,4008,4011,4016,4019],{},[122,4009,4010],{"align":108},"多接口命令转发",[122,4012,4013],{"align":108},[29,4014,4015],{},"forward_command_controller/MultiInterfaceForwardCommandController",[122,4017,4018],{"align":108},"同时向多个接口转发命令，适合复杂硬件调试",[122,4020,4021],{"align":108},"多个 joint 的多个 command interfaces",[103,4023,4024,4027,4032,4035],{},[122,4025,4026],{"align":108},"夹爪",[122,4028,4029],{"align":108},[29,4030,4031],{},"parallel_gripper_action_controller/GripperActionController",[122,4033,4034],{"align":108},"平行夹爪 action 控制，适合两个手指联动的夹爪",[122,4036,4037,4038,4040],{"align":108},"夹爪关节 ",[29,4039,652],{}," 或相关接口",[103,4042,4043,4046,4051,4054],{},[122,4044,4045],{"align":108},"Ackermann",[122,4047,4048],{"align":108},[29,4049,4050],{},"ackermann_steering_controller/AckermannSteeringController",[122,4052,4053],{"align":108},"汽车式阿克曼底盘，通常前轮转向、后轮驱动",[122,4055,4056,4057,4059,4060,219],{"align":108},"转向关节 ",[29,4058,652],{}," 命令，驱动轮 ",[29,4061,655],{},[103,4063,4064,4067,4072,4075],{},[122,4065,4066],{"align":108},"Mecanum",[122,4068,4069],{"align":108},[29,4070,4071],{},"mecanum_drive_controller/MecanumDriveController",[122,4073,4074],{"align":108},"四轮麦克纳姆底盘，可前后、横移、旋转",[122,4076,4077,4078,219],{"align":108},"四个轮关节 ",[29,4079,655],{},[103,4081,4082,4085,4090,4093],{},[122,4083,4084],{"align":108},"三轮车",[122,4086,4087],{"align":108},[29,4088,4089],{},"tricycle_controller/TricycleController",[122,4091,4092],{"align":108},"三轮底盘，通常一个转向轮加驱动轮",[122,4094,880],{"align":108},[103,4096,4097,4100,4105,4108],{},[122,4098,4099],{"align":108},"全向轮",[122,4101,4102],{"align":108},[29,4103,4104],{},"omni_wheel_drive_controller/OmniWheelDriveController",[122,4106,4107],{"align":108},"三个或更多全向轮底盘，支持平面全向运动",[122,4109,4110,4111,219],{"align":108},"多个轮关节 ",[29,4112,655],{},[103,4114,4115,4118,4123,4130],{},[122,4116,4117],{"align":108},"PID",[122,4119,4120],{"align":108},[29,4121,4122],{},"pid_controller/PidController",[122,4124,4125,4126,4129],{"align":108},"基于 ",[29,4127,4128],{},"control_toolbox"," 的 PID 控制器，可用于控制器链",[122,4131,4132],{"align":108},"读取状态/参考，输出 PID 后的命令",[103,4134,4135,4138,4143,4146],{},[122,4136,4137],{"align":108},"IMU 发布",[122,4139,4140],{"align":108},[29,4141,4142],{},"imu_sensor_broadcaster/IMUSensorBroadcaster",[122,4144,4145],{"align":108},"把硬件状态接口发布成 IMU 消息",[122,4147,4148],{"align":108},"读取 IMU 相关 state interfaces",[103,4150,4151,4154,4159,4162],{},[122,4152,4153],{"align":108},"力/力矩发布",[122,4155,4156],{"align":108},[29,4157,4158],{},"force_torque_sensor_broadcaster/ForceTorqueSensorBroadcaster",[122,4160,4161],{"align":108},"发布六维力传感器数据",[122,4163,4164],{"align":108},"读取 force/torque state interfaces",[103,4166,4167,4170,4175,4178],{},[122,4168,4169],{"align":108},"距离传感器发布",[122,4171,4172],{"align":108},[29,4173,4174],{},"range_sensor_broadcaster/RangeSensorBroadcaster",[122,4176,4177],{"align":108},"发布距离传感器数据",[122,4179,4180],{"align":108},"读取 range state interface",[103,4182,4183,4186,4191,4194],{},[122,4184,4185],{"align":108},"电池状态发布",[122,4187,4188],{"align":108},[29,4189,4190],{},"battery_state_broadcaster/BatteryStateBroadcaster",[122,4192,4193],{"align":108},"发布电池电压、电流、电量等状态",[122,4195,4196],{"align":108},"读取电池相关 state interfaces",[103,4198,4199,4202,4207,4210],{},[122,4200,4201],{"align":108},"通用状态发布",[122,4203,4204],{"align":108},[29,4205,4206],{},"state_interfaces_broadcaster/StateInterfacesBroadcaster",[122,4208,4209],{"align":108},"发布指定 state interfaces，适合调试自定义状态",[122,4211,4212],{"align":108},"读取用户指定 state interfaces",[20,4214,4215,4216,151,4218,151,4220,706,4222,4224,4225,151,4227,151,4229,151,4231,706,4233,4235,4236,4238],{},"如果只是想确认硬件接口能不能收到命令，优先用 ",[29,4217,726],{},[29,4219,747],{},[29,4221,769],{},[29,4223,789],{},"。如果已经有明确运动学模型，再选择 ",[29,4226,669],{},[29,4228,828],{},[29,4230,849],{},[29,4232,868],{},[29,4234,887],{},"。机械臂和多关节机构通常先从 ",[29,4237,694],{}," 学起。",[20,4240,4241],{},"选控制器时先问自己三个问题：",[34,4243,4244,4247,4250],{},[37,4245,4246],{},"我的机器人运动学是不是官方已经支持？",[37,4248,4249],{},"我的硬件接收的是 position、velocity 还是 effort？",[37,4251,4252],{},"我需要控制器自己算运动学，还是只需要把命令转发给硬件？",[20,4254,4255],{},"如果官方控制器能满足需求，就优先使用官方控制器。只有在运动学特殊、控制律特殊、需要融合特殊传感器时，才建议写自定义控制器。",[10,4257,4259],{"id":4258},"一个差速底盘最小-bringup","一个差速底盘最小 Bringup",[15,4261,4263],{"id":4262},"controllersyaml","controllers.yaml",[77,4265,4267],{"className":2126,"code":4266,"language":2128,"meta":82,"style":82},"controller_manager:\n  ros__parameters:\n    update_rate: 100\n\n    joint_state_broadcaster:\n      type: joint_state_broadcaster/JointStateBroadcaster\n\n    diff_drive_controller:\n      type: diff_drive_controller/DiffDriveController\n\njoint_state_broadcaster:\n  ros__parameters:\n    use_local_topics: false\n    publish_dynamic_joint_states: true\n\ndiff_drive_controller:\n  ros__parameters:\n    left_wheel_names: [\"left_wheel_joint\"]\n    right_wheel_names: [\"right_wheel_joint\"]\n    wheel_separation: 0.40\n    wheel_radius: 0.05\n    odom_frame_id: odom\n    base_frame_id: base_link\n    position_feedback: false\n    open_loop: true\n    enable_odom_tf: true\n    publish_limited_velocity: true\n",[29,4268,4269,4275,4281,4289,4293,4299,4307,4311,4317,4325,4329,4335,4341,4349,4357,4361,4367,4373,4384,4395,4404,4413,4422,4431,4440,4449,4458],{"__ignoreMap":82},[86,4270,4271,4273],{"class":88,"line":89},[86,4272,71],{"class":1224},[86,4274,2137],{"class":1220},[86,4276,4277,4279],{"class":88,"line":311},[86,4278,2142],{"class":1224},[86,4280,2137],{"class":1220},[86,4282,4283,4285,4287],{"class":88,"line":354},[86,4284,2149],{"class":1224},[86,4286,2152],{"class":1220},[86,4288,2155],{"class":343},[86,4290,4291],{"class":88,"line":362},[86,4292,1569],{"emptyLinePlaceholder":1568},[86,4294,4295,4297],{"class":88,"line":370},[86,4296,2164],{"class":1224},[86,4298,2137],{"class":1220},[86,4300,4301,4303,4305],{"class":88,"line":378},[86,4302,2171],{"class":1224},[86,4304,2152],{"class":1220},[86,4306,2176],{"class":304},[86,4308,4309],{"class":88,"line":386},[86,4310,1569],{"emptyLinePlaceholder":1568},[86,4312,4313,4315],{"class":88,"line":394},[86,4314,2185],{"class":1224},[86,4316,2137],{"class":1220},[86,4318,4319,4321,4323],{"class":88,"line":402},[86,4320,2171],{"class":1224},[86,4322,2152],{"class":1220},[86,4324,2196],{"class":304},[86,4326,4327],{"class":88,"line":585},[86,4328,1569],{"emptyLinePlaceholder":1568},[86,4330,4331,4333],{"class":88,"line":591},[86,4332,633],{"class":1224},[86,4334,2137],{"class":1220},[86,4336,4337,4339],{"class":88,"line":597},[86,4338,2142],{"class":1224},[86,4340,2137],{"class":1220},[86,4342,4343,4345,4347],{"class":88,"line":1879},[86,4344,3017],{"class":1224},[86,4346,2152],{"class":1220},[86,4348,3022],{"class":343},[86,4350,4351,4353,4355],{"class":88,"line":1894},[86,4352,3037],{"class":1224},[86,4354,2152],{"class":1220},[86,4356,3032],{"class":343},[86,4358,4359],{"class":88,"line":1909},[86,4360,1569],{"emptyLinePlaceholder":1568},[86,4362,4363,4365],{"class":88,"line":1924},[86,4364,669],{"class":1224},[86,4366,2137],{"class":1220},[86,4368,4369,4371],{"class":88,"line":1933},[86,4370,2142],{"class":1224},[86,4372,2137],{"class":1220},[86,4374,4376,4378,4380,4382],{"class":88,"line":4375},18,[86,4377,3247],{"class":1224},[86,4379,3250],{"class":1220},[86,4381,1234],{"class":304},[86,4383,3255],{"class":1220},[86,4385,4387,4389,4391,4393],{"class":88,"line":4386},19,[86,4388,3260],{"class":1224},[86,4390,3250],{"class":1220},[86,4392,1874],{"class":304},[86,4394,3255],{"class":1220},[86,4396,4398,4400,4402],{"class":88,"line":4397},20,[86,4399,3271],{"class":1224},[86,4401,2152],{"class":1220},[86,4403,3276],{"class":343},[86,4405,4407,4409,4411],{"class":88,"line":4406},21,[86,4408,3281],{"class":1224},[86,4410,2152],{"class":1220},[86,4412,3286],{"class":343},[86,4414,4416,4418,4420],{"class":88,"line":4415},22,[86,4417,3291],{"class":1224},[86,4419,2152],{"class":1220},[86,4421,3296],{"class":304},[86,4423,4425,4427,4429],{"class":88,"line":4424},23,[86,4426,3301],{"class":1224},[86,4428,2152],{"class":1220},[86,4430,3306],{"class":304},[86,4432,4434,4436,4438],{"class":88,"line":4433},24,[86,4435,3311],{"class":1224},[86,4437,2152],{"class":1220},[86,4439,3022],{"class":343},[86,4441,4443,4445,4447],{"class":88,"line":4442},25,[86,4444,3320],{"class":1224},[86,4446,2152],{"class":1220},[86,4448,3032],{"class":343},[86,4450,4452,4454,4456],{"class":88,"line":4451},26,[86,4453,3329],{"class":1224},[86,4455,2152],{"class":1220},[86,4457,3032],{"class":343},[86,4459,4461,4464,4466],{"class":88,"line":4460},27,[86,4462,4463],{"class":1224},"    publish_limited_velocity",[86,4465,2152],{"class":1220},[86,4467,3032],{"class":343},[15,4469,4471],{"id":4470},"urdf-关键片段","URDF 关键片段",[77,4473,4474],{"className":1211,"code":1737,"language":1213,"meta":82,"style":82},[29,4475,4476,4496,4504,4516,4524,4528,4542,4556,4570,4584,4592,4596,4610,4624,4638,4652,4660],{"__ignoreMap":82},[86,4477,4478,4480,4482,4484,4486,4488,4490,4492,4494],{"class":88,"line":89},[86,4479,1221],{"class":1220},[86,4481,31],{"class":1224},[86,4483,1228],{"class":92},[86,4485,1231],{"class":1220},[86,4487,1752],{"class":304},[86,4489,1523],{"class":92},[86,4491,1231],{"class":1220},[86,4493,1528],{"class":304},[86,4495,1237],{"class":1220},[86,4497,4498,4500,4502],{"class":88,"line":311},[86,4499,1242],{"class":1220},[86,4501,1537],{"class":1224},[86,4503,1237],{"class":1220},[86,4505,4506,4508,4510,4512,4514],{"class":88,"line":354},[86,4507,1544],{"class":1220},[86,4509,1547],{"class":1224},[86,4511,1777],{"class":1220},[86,4513,1547],{"class":1224},[86,4515,1237],{"class":1220},[86,4517,4518,4520,4522],{"class":88,"line":362},[86,4519,1559],{"class":1220},[86,4521,1537],{"class":1224},[86,4523,1237],{"class":1220},[86,4525,4526],{"class":88,"line":370},[86,4527,1569],{"emptyLinePlaceholder":1568},[86,4529,4530,4532,4534,4536,4538,4540],{"class":88,"line":378},[86,4531,1242],{"class":1220},[86,4533,1225],{"class":1224},[86,4535,1228],{"class":92},[86,4537,1231],{"class":1220},[86,4539,1234],{"class":304},[86,4541,1237],{"class":1220},[86,4543,4544,4546,4548,4550,4552,4554],{"class":88,"line":386},[86,4545,1544],{"class":1220},[86,4547,1245],{"class":1224},[86,4549,1228],{"class":92},[86,4551,1231],{"class":1220},[86,4553,1252],{"class":304},[86,4555,1255],{"class":1220},[86,4557,4558,4560,4562,4564,4566,4568],{"class":88,"line":394},[86,4559,1544],{"class":1220},[86,4561,1262],{"class":1224},[86,4563,1228],{"class":92},[86,4565,1231],{"class":1220},[86,4567,1269],{"class":304},[86,4569,1255],{"class":1220},[86,4571,4572,4574,4576,4578,4580,4582],{"class":88,"line":402},[86,4573,1544],{"class":1220},[86,4575,1262],{"class":1224},[86,4577,1228],{"class":92},[86,4579,1231],{"class":1220},[86,4581,1252],{"class":304},[86,4583,1255],{"class":1220},[86,4585,4586,4588,4590],{"class":88,"line":585},[86,4587,1559],{"class":1220},[86,4589,1225],{"class":1224},[86,4591,1237],{"class":1220},[86,4593,4594],{"class":88,"line":591},[86,4595,1569],{"emptyLinePlaceholder":1568},[86,4597,4598,4600,4602,4604,4606,4608],{"class":88,"line":597},[86,4599,1242],{"class":1220},[86,4601,1225],{"class":1224},[86,4603,1228],{"class":92},[86,4605,1231],{"class":1220},[86,4607,1874],{"class":304},[86,4609,1237],{"class":1220},[86,4611,4612,4614,4616,4618,4620,4622],{"class":88,"line":1879},[86,4613,1544],{"class":1220},[86,4615,1245],{"class":1224},[86,4617,1228],{"class":92},[86,4619,1231],{"class":1220},[86,4621,1252],{"class":304},[86,4623,1255],{"class":1220},[86,4625,4626,4628,4630,4632,4634,4636],{"class":88,"line":1894},[86,4627,1544],{"class":1220},[86,4629,1262],{"class":1224},[86,4631,1228],{"class":92},[86,4633,1231],{"class":1220},[86,4635,1269],{"class":304},[86,4637,1255],{"class":1220},[86,4639,4640,4642,4644,4646,4648,4650],{"class":88,"line":1909},[86,4641,1544],{"class":1220},[86,4643,1262],{"class":1224},[86,4645,1228],{"class":92},[86,4647,1231],{"class":1220},[86,4649,1252],{"class":304},[86,4651,1255],{"class":1220},[86,4653,4654,4656,4658],{"class":88,"line":1924},[86,4655,1559],{"class":1220},[86,4657,1225],{"class":1224},[86,4659,1237],{"class":1220},[86,4661,4662,4664,4666],{"class":88,"line":1933},[86,4663,1290],{"class":1220},[86,4665,31],{"class":1224},[86,4667,1237],{"class":1220},[15,4669,4670],{"id":4670},"启动顺序",[20,4672,4673],{},"推荐顺序如下：",[4675,4676,4677,4681,4685,4689,4693,4698],"ol",{},[37,4678,3843,4679,1307],{},[29,4680,2977],{},[37,4682,3843,4683,1307],{},[29,4684,150],{},[37,4686,3843,4687,1307],{},[29,4688,633],{},[37,4690,3843,4691,1307],{},[29,4692,669],{},[37,4694,4695,4696,1307],{},"发布 ",[29,4697,1400],{},[37,4699,4700,4701,151,4703,643,4706,1307],{},"查看 ",[29,4702,642],{},[29,4704,4705],{},"/diff_drive_controller/odom",[29,4707,3193],{},[20,4709,4710],{},"调试命令如下：",[77,4712,4714],{"className":79,"code":4713,"language":81,"meta":82,"style":82},"ros2 control list_hardware_components\nros2 control list_hardware_interfaces\nros2 control list_controllers --verbose\nros2 topic echo /joint_states\nros2 topic echo /diff_drive_controller/odom\n",[29,4715,4716,4724,4732,4742,4754],{"__ignoreMap":82},[86,4717,4718,4720,4722],{"class":88,"line":89},[86,4719,452],{"class":92},[86,4721,2760],{"class":304},[86,4723,2800],{"class":304},[86,4725,4726,4728,4730],{"class":88,"line":311},[86,4727,452],{"class":92},[86,4729,2760],{"class":304},[86,4731,2817],{"class":304},[86,4733,4734,4736,4738,4740],{"class":88,"line":354},[86,4735,452],{"class":92},[86,4737,2760],{"class":304},[86,4739,2780],{"class":304},[86,4741,2783],{"class":343},[86,4743,4744,4746,4748,4751],{"class":88,"line":362},[86,4745,452],{"class":92},[86,4747,3438],{"class":304},[86,4749,4750],{"class":304}," echo",[86,4752,4753],{"class":304}," /joint_states\n",[86,4755,4756,4758,4760,4762],{"class":88,"line":370},[86,4757,452],{"class":92},[86,4759,3438],{"class":304},[86,4761,4750],{"class":304},[86,4763,4764],{"class":304}," /diff_drive_controller/odom\n",[10,4766,4767],{"id":4767},"自定义硬件接口",[15,4769,4770],{"id":4770},"什么时候需要写硬件接口",[20,4772,4773],{},"如果你的机器人使用真实电机驱动板，通常需要写硬件接口。典型场景包括：",[34,4775,4776,4779,4782,4785,4788,4791],{},[37,4777,4778],{},"串口电机驱动板；",[37,4780,4781],{},"CAN 总线电机；",[37,4783,4784],{},"EtherCAT 驱动器；",[37,4786,4787],{},"自研 STM32 下位机；",[37,4789,4790],{},"自定义传感器数据；",[37,4792,4793],{},"非 Gazebo 的自定义仿真器。",[20,4795,4796,4797,4800,4801,1307],{},"如果你只是想验证控制器配置，可以先用 ",[29,4798,4799],{},"mock_components/GenericSystem","。如果你要接真机，就需要继承 ",[29,4802,1664],{},[15,4804,4806],{"id":4805},"systeminterface-生命周期","SystemInterface 生命周期",[20,4808,4809],{},"Jazzy 中系统硬件接口的常用函数如下：",[97,4811,4812,4824],{},[100,4813,4814],{},[103,4815,4816,4819,4822],{},[106,4817,4818],{"align":108},"函数",[106,4820,4821],{"align":108},"调用时机",[106,4823,1341],{"align":108},[117,4825,4826,4839,4852,4865,4878,4891],{},[103,4827,4828,4833,4836],{},[122,4829,4830],{"align":108},[29,4831,4832],{},"on_init(const HardwareInfo & info)",[122,4834,4835],{"align":108},"加载硬件时",[122,4837,4838],{"align":108},"读取 URDF 中的 joint、interface、param",[103,4840,4841,4846,4849],{},[122,4842,4843],{"align":108},[29,4844,4845],{},"on_configure(...)",[122,4847,4848],{"align":108},"配置硬件时",[122,4850,4851],{"align":108},"打开串口、初始化驱动板、分配资源",[103,4853,4854,4859,4862],{},[122,4855,4856],{"align":108},[29,4857,4858],{},"on_activate(...)",[122,4860,4861],{"align":108},"激活硬件时",[122,4863,4864],{"align":108},"使能电机，清零命令",[103,4866,4867,4872,4875],{},[122,4868,4869],{"align":108},[29,4870,4871],{},"on_deactivate(...)",[122,4873,4874],{"align":108},"停用硬件时",[122,4876,4877],{"align":108},"失能电机，停止输出",[103,4879,4880,4885,4888],{},[122,4881,4882],{"align":108},[29,4883,4884],{},"read(const rclcpp::Time &, const rclcpp::Duration &)",[122,4886,4887],{"align":108},"每个控制周期",[122,4889,4890],{"align":108},"读取硬件状态并写入 state interfaces",[103,4892,4893,4898,4900],{},[122,4894,4895],{"align":108},[29,4896,4897],{},"write(const rclcpp::Time &, const rclcpp::Duration &)",[122,4899,4887],{"align":108},[122,4901,4902],{"align":108},"读取 command interfaces 并写给硬件",[20,4904,4905,4906,4908,4909,4912],{},"在 Jazzy 中，如果接口已经在 URDF 的 ",[29,4907,1498],{}," 中声明，",[29,4910,4911],{},"SystemInterface"," 默认会根据 URDF 创建接口。自定义硬件里可以使用：",[77,4914,4918],{"className":4915,"code":4916,"language":4917,"meta":82,"style":82},"language-cpp shiki shiki-themes github-light github-dark","set_state(\"left_wheel_joint/position\", value);\nset_state(\"left_wheel_joint/velocity\", value);\ndouble cmd = get_command(\"left_wheel_joint/velocity\");\n","cpp",[29,4919,4920,4934,4945],{"__ignoreMap":82},[86,4921,4922,4925,4928,4931],{"class":88,"line":89},[86,4923,4924],{"class":92},"set_state",[86,4926,4927],{"class":1220},"(",[86,4929,4930],{"class":304},"\"left_wheel_joint/position\"",[86,4932,4933],{"class":1220},", value);\n",[86,4935,4936,4938,4940,4943],{"class":88,"line":311},[86,4937,4924],{"class":92},[86,4939,4927],{"class":1220},[86,4941,4942],{"class":304},"\"left_wheel_joint/velocity\"",[86,4944,4933],{"class":1220},[86,4946,4947,4950,4953,4955,4958,4960,4962],{"class":88,"line":354},[86,4948,4949],{"class":461},"double",[86,4951,4952],{"class":1220}," cmd ",[86,4954,1231],{"class":461},[86,4956,4957],{"class":92}," get_command",[86,4959,4927],{"class":1220},[86,4961,4942],{"class":304},[86,4963,4964],{"class":1220},");\n",[20,4966,4967,4968,1307],{},"这些函数来自 Jazzy 的 ",[29,4969,1664],{},[15,4971,4972],{"id":4972},"最小硬件接口框架",[20,4974,4975],{},"下面是一个用于差速底盘的硬件接口骨架。它展示结构，不包含具体串口协议：",[77,4977,4979],{"className":4915,"code":4978,"language":4917,"meta":82,"style":82},"#include \u003Cstring>\n\n#include \"hardware_interface/system_interface.hpp\"\n#include \"hardware_interface/types/hardware_interface_return_values.hpp\"\n#include \"rclcpp/rclcpp.hpp\"\n\nnamespace my_robot_hardware\n{\n\nclass MyDiffBotSystem : public hardware_interface::SystemInterface\n{\npublic:\n  hardware_interface::CallbackReturn on_init(const hardware_interface::HardwareInfo & info) override\n  {\n    if (hardware_interface::SystemInterface::on_init(info) !=\n      hardware_interface::CallbackReturn::SUCCESS)\n    {\n      return hardware_interface::CallbackReturn::ERROR;\n    }\n\n    serial_port_ = info_.hardware_parameters.at(\"serial_port\");\n    return hardware_interface::CallbackReturn::SUCCESS;\n  }\n\n  hardware_interface::CallbackReturn on_configure(const rclcpp_lifecycle::State &) override\n  {\n    // 在这里打开串口、CAN 或网络连接。\n    return hardware_interface::CallbackReturn::SUCCESS;\n  }\n\n  hardware_interface::CallbackReturn on_activate(const rclcpp_lifecycle::State &) override\n  {\n    set_command(\"left_wheel_joint/velocity\", 0.0);\n    set_command(\"right_wheel_joint/velocity\", 0.0);\n    return hardware_interface::CallbackReturn::SUCCESS;\n  }\n\n  hardware_interface::return_type read(\n    const rclcpp::Time &, const rclcpp::Duration &) override\n  {\n    // 从编码器读取真实位置和速度。\n    set_state(\"left_wheel_joint/position\", left_position_);\n    set_state(\"left_wheel_joint/velocity\", left_velocity_);\n    set_state(\"right_wheel_joint/position\", right_position_);\n    set_state(\"right_wheel_joint/velocity\", right_velocity_);\n    return hardware_interface::return_type::OK;\n  }\n\n  hardware_interface::return_type write(\n    const rclcpp::Time &, const rclcpp::Duration &) override\n  {\n    const double left_cmd = get_command(\"left_wheel_joint/velocity\");\n    const double right_cmd = get_command(\"right_wheel_joint/velocity\");\n\n    // 把 left_cmd 和 right_cmd 写给电机驱动板。\n    return hardware_interface::return_type::OK;\n  }\n\nprivate:\n  std::string serial_port_;\n  double left_position_ = 0.0;\n  double left_velocity_ = 0.0;\n  double right_position_ = 0.0;\n  double right_velocity_ = 0.0;\n};\n\n}  // namespace my_robot_hardware\n",[29,4980,4981,4989,4993,5000,5007,5014,5018,5026,5031,5035,5058,5062,5067,5104,5109,5134,5146,5151,5165,5170,5174,5193,5206,5210,5214,5243,5247,5253,5266,5271,5276,5304,5309,5327,5343,5356,5361,5366,5382,5415,5420,5426,5439,5451,5464,5476,5490,5495,5500,5514,5543,5548,5569,5589,5594,5600,5613,5618,5623,5629,5638,5655,5669,5683,5697,5703,5708],{"__ignoreMap":82},[86,4982,4983,4986],{"class":88,"line":89},[86,4984,4985],{"class":461},"#include",[86,4987,4988],{"class":304}," \u003Cstring>\n",[86,4990,4991],{"class":88,"line":311},[86,4992,1569],{"emptyLinePlaceholder":1568},[86,4994,4995,4997],{"class":88,"line":354},[86,4996,4985],{"class":461},[86,4998,4999],{"class":304}," \"hardware_interface/system_interface.hpp\"\n",[86,5001,5002,5004],{"class":88,"line":362},[86,5003,4985],{"class":461},[86,5005,5006],{"class":304}," \"hardware_interface/types/hardware_interface_return_values.hpp\"\n",[86,5008,5009,5011],{"class":88,"line":370},[86,5010,4985],{"class":461},[86,5012,5013],{"class":304}," \"rclcpp/rclcpp.hpp\"\n",[86,5015,5016],{"class":88,"line":378},[86,5017,1569],{"emptyLinePlaceholder":1568},[86,5019,5020,5023],{"class":88,"line":386},[86,5021,5022],{"class":461},"namespace",[86,5024,5025],{"class":92}," my_robot_hardware\n",[86,5027,5028],{"class":88,"line":394},[86,5029,5030],{"class":1220},"{\n",[86,5032,5033],{"class":88,"line":402},[86,5034,1569],{"emptyLinePlaceholder":1568},[86,5036,5037,5040,5043,5046,5049,5052,5055],{"class":88,"line":585},[86,5038,5039],{"class":461},"class",[86,5041,5042],{"class":92}," MyDiffBotSystem",[86,5044,5045],{"class":1220}," : ",[86,5047,5048],{"class":461},"public",[86,5050,5051],{"class":92}," hardware_interface",[86,5053,5054],{"class":1220},"::",[86,5056,5057],{"class":92},"SystemInterface\n",[86,5059,5060],{"class":88,"line":591},[86,5061,5030],{"class":1220},[86,5063,5064],{"class":88,"line":597},[86,5065,5066],{"class":461},"public:\n",[86,5068,5069,5072,5074,5077,5080,5082,5085,5087,5089,5092,5095,5098,5101],{"class":88,"line":1879},[86,5070,5071],{"class":92},"  hardware_interface",[86,5073,5054],{"class":1220},[86,5075,5076],{"class":92},"CallbackReturn",[86,5078,5079],{"class":92}," on_init",[86,5081,4927],{"class":1220},[86,5083,5084],{"class":461},"const",[86,5086,5051],{"class":92},[86,5088,5054],{"class":1220},[86,5090,5091],{"class":92},"HardwareInfo",[86,5093,5094],{"class":461}," &",[86,5096,5097],{"class":2365}," info",[86,5099,5100],{"class":1220},") ",[86,5102,5103],{"class":461},"override\n",[86,5105,5106],{"class":88,"line":1894},[86,5107,5108],{"class":1220},"  {\n",[86,5110,5111,5114,5117,5119,5121,5123,5125,5128,5131],{"class":88,"line":1909},[86,5112,5113],{"class":461},"    if",[86,5115,5116],{"class":1220}," (",[86,5118,177],{"class":92},[86,5120,5054],{"class":1220},[86,5122,4911],{"class":92},[86,5124,5054],{"class":1220},[86,5126,5127],{"class":92},"on_init",[86,5129,5130],{"class":1220},"(info) ",[86,5132,5133],{"class":461},"!=\n",[86,5135,5136,5139,5141,5143],{"class":88,"line":1924},[86,5137,5138],{"class":92},"      hardware_interface",[86,5140,5054],{"class":1220},[86,5142,5076],{"class":92},[86,5144,5145],{"class":1220},"::SUCCESS)\n",[86,5147,5148],{"class":88,"line":1933},[86,5149,5150],{"class":1220},"    {\n",[86,5152,5153,5156,5158,5160,5162],{"class":88,"line":4375},[86,5154,5155],{"class":461},"      return",[86,5157,5051],{"class":92},[86,5159,5054],{"class":1220},[86,5161,5076],{"class":92},[86,5163,5164],{"class":1220},"::ERROR;\n",[86,5166,5167],{"class":88,"line":4386},[86,5168,5169],{"class":1220},"    }\n",[86,5171,5172],{"class":88,"line":4397},[86,5173,1569],{"emptyLinePlaceholder":1568},[86,5175,5176,5179,5181,5184,5187,5189,5191],{"class":88,"line":4406},[86,5177,5178],{"class":1220},"    serial_port_ ",[86,5180,1231],{"class":461},[86,5182,5183],{"class":1220}," info_.hardware_parameters.",[86,5185,5186],{"class":92},"at",[86,5188,4927],{"class":1220},[86,5190,2021],{"class":304},[86,5192,4964],{"class":1220},[86,5194,5195,5197,5199,5201,5203],{"class":88,"line":4415},[86,5196,2450],{"class":461},[86,5198,5051],{"class":92},[86,5200,5054],{"class":1220},[86,5202,5076],{"class":92},[86,5204,5205],{"class":1220},"::SUCCESS;\n",[86,5207,5208],{"class":88,"line":4424},[86,5209,3475],{"class":1220},[86,5211,5212],{"class":88,"line":4433},[86,5213,1569],{"emptyLinePlaceholder":1568},[86,5215,5216,5218,5220,5222,5225,5227,5229,5232,5234,5237,5239,5241],{"class":88,"line":4442},[86,5217,5071],{"class":92},[86,5219,5054],{"class":1220},[86,5221,5076],{"class":92},[86,5223,5224],{"class":92}," on_configure",[86,5226,4927],{"class":1220},[86,5228,5084],{"class":461},[86,5230,5231],{"class":92}," rclcpp_lifecycle",[86,5233,5054],{"class":1220},[86,5235,5236],{"class":92},"State",[86,5238,5094],{"class":461},[86,5240,5100],{"class":1220},[86,5242,5103],{"class":461},[86,5244,5245],{"class":88,"line":4451},[86,5246,5108],{"class":1220},[86,5248,5249],{"class":88,"line":4460},[86,5250,5252],{"class":5251},"sJ8bj","    // 在这里打开串口、CAN 或网络连接。\n",[86,5254,5256,5258,5260,5262,5264],{"class":88,"line":5255},28,[86,5257,2450],{"class":461},[86,5259,5051],{"class":92},[86,5261,5054],{"class":1220},[86,5263,5076],{"class":92},[86,5265,5205],{"class":1220},[86,5267,5269],{"class":88,"line":5268},29,[86,5270,3475],{"class":1220},[86,5272,5274],{"class":88,"line":5273},30,[86,5275,1569],{"emptyLinePlaceholder":1568},[86,5277,5279,5281,5283,5285,5288,5290,5292,5294,5296,5298,5300,5302],{"class":88,"line":5278},31,[86,5280,5071],{"class":92},[86,5282,5054],{"class":1220},[86,5284,5076],{"class":92},[86,5286,5287],{"class":92}," on_activate",[86,5289,4927],{"class":1220},[86,5291,5084],{"class":461},[86,5293,5231],{"class":92},[86,5295,5054],{"class":1220},[86,5297,5236],{"class":92},[86,5299,5094],{"class":461},[86,5301,5100],{"class":1220},[86,5303,5103],{"class":461},[86,5305,5307],{"class":88,"line":5306},32,[86,5308,5108],{"class":1220},[86,5310,5312,5315,5317,5319,5322,5325],{"class":88,"line":5311},33,[86,5313,5314],{"class":92},"    set_command",[86,5316,4927],{"class":1220},[86,5318,4942],{"class":304},[86,5320,5321],{"class":1220},", ",[86,5323,5324],{"class":343},"0.0",[86,5326,4964],{"class":1220},[86,5328,5330,5332,5334,5337,5339,5341],{"class":88,"line":5329},34,[86,5331,5314],{"class":92},[86,5333,4927],{"class":1220},[86,5335,5336],{"class":304},"\"right_wheel_joint/velocity\"",[86,5338,5321],{"class":1220},[86,5340,5324],{"class":343},[86,5342,4964],{"class":1220},[86,5344,5346,5348,5350,5352,5354],{"class":88,"line":5345},35,[86,5347,2450],{"class":461},[86,5349,5051],{"class":92},[86,5351,5054],{"class":1220},[86,5353,5076],{"class":92},[86,5355,5205],{"class":1220},[86,5357,5359],{"class":88,"line":5358},36,[86,5360,3475],{"class":1220},[86,5362,5364],{"class":88,"line":5363},37,[86,5365,1569],{"emptyLinePlaceholder":1568},[86,5367,5369,5371,5373,5376,5379],{"class":88,"line":5368},38,[86,5370,5071],{"class":92},[86,5372,5054],{"class":1220},[86,5374,5375],{"class":92},"return_type",[86,5377,5378],{"class":92}," read",[86,5380,5381],{"class":1220},"(\n",[86,5383,5385,5388,5391,5393,5396,5398,5400,5402,5404,5406,5409,5411,5413],{"class":88,"line":5384},39,[86,5386,5387],{"class":461},"    const",[86,5389,5390],{"class":92}," rclcpp",[86,5392,5054],{"class":1220},[86,5394,5395],{"class":92},"Time",[86,5397,5094],{"class":461},[86,5399,5321],{"class":1220},[86,5401,5084],{"class":461},[86,5403,5390],{"class":92},[86,5405,5054],{"class":1220},[86,5407,5408],{"class":92},"Duration",[86,5410,5094],{"class":461},[86,5412,5100],{"class":1220},[86,5414,5103],{"class":461},[86,5416,5418],{"class":88,"line":5417},40,[86,5419,5108],{"class":1220},[86,5421,5423],{"class":88,"line":5422},41,[86,5424,5425],{"class":5251},"    // 从编码器读取真实位置和速度。\n",[86,5427,5429,5432,5434,5436],{"class":88,"line":5428},42,[86,5430,5431],{"class":92},"    set_state",[86,5433,4927],{"class":1220},[86,5435,4930],{"class":304},[86,5437,5438],{"class":1220},", left_position_);\n",[86,5440,5442,5444,5446,5448],{"class":88,"line":5441},43,[86,5443,5431],{"class":92},[86,5445,4927],{"class":1220},[86,5447,4942],{"class":304},[86,5449,5450],{"class":1220},", left_velocity_);\n",[86,5452,5454,5456,5458,5461],{"class":88,"line":5453},44,[86,5455,5431],{"class":92},[86,5457,4927],{"class":1220},[86,5459,5460],{"class":304},"\"right_wheel_joint/position\"",[86,5462,5463],{"class":1220},", right_position_);\n",[86,5465,5467,5469,5471,5473],{"class":88,"line":5466},45,[86,5468,5431],{"class":92},[86,5470,4927],{"class":1220},[86,5472,5336],{"class":304},[86,5474,5475],{"class":1220},", right_velocity_);\n",[86,5477,5479,5481,5483,5485,5487],{"class":88,"line":5478},46,[86,5480,2450],{"class":461},[86,5482,5051],{"class":92},[86,5484,5054],{"class":1220},[86,5486,5375],{"class":92},[86,5488,5489],{"class":1220},"::OK;\n",[86,5491,5493],{"class":88,"line":5492},47,[86,5494,3475],{"class":1220},[86,5496,5498],{"class":88,"line":5497},48,[86,5499,1569],{"emptyLinePlaceholder":1568},[86,5501,5503,5505,5507,5509,5512],{"class":88,"line":5502},49,[86,5504,5071],{"class":92},[86,5506,5054],{"class":1220},[86,5508,5375],{"class":92},[86,5510,5511],{"class":92}," write",[86,5513,5381],{"class":1220},[86,5515,5517,5519,5521,5523,5525,5527,5529,5531,5533,5535,5537,5539,5541],{"class":88,"line":5516},50,[86,5518,5387],{"class":461},[86,5520,5390],{"class":92},[86,5522,5054],{"class":1220},[86,5524,5395],{"class":92},[86,5526,5094],{"class":461},[86,5528,5321],{"class":1220},[86,5530,5084],{"class":461},[86,5532,5390],{"class":92},[86,5534,5054],{"class":1220},[86,5536,5408],{"class":92},[86,5538,5094],{"class":461},[86,5540,5100],{"class":1220},[86,5542,5103],{"class":461},[86,5544,5546],{"class":88,"line":5545},51,[86,5547,5108],{"class":1220},[86,5549,5551,5553,5556,5559,5561,5563,5565,5567],{"class":88,"line":5550},52,[86,5552,5387],{"class":461},[86,5554,5555],{"class":461}," double",[86,5557,5558],{"class":1220}," left_cmd ",[86,5560,1231],{"class":461},[86,5562,4957],{"class":92},[86,5564,4927],{"class":1220},[86,5566,4942],{"class":304},[86,5568,4964],{"class":1220},[86,5570,5572,5574,5576,5579,5581,5583,5585,5587],{"class":88,"line":5571},53,[86,5573,5387],{"class":461},[86,5575,5555],{"class":461},[86,5577,5578],{"class":1220}," right_cmd ",[86,5580,1231],{"class":461},[86,5582,4957],{"class":92},[86,5584,4927],{"class":1220},[86,5586,5336],{"class":304},[86,5588,4964],{"class":1220},[86,5590,5592],{"class":88,"line":5591},54,[86,5593,1569],{"emptyLinePlaceholder":1568},[86,5595,5597],{"class":88,"line":5596},55,[86,5598,5599],{"class":5251},"    // 把 left_cmd 和 right_cmd 写给电机驱动板。\n",[86,5601,5603,5605,5607,5609,5611],{"class":88,"line":5602},56,[86,5604,2450],{"class":461},[86,5606,5051],{"class":92},[86,5608,5054],{"class":1220},[86,5610,5375],{"class":92},[86,5612,5489],{"class":1220},[86,5614,5616],{"class":88,"line":5615},57,[86,5617,3475],{"class":1220},[86,5619,5621],{"class":88,"line":5620},58,[86,5622,1569],{"emptyLinePlaceholder":1568},[86,5624,5626],{"class":88,"line":5625},59,[86,5627,5628],{"class":461},"private:\n",[86,5630,5632,5635],{"class":88,"line":5631},60,[86,5633,5634],{"class":92},"  std",[86,5636,5637],{"class":1220},"::string serial_port_;\n",[86,5639,5641,5644,5647,5649,5652],{"class":88,"line":5640},61,[86,5642,5643],{"class":461},"  double",[86,5645,5646],{"class":1220}," left_position_ ",[86,5648,1231],{"class":461},[86,5650,5651],{"class":343}," 0.0",[86,5653,5654],{"class":1220},";\n",[86,5656,5658,5660,5663,5665,5667],{"class":88,"line":5657},62,[86,5659,5643],{"class":461},[86,5661,5662],{"class":1220}," left_velocity_ ",[86,5664,1231],{"class":461},[86,5666,5651],{"class":343},[86,5668,5654],{"class":1220},[86,5670,5672,5674,5677,5679,5681],{"class":88,"line":5671},63,[86,5673,5643],{"class":461},[86,5675,5676],{"class":1220}," right_position_ ",[86,5678,1231],{"class":461},[86,5680,5651],{"class":343},[86,5682,5654],{"class":1220},[86,5684,5686,5688,5691,5693,5695],{"class":88,"line":5685},64,[86,5687,5643],{"class":461},[86,5689,5690],{"class":1220}," right_velocity_ ",[86,5692,1231],{"class":461},[86,5694,5651],{"class":343},[86,5696,5654],{"class":1220},[86,5698,5700],{"class":88,"line":5699},65,[86,5701,5702],{"class":1220},"};\n",[86,5704,5706],{"class":88,"line":5705},66,[86,5707,1569],{"emptyLinePlaceholder":1568},[86,5709,5711,5714],{"class":88,"line":5710},67,[86,5712,5713],{"class":1220},"}",[86,5715,5716],{"class":5251},"  // namespace my_robot_hardware\n",[20,5718,5719],{},"插件导出：",[77,5721,5723],{"className":4915,"code":5722,"language":4917,"meta":82,"style":82},"#include \"pluginlib/class_list_macros.hpp\"\n\nPLUGINLIB_EXPORT_CLASS(\n  my_robot_hardware::MyDiffBotSystem,\n  hardware_interface::SystemInterface)\n",[29,5724,5725,5732,5736,5743,5751],{"__ignoreMap":82},[86,5726,5727,5729],{"class":88,"line":89},[86,5728,4985],{"class":461},[86,5730,5731],{"class":304}," \"pluginlib/class_list_macros.hpp\"\n",[86,5733,5734],{"class":88,"line":311},[86,5735,1569],{"emptyLinePlaceholder":1568},[86,5737,5738,5741],{"class":88,"line":354},[86,5739,5740],{"class":92},"PLUGINLIB_EXPORT_CLASS",[86,5742,5381],{"class":1220},[86,5744,5745,5748],{"class":88,"line":362},[86,5746,5747],{"class":92},"  my_robot_hardware",[86,5749,5750],{"class":1220},"::MyDiffBotSystem,\n",[86,5752,5753,5755],{"class":88,"line":370},[86,5754,5071],{"class":92},[86,5756,5757],{"class":1220},"::SystemInterface)\n",[20,5759,5760],{},"对应插件 XML：",[77,5762,5764],{"className":1211,"code":5763,"language":1213,"meta":82,"style":82},"\u003Clibrary path=\"my_robot_hardware\">\n  \u003Cclass\n    name=\"my_robot_hardware/MyDiffBotSystem\"\n    type=\"my_robot_hardware::MyDiffBotSystem\"\n    base_class_type=\"hardware_interface::SystemInterface\">\n    \u003Cdescription>My differential drive robot hardware interface.\u003C/description>\n  \u003C/class>\n\u003C/library>\n",[29,5765,5766,5783,5790,5800,5810,5822,5836,5844],{"__ignoreMap":82},[86,5767,5768,5770,5773,5776,5778,5781],{"class":88,"line":89},[86,5769,1221],{"class":1220},[86,5771,5772],{"class":1224},"library",[86,5774,5775],{"class":92}," path",[86,5777,1231],{"class":1220},[86,5779,5780],{"class":304},"\"my_robot_hardware\"",[86,5782,1237],{"class":1220},[86,5784,5785,5787],{"class":88,"line":311},[86,5786,1242],{"class":1220},[86,5788,5789],{"class":1224},"class\n",[86,5791,5792,5795,5797],{"class":88,"line":354},[86,5793,5794],{"class":92},"    name",[86,5796,1231],{"class":1220},[86,5798,5799],{"class":304},"\"my_robot_hardware/MyDiffBotSystem\"\n",[86,5801,5802,5805,5807],{"class":88,"line":362},[86,5803,5804],{"class":92},"    type",[86,5806,1231],{"class":1220},[86,5808,5809],{"class":304},"\"my_robot_hardware::MyDiffBotSystem\"\n",[86,5811,5812,5815,5817,5820],{"class":88,"line":370},[86,5813,5814],{"class":92},"    base_class_type",[86,5816,1231],{"class":1220},[86,5818,5819],{"class":304},"\"hardware_interface::SystemInterface\"",[86,5821,1237],{"class":1220},[86,5823,5824,5826,5829,5832,5834],{"class":88,"line":378},[86,5825,1544],{"class":1220},[86,5827,5828],{"class":1224},"description",[86,5830,5831],{"class":1220},">My differential drive robot hardware interface.\u003C/",[86,5833,5828],{"class":1224},[86,5835,1237],{"class":1220},[86,5837,5838,5840,5842],{"class":88,"line":386},[86,5839,1559],{"class":1220},[86,5841,5039],{"class":1224},[86,5843,1237],{"class":1220},[86,5845,5846,5848,5850],{"class":88,"line":394},[86,5847,1290],{"class":1220},[86,5849,5772],{"class":1224},[86,5851,1237],{"class":1220},[20,5853,5854],{},"URDF 中使用：",[77,5856,5858],{"className":1211,"code":5857,"language":1213,"meta":82,"style":82},"\u003Chardware>\n  \u003Cplugin>my_robot_hardware/MyDiffBotSystem\u003C/plugin>\n  \u003Cparam name=\"serial_port\">/dev/ttyUSB0\u003C/param>\n\u003C/hardware>\n",[29,5859,5860,5868,5881,5899],{"__ignoreMap":82},[86,5861,5862,5864,5866],{"class":88,"line":89},[86,5863,1221],{"class":1220},[86,5865,1537],{"class":1224},[86,5867,1237],{"class":1220},[86,5869,5870,5872,5874,5877,5879],{"class":88,"line":311},[86,5871,1242],{"class":1220},[86,5873,1547],{"class":1224},[86,5875,5876],{"class":1220},">my_robot_hardware/MyDiffBotSystem\u003C/",[86,5878,1547],{"class":1224},[86,5880,1237],{"class":1220},[86,5882,5883,5885,5887,5889,5891,5893,5895,5897],{"class":88,"line":354},[86,5884,1242],{"class":1220},[86,5886,2014],{"class":1224},[86,5888,1228],{"class":92},[86,5890,1231],{"class":1220},[86,5892,2021],{"class":304},[86,5894,2024],{"class":1220},[86,5896,2014],{"class":1224},[86,5898,1237],{"class":1220},[86,5900,5901,5903,5905],{"class":88,"line":362},[86,5902,1290],{"class":1220},[86,5904,1537],{"class":1224},[86,5906,1237],{"class":1220},[15,5908,5909],{"id":5909},"硬件接口调试顺序",[20,5911,5912],{},"写硬件接口时建议按这个顺序调试：",[4675,5914,5915,5922,5930,5935,5942,5947,5953,5956,5963],{},[37,5916,5917,5918,5921],{},"先不用真实电机，确认 ",[29,5919,5920],{},"pluginlib"," 能加载硬件插件。",[37,5923,3843,5924,5926,5927,1307],{},[29,5925,150],{},"，运行 ",[29,5928,5929],{},"ros2 control list_hardware_components",[37,5931,5932,5933,1307],{},"确认硬件组件能进入 ",[29,5934,1442],{},[37,5936,5937,5938,5941],{},"确认 ",[29,5939,5940],{},"list_hardware_interfaces"," 中有期望的 command/state interfaces。",[37,5943,256,5944,5946],{},[29,5945,726],{}," 直接给 command interface 发命令。",[37,5948,5949,5950,5952],{},"在 ",[29,5951,1376],{}," 中打印一次命令，确认控制器确实写入。",[37,5954,5955],{},"接真实电机，但先架空轮子或关闭高功率输出。",[37,5957,5937,5958,5960,5961,1307],{},[29,5959,1350],{}," 能正确更新 ",[29,5962,642],{},[37,5964,5965,5966,706,5968,1307],{},"最后再接 ",[29,5967,669],{},[29,5969,694],{},[10,5971,5972],{"id":5972},"自定义运动学控制器",[15,5974,5975],{"id":5975},"什么时候需要写控制器",[20,5977,5978],{},"不建议一开始就写自定义控制器。优先判断官方控制器是否够用：",[97,5980,5981,5991],{},[100,5982,5983],{},[103,5984,5985,5988],{},[106,5986,5987],{"align":108},"需求",[106,5989,5990],{"align":108},"推荐方案",[117,5992,5993,6001,6009,6018,6027,6036,6044],{},[103,5994,5995,5997],{},[122,5996,3889],{"align":108},[122,5998,5999],{"align":108},[29,6000,669],{},[103,6002,6003,6005],{},[122,6004,3912],{"align":108},[122,6006,6007],{"align":108},[29,6008,694],{},[103,6010,6011,6014],{},[122,6012,6013],{"align":108},"只想直接写关节位置",[122,6015,6016],{"align":108},[29,6017,3944],{},[103,6019,6020,6023],{},[122,6021,6022],{"align":108},"只想直接写关节速度",[122,6024,6025],{"align":108},[29,6026,3961],{},[103,6028,6029,6032],{},[122,6030,6031],{"align":108},"只想测试硬件命令",[122,6033,6034],{"align":108},[29,6035,726],{},[103,6037,6038,6041],{},[122,6039,6040],{"align":108},"特殊底盘运动学",[122,6042,6043],{"align":108},"自定义 controller",[103,6045,6046,6049],{},[122,6047,6048],{"align":108},"自定义阻抗、导纳、力控策略",[122,6050,6051,6052,6055],{"align":108},"自定义 controller 或基于 ",[29,6053,6054],{},"admittance_controller"," 扩展",[20,6057,6058],{},"如果你的机器人是四轮差速、麦克纳姆轮、舵轮、特殊并联机构、腿式机器人，官方控制器不一定完全匹配，这时就需要写自定义控制器。",[15,6060,6062],{"id":6061},"controllerinterface-关键函数","ControllerInterface 关键函数",[20,6064,6065],{},"Jazzy 中普通控制器继承：",[77,6067,6069],{"className":4915,"code":6068,"language":4917,"meta":82,"style":82},"controller_interface::ControllerInterface\n",[29,6070,6071],{"__ignoreMap":82},[86,6072,6073,6075],{"class":88,"line":89},[86,6074,162],{"class":92},[86,6076,6077],{"class":1220},"::ControllerInterface\n",[20,6079,6080],{},"必须关注这些函数：",[97,6082,6083,6091],{},[100,6084,6085],{},[103,6086,6087,6089],{},[106,6088,4818],{"align":108},[106,6090,1341],{"align":108},[117,6092,6093,6103,6113,6123,6133,6143,6153],{},[103,6094,6095,6100],{},[122,6096,6097],{"align":108},[29,6098,6099],{},"on_init()",[122,6101,6102],{"align":108},"声明参数，初始化非实时对象",[103,6104,6105,6110],{},[122,6106,6107],{"align":108},[29,6108,6109],{},"command_interface_configuration()",[122,6111,6112],{"align":108},"声明要占用哪些 command interfaces",[103,6114,6115,6120],{},[122,6116,6117],{"align":108},[29,6118,6119],{},"state_interface_configuration()",[122,6121,6122],{"align":108},"声明要读取哪些 state interfaces",[103,6124,6125,6130],{},[122,6126,6127],{"align":108},[29,6128,6129],{},"on_configure()",[122,6131,6132],{"align":108},"读取参数、创建订阅者、初始化缓存",[103,6134,6135,6140],{},[122,6136,6137],{"align":108},[29,6138,6139],{},"on_activate()",[122,6141,6142],{"align":108},"激活前检查接口、清零命令",[103,6144,6145,6150],{},[122,6146,6147],{"align":108},[29,6148,6149],{},"on_deactivate()",[122,6151,6152],{"align":108},"停止控制器、释放状态",[103,6154,6155,6160],{},[122,6156,6157],{"align":108},[29,6158,6159],{},"update(time, period)",[122,6161,6162],{"align":108},"实时控制循环，计算并写入命令",[20,6164,408,6165,6167],{},[29,6166,1363],{}," 在实时控制循环中执行，不应该做这些事：",[34,6169,6170,6173,6176,6179,6182,6185],{},[37,6171,6172],{},"不要动态分配大量内存；",[37,6174,6175],{},"不要访问阻塞 I/O；",[37,6177,6178],{},"不要等待锁；",[37,6180,6181],{},"不要频繁打印日志；",[37,6183,6184],{},"不要在里面创建 publisher/subscriber；",[37,6186,6187],{},"不要做可能阻塞的参数读取。",[20,6189,6190,6191,6194,6195,6197],{},"订阅 ROS 话题时，常用做法是普通回调写入 ",[29,6192,6193],{},"realtime_tools::RealtimeBuffer","，",[29,6196,1363],{}," 中只读这个 buffer。",[15,6199,6200],{"id":6200},"一个自定义底盘控制器的思路",[20,6202,6203,6204,6206],{},"假设要写一个自定义底盘控制器，输入是 ",[29,6205,45],{},"，输出是四个轮子的速度命令。核心步骤是：",[4675,6208,6209,6212,6220,6230,6238,6241,6246,6249],{},[37,6210,6211],{},"参数中声明四个轮子的 joint 名。",[37,6213,6214,6216,6217,1307],{},[29,6215,6109],{}," 返回四个 ",[29,6218,6219],{},"\u003Cjoint>/velocity",[37,6221,6222,6224,6225,706,6228,1307],{},[29,6223,6119],{}," 返回需要读取的 ",[29,6226,6227],{},"\u003Cjoint>/position",[29,6229,6219],{},[37,6231,6232,6234,6235,6237],{},[29,6233,6129],{}," 创建 ",[29,6236,45],{}," 订阅者。",[37,6239,6240],{},"订阅者把最新速度命令写入实时 buffer。",[37,6242,6243,6245],{},[29,6244,1363],{}," 读取最新速度命令。",[37,6247,6248],{},"根据运动学模型计算各轮速度。",[37,6250,6251,6252,1307],{},"把结果写入 ",[29,6253,6254],{},"command_interfaces_",[20,6256,6257],{},"伪代码如下：",[77,6259,6261],{"className":4915,"code":6260,"language":4917,"meta":82,"style":82},"controller_interface::InterfaceConfiguration\nMyKinematicsController::command_interface_configuration() const\n{\n  controller_interface::InterfaceConfiguration config;\n  config.type = controller_interface::interface_configuration_type::INDIVIDUAL;\n  config.names = {\n    \"front_left_wheel_joint/velocity\",\n    \"front_right_wheel_joint/velocity\",\n    \"rear_left_wheel_joint/velocity\",\n    \"rear_right_wheel_joint/velocity\"\n  };\n  return config;\n}\n\ncontroller_interface::return_type MyKinematicsController::update(\n  const rclcpp::Time &, const rclcpp::Duration &)\n{\n  const auto cmd = *command_buffer_.readFromRT();\n\n  const double vx = cmd.linear.x;\n  const double wz = cmd.angular.z;\n\n  const double left = vx - wz * wheel_separation_ * 0.5;\n  const double right = vx + wz * wheel_separation_ * 0.5;\n\n  command_interfaces_[0].set_value(left / wheel_radius_);\n  command_interfaces_[1].set_value(right / wheel_radius_);\n  command_interfaces_[2].set_value(left / wheel_radius_);\n  command_interfaces_[3].set_value(right / wheel_radius_);\n\n  return controller_interface::return_type::OK;\n}\n",[29,6262,6263,6270,6286,6290,6298,6316,6326,6333,6340,6347,6352,6357,6365,6370,6374,6392,6420,6424,6447,6451,6465,6479,6483,6514,6542,6546,6569,6587,6604,6621,6625,6637],{"__ignoreMap":82},[86,6264,6265,6267],{"class":88,"line":89},[86,6266,162],{"class":92},[86,6268,6269],{"class":1220},"::InterfaceConfiguration\n",[86,6271,6272,6275,6277,6280,6283],{"class":88,"line":311},[86,6273,6274],{"class":92},"MyKinematicsController",[86,6276,5054],{"class":1220},[86,6278,6279],{"class":92},"command_interface_configuration",[86,6281,6282],{"class":1220},"() ",[86,6284,6285],{"class":461},"const\n",[86,6287,6288],{"class":88,"line":354},[86,6289,5030],{"class":1220},[86,6291,6292,6295],{"class":88,"line":362},[86,6293,6294],{"class":92},"  controller_interface",[86,6296,6297],{"class":1220},"::InterfaceConfiguration config;\n",[86,6299,6300,6303,6305,6308,6310,6313],{"class":88,"line":370},[86,6301,6302],{"class":1220},"  config.type ",[86,6304,1231],{"class":461},[86,6306,6307],{"class":92}," controller_interface",[86,6309,5054],{"class":1220},[86,6311,6312],{"class":92},"interface_configuration_type",[86,6314,6315],{"class":1220},"::INDIVIDUAL;\n",[86,6317,6318,6321,6323],{"class":88,"line":378},[86,6319,6320],{"class":1220},"  config.names ",[86,6322,1231],{"class":461},[86,6324,6325],{"class":1220}," {\n",[86,6327,6328,6331],{"class":88,"line":386},[86,6329,6330],{"class":304},"    \"front_left_wheel_joint/velocity\"",[86,6332,2374],{"class":1220},[86,6334,6335,6338],{"class":88,"line":394},[86,6336,6337],{"class":304},"    \"front_right_wheel_joint/velocity\"",[86,6339,2374],{"class":1220},[86,6341,6342,6345],{"class":88,"line":402},[86,6343,6344],{"class":304},"    \"rear_left_wheel_joint/velocity\"",[86,6346,2374],{"class":1220},[86,6348,6349],{"class":88,"line":585},[86,6350,6351],{"class":304},"    \"rear_right_wheel_joint/velocity\"\n",[86,6353,6354],{"class":88,"line":591},[86,6355,6356],{"class":1220},"  };\n",[86,6358,6359,6362],{"class":88,"line":597},[86,6360,6361],{"class":461},"  return",[86,6363,6364],{"class":1220}," config;\n",[86,6366,6367],{"class":88,"line":1879},[86,6368,6369],{"class":1220},"}\n",[86,6371,6372],{"class":88,"line":1894},[86,6373,1569],{"emptyLinePlaceholder":1568},[86,6375,6376,6378,6380,6382,6385,6387,6390],{"class":88,"line":1909},[86,6377,162],{"class":92},[86,6379,5054],{"class":1220},[86,6381,5375],{"class":92},[86,6383,6384],{"class":92}," MyKinematicsController",[86,6386,5054],{"class":1220},[86,6388,6389],{"class":92},"update",[86,6391,5381],{"class":1220},[86,6393,6394,6397,6399,6401,6403,6405,6407,6409,6411,6413,6415,6417],{"class":88,"line":1924},[86,6395,6396],{"class":461},"  const",[86,6398,5390],{"class":92},[86,6400,5054],{"class":1220},[86,6402,5395],{"class":92},[86,6404,5094],{"class":461},[86,6406,5321],{"class":1220},[86,6408,5084],{"class":461},[86,6410,5390],{"class":92},[86,6412,5054],{"class":1220},[86,6414,5408],{"class":92},[86,6416,5094],{"class":461},[86,6418,6419],{"class":1220},")\n",[86,6421,6422],{"class":88,"line":1933},[86,6423,5030],{"class":1220},[86,6425,6426,6428,6431,6433,6435,6438,6441,6444],{"class":88,"line":4375},[86,6427,6396],{"class":461},[86,6429,6430],{"class":461}," auto",[86,6432,4952],{"class":1220},[86,6434,1231],{"class":461},[86,6436,6437],{"class":461}," *",[86,6439,6440],{"class":1220},"command_buffer_.",[86,6442,6443],{"class":92},"readFromRT",[86,6445,6446],{"class":1220},"();\n",[86,6448,6449],{"class":88,"line":4386},[86,6450,1569],{"emptyLinePlaceholder":1568},[86,6452,6453,6455,6457,6460,6462],{"class":88,"line":4397},[86,6454,6396],{"class":461},[86,6456,5555],{"class":461},[86,6458,6459],{"class":1220}," vx ",[86,6461,1231],{"class":461},[86,6463,6464],{"class":1220}," cmd.linear.x;\n",[86,6466,6467,6469,6471,6474,6476],{"class":88,"line":4406},[86,6468,6396],{"class":461},[86,6470,5555],{"class":461},[86,6472,6473],{"class":1220}," wz ",[86,6475,1231],{"class":461},[86,6477,6478],{"class":1220}," cmd.angular.z;\n",[86,6480,6481],{"class":88,"line":4415},[86,6482,1569],{"emptyLinePlaceholder":1568},[86,6484,6485,6487,6489,6492,6494,6496,6499,6501,6504,6507,6509,6512],{"class":88,"line":4424},[86,6486,6396],{"class":461},[86,6488,5555],{"class":461},[86,6490,6491],{"class":1220}," left ",[86,6493,1231],{"class":461},[86,6495,6459],{"class":1220},[86,6497,6498],{"class":461},"-",[86,6500,6473],{"class":1220},[86,6502,6503],{"class":461},"*",[86,6505,6506],{"class":1220}," wheel_separation_ ",[86,6508,6503],{"class":461},[86,6510,6511],{"class":343}," 0.5",[86,6513,5654],{"class":1220},[86,6515,6516,6518,6520,6523,6525,6527,6530,6532,6534,6536,6538,6540],{"class":88,"line":4433},[86,6517,6396],{"class":461},[86,6519,5555],{"class":461},[86,6521,6522],{"class":1220}," right ",[86,6524,1231],{"class":461},[86,6526,6459],{"class":1220},[86,6528,6529],{"class":461},"+",[86,6531,6473],{"class":1220},[86,6533,6503],{"class":461},[86,6535,6506],{"class":1220},[86,6537,6503],{"class":461},[86,6539,6511],{"class":343},[86,6541,5654],{"class":1220},[86,6543,6544],{"class":88,"line":4442},[86,6545,1569],{"emptyLinePlaceholder":1568},[86,6547,6548,6551,6554,6557,6560,6563,6566],{"class":88,"line":4451},[86,6549,6550],{"class":1220},"  command_interfaces_[",[86,6552,6553],{"class":343},"0",[86,6555,6556],{"class":1220},"].",[86,6558,6559],{"class":92},"set_value",[86,6561,6562],{"class":1220},"(left ",[86,6564,6565],{"class":461},"/",[86,6567,6568],{"class":1220}," wheel_radius_);\n",[86,6570,6571,6573,6576,6578,6580,6583,6585],{"class":88,"line":4460},[86,6572,6550],{"class":1220},[86,6574,6575],{"class":343},"1",[86,6577,6556],{"class":1220},[86,6579,6559],{"class":92},[86,6581,6582],{"class":1220},"(right ",[86,6584,6565],{"class":461},[86,6586,6568],{"class":1220},[86,6588,6589,6591,6594,6596,6598,6600,6602],{"class":88,"line":5255},[86,6590,6550],{"class":1220},[86,6592,6593],{"class":343},"2",[86,6595,6556],{"class":1220},[86,6597,6559],{"class":92},[86,6599,6562],{"class":1220},[86,6601,6565],{"class":461},[86,6603,6568],{"class":1220},[86,6605,6606,6608,6611,6613,6615,6617,6619],{"class":88,"line":5268},[86,6607,6550],{"class":1220},[86,6609,6610],{"class":343},"3",[86,6612,6556],{"class":1220},[86,6614,6559],{"class":92},[86,6616,6582],{"class":1220},[86,6618,6565],{"class":461},[86,6620,6568],{"class":1220},[86,6622,6623],{"class":88,"line":5273},[86,6624,1569],{"emptyLinePlaceholder":1568},[86,6626,6627,6629,6631,6633,6635],{"class":88,"line":5278},[86,6628,6361],{"class":461},[86,6630,6307],{"class":92},[86,6632,5054],{"class":1220},[86,6634,5375],{"class":92},[86,6636,5489],{"class":1220},[86,6638,6639],{"class":88,"line":5306},[86,6640,6369],{"class":1220},[20,6642,6643],{},"这段代码只是运动学控制器核心逻辑。完整控制器还需要参数声明、订阅者、接口排序检查、超时保护、插件导出和 CMake 配置。",[15,6645,6647],{"id":6646},"自定义控制器-packagexml","自定义控制器 package.xml",[20,6649,6650],{},"常见依赖如下：",[77,6652,6654],{"className":1211,"code":6653,"language":1213,"meta":82,"style":82},"\u003Cdepend>controller_interface\u003C/depend>\n\u003Cdepend>hardware_interface\u003C/depend>\n\u003Cdepend>pluginlib\u003C/depend>\n\u003Cdepend>rclcpp\u003C/depend>\n\u003Cdepend>rclcpp_lifecycle\u003C/depend>\n\u003Cdepend>realtime_tools\u003C/depend>\n\u003Cdepend>geometry_msgs\u003C/depend>\n",[29,6655,6656,6670,6683,6696,6709,6722,6735],{"__ignoreMap":82},[86,6657,6658,6660,6663,6666,6668],{"class":88,"line":89},[86,6659,1221],{"class":1220},[86,6661,6662],{"class":1224},"depend",[86,6664,6665],{"class":1220},">controller_interface\u003C/",[86,6667,6662],{"class":1224},[86,6669,1237],{"class":1220},[86,6671,6672,6674,6676,6679,6681],{"class":88,"line":311},[86,6673,1221],{"class":1220},[86,6675,6662],{"class":1224},[86,6677,6678],{"class":1220},">hardware_interface\u003C/",[86,6680,6662],{"class":1224},[86,6682,1237],{"class":1220},[86,6684,6685,6687,6689,6692,6694],{"class":88,"line":354},[86,6686,1221],{"class":1220},[86,6688,6662],{"class":1224},[86,6690,6691],{"class":1220},">pluginlib\u003C/",[86,6693,6662],{"class":1224},[86,6695,1237],{"class":1220},[86,6697,6698,6700,6702,6705,6707],{"class":88,"line":362},[86,6699,1221],{"class":1220},[86,6701,6662],{"class":1224},[86,6703,6704],{"class":1220},">rclcpp\u003C/",[86,6706,6662],{"class":1224},[86,6708,1237],{"class":1220},[86,6710,6711,6713,6715,6718,6720],{"class":88,"line":370},[86,6712,1221],{"class":1220},[86,6714,6662],{"class":1224},[86,6716,6717],{"class":1220},">rclcpp_lifecycle\u003C/",[86,6719,6662],{"class":1224},[86,6721,1237],{"class":1220},[86,6723,6724,6726,6728,6731,6733],{"class":88,"line":378},[86,6725,1221],{"class":1220},[86,6727,6662],{"class":1224},[86,6729,6730],{"class":1220},">realtime_tools\u003C/",[86,6732,6662],{"class":1224},[86,6734,1237],{"class":1220},[86,6736,6737,6739,6741,6744,6746],{"class":88,"line":386},[86,6738,1221],{"class":1220},[86,6740,6662],{"class":1224},[86,6742,6743],{"class":1220},">geometry_msgs\u003C/",[86,6745,6662],{"class":1224},[86,6747,1237],{"class":1220},[20,6749,6750],{},"如果要发布里程计，还需要：",[77,6752,6754],{"className":1211,"code":6753,"language":1213,"meta":82,"style":82},"\u003Cdepend>nav_msgs\u003C/depend>\n\u003Cdepend>tf2\u003C/depend>\n\u003Cdepend>tf2_msgs\u003C/depend>\n\u003Cdepend>tf2_ros\u003C/depend>\n",[29,6755,6756,6769,6782,6795],{"__ignoreMap":82},[86,6757,6758,6760,6762,6765,6767],{"class":88,"line":89},[86,6759,1221],{"class":1220},[86,6761,6662],{"class":1224},[86,6763,6764],{"class":1220},">nav_msgs\u003C/",[86,6766,6662],{"class":1224},[86,6768,1237],{"class":1220},[86,6770,6771,6773,6775,6778,6780],{"class":88,"line":311},[86,6772,1221],{"class":1220},[86,6774,6662],{"class":1224},[86,6776,6777],{"class":1220},">tf2\u003C/",[86,6779,6662],{"class":1224},[86,6781,1237],{"class":1220},[86,6783,6784,6786,6788,6791,6793],{"class":88,"line":354},[86,6785,1221],{"class":1220},[86,6787,6662],{"class":1224},[86,6789,6790],{"class":1220},">tf2_msgs\u003C/",[86,6792,6662],{"class":1224},[86,6794,1237],{"class":1220},[86,6796,6797,6799,6801,6804,6806],{"class":88,"line":362},[86,6798,1221],{"class":1220},[86,6800,6662],{"class":1224},[86,6802,6803],{"class":1220},">tf2_ros\u003C/",[86,6805,6662],{"class":1224},[86,6807,1237],{"class":1220},[15,6809,6811],{"id":6810},"自定义控制器-cmakelists","自定义控制器 CMakeLists",[20,6813,6814],{},"核心写法如下：",[77,6816,6820],{"className":6817,"code":6818,"language":6819,"meta":82,"style":82},"language-cmake shiki shiki-themes github-light github-dark","add_library(my_kinematics_controller SHARED\n  src/my_kinematics_controller.cpp\n)\n\nament_target_dependencies(my_kinematics_controller\n  controller_interface\n  hardware_interface\n  pluginlib\n  rclcpp\n  rclcpp_lifecycle\n  realtime_tools\n  geometry_msgs\n)\n\npluginlib_export_plugin_description_file(\n  controller_interface\n  my_kinematics_controller.xml\n)\n\ninstall(\n  TARGETS my_kinematics_controller\n  ARCHIVE DESTINATION lib\n  LIBRARY DESTINATION lib\n  RUNTIME DESTINATION bin\n)\n\ninstall(\n  FILES my_kinematics_controller.xml\n  DESTINATION share/${PROJECT_NAME}\n)\n","cmake",[29,6821,6822,6830,6835,6839,6843,6848,6853,6858,6863,6868,6873,6878,6883,6887,6891,6896,6900,6905,6909,6913,6920,6925,6930,6935,6940,6944,6948,6954,6959,6967],{"__ignoreMap":82},[86,6823,6824,6827],{"class":88,"line":89},[86,6825,6826],{"class":461},"add_library",[86,6828,6829],{"class":1220},"(my_kinematics_controller SHARED\n",[86,6831,6832],{"class":88,"line":311},[86,6833,6834],{"class":1220},"  src/my_kinematics_controller.cpp\n",[86,6836,6837],{"class":88,"line":354},[86,6838,6419],{"class":1220},[86,6840,6841],{"class":88,"line":362},[86,6842,1569],{"emptyLinePlaceholder":1568},[86,6844,6845],{"class":88,"line":370},[86,6846,6847],{"class":1220},"ament_target_dependencies(my_kinematics_controller\n",[86,6849,6850],{"class":88,"line":378},[86,6851,6852],{"class":1220},"  controller_interface\n",[86,6854,6855],{"class":88,"line":386},[86,6856,6857],{"class":1220},"  hardware_interface\n",[86,6859,6860],{"class":88,"line":394},[86,6861,6862],{"class":1220},"  pluginlib\n",[86,6864,6865],{"class":88,"line":402},[86,6866,6867],{"class":1220},"  rclcpp\n",[86,6869,6870],{"class":88,"line":585},[86,6871,6872],{"class":1220},"  rclcpp_lifecycle\n",[86,6874,6875],{"class":88,"line":591},[86,6876,6877],{"class":1220},"  realtime_tools\n",[86,6879,6880],{"class":88,"line":597},[86,6881,6882],{"class":1220},"  geometry_msgs\n",[86,6884,6885],{"class":88,"line":1879},[86,6886,6419],{"class":1220},[86,6888,6889],{"class":88,"line":1894},[86,6890,1569],{"emptyLinePlaceholder":1568},[86,6892,6893],{"class":88,"line":1909},[86,6894,6895],{"class":1220},"pluginlib_export_plugin_description_file(\n",[86,6897,6898],{"class":88,"line":1924},[86,6899,6852],{"class":1220},[86,6901,6902],{"class":88,"line":1933},[86,6903,6904],{"class":1220},"  my_kinematics_controller.xml\n",[86,6906,6907],{"class":88,"line":4375},[86,6908,6419],{"class":1220},[86,6910,6911],{"class":88,"line":4386},[86,6912,1569],{"emptyLinePlaceholder":1568},[86,6914,6915,6918],{"class":88,"line":4397},[86,6916,6917],{"class":461},"install",[86,6919,5381],{"class":1220},[86,6921,6922],{"class":88,"line":4406},[86,6923,6924],{"class":1220},"  TARGETS my_kinematics_controller\n",[86,6926,6927],{"class":88,"line":4415},[86,6928,6929],{"class":1220},"  ARCHIVE DESTINATION lib\n",[86,6931,6932],{"class":88,"line":4424},[86,6933,6934],{"class":1220},"  LIBRARY DESTINATION lib\n",[86,6936,6937],{"class":88,"line":4433},[86,6938,6939],{"class":1220},"  RUNTIME DESTINATION bin\n",[86,6941,6942],{"class":88,"line":4442},[86,6943,6419],{"class":1220},[86,6945,6946],{"class":88,"line":4451},[86,6947,1569],{"emptyLinePlaceholder":1568},[86,6949,6950,6952],{"class":88,"line":4460},[86,6951,6917],{"class":461},[86,6953,5381],{"class":1220},[86,6955,6956],{"class":88,"line":5255},[86,6957,6958],{"class":1220},"  FILES my_kinematics_controller.xml\n",[86,6960,6961,6964],{"class":88,"line":5268},[86,6962,6963],{"class":1220},"  DESTINATION share/",[86,6965,6966],{"class":461},"${PROJECT_NAME}\n",[86,6968,6969],{"class":88,"line":5273},[86,6970,6419],{"class":1220},[20,6972,6973],{},"插件 XML：",[77,6975,6977],{"className":1211,"code":6976,"language":1213,"meta":82,"style":82},"\u003Clibrary path=\"my_kinematics_controller\">\n  \u003Cclass\n    name=\"my_kinematics_controller/MyKinematicsController\"\n    type=\"my_kinematics_controller::MyKinematicsController\"\n    base_class_type=\"controller_interface::ControllerInterface\">\n    \u003Cdescription>My custom mobile robot kinematics controller.\u003C/description>\n  \u003C/class>\n\u003C/library>\n",[29,6978,6979,6994,7000,7009,7018,7029,7042,7050],{"__ignoreMap":82},[86,6980,6981,6983,6985,6987,6989,6992],{"class":88,"line":89},[86,6982,1221],{"class":1220},[86,6984,5772],{"class":1224},[86,6986,5775],{"class":92},[86,6988,1231],{"class":1220},[86,6990,6991],{"class":304},"\"my_kinematics_controller\"",[86,6993,1237],{"class":1220},[86,6995,6996,6998],{"class":88,"line":311},[86,6997,1242],{"class":1220},[86,6999,5789],{"class":1224},[86,7001,7002,7004,7006],{"class":88,"line":354},[86,7003,5794],{"class":92},[86,7005,1231],{"class":1220},[86,7007,7008],{"class":304},"\"my_kinematics_controller/MyKinematicsController\"\n",[86,7010,7011,7013,7015],{"class":88,"line":362},[86,7012,5804],{"class":92},[86,7014,1231],{"class":1220},[86,7016,7017],{"class":304},"\"my_kinematics_controller::MyKinematicsController\"\n",[86,7019,7020,7022,7024,7027],{"class":88,"line":370},[86,7021,5814],{"class":92},[86,7023,1231],{"class":1220},[86,7025,7026],{"class":304},"\"controller_interface::ControllerInterface\"",[86,7028,1237],{"class":1220},[86,7030,7031,7033,7035,7038,7040],{"class":88,"line":378},[86,7032,1544],{"class":1220},[86,7034,5828],{"class":1224},[86,7036,7037],{"class":1220},">My custom mobile robot kinematics controller.\u003C/",[86,7039,5828],{"class":1224},[86,7041,1237],{"class":1220},[86,7043,7044,7046,7048],{"class":88,"line":386},[86,7045,1559],{"class":1220},[86,7047,5039],{"class":1224},[86,7049,1237],{"class":1220},[86,7051,7052,7054,7056],{"class":88,"line":394},[86,7053,1290],{"class":1220},[86,7055,5772],{"class":1224},[86,7057,1237],{"class":1220},[20,7059,7060],{},"YAML 中加载：",[77,7062,7064],{"className":2126,"code":7063,"language":2128,"meta":82,"style":82},"controller_manager:\n  ros__parameters:\n    my_kinematics_controller:\n      type: my_kinematics_controller/MyKinematicsController\n\nmy_kinematics_controller:\n  ros__parameters:\n    wheel_radius: 0.05\n    wheel_separation: 0.40\n",[29,7065,7066,7072,7078,7085,7094,7098,7105,7111,7119],{"__ignoreMap":82},[86,7067,7068,7070],{"class":88,"line":89},[86,7069,71],{"class":1224},[86,7071,2137],{"class":1220},[86,7073,7074,7076],{"class":88,"line":311},[86,7075,2142],{"class":1224},[86,7077,2137],{"class":1220},[86,7079,7080,7083],{"class":88,"line":354},[86,7081,7082],{"class":1224},"    my_kinematics_controller",[86,7084,2137],{"class":1220},[86,7086,7087,7089,7091],{"class":88,"line":362},[86,7088,2171],{"class":1224},[86,7090,2152],{"class":1220},[86,7092,7093],{"class":304},"my_kinematics_controller/MyKinematicsController\n",[86,7095,7096],{"class":88,"line":370},[86,7097,1569],{"emptyLinePlaceholder":1568},[86,7099,7100,7103],{"class":88,"line":378},[86,7101,7102],{"class":1224},"my_kinematics_controller",[86,7104,2137],{"class":1220},[86,7106,7107,7109],{"class":88,"line":386},[86,7108,2142],{"class":1224},[86,7110,2137],{"class":1220},[86,7112,7113,7115,7117],{"class":88,"line":394},[86,7114,3281],{"class":1224},[86,7116,2152],{"class":1220},[86,7118,3286],{"class":343},[86,7120,7121,7123,7125],{"class":88,"line":402},[86,7122,3271],{"class":1224},[86,7124,2152],{"class":1220},[86,7126,3276],{"class":343},[15,7128,7129],{"id":7129},"普通控制器还是链式控制器",[20,7131,7132],{},"Jazzy 中控制器分两类：",[97,7134,7135,7147],{},[100,7136,7137],{},[103,7138,7139,7141,7144],{},[106,7140,2956],{"align":108},[106,7142,7143],{"align":108},"基类",[106,7145,7146],{"align":108},"适用场景",[117,7148,7149,7162],{},[103,7150,7151,7154,7159],{},[122,7152,7153],{"align":108},"普通控制器",[122,7155,7156],{"align":108},[29,7157,7158],{},"controller_interface::ControllerInterface",[122,7160,7161],{"align":108},"从 ROS 话题/action 接收命令，直接写硬件 command interface",[103,7163,7164,7167,7172],{},[122,7165,7166],{"align":108},"链式控制器",[122,7168,7169],{"align":108},[29,7170,7171],{},"controller_interface::ChainableControllerInterface",[122,7173,7174],{"align":108},"上游控制器输出 reference interface，下游控制器继续处理",[20,7176,7177],{},"初学者建议先写普通控制器。只有当你需要多个控制器串联时，再研究链式控制器。例如：",[77,7179,7182],{"className":7180,"code":7181,"language":1321,"meta":82},[1319],"导航速度命令 -> 运动学控制器 -> PID 控制器 -> 硬件接口\n",[29,7183,7181],{"__ignoreMap":82},[20,7185,7186],{},"链式控制器可以用：",[77,7188,7189],{"className":79,"code":2899,"language":81,"meta":82,"style":82},[29,7190,7191],{"__ignoreMap":82},[86,7192,7193,7195,7197],{"class":88,"line":89},[86,7194,452],{"class":92},[86,7196,2760],{"class":304},[86,7198,2910],{"class":304},[20,7200,7201],{},"查看控制器链路。",[10,7203,7205],{"id":7204},"gazebo-与-ros2-control","Gazebo 与 ROS2 Control",[20,7207,7208,7209,7212],{},"ros2_control 本身不是仿真器。它只是控制框架。要在 Gazebo Sim 中使用，需要 ",[29,7210,7211],{},"gz_ros2_control"," 插件。",[20,7214,7215],{},"安装：",[77,7217,7219],{"className":79,"code":7218,"language":81,"meta":82,"style":82},"sudo apt install ros-jazzy-gz-ros2-control ros-jazzy-gz-ros2-control-demos\n",[29,7220,7221],{"__ignoreMap":82},[86,7222,7223,7225,7227,7229,7232],{"class":88,"line":89},[86,7224,301],{"class":92},[86,7226,305],{"class":304},[86,7228,318],{"class":304},[86,7230,7231],{"class":304}," ros-jazzy-gz-ros2-control",[86,7233,7234],{"class":304}," ros-jazzy-gz-ros2-control-demos\n",[20,7236,7237],{},"Gazebo 集成时仍然需要：",[34,7239,7240,7245,7248,7252],{},[37,7241,7242,7243,238],{},"URDF 中的 ",[29,7244,1498],{},[37,7246,7247],{},"控制器 YAML；",[37,7249,7250,238],{},[29,7251,71],{},[37,7253,7254,7256],{},[29,7255,633],{}," 和具体控制器。",[20,7258,7259,7260,7263,7264,7266],{},"区别在于 ",[29,7261,7262],{},"\u003Chardware>\u003Cplugin>...\u003C/plugin>\u003C/hardware>"," 通常换成 Gazebo 对应硬件插件，而不是 ",[29,7265,4799],{}," 或你的真实硬件插件。",[20,7268,7269,7270,7272],{},"Jazzy 中 ",[29,7271,7211],{}," 的硬件插件描述文件是：",[77,7274,7276],{"className":79,"code":7275,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/gz_ros2_control/gz_hardware_plugins.xml\n",[29,7277,7278],{"__ignoreMap":82},[86,7279,7280],{"class":88,"line":89},[86,7281,7275],{"class":92},[20,7283,7284],{},"Gazebo Sim 系统硬件插件名是：",[77,7286,7288],{"className":79,"code":7287,"language":81,"meta":82,"style":82},"gz_ros2_control/GazeboSimSystem\n",[29,7289,7290],{"__ignoreMap":82},[86,7291,7292],{"class":88,"line":89},[86,7293,7287],{"class":92},[20,7295,7296,7297,7299],{},"URDF 或 xacro 中的 ",[29,7298,1498],{}," 关键片段通常写成：",[77,7301,7303],{"className":1211,"code":7302,"language":1213,"meta":82,"style":82},"\u003Cros2_control name=\"GazeboSimSystem\" type=\"system\">\n  \u003Chardware>\n    \u003Cplugin>gz_ros2_control/GazeboSimSystem\u003C/plugin>\n  \u003C/hardware>\n\n  \u003Cjoint name=\"left_wheel_joint\">\n    \u003Ccommand_interface name=\"velocity\"/>\n    \u003Cstate_interface name=\"position\"/>\n    \u003Cstate_interface name=\"velocity\"/>\n  \u003C/joint>\n\n  \u003Cjoint name=\"right_wheel_joint\">\n    \u003Ccommand_interface name=\"velocity\"/>\n    \u003Cstate_interface name=\"position\"/>\n    \u003Cstate_interface name=\"velocity\"/>\n  \u003C/joint>\n\u003C/ros2_control>\n",[29,7304,7305,7326,7334,7347,7355,7359,7373,7387,7401,7415,7423,7427,7441,7455,7469,7483,7491],{"__ignoreMap":82},[86,7306,7307,7309,7311,7313,7315,7318,7320,7322,7324],{"class":88,"line":89},[86,7308,1221],{"class":1220},[86,7310,31],{"class":1224},[86,7312,1228],{"class":92},[86,7314,1231],{"class":1220},[86,7316,7317],{"class":304},"\"GazeboSimSystem\"",[86,7319,1523],{"class":92},[86,7321,1231],{"class":1220},[86,7323,1528],{"class":304},[86,7325,1237],{"class":1220},[86,7327,7328,7330,7332],{"class":88,"line":311},[86,7329,1242],{"class":1220},[86,7331,1537],{"class":1224},[86,7333,1237],{"class":1220},[86,7335,7336,7338,7340,7343,7345],{"class":88,"line":354},[86,7337,1544],{"class":1220},[86,7339,1547],{"class":1224},[86,7341,7342],{"class":1220},">gz_ros2_control/GazeboSimSystem\u003C/",[86,7344,1547],{"class":1224},[86,7346,1237],{"class":1220},[86,7348,7349,7351,7353],{"class":88,"line":362},[86,7350,1559],{"class":1220},[86,7352,1537],{"class":1224},[86,7354,1237],{"class":1220},[86,7356,7357],{"class":88,"line":370},[86,7358,1569],{"emptyLinePlaceholder":1568},[86,7360,7361,7363,7365,7367,7369,7371],{"class":88,"line":378},[86,7362,1242],{"class":1220},[86,7364,1225],{"class":1224},[86,7366,1228],{"class":92},[86,7368,1231],{"class":1220},[86,7370,1234],{"class":304},[86,7372,1237],{"class":1220},[86,7374,7375,7377,7379,7381,7383,7385],{"class":88,"line":386},[86,7376,1544],{"class":1220},[86,7378,1245],{"class":1224},[86,7380,1228],{"class":92},[86,7382,1231],{"class":1220},[86,7384,1252],{"class":304},[86,7386,1255],{"class":1220},[86,7388,7389,7391,7393,7395,7397,7399],{"class":88,"line":394},[86,7390,1544],{"class":1220},[86,7392,1262],{"class":1224},[86,7394,1228],{"class":92},[86,7396,1231],{"class":1220},[86,7398,1269],{"class":304},[86,7400,1255],{"class":1220},[86,7402,7403,7405,7407,7409,7411,7413],{"class":88,"line":402},[86,7404,1544],{"class":1220},[86,7406,1262],{"class":1224},[86,7408,1228],{"class":92},[86,7410,1231],{"class":1220},[86,7412,1252],{"class":304},[86,7414,1255],{"class":1220},[86,7416,7417,7419,7421],{"class":88,"line":585},[86,7418,1559],{"class":1220},[86,7420,1225],{"class":1224},[86,7422,1237],{"class":1220},[86,7424,7425],{"class":88,"line":591},[86,7426,1569],{"emptyLinePlaceholder":1568},[86,7428,7429,7431,7433,7435,7437,7439],{"class":88,"line":597},[86,7430,1242],{"class":1220},[86,7432,1225],{"class":1224},[86,7434,1228],{"class":92},[86,7436,1231],{"class":1220},[86,7438,1874],{"class":304},[86,7440,1237],{"class":1220},[86,7442,7443,7445,7447,7449,7451,7453],{"class":88,"line":1879},[86,7444,1544],{"class":1220},[86,7446,1245],{"class":1224},[86,7448,1228],{"class":92},[86,7450,1231],{"class":1220},[86,7452,1252],{"class":304},[86,7454,1255],{"class":1220},[86,7456,7457,7459,7461,7463,7465,7467],{"class":88,"line":1894},[86,7458,1544],{"class":1220},[86,7460,1262],{"class":1224},[86,7462,1228],{"class":92},[86,7464,1231],{"class":1220},[86,7466,1269],{"class":304},[86,7468,1255],{"class":1220},[86,7470,7471,7473,7475,7477,7479,7481],{"class":88,"line":1909},[86,7472,1544],{"class":1220},[86,7474,1262],{"class":1224},[86,7476,1228],{"class":92},[86,7478,1231],{"class":1220},[86,7480,1252],{"class":304},[86,7482,1255],{"class":1220},[86,7484,7485,7487,7489],{"class":88,"line":1924},[86,7486,1559],{"class":1220},[86,7488,1225],{"class":1224},[86,7490,1237],{"class":1220},[86,7492,7493,7495,7497],{"class":88,"line":1933},[86,7494,1290],{"class":1220},[86,7496,31],{"class":1224},[86,7498,1237],{"class":1220},[20,7500,7501],{},"同时还要在机器人描述中加入 Gazebo 系统插件，让 Gazebo Sim 创建 ros2_control 资源管理器并加载控制器参数：",[77,7503,7505],{"className":1211,"code":7504,"language":1213,"meta":82,"style":82},"\u003Cgazebo>\n  \u003Cplugin filename=\"gz_ros2_control-system\" name=\"gz_ros2_control::GazeboSimROS2ControlPlugin\">\n    \u003Cparameters>$(find my_robot_bringup)/config/controllers.yaml\u003C/parameters>\n  \u003C/plugin>\n\u003C/gazebo>\n",[29,7506,7507,7516,7539,7553,7561],{"__ignoreMap":82},[86,7508,7509,7511,7514],{"class":88,"line":89},[86,7510,1221],{"class":1220},[86,7512,7513],{"class":1224},"gazebo",[86,7515,1237],{"class":1220},[86,7517,7518,7520,7522,7525,7527,7530,7532,7534,7537],{"class":88,"line":311},[86,7519,1242],{"class":1220},[86,7521,1547],{"class":1224},[86,7523,7524],{"class":92}," filename",[86,7526,1231],{"class":1220},[86,7528,7529],{"class":304},"\"gz_ros2_control-system\"",[86,7531,1228],{"class":92},[86,7533,1231],{"class":1220},[86,7535,7536],{"class":304},"\"gz_ros2_control::GazeboSimROS2ControlPlugin\"",[86,7538,1237],{"class":1220},[86,7540,7541,7543,7546,7549,7551],{"class":88,"line":354},[86,7542,1544],{"class":1220},[86,7544,7545],{"class":1224},"parameters",[86,7547,7548],{"class":1220},">$(find my_robot_bringup)/config/controllers.yaml\u003C/",[86,7550,7545],{"class":1224},[86,7552,1237],{"class":1220},[86,7554,7555,7557,7559],{"class":88,"line":362},[86,7556,1559],{"class":1220},[86,7558,1547],{"class":1224},[86,7560,1237],{"class":1220},[86,7562,7563,7565,7567],{"class":88,"line":370},[86,7564,1290],{"class":1220},[86,7566,7513],{"class":1224},[86,7568,1237],{"class":1220},[20,7570,3861,7571,7574],{},[29,7572,7573],{},"gz_ros2_control_demos"," 后，可以直接参考官方示例：",[77,7576,7578],{"className":79,"code":7577,"language":81,"meta":82,"style":82},"ros2 launch gz_ros2_control_demos diff_drive_example.launch.py\nros2 launch gz_ros2_control_demos cart_example_position.launch.py\nros2 launch gz_ros2_control_demos cart_example_velocity.launch.py\nros2 launch gz_ros2_control_demos cart_example_effort.launch.py\nros2 launch gz_ros2_control_demos mecanum_drive_example.launch.py\nros2 launch gz_ros2_control_demos ackermann_drive_example.launch.py\nros2 launch gz_ros2_control_demos tricycle_drive_example.launch.py\n",[29,7579,7580,7593,7604,7615,7626,7637,7648],{"__ignoreMap":82},[86,7581,7582,7584,7587,7590],{"class":88,"line":89},[86,7583,452],{"class":92},[86,7585,7586],{"class":304}," launch",[86,7588,7589],{"class":304}," gz_ros2_control_demos",[86,7591,7592],{"class":304}," diff_drive_example.launch.py\n",[86,7594,7595,7597,7599,7601],{"class":88,"line":311},[86,7596,452],{"class":92},[86,7598,7586],{"class":304},[86,7600,7589],{"class":304},[86,7602,7603],{"class":304}," cart_example_position.launch.py\n",[86,7605,7606,7608,7610,7612],{"class":88,"line":354},[86,7607,452],{"class":92},[86,7609,7586],{"class":304},[86,7611,7589],{"class":304},[86,7613,7614],{"class":304}," cart_example_velocity.launch.py\n",[86,7616,7617,7619,7621,7623],{"class":88,"line":362},[86,7618,452],{"class":92},[86,7620,7586],{"class":304},[86,7622,7589],{"class":304},[86,7624,7625],{"class":304}," cart_example_effort.launch.py\n",[86,7627,7628,7630,7632,7634],{"class":88,"line":370},[86,7629,452],{"class":92},[86,7631,7586],{"class":304},[86,7633,7589],{"class":304},[86,7635,7636],{"class":304}," mecanum_drive_example.launch.py\n",[86,7638,7639,7641,7643,7645],{"class":88,"line":378},[86,7640,452],{"class":92},[86,7642,7586],{"class":304},[86,7644,7589],{"class":304},[86,7646,7647],{"class":304}," ackermann_drive_example.launch.py\n",[86,7649,7650,7652,7654,7656],{"class":88,"line":386},[86,7651,452],{"class":92},[86,7653,7586],{"class":304},[86,7655,7589],{"class":304},[86,7657,7658],{"class":304}," tricycle_drive_example.launch.py\n",[20,7660,7661],{},"这些示例文件安装在：",[77,7663,7665],{"className":79,"code":7664,"language":81,"meta":82,"style":82},"/opt/ros/jazzy/share/gz_ros2_control_demos\n",[29,7666,7667],{"__ignoreMap":82},[86,7668,7669],{"class":88,"line":89},[86,7670,7664],{"class":92},[20,7672,7673],{},"比较重要的文件包括：",[97,7675,7676,7685],{},[100,7677,7678],{},[103,7679,7680,7683],{},[106,7681,7682],{"align":108},"文件",[106,7684,1341],{"align":108},[117,7686,7687,7697,7707,7717,7727],{},[103,7688,7689,7694],{},[122,7690,7691],{"align":108},[29,7692,7693],{},"urdf/test_diff_drive.xacro.urdf",[122,7695,7696],{"align":108},"Gazebo 差速底盘 URDF/xacro 示例",[103,7698,7699,7704],{},[122,7700,7701],{"align":108},[29,7702,7703],{},"config/diff_drive_controller.yaml",[122,7705,7706],{"align":108},"差速控制器参数示例",[103,7708,7709,7714],{},[122,7710,7711],{"align":108},[29,7712,7713],{},"launch/diff_drive_example.launch.py",[122,7715,7716],{"align":108},"差速底盘完整启动示例",[103,7718,7719,7724],{},[122,7720,7721],{"align":108},[29,7722,7723],{},"urdf/test_mecanum_drive.xacro.urdf",[122,7725,7726],{"align":108},"麦克纳姆底盘示例",[103,7728,7729,7734],{},[122,7730,7731],{"align":108},[29,7732,7733],{},"config/mecanum_drive_controller.yaml",[122,7735,7736],{"align":108},"麦克纳姆控制器参数示例",[20,7738,7739],{},"学习 Gazebo 集成时，不要只看 launch 文件。更推荐按这个顺序读：",[4675,7741,7742,7753,7759],{},[37,7743,7744,7745,7747,7748,643,7750,7212],{},"先看 ",[29,7746,7693],{}," 中 ",[29,7749,1498],{},[29,7751,7752],{},"\u003Cgazebo>",[37,7754,7755,7756,7758],{},"再看 ",[29,7757,7703],{}," 中控制器参数。",[37,7760,7761,7762,7764],{},"最后看 ",[29,7763,7713],{}," 如何把机器人描述、Gazebo 和 spawner 连起来。",[20,7766,7767],{},"建议学习顺序是：",[4675,7769,7770,7776,7779],{},[37,7771,7772,7773,7775],{},"先用 ",[29,7774,4799],{}," 跑通控制器。",[37,7777,7778],{},"再接 Gazebo。",[37,7780,7781],{},"最后接真实硬件。",[20,7783,7784],{},"这样出错时更容易定位问题。",[10,7786,7787],{"id":7787},"常见问题",[15,7789,7790],{"id":7790},"控制器加载失败",[20,7792,7793],{},"优先检查：",[77,7795,7796],{"className":79,"code":2834,"language":81,"meta":82,"style":82},[29,7797,7798],{"__ignoreMap":82},[86,7799,7800,7802,7804],{"class":88,"line":89},[86,7801,452],{"class":92},[86,7803,2760],{"class":304},[86,7805,2845],{"class":304},[20,7807,7808],{},"如果你的控制器类型不在列表里，通常是：",[34,7810,7811,7814,7820,7830],{},[37,7812,7813],{},"插件 XML 没有安装；",[37,7815,7816,7819],{},[29,7817,7818],{},"pluginlib_export_plugin_description_file()"," 写错；",[37,7821,7822,7823,7825,7826,7829],{},"YAML 中 ",[29,7824,1635],{}," 字段和插件 XML 中的 ",[29,7827,7828],{},"name"," 不一致；",[37,7831,7832],{},"没有 source 工作空间：",[77,7834,7836],{"className":79,"code":7835,"language":81,"meta":82,"style":82},"source install/setup.bash\n",[29,7837,7838],{"__ignoreMap":82},[86,7839,7840,7842],{"class":88,"line":89},[86,7841,433],{"class":343},[86,7843,7844],{"class":304}," install/setup.bash\n",[15,7846,7847],{"id":7847},"控制器激活失败",[20,7849,7850],{},"查看接口：",[77,7852,7854],{"className":79,"code":7853,"language":81,"meta":82,"style":82},"ros2 control list_hardware_interfaces --verbose\nros2 control list_controllers --verbose\n",[29,7855,7856,7866],{"__ignoreMap":82},[86,7857,7858,7860,7862,7864],{"class":88,"line":89},[86,7859,452],{"class":92},[86,7861,2760],{"class":304},[86,7863,2826],{"class":304},[86,7865,2783],{"class":343},[86,7867,7868,7870,7872,7874],{"class":88,"line":311},[86,7869,452],{"class":92},[86,7871,2760],{"class":304},[86,7873,2780],{"class":304},[86,7875,2783],{"class":343},[20,7877,7878],{},"常见原因：",[34,7880,7881,7884,7887,7890,7893],{},[37,7882,7883],{},"控制器要的 command interface 不存在；",[37,7885,7886],{},"控制器要的 state interface 不存在；",[37,7888,7889],{},"另一个控制器已经占用了同一个 command interface；",[37,7891,7892],{},"URDF 里的 joint 名和 YAML 里的 joint 名不一致；",[37,7894,7895],{},"硬件组件还没有激活。",[15,7897,7899,7901],{"id":7898},"joint_states-没有数据",[29,7900,642],{}," 没有数据",[20,7903,7904],{},"检查：",[77,7906,7908],{"className":79,"code":7907,"language":81,"meta":82,"style":82},"ros2 control list_controllers\nros2 topic echo /dynamic_joint_states\nros2 topic echo /joint_states\n",[29,7909,7910,7918,7929],{"__ignoreMap":82},[86,7911,7912,7914,7916],{"class":88,"line":89},[86,7913,452],{"class":92},[86,7915,2760],{"class":304},[86,7917,2763],{"class":304},[86,7919,7920,7922,7924,7926],{"class":88,"line":311},[86,7921,452],{"class":92},[86,7923,3438],{"class":304},[86,7925,4750],{"class":304},[86,7927,7928],{"class":304}," /dynamic_joint_states\n",[86,7930,7931,7933,7935,7937],{"class":88,"line":354},[86,7932,452],{"class":92},[86,7934,3438],{"class":304},[86,7936,4750],{"class":304},[86,7938,4753],{"class":304},[20,7940,7878],{},[34,7942,7943,7948,7951,7957],{},[37,7944,7945,7946,238],{},"没有启动 ",[29,7947,633],{},[37,7949,7950],{},"硬件接口没有 state interfaces；",[37,7952,7953,7956],{},[29,7954,7955],{},"use_urdf_to_filter=true"," 时，URDF 中没有对应 joint；",[37,7958,7959,7960,7962],{},"硬件 ",[29,7961,1350],{}," 没有更新 state。",[15,7964,7966,7968],{"id":7965},"cmd_vel-发了但底盘不动",[29,7967,45],{}," 发了但底盘不动",[20,7970,1704,7971,7973,7974,2123],{},[29,7972,669],{}," 默认订阅 ",[29,7975,3489],{},[77,7977,7979],{"className":79,"code":7978,"language":81,"meta":82,"style":82},"ros2 topic info /diff_drive_controller/cmd_vel\n",[29,7980,7981],{"__ignoreMap":82},[86,7982,7983,7985,7987,7989],{"class":88,"line":89},[86,7984,452],{"class":92},[86,7986,3438],{"class":304},[86,7988,5097],{"class":304},[86,7990,7991],{"class":304}," /diff_drive_controller/cmd_vel\n",[20,7993,7994],{},"确认消息类型是：",[77,7996,7998],{"className":79,"code":7997,"language":81,"meta":82,"style":82},"geometry_msgs/msg/TwistStamped\n",[29,7999,8000],{"__ignoreMap":82},[86,8001,8002],{"class":88,"line":89},[86,8003,7997],{"class":92},[20,8005,8006],{},"还要检查：",[34,8008,8009,8014,8017,8022,8031,8037],{},[37,8010,8011,8012,238],{},"话题名是不是 ",[29,8013,1400],{},[37,8015,8016],{},"控制器是否 active；",[37,8018,3900,8019,8021],{},[29,8020,655],{}," command interface 是否存在；",[37,8023,8024,151,8027,8030],{},[29,8025,8026],{},"left_wheel_names",[29,8028,8029],{},"right_wheel_names"," 是否写对；",[37,8032,8033,8036],{},[29,8034,8035],{},"cmd_vel_timeout"," 是否太短；",[37,8038,8039,8040,8042],{},"真机硬件接口 ",[29,8041,1376],{}," 是否真的把命令发给电机。",[15,8044,8045],{"id":8045},"里程计方向不对",[20,8047,7793],{},[34,8049,8050,8053,8056,8062,8067,8070],{},[37,8051,8052],{},"左右轮 joint 是否写反；",[37,8054,8055],{},"轮子正方向是否与 URDF 轴方向一致；",[37,8057,8058,8061],{},[29,8059,8060],{},"wheel_radius"," 是否正确；",[37,8063,8064,8061],{},[29,8065,8066],{},"wheel_separation",[37,8068,8069],{},"编码器位置单位是否是弧度；",[37,8071,8072],{},"硬件接口中的速度单位是否是 rad/s。",[15,8074,8076],{"id":8075},"tf-冲突","TF 冲突",[20,8078,8079,8080,8082,8083,8085],{},"如果系统中已有其他节点发布 ",[29,8081,3101],{},"，不要再让 ",[29,8084,669],{}," 发布同一段 TF：",[77,8087,8089],{"className":2126,"code":8088,"language":2128,"meta":82,"style":82},"diff_drive_controller:\n  ros__parameters:\n    enable_odom_tf: false\n",[29,8090,8091,8097,8103],{"__ignoreMap":82},[86,8092,8093,8095],{"class":88,"line":89},[86,8094,669],{"class":1224},[86,8096,2137],{"class":1220},[86,8098,8099,8101],{"class":88,"line":311},[86,8100,2142],{"class":1224},[86,8102,2137],{"class":1220},[86,8104,8105,8107,8109],{"class":88,"line":354},[86,8106,3329],{"class":1224},[86,8108,2152],{"class":1220},[86,8110,3022],{"class":343},[10,8112,8113],{"id":8113},"学习路线建议",[20,8115,8116],{},"建议按下面顺序学习：",[4675,8118,8119,8122,8130,8135,8140,8145,8151,8159,8162],{},[37,8120,8121],{},"看懂 command interface 和 state interface。",[37,8123,8124,8125,8127,8128,1307],{},"用 ",[29,8126,4799],{}," 跑通 ",[29,8129,633],{},[37,8131,8124,8132,8134],{},[29,8133,726],{}," 直接写关节命令。",[37,8136,8124,8137,8139],{},[29,8138,669],{}," 跑通差速底盘。",[37,8141,8124,8142,8144],{},[29,8143,694],{}," 跑通机械臂或多关节模型。",[37,8146,8147,8148,8150],{},"写一个最小 ",[29,8149,4911],{},"，先只打印命令。",[37,8152,8153,8154,8156,8157,1307],{},"接真实串口/CAN，让 ",[29,8155,1350],{}," 更新 ",[29,8158,642],{},[37,8160,8161],{},"再接官方控制器。",[37,8163,8164],{},"最后再写自定义运动学控制器。",[20,8166,8167],{},"不要一开始就同时写硬件接口、运动学控制器、仿真插件和导航。这样一旦出错，很难判断是 URDF、YAML、控制器、硬件接口还是底层通信的问题。",[10,8169,8170],{"id":8170},"参考资料",[34,8172,8173,8182,8189,8196,8203,8210,8217,8224,8231],{},[37,8174,8175,8176],{},"ROS2 Control Jazzy 官方文档：",[8177,8178,8179],"a",{"href":8179,"rel":8180},"https://control.ros.org/jazzy/index.html",[8181],"nofollow",[37,8183,8184,8185],{},"Getting Started：",[8177,8186,8187],{"href":8187,"rel":8188},"https://control.ros.org/jazzy/doc/getting_started/getting_started.html",[8181],[37,8190,8191,8192],{},"Controller Manager：",[8177,8193,8194],{"href":8194,"rel":8195},"https://control.ros.org/jazzy/doc/ros2_control/controller_manager/doc/userdoc.html",[8181],[37,8197,8198,8199],{},"Hardware Interface：",[8177,8200,8201],{"href":8201,"rel":8202},"https://control.ros.org/jazzy/doc/ros2_control/hardware_interface/doc/hardware_interface_types_userdoc.html",[8181],[37,8204,8205,8206],{},"Joint State Broadcaster：",[8177,8207,8208],{"href":8208,"rel":8209},"https://control.ros.org/jazzy/doc/ros2_controllers/joint_state_broadcaster/doc/userdoc.html",[8181],[37,8211,8212,8213],{},"Diff Drive Controller：",[8177,8214,8215],{"href":8215,"rel":8216},"https://control.ros.org/jazzy/doc/ros2_controllers/diff_drive_controller/doc/userdoc.html",[8181],[37,8218,8219,8220],{},"Joint Trajectory Controller：",[8177,8221,8222],{"href":8222,"rel":8223},"https://control.ros.org/jazzy/doc/ros2_controllers/joint_trajectory_controller/doc/userdoc.html",[8181],[37,8225,8226,8227],{},"ros2_control GitHub：",[8177,8228,8229],{"href":8229,"rel":8230},"https://github.com/ros-controls/ros2_control",[8181],[37,8232,8233,8234],{},"ros2_controllers GitHub：",[8177,8235,8236],{"href":8236,"rel":8237},"https://github.com/ros-controls/ros2_controllers",[8181],[8239,8240,8241],"style",{},"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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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":82,"searchDepth":311,"depth":311,"links":8243},[8244,8245,8246,8247,8248,8249,8250,8251,8252,8253,8254,8255,8256,8257],{"id":12,"depth":354,"text":13},{"id":284,"depth":354,"text":285},{"id":1097,"depth":354,"text":1098},{"id":1488,"depth":354,"text":1489},{"id":2090,"depth":354,"text":2091},{"id":2469,"depth":354,"text":2469},{"id":2913,"depth":354,"text":2913},{"id":4258,"depth":354,"text":4259},{"id":4767,"depth":354,"text":4767},{"id":5972,"depth":354,"text":5972},{"id":7204,"depth":354,"text":7205},{"id":7787,"depth":354,"text":7787},{"id":8113,"depth":354,"text":8113},{"id":8170,"depth":354,"text":8170},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control","15",15000000,"2023-12-30","wiki/2023-12-30-ros2-tutorial","zh-cn:2023-12-30-ros2-tutorial","/zh-cn/wiki/2023-12-30-ros2-tutorial","Ros2 Tutorial","md","wiki/2023-12-30-ros2-tutorial/ch15-ROS2_Control",false,null,"/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control","zh-CN","zh-cn",{},{"title":5,"description":82},"AnZ8Dj1EDlM2TZpcW3r0ysO84Lyn_jYt67e5rjEaqdE",[8277,8282,8288,8294,8300,8306,8312,8318,8324,8330,8336,8342,8348,8354,8360,8366,8372,8378,8383,8389,8394,8400,8406,8412,8418,8424,8430,8436,8442,8448,8454,8460,8466,8472,8478,8484,8490,8496,8502,8508,8514,8520,8526,8532,8538,8544,8545,8551,8557,8563,8569,8574,8580,8585,8591,8597,8603,8609,8615,8621],{"path":8278,"stem":8279,"title":8280,"date":8261,"chapter":6575,"chapterSort":8281,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch1-ros2-jie-shao","wiki/2023-12-30-ros2-tutorial/ch1-ROS2介绍","ROS2介绍",1000000,{"path":8283,"stem":8284,"title":8285,"date":8261,"chapter":8286,"chapterSort":8287,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch10-stage-ros2-fang-zhen-ping-tai","wiki/2023-12-30-ros2-tutorial/ch10-Stage_Ros2仿真平台","Stage_Ros2仿真平台","10",10000000,{"path":8289,"stem":8290,"title":8291,"date":8261,"chapter":8292,"chapterSort":8293,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch11-1-gazeboclassic","wiki/2023-12-30-ros2-tutorial/ch11-1-GazeboClassic","Gezebo Classic","11.1",11010000,{"path":8295,"stem":8296,"title":8297,"date":8261,"chapter":8298,"chapterSort":8299,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch11-2-ignitiongazebo","wiki/2023-12-30-ros2-tutorial/ch11-2-IgnitionGazebo","Ignition Gazebo（Gazebo Fortress）","11.2",11020000,{"path":8301,"stem":8302,"title":8303,"date":8261,"chapter":8304,"chapterSort":8305,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch11-3-gazebosim","wiki/2023-12-30-ros2-tutorial/ch11-3-GazeboSim","Gz Sim（Gazebo Harmonic）","11.3",11030000,{"path":8307,"stem":8308,"title":8309,"date":8261,"chapter":8310,"chapterSort":8311,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch11-4-igngz2gzsim","wiki/2023-12-30-ros2-tutorial/ch11-4-Igngz2gzsim","将Ign Gazebo迁移至Gz Sim","11.4",11040000,{"path":8313,"stem":8314,"title":8315,"date":8261,"chapter":8316,"chapterSort":8317,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch11-gezebo-fang-zhen-ping-tai","wiki/2023-12-30-ros2-tutorial/ch11-Gezebo仿真平台","Gezebo仿真平台","11",11000000,{"path":8319,"stem":8320,"title":8321,"date":8261,"chapter":8322,"chapterSort":8323,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch12-1-humble-ban-ben","wiki/2023-12-30-ros2-tutorial/ch12-1-Humble版本","Humble导航仿真","12.1",12010000,{"path":8325,"stem":8326,"title":8327,"date":8261,"chapter":8328,"chapterSort":8329,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch12-2-jazzy-ban-ben","wiki/2023-12-30-ros2-tutorial/ch12-2-Jazzy版本","Jazzy导航仿真","12.2",12020000,{"path":8331,"stem":8332,"title":8333,"date":8261,"chapter":8334,"chapterSort":8335,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch12-3-humble2jazzy","wiki/2023-12-30-ros2-tutorial/ch12-3-Humble2Jazzy","Humble与Jazzy导航的差异","12.3",12030000,{"path":8337,"stem":8338,"title":8339,"date":8261,"chapter":8340,"chapterSort":8341,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch12-ji-qi-ren-dao-hang-navigation2-fang-zhen-pian","wiki/2023-12-30-ros2-tutorial/ch12-机器人导航Navigation2(仿真篇)","机器人导航Navigation2(仿真篇)","12",12000000,{"path":8343,"stem":8344,"title":8345,"date":8261,"chapter":8346,"chapterSort":8347,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-1-1-boost-aiso","wiki/2023-12-30-ros2-tutorial/ch13-1-1-Boost.Aiso","Boost.Asio库","13.1.1",13010100,{"path":8349,"stem":8350,"title":8351,"date":8261,"chapter":8352,"chapterSort":8353,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-1-2-ros2-serial-driver","wiki/2023-12-30-ros2-tutorial/ch13-1-2-ros2_serial_driver","ROS2 Serial Driver库","13.1.2",13010200,{"path":8355,"stem":8356,"title":8357,"date":8261,"chapter":8358,"chapterSort":8359,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-1-chuan-kou-tong-xin","wiki/2023-12-30-ros2-tutorial/ch13-1-串口通信","UART串口通信","13.1",13010000,{"path":8361,"stem":8362,"title":8363,"date":8261,"chapter":8364,"chapterSort":8365,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-2-1-socketcan","wiki/2023-12-30-ros2-tutorial/ch13-2-1-socketcan","SocketCAN","13.2.1",13020100,{"path":8367,"stem":8368,"title":8369,"date":8261,"chapter":8370,"chapterSort":8371,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-2-2-ros2-socketcan","wiki/2023-12-30-ros2-tutorial/ch13-2-2-ros2_socketcan","ROS2_SocketCAN","13.2.2",13020200,{"path":8373,"stem":8374,"title":8375,"date":8261,"chapter":8376,"chapterSort":8377,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-2-can-tong-xin","wiki/2023-12-30-ros2-tutorial/ch13-2-CAN通信","CAN通信","13.2",13020000,{"path":8379,"stem":8380,"title":8345,"date":8261,"chapter":8381,"chapterSort":8382,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-3-1-boost-aiso","wiki/2023-12-30-ros2-tutorial/ch13-3-1-Boost.Aiso","13.3.1",13030100,{"path":8384,"stem":8385,"title":8386,"date":8261,"chapter":8387,"chapterSort":8388,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-3-tcp-tong-xin","wiki/2023-12-30-ros2-tutorial/ch13-3-TCP通信","TCP通信","13.3",13030000,{"path":8390,"stem":8391,"title":8345,"date":8261,"chapter":8392,"chapterSort":8393,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-4-1-boost-aiso","wiki/2023-12-30-ros2-tutorial/ch13-4-1-Boost.Aiso","13.4.1",13040100,{"path":8395,"stem":8396,"title":8397,"date":8261,"chapter":8398,"chapterSort":8399,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-4-udp-tong-xin","wiki/2023-12-30-ros2-tutorial/ch13-4-UDP通信","UDP通信","13.4",13040000,{"path":8401,"stem":8402,"title":8403,"date":8261,"chapter":8404,"chapterSort":8405,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch13-linux-ying-jian-tong-xin","wiki/2023-12-30-ros2-tutorial/ch13-Linux硬件通信","Linux硬件通信","13",13000000,{"path":8407,"stem":8408,"title":8409,"date":8261,"chapter":8410,"chapterSort":8411,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-1-ji-qi-ren-zu-cheng","wiki/2023-12-30-ros2-tutorial/ch14-1-机器人组成","机器人组成","14.1",14010000,{"path":8413,"stem":8414,"title":8415,"date":8261,"chapter":8416,"chapterSort":8417,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-2-1-yuan-cheng-kai-fa-ssh","wiki/2023-12-30-ros2-tutorial/ch14-2-1-远程开发SSH","远程开发SSH","14.2.1",14020100,{"path":8419,"stem":8420,"title":8421,"date":8261,"chapter":8422,"chapterSort":8423,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-2-2-yuan-cheng-fang-wen-zhuo-mian","wiki/2023-12-30-ros2-tutorial/ch14-2-2-远程访问桌面","远程访问桌面","14.2.2",14020200,{"path":8425,"stem":8426,"title":8427,"date":8261,"chapter":8428,"chapterSort":8429,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-2-gong-kong-ji-zhi-yuan-cheng-kai-fa-huan-jing","wiki/2023-12-30-ros2-tutorial/ch14-2-工控机之远程开发环境","工控机之远程开发环境","14.2",14020000,{"path":8431,"stem":8432,"title":8433,"date":8261,"chapter":8434,"chapterSort":8435,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-3-gong-kong-ji-zhi-wai-jie-usb-she-bei","wiki/2023-12-30-ros2-tutorial/ch14-3-工控机之外接USB设备","工控机之外接USB设备","14.3",14030000,{"path":8437,"stem":8438,"title":8439,"date":8261,"chapter":8440,"chapterSort":8441,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-4-fen-bu-shi-da-jian","wiki/2023-12-30-ros2-tutorial/ch14-4-分布式搭建","分布式搭建","14.4",14040000,{"path":8443,"stem":8444,"title":8445,"date":8261,"chapter":8446,"chapterSort":8447,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-5-you-hua-ri-zhi","wiki/2023-12-30-ros2-tutorial/ch14-5-优化日志","优化日志","14.5",14050000,{"path":8449,"stem":8450,"title":8451,"date":8261,"chapter":8452,"chapterSort":8453,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-1-li-cheng-ji-odom","wiki/2023-12-30-ros2-tutorial/ch14-6-1-里程计Odom","里程计Odom","14.6.1",14060100,{"path":8455,"stem":8456,"title":8457,"date":8261,"chapter":8458,"chapterSort":8459,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-2-guan-xing-ji-imu","wiki/2023-12-30-ros2-tutorial/ch14-6-2-惯性计IMU","惯性计IMU","14.6.2",14060200,{"path":8461,"stem":8462,"title":8463,"date":8261,"chapter":8464,"chapterSort":8465,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-3-ji-guang-lei-da-lidar","wiki/2023-12-30-ros2-tutorial/ch14-6-3-激光雷达LiDAR","激光雷达LiDAR","14.6.3",14060300,{"path":8467,"stem":8468,"title":8469,"date":8261,"chapter":8470,"chapterSort":8471,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-4-xiang-ji-camera","wiki/2023-12-30-ros2-tutorial/ch14-6-4-相机Camera","相机Camera","14.6.4",14060400,{"path":8473,"stem":8474,"title":8475,"date":8261,"chapter":8476,"chapterSort":8477,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-5-quan-qiu-ding-wei-gnss","wiki/2023-12-30-ros2-tutorial/ch14-6-5-全球定位GNSS","全球定位GNSS","14.6.5",14060500,{"path":8479,"stem":8480,"title":8481,"date":8261,"chapter":8482,"chapterSort":8483,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-6-shou-bing-joy","wiki/2023-12-30-ros2-tutorial/ch14-6-6-手柄joy","手柄JOY","14.6.6",14060600,{"path":8485,"stem":8486,"title":8487,"date":8261,"chapter":8488,"chapterSort":8489,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-7-jian-pan-kong-zhi-jie-dian","wiki/2023-12-30-ros2-tutorial/ch14-6-7-键盘控制节点","键盘控制节点","14.6.7",14060700,{"path":8491,"stem":8492,"title":8493,"date":8261,"chapter":8494,"chapterSort":8495,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-6-ying-jian-ping-tai","wiki/2023-12-30-ros2-tutorial/ch14-6-硬件平台","硬件平台","14.6",14060000,{"path":8497,"stem":8498,"title":8499,"date":8261,"chapter":8500,"chapterSort":8501,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-7-zuo-biao-xi-yu-hua-ti-guan-xi","wiki/2023-12-30-ros2-tutorial/ch14-7-坐标系与话题关系","坐标系与话题关系","14.7",14070000,{"path":8503,"stem":8504,"title":8505,"date":8261,"chapter":8506,"chapterSort":8507,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-8-1-1-lun-shi-li-cheng-ji-biao-ding","wiki/2023-12-30-ros2-tutorial/ch14-8-1-1-轮式里程计标定","轮式里程计标定","14.8.1.1",14080101,{"path":8509,"stem":8510,"title":8511,"date":8261,"chapter":8512,"chapterSort":8513,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-8-1-2-lun-shi-li-cheng-ji-yu-imu-rong-he","wiki/2023-12-30-ros2-tutorial/ch14-8-1-2-轮式里程计与IMU融合","轮式里程计与IMU融合","14.8.1.2",14080102,{"path":8515,"stem":8516,"title":8517,"date":8261,"chapter":8518,"chapterSort":8519,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-8-1-lun-shi-li-cheng-ji-biao-ding-yu-rong-he","wiki/2023-12-30-ros2-tutorial/ch14-8-1-轮式里程计标定与融合","轮式里程计标定与融合","14.8.1",14080100,{"path":8521,"stem":8522,"title":8523,"date":8261,"chapter":8524,"chapterSort":8525,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-8-2-ji-guang-lei-da-gong-ju","wiki/2023-12-30-ros2-tutorial/ch14-8-2-激光雷达工具","激光雷达工具","14.8.2",14080200,{"path":8527,"stem":8528,"title":8529,"date":8261,"chapter":8530,"chapterSort":8531,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-8-3-xiang-ji-shi-yong-jin-jie","wiki/2023-12-30-ros2-tutorial/ch14-8-3-相机使用进阶","相机使用进阶","14.8.3",14080300,{"path":8533,"stem":8534,"title":8535,"date":8261,"chapter":8536,"chapterSort":8537,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-8-ying-jian-ping-tai-jin-jie","wiki/2023-12-30-ros2-tutorial/ch14-8-硬件平台进阶","硬件平台进阶","14.8",14080000,{"path":8539,"stem":8540,"title":8541,"date":8261,"chapter":8542,"chapterSort":8543,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch14-ji-qi-ren-ying-jian","wiki/2023-12-30-ros2-tutorial/ch14-机器人硬件","机器人硬件","14",14000000,{"path":8258,"stem":8267,"title":5,"date":8261,"chapter":8259,"chapterSort":8260,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},{"path":8546,"stem":8547,"title":8548,"date":8261,"chapter":8549,"chapterSort":8550,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch16-moveit2-gong-ye-ji-qi-ren-ji-xie-bi","wiki/2023-12-30-ros2-tutorial/ch16-Moveit2工业机器人机械臂","Moveit2工业机器人机械臂","16",16000000,{"path":8552,"stem":8553,"title":8554,"date":8261,"chapter":8555,"chapterSort":8556,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch17-ji-qi-ren-dao-hang-navigation2-shi-ti-pian","wiki/2023-12-30-ros2-tutorial/ch17-机器人导航Navigation2(实体篇)","机器人导航Navigation2(实体篇)","17",17000000,{"path":8558,"stem":8559,"title":8560,"date":8261,"chapter":8561,"chapterSort":8562,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch18-microros","wiki/2023-12-30-ros2-tutorial/ch18-MicroROS","MicroROS","18",18000000,{"path":8564,"stem":8565,"title":8566,"date":8261,"chapter":8567,"chapterSort":8568,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch19-webots-fang-zhen-ping-tai","wiki/2023-12-30-ros2-tutorial/ch19-Webots仿真平台","Webots仿真平台","19",19000000,{"path":8570,"stem":8571,"title":8572,"date":8261,"chapter":6593,"chapterSort":8573,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch2-ru-men-cao-zuo","wiki/2023-12-30-ros2-tutorial/ch2-入门操作","入门操作",2000000,{"path":8575,"stem":8576,"title":8577,"date":8261,"chapter":8578,"chapterSort":8579,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch20-opencv","wiki/2023-12-30-ros2-tutorial/ch20-OpenCV","OpenCV","20",20000000,{"path":8581,"stem":8582,"title":8583,"date":8261,"chapter":6610,"chapterSort":8584,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch3-gong-zuo-kong-jian-yu-gong-neng-bao","wiki/2023-12-30-ros2-tutorial/ch3-工作空间与功能包","工作空间与功能包",3000000,{"path":8586,"stem":8587,"title":8588,"date":8261,"chapter":8589,"chapterSort":8590,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch4-si-da-tong-xin","wiki/2023-12-30-ros2-tutorial/ch4-四大通信","四大通信","4",4000000,{"path":8592,"stem":8593,"title":8594,"date":8261,"chapter":8595,"chapterSort":8596,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch5-ros2-qi-ta-tong-xin-ji-zhi","wiki/2023-12-30-ros2-tutorial/ch5-ROS2其他通信机制","ROS2其他通信机制","5",5000000,{"path":8598,"stem":8599,"title":8600,"date":8261,"chapter":8601,"chapterSort":8602,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch6-launch","wiki/2023-12-30-ros2-tutorial/ch6-Launch","Launch","6",6000000,{"path":8604,"stem":8605,"title":8606,"date":8261,"chapter":8607,"chapterSort":8608,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch7-hui-su-rosbag2","wiki/2023-12-30-ros2-tutorial/ch7-回溯rosbag2","回溯rosbag2","7",7000000,{"path":8610,"stem":8611,"title":8612,"date":8261,"chapter":8613,"chapterSort":8614,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch8-zuo-biao-bian-huan-tf","wiki/2023-12-30-ros2-tutorial/ch8-坐标变换TF","坐标变换TF","8",8000000,{"path":8616,"stem":8617,"title":8618,"date":8261,"chapter":8619,"chapterSort":8620,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":8268},"/zh-cn/wiki/2023-12-30-ros2-tutorial/ch9-ke-shi-hua-ping-tai-rviz2-yu-urdf-jian-mo-yu-yan","wiki/2023-12-30-ros2-tutorial/ch9-可视化平台RVIZ2与URDF建模语言","可视化平台RVIZ2与URDF建模语言","9",9000000,{"path":8264,"stem":8622,"title":8623,"date":8261,"chapter":8269,"chapterSort":8624,"docKey":8263,"docRoot":8264,"docTitle":8265,"isWikiDoc":1568,"isWikiIndex":1568},"wiki/2023-12-30-ros2-tutorial/index","ROS2机器人操作系统教程",0,{"variants":8626},[8627,8630,8633,8636,8639],{"path":8628,"localeSlug":8629,"i18nKey":8267},"/en-us/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control","en-us",{"path":8631,"localeSlug":8632,"i18nKey":8267},"/zh-hant/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control","zh-hant",{"path":8634,"localeSlug":8635,"i18nKey":8267},"/zh-hk/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control","zh-hk",{"path":8637,"localeSlug":8638,"i18nKey":8267},"/zh-tw/wiki/2023-12-30-ros2-tutorial/ch15-ros2-control","zh-tw",{"path":8258,"localeSlug":8272,"i18nKey":8267},[8628,8270,8631,8270,8634,8270,8637,8270,8258,8270],1780663052627]