
上一課答案第 1 題class Dog : public Animal其中Animal → 父類(lèi) Dog → 子類(lèi)第 2 題class MyNode : public rclcpp::Node表示定義一個(gè)自己的類(lèi)MyNode它繼承 ROS2 的rclcpp::Node因此擁有 ROS2 節(jié)點(diǎn)的基礎(chǔ)能力。第 3 題MyNode()是MyNode的構(gòu)造函數(shù)。創(chuàng)建MyNode對(duì)象時(shí)會(huì)自動(dòng)調(diào)用。第 4 題Node(my_node)表示調(diào)用父類(lèi)Node的構(gòu)造函數(shù)并把節(jié)點(diǎn)名稱(chēng)設(shè)置為my_node。第 5 題因?yàn)閞clcpp::Node已經(jīng)提供了 ROS2 節(jié)點(diǎn)所需的大量基礎(chǔ)能力比如Publisher Subscriber Timer Parameter Logger Service我們繼承它就不需要自己從零實(shí)現(xiàn) ROS2 通信系統(tǒng)。上一課跳得太快了。問(wèn)題不在 Publisher/Subscriber 本身而是我一口氣把ROS2、模板 、::、智能指針、this-、lambda、消息類(lèi)型全塞進(jìn)了一段代碼里。我們先不繼續(xù)下一課把第 31 課拆成幾節(jié)微課。今天只搞懂Publisher 到底是什么以及一條 ROS2 發(fā)布代碼怎么讀。第 31A 課先把 ROS2 Publisher 講成人話先完全忘掉這句rclcpp::Publisherstd_msgs::msg::Int32::SharedPtr publisher_;現(xiàn)在不看它。你先只理解一個(gè)現(xiàn)實(shí)場(chǎng)景。假設(shè)我們的機(jī)械臂程序里有兩個(gè)程序程序 AVLA 程序 B機(jī)械臂控制器VLA 算出了“機(jī)械臂第 1 軸轉(zhuǎn)到 30°”問(wèn)題是程序 A 怎么把這個(gè)消息告訴程序 BROS2 提供了一套通信系統(tǒng)。最簡(jiǎn)單可以想象成微信VLA程序 │ │ 發(fā)消息 ▼ 【機(jī)器人動(dòng)作群】 │ │ 收消息 ▼ 機(jī)械臂控制程序這里發(fā)消息的人 Publisher 微信群 Topic 收消息的人 Subscriber 發(fā)送的內(nèi)容 Message今天我們只學(xué)發(fā)消息這一半Publisher ↓ Topic ↓ Message1. 如果不用 ROS2最簡(jiǎn)單的“發(fā)消息”應(yīng)該長(zhǎng)什么樣假設(shè)我自己設(shè)計(jì)一個(gè)超級(jí)簡(jiǎn)單的程序Publisher publisher;意思創(chuàng)建一個(gè)發(fā)布器。然后publisher.publish(100);意思發(fā)布數(shù)字 100。就這么簡(jiǎn)單。你先把 ROS2 Publisher 的本質(zhì)理解成publisher.publish(消息);也就是讓發(fā)布器把一條消息發(fā)出去。2. ROS2 為什么看起來(lái)復(fù)雜因?yàn)?ROS2 還必須知道兩件事第一你發(fā)到哪個(gè)頻道第二你發(fā)送的是什么類(lèi)型的數(shù)據(jù)比如我們說(shuō)頻道number 數(shù)據(jù)類(lèi)型整數(shù)所以概念上相當(dāng)于創(chuàng)建發(fā)布器 我要發(fā)送 整數(shù) 發(fā)送到 number頻道真實(shí) ROS2 才寫(xiě)成create_publisherstd_msgs::msg::Int32( number, 10 );別急著整句理解。我們開(kāi)始拆。3. 先看number這個(gè)最簡(jiǎn)單number只是一個(gè)字符串。你已經(jīng)學(xué)過(guò)std::string name Robot;所以number沒(méi)什么特殊的。在這里它表示Topic 的名字。也就是頻道名稱(chēng)。可以想成微信群名字 number所以number≈我要往 number 這個(gè)頻道發(fā)東西4. Topic 到底是什么Topic 不是什么神秘對(duì)象。你現(xiàn)在先把它理解成ROS2 中用名字標(biāo)識(shí)的消息頻道。比如機(jī)器人里可能有/joint_states /camera/image /vla_action /cmd_vel分別可以理解成不同頻道。例如/joint_states里面專(zhuān)門(mén)傳機(jī)器人關(guān)節(jié)狀態(tài)。而/vla_action里面以后可以傳VLA 產(chǎn)生的機(jī)器人動(dòng)作。5. 再看這一坨std_msgs::msg::Int32第一次看肯定很煩。我們拆成std_msgs :: msg :: Int32你以前學(xué)過(guò)std::cout其中::可以先理解成“里面的”所以std::cout可以粗略讀std里面的cout一樣的std_msgs::msg::Int32可以一層層讀std_msgs 里面的 msg 里面的 Int32你現(xiàn)在不用關(guān)心為什么要分這么多層。只需要知道最終的Int32表示一種 ROS2 整數(shù)消息。6. 為什么不能直接發(fā)普通int你可能會(huì)問(wèn)我已經(jīng)有int x 100;為什么 ROS2 還要搞std_msgs::msg::Int32因?yàn)?ROS2 在不同程序、甚至不同電腦之間傳數(shù)據(jù)。它需要一種大家都認(rèn)識(shí)的統(tǒng)一“快遞包裝”。普通int x 100;可以理解成桌子上的數(shù)字 100。而std_msgs::msg::Int32 msg;可以理解成一個(gè)符合 ROS2 快遞標(biāo)準(zhǔn)的盒子。這個(gè)盒子里面有msg.data于是msg.data 100;就是┌─────────────┐ │ ROS2消息盒子 │ │ │ │ data 100 │ └─────────────┘這樣 ROS2 才方便把整個(gè)盒子送出去。7. 所以這兩句一定要理解std_msgs::msg::Int32 msg;意思創(chuàng)建一個(gè) ROS2 整數(shù)消息對(duì)象名字叫msg。然后msg.data 100;意思把數(shù)字100放進(jìn)這個(gè)消息對(duì)象的data成員。是不是一下簡(jiǎn)單很多其實(shí)和你學(xué)過(guò)的 struct 很像。完全可以把它暫時(shí)想象成struct Int32 { int data; };然后Int32 msg; msg.data 100;ROS2 的Int32消息你現(xiàn)在就可以這樣理解。8. 再看 我們之前學(xué)過(guò)std::vectordouble什么意思你已經(jīng)知道一個(gè)裝double的 vector。這里同樣create_publisherstd_msgs::msg::Int32你可以理解成創(chuàng)建一個(gè)專(zhuān)門(mén)發(fā)送 Int32 消息的 Publisher。所以...暫時(shí)就翻譯成“我要處理什么類(lèi)型的數(shù)據(jù)”比如std::vectordouble→ 裝double而create_publisherInt32→ 發(fā)布Int32先到這個(gè)深度就夠。9. 現(xiàn)在重新看這半句create_publisherstd_msgs::msg::Int32( number, 10 );現(xiàn)在你應(yīng)該至少能翻譯出創(chuàng)建一個(gè) Publisher它專(zhuān)門(mén)發(fā)送Int32整數(shù)消息發(fā)送到number這個(gè) Topic。至于10今天甚至可以先不管。暫時(shí)把它記成ROS2 通信參數(shù)目前先默認(rèn)寫(xiě) 10。后面我們專(zhuān)門(mén)學(xué) QoS 時(shí)再解釋。10. 那this-又來(lái)了完整一點(diǎn)this-create_publisherstd_msgs::msg::Int32( number, 10 );還記得this是什么意思嗎就是當(dāng)前這個(gè)對(duì)象自己。現(xiàn)在我們的類(lèi)是class MyNode : public rclcpp::Node所以當(dāng)前對(duì)象就是當(dāng)前這個(gè) ROS2 節(jié)點(diǎn)。那么this-create_publisher(...)就可以翻譯成讓當(dāng)前這個(gè) ROS2 節(jié)點(diǎn)創(chuàng)建一個(gè) Publisher。你甚至可以把this-腦子里先翻譯成“我這個(gè)節(jié)點(diǎn)的……”所以this-create_publisher(...)≈“我這個(gè)節(jié)點(diǎn)創(chuàng)建發(fā)布器。”11. 為什么這里是-因?yàn)閠his是一個(gè)指針。我們前面已經(jīng)學(xué)過(guò)普通對(duì)象 . 指針 -所以this-create_publisher(...)用的是-因?yàn)閠his是“當(dāng)前對(duì)象的地址”??梢援?huà)成this │ │ 地址 ▼ 當(dāng)前 MyNode 對(duì)象 │ └── create_publisher()所以this-create_publisher()就是順著this找到當(dāng)前 Node 對(duì)象然后調(diào)用它的create_publisher()。12. 我們現(xiàn)在已經(jīng)把右邊讀完了this-create_publisherstd_msgs::msg::Int32( number, 10 );整句話當(dāng)前 ROS2 節(jié)點(diǎn)創(chuàng)建一個(gè)發(fā)布器這個(gè)發(fā)布器發(fā)送Int32整數(shù)消息使用numberTopic。很好。先到這里。13. 創(chuàng)建出來(lái)以后怎么辦我們需要把這個(gè) Publisher 保存下來(lái)。就像int x 5;右邊5產(chǎn)生一個(gè)值。左邊x保存它。一樣publisher_ this-create_publisher...();左邊publisher_就是保存創(chuàng)建好的 Publisher。所以你可以暫時(shí)把整句理解成publisher_ 我剛剛創(chuàng)建的發(fā)布器14.publisher_之后怎么用比如已經(jīng)有消息std_msgs::msg::Int32 msg; msg.data 100;然后publisher_-publish(msg);又出現(xiàn)-。為什么因?yàn)閜ublisher_是一個(gè)智能指針。所以publisher_ │ ▼ Publisher對(duì)象 │ └── publish()于是publisher_-publish(msg);翻譯找到 Publisher 對(duì)象調(diào)用它的publish()把msg發(fā)出去。最終msg.data 100 ↓ publisher_-publish(msg) ↓ number Topic ↓ 100 被發(fā)送出去15. 你今天只需要真正看懂這四步先別管整份 ROS2 代碼。只看std_msgs::msg::Int32 msg;創(chuàng)建一個(gè)整數(shù)消息盒子。然后msg.data 100;往盒子里放 100。然后 Publisher 已經(jīng)創(chuàng)建好了publisher_最后publisher_-publish(msg);讓 Publisher 把這個(gè)盒子發(fā)出去。整個(gè)邏輯就是創(chuàng)建盒子 ↓ 裝入100 ↓ 找到發(fā)布器 ↓ 發(fā)送盒子16. 和 VLA 聯(lián)系起來(lái)其實(shí)也一樣現(xiàn)在我們發(fā)的是100以后 VLA 發(fā)的是機(jī)械臂動(dòng)作例如概念上VLA輸出 Joint1 30° Joint2 15° ...依舊是創(chuàng)建ROS2消息 ↓ 把VLA動(dòng)作裝進(jìn)去 ↓ Publisher發(fā)布 ↓ Topic ↓ 機(jī)器人控制節(jié)點(diǎn)收到只是消息盒子變復(fù)雜而已。17. 現(xiàn)在先別學(xué) Subscriber上一課的問(wèn)題就是Publisher 還沒(méi)吃透我馬上又加 Subscriber lambda SharedPtr。所以我們現(xiàn)在把課程拆開(kāi)31APublisher 是什么——現(xiàn)在這節(jié)31BPublisher 的完整最小程序31CSubscriber 是什么31DPublisher → Topic → Subscriber 完整鏈路等這四小節(jié)真正搞懂我們?cè)倮^續(xù)spin()。先檢查你有沒(méi)有抓住核心你現(xiàn)在只需要能回答下面 4 個(gè)很小的問(wèn)題std_msgs::msg::Int32 msg;是什么意思msg.data 100;是什么意思this-create_publisher...()大概是什么意思publisher_-publish(msg);大概是什么意思