2.2 話題通信
場景
話題通信是ROS中使用頻率最高的一種通信模式,話題通信是基于發(fā)布訂閱模式的,也即:一個節(jié)點發(fā)布消息,另一個節(jié)點訂閱該消息。話題通信的應(yīng)用場景也極其廣泛,比如如下場景:
機器人在執(zhí)行導(dǎo)航功能,使用的傳感器是激光雷達(dá),機器人會采集激光雷達(dá)感知到的信息并計算,然后生成運動控制信息驅(qū)動機器人底盤運動。
在該場景中,就不止一次使用到了話題通信。
以激光雷達(dá)信息的采集處理為例,在ROS中有一個節(jié)點需要時時的發(fā)布當(dāng)前雷達(dá)采集到的數(shù)據(jù),導(dǎo)航模塊中也有節(jié)點會訂閱并解析雷達(dá)數(shù)據(jù)。
再以運動消息的發(fā)布為例,導(dǎo)航模塊會綜合多方面數(shù)據(jù)實時計算出運動控制信息并發(fā)布給底盤驅(qū)動模塊,底盤驅(qū)動有一個節(jié)點訂閱運動信息并將其轉(zhuǎn)換成控制電機的脈沖信號。
以此類推,像雷達(dá)、攝像頭、GPS.... 等等一些傳感器數(shù)據(jù)的采集,也都是使用了話題通信,話題通信適用于不斷更新的數(shù)據(jù)傳輸相關(guān)的應(yīng)用場景。
概念
話題通信是一種以發(fā)布訂閱的方式實現(xiàn)不同節(jié)點之間數(shù)據(jù)傳輸?shù)耐ㄐ拍P?。?shù)據(jù)發(fā)布對象稱為發(fā)布方,數(shù)據(jù)訂閱對象稱之為訂閱方,發(fā)布方和訂閱方通過話題相關(guān)聯(lián),發(fā)布方將消息發(fā)布在話題上,訂閱方則從該話題訂閱消息,消息的流向是單向的。

話題通信的發(fā)布方與訂閱方是一種多對多的關(guān)系,也即,同一話題下可以存在多個發(fā)布方,也可以存在多個訂閱方,這意味著數(shù)據(jù)會出現(xiàn)交叉?zhèn)鬏數(shù)那闆r,當(dāng)然如果沒有訂閱方,數(shù)據(jù)傳輸也會出現(xiàn)丟失的情況。

作用
話題通信一般應(yīng)用于不斷更新的、少邏輯處理的數(shù)據(jù)傳輸場景。
關(guān)于消息接口
關(guān)于消息接口的使用有多種方式:
在ROS2中通過std_msgs包封裝了一些原生的數(shù)據(jù)類型,比如:String、Int8、Int16、Int32、Int64、Float32、Float64、Char、Bool、Empty.... 這些原生數(shù)據(jù)類型也可以作為話題通信的載體,不過這些數(shù)據(jù)一般只包含一個 data 字段,而std_msgs包中其他的接口文件也比較簡單,結(jié)構(gòu)的單一意味著功能上的局限性,當(dāng)傳輸一些結(jié)構(gòu)復(fù)雜的數(shù)據(jù)時,就顯得力不從心了;
在ROS2中還預(yù)定義了許多標(biāo)準(zhǔn)話題消息接口,這在實際工作中有著廣泛的應(yīng)用,比如:sensor_msgs包中定義了許多關(guān)于傳感器消息的接口(雷達(dá)、攝像頭、點云......),geometry_msgs包中則定義了許多幾何消息相關(guān)的接口(坐標(biāo)點、坐標(biāo)系、速度指令......);
如果上述接口文件都不能滿足我們的需求,那么就可以自定義接口消息;
具體如何選型,大家可以根據(jù)具體情況具體分析。
2.2.1 案例以及案例分析
1.案例需求
需求1:編寫話題通信實現(xiàn),發(fā)布方以某個頻率發(fā)布一段文本,訂閱方訂閱消息,并輸出在終端。

需求2:編寫話題通信實現(xiàn),發(fā)布方以某個頻率發(fā)布自定義接口消息,訂閱方訂閱消息,并輸出在終端。

2.案例分析
在上述案例中,需要關(guān)注的要素有三個:
發(fā)布方;
訂閱方;
消息載體。
案例1和案例2的主要區(qū)別在于消息載體,前者可以使用原生的數(shù)據(jù)類型,后者需要自定義接口消息。
3.流程簡介
案例2需要先自定義接口消息,除此之外的實現(xiàn)流程與案例1一致,主要步驟如下:
編寫發(fā)布方實現(xiàn);
編寫訂閱方實現(xiàn);
編輯配置文件;
編譯;
執(zhí)行。
案例我們會采用C++和Python分別實現(xiàn),二者都遵循上述實現(xiàn)流程。
4.準(zhǔn)備工作
終端下進入工作空間的src目錄,調(diào)用如下兩條命令分別創(chuàng)建C++功能包和Python功能包。
ros2 pkg create cpp01_topic --build-type ament_cmake --dependencies rclcpp std_msgs base_interfaces_demo ros2 pkg create py01_topic --build-type ament_python --dependencies rclpy std_msgs base_interfaces_demo
2.2.2 話題通信之原生消息(C++)
1.發(fā)布方實現(xiàn)
功能包cpp01_topic的src目錄下,新建C++文件demo01_talker_str.cpp,并編輯文件,輸入如下內(nèi)容:

2.訂閱方實現(xiàn)
功能包cpp01_topic的src目錄下,新建C++文件demo02_listener_str.cpp,并編輯文件,輸入如下內(nèi)容:
/* ?
? ?
? 需求:訂閱發(fā)布方發(fā)布的消息,并輸出到終端。
? ?
? 步驟:
? ? ?
? ? ? 1.包含頭文件;
? ? ? ?
? ? ? 2.初始化 ROS2 客戶端;
? ? ? ?
? ? ? 3.定義節(jié)點類;
? ? ? ? ? ?
? ? ? ? 3-1.創(chuàng)建訂閱方;
? ? ? ? ? ?
? ? ? ? 3-2.處理訂閱到的消息。
? ? ? ?
? ? ? 4.調(diào)用spin函數(shù),并傳入節(jié)點對象指針;
? ? ? ?
? ? ? 5.釋放資源。?
*/
// 1.包含頭文件;
using std::placeholders::_1;
// 3.定義節(jié)點類;
class MinimalSubscriber : public rclcpp::Node?
{
??public:
?
? ? MinimalSubscriber()
??
? ? : Node("minimal_subscriber")
? ?
? ? { ? ? ?
? ? ? // 3-1.創(chuàng)建訂閱方;
??
?? ? subscription_ = this->create_subscription<std_msgs::msg::String>("topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
? ?
? ? ? } ?
?private: ? ?
? ? // 3-2.處理訂閱到的消息;
? ?
? ? void topic_callback(const std_msgs::msg::String & msg) const
? ?
? ? {
? ? ?
? ? ? RCLCPP_INFO(this->get_logger(), "訂閱的消息: '%s'", msg.data.c_str());? ? ? ?}
? ?
? ? ? rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;?
};
int main(int argc, char * argv[])
{ ?
? // 2.初始化 ROS2 客戶端;
?
? rclcpp::init(argc, argv); ?
? // 4.調(diào)用spin函數(shù),并傳入節(jié)點對象指針。
?rclcpp::spin(std::make_shared<MinimalSubscriber>()); ?
? // 5.釋放資源;
?
? rclcpp::shutdown(); ?
? return 0;?
}
3.編輯配置文件
在C++功能包中,配置文件主要關(guān)注package.xml與CMakeLists.txt。
1.package.xml
在創(chuàng)建功能包時,所依賴的功能包已經(jīng)自動配置了,配置內(nèi)容如下:
<depend>rclcpp</depend><depend>std_msgs</depend><depend>base_interfaces_demo</depend>
需要說明的是<depend>base_interfaces_demo</depend>
在本案例中不是必須的。
2.CMakeLists.txt
CMakeLists.txt中發(fā)布和訂閱程序核心配置如下:
find_package(rclcpp REQUIRED)?
find_package(std_msgs REQUIRED)
find_package(base_interfaces_demo REQUIRED)?
add_executable(demo01_talker_str src/demo01_talker_str.cpp)
ament_target_dependencies(
?
? demo01_talker_str
?
? "rclcpp"
?
? "std_msgs"?
)?
add_executable(demo02_listener_str src/demo02_listener_str.cpp)
ament_target_dependencies(
?
?demo02_listener_str
?
?"rclcpp"
?
?"std_msgs"?
)?
install(TARGETS
?
?demo01_talker_str
?
?demo02_listener_str
?
?DESTINATION lib/${PROJECT_NAME})
4.編譯
終端中進入當(dāng)前工作空間,編譯功能包:
colcon build --packages-select cpp01_topic
5.執(zhí)行
當(dāng)前工作空間下,啟動兩個終端,終端1執(zhí)行發(fā)布程序,終端2執(zhí)行訂閱程序。
終端1輸入如下指令:
. install/setup.bash
ros2 run cpp01_topic demo01_talker_str
終端2輸入如下指令:
. install/setup.bash
ros2 run cpp01_topic demo02_listener_str
最終運行結(jié)果與案例1類似。
2.2.3 話題通信之原生消息(Python)
1.發(fā)布方實現(xiàn)
功能包py01_topic的py01_topic目錄下,新建Python文件demo01_talker_str_py.py,并編輯文件,輸入如下內(nèi)容:
""" ?
? ?
? 需求:以某個固定頻率發(fā)送文本“hello world!”,文本后綴編號,每發(fā)送一條消息,編號遞增1。
? ?
? 步驟:
? ? ? ?
? ? ? 1.導(dǎo)包;
? ? ? ?
? ? ? 2.初始化 ROS2 客戶端;
? ? ? ?
? ? ? 3.定義節(jié)點類;
? ? ? ? ? ?
? ? ? ? 3-1.創(chuàng)建發(fā)布方;
? ? ? ? ? ?
? ? ? ? 3-2.創(chuàng)建定時器;
? ? ? ? ? ?
? ? ? ? 3-3.組織消息并發(fā)布。
? ? ? ?
? ? ? 4.調(diào)用spin函數(shù),并傳入節(jié)點對象;
? ? ? ?
? ? ? 5.釋放資源。
"""
# 1.導(dǎo)包;
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
# 3.定義節(jié)點類;
class MinimalPublisher(Node):
? ?
??
? def __init__(self):
? ? ? ?
? ? super().__init__('minimal_publisher_py') ? ? ? ?
? ? # 3-1.創(chuàng)建發(fā)布方;
? ? ? ?
? ? self.publisher_ = self.create_publisher(String, 'topic', 10) ? ? ? ?
? ? # 3-2.創(chuàng)建定時器;
? ? ? ?
? ? timer_period = 0.5
? ? ? ?
? ? self.timer = self.create_timer(timer_period, self.timer_callback)? ? ? ? ? ? self.i = 0
? ?
? ? # 3-3.組織消息并發(fā)布。
? ?
? ? def timer_callback(self):
? ? ? ?
? ? msg = String()
? ? ? ?
? ? msg.data = 'Hello World(py): %d' % self.i
? ?? ? ??
? ? self.publisher_.publish(msg)
??
? ? self.get_logger().info('發(fā)布的消息: "%s"' % msg.data)
? ? ? ?
? ? self.i += 1
def main(args=None):
? ?
? ?# 2.初始化 ROS2 客戶端;
? ?
? ? rclpy.init(args=args) ? ?
? ?# 4.調(diào)用spin函數(shù),并傳入節(jié)點對象;
? ?
? ? minimal_publisher = MinimalPublisher()
? ?
? ? rclpy.spin(minimal_publisher) ? ?
? ?# 5.釋放資源。
? ?
? ? rclpy.shutdown()
if __name__ == '__main__':
? ?
? main()
2.訂閱方實現(xiàn)
功能包py01_topic的py01_topic目錄下,新建Python文件demo02_listener_str_py.py,并編輯文件,輸入如下內(nèi)容:

3.編輯配置文件
在Python功能包中,配置文件主要關(guān)注package.xml與setup.py。
1.package.xml
在創(chuàng)建功能包時,所依賴的功能包已經(jīng)自動配置了,配置內(nèi)容如下:
<depend>rclcpp</depend><depend>std_msgs</depend><depend>base_interfaces_demo</depend>
需要說明的是和上一節(jié)C++實現(xiàn)一樣<depend>base_interfaces_demo</depend>
在本案例中不是必須的。
2.setup.py
entry_points
字段的console_scripts
中添加如下內(nèi)容:
entry_points={ ? ?'console_scripts': [ ? ? ? ?'demo01_talker_str_py = py01_topic.demo01_talker_str_py:main', ? ? ? ?'demo02_listener_str_py = py01_topic.demo02_listener_str_py:main'
? ?],
},
4.編譯
終端中進入當(dāng)前工作空間,編譯功能包:
colcon build --packages-select py01_topic
5.執(zhí)行
當(dāng)前工作空間下,啟動兩個終端,終端1執(zhí)行發(fā)布程序,終端2執(zhí)行訂閱程序。
終端1輸入如下指令:
. install/setup.bash
ros2 run py01_topic demo01_talker_str_py
終端2輸入如下指令:
. install/setup.bash
ros2 run py01_topic demo02_listener_str_py
最終運行結(jié)果與案例1類似。
2.2.4 話題通信自定義接口消息
自定義接口消息的流程與在功能包中編寫可執(zhí)行程序的流程類似,主要步驟如下:
創(chuàng)建并編輯?
.msg
文件;編輯配置文件;
編譯;
測試。
接下來,我們可以參考案例2編譯一個msg文件,該文件中包含學(xué)生的姓名、年齡、身高等字段。
1.創(chuàng)建并編輯 .msg 文件
功能包base_interfaces_demo下新建 msg 文件夾,msg文件夾下新建Student.msg文件,文件中輸入如下內(nèi)容:
string ? name
int32 ? ?age
float64 ?height
2.編輯配置文件
1.package.xml文件
在package.xml中需要添加一些依賴包,具體內(nèi)容如下:
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
2.CMakeLists.txt文件
為了將.msg
文件轉(zhuǎn)換成對應(yīng)的C++和Python代碼,還需要在CMakeLists.txt中添加如下配置:
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
?"msg/Student.msg"
)
3.編譯
終端中進入當(dāng)前工作空間,編譯功能包:
colcon build --packages-select base_interfaces_demo
4.測試
編譯完成之后,在工作空間下的install目錄下將生成Student.msg
文件對應(yīng)的C++和Python文件,我們也可以在終端下進入工作空間,通過如下命令查看文件定義以及編譯是否正常:
. install/setup.bash
ros2 interface show base_interfaces_demo/msg/Student
正常情況下,終端將會輸出與Student.msg
文件一致的內(nèi)容。
2.2.5 話題通信之自定義消息(C++)
準(zhǔn)備
C++文件中包含自定義消息相關(guān)頭文件時,可能會拋出異常,可以配置VSCode中c_cpp_properties.json文件,在文件中的 includePath屬性下添加一行:"${workspaceFolder}/install/base_interfaces_demo/include/**"
添加完畢后,包含相關(guān)頭文件時,就不會拋出異常了,其他接口文件或接口包的使用也與此同理。
1.發(fā)布方實現(xiàn)
功能包cpp01_topic的src目錄下,新建C++文件demo01_talker_stu.cpp,并編輯文件,輸入如下內(nèi)容:
/* ?
?
?需求:以某個固定頻率發(fā)送文本學(xué)生信息,包含學(xué)生的姓名、年齡、身高等數(shù)據(jù)。?
*/
// 1.包含頭文件;
using namespace std::chrono_literals;
using base_interfaces_demo::msg::Student;
// 3.定義節(jié)點類;?
class MinimalPublisher : public rclcpp::Node?
{ ?public:?
? ? MinimalPublisher()
? ?
? ? : Node("student_publisher"), count_(0)
? ?
? ? { ? ? ?
? ? ?// 3-1.創(chuàng)建發(fā)布方;
? ? ?
? ? ?publisher_ = this->create_publisher<Student>("topic_stu", 10); ? ? ?
? ? ?// 3-2.創(chuàng)建定時器;
? ? ?
? ? ?timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));
? ?
} ?
private: ? ?
?void timer_callback()
? ?
? { ? ? ?
? // 3-3.組織消息并發(fā)布。
? ? ?
? auto stu = Student();
? ? ?
? stu.name = "張三";
? ? ?
? stu.age = count_++;
? ? ?
? stu.height = 1.65;
? ? ?
? RCLCPP_INFO(this->get_logger(), "學(xué)生信息:name=%s,age=%d,height=%.2f", stu.name.c_str(),stu.age,stu.height);
? ? ?
? publisher_->publish(stu);
? ?
?}
? ?
? rclcpp::TimerBase::SharedPtr timer_;
? ?
? rclcpp::Publisher<Student>::SharedPtr publisher_; ? ?
? size_t count_;?
};
int main(int argc, char * argv[])
{ ?
? // 2.初始化 ROS2 客戶端;
?rclcpp::init(argc, argv); ?
? // 4.調(diào)用spin函數(shù),并傳入節(jié)點對象指針。
?rclcpp::spin(std::make_shared<MinimalPublisher>()); ?
? // 5.釋放資源;
?rclcpp::shutdown(); ?return 0;?
}
2.訂閱方實現(xiàn)
功能包cpp01_topic的src目錄下,新建C++文件demo04_listener_stu.cpp,并編輯文件,輸入如下內(nèi)容:
功能包cpp01_topic的src目錄下,新建C++文件demo04_listener_stu.cpp,并編輯文件,輸入如下內(nèi)容:
/* ?
? ?
? ?需求:訂閱發(fā)布方發(fā)布的學(xué)生消息,并輸出到終端。?
*/
// 1.包含頭文件;
using std::placeholders::_1;
using base_interfaces_demo::msg::Student;
// 3.定義節(jié)點類;
class MinimalSubscriber : public rclcpp::Node
{ ?
? public:
? ?
? ? MinimalSubscriber()
? ?
? ? : Node("student_subscriber")
? ?
? ? { ? ? ?
? ? ?// 3-1.創(chuàng)建訂閱方;
? ? ?
? ? ?subscription_ = this->create_subscription<Student>("topic_stu", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
? ?
? ? ?} ?
?private: ? ?
? ?// 3-2.處理訂閱到的消息;
? ?
? ?void topic_callback(const Student & msg) const
? ?
? ?{
? ? ?
? ? ?RCLCPP_INFO(this->get_logger(), "訂閱的學(xué)生消息:name=%s,age=%d,height=%.2f", msg.name.c_str(),msg.age, msg.height);
? ?
? ? }
? ?
? ? rclcpp::Subscription<Student>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
?// 2.初始化 ROS2 客戶端;
?
?rclcpp::init(argc, argv); ?
?// 4.調(diào)用spin函數(shù),并傳入節(jié)點對象指針。
?rclcpp::spin(std::make_shared<MinimalSubscriber>()); ?
?// 5.釋放資源;
?
?rclcpp::shutdown(); ?
?return 0;?
}
3.編輯配置文件
package.xml無需修改,CMakeLists.txt文件需要添加如下內(nèi)容:
add_executable(demo03_talker_stu src/demo03_talker_stu.cpp)
ament_target_dependencies(
?
?demo03_talker_stu
?
?"rclcpp"
?
?"std_msgs"
?
?"base_interfaces_demo"?
)?
add_executable(demo04_listener_stu src/demo04_listener_stu.cpp)
ament_target_dependencies(
?
?demo04_listener_stu
?
?"rclcpp"
?
?"std_msgs"
?
?"base_interfaces_demo"?
)
文件中install修改為如下內(nèi)容:
install(TARGETS
?
?demo01_talker_str
?
?demo02_listener_str
?
?demo03_talker_stu
?
?demo04_listener_stu
?
?DESTINATION lib/${PROJECT_NAME})
4.編譯
終端中進入當(dāng)前工作空間,編譯功能包:
colcon build --packages-select cpp01_topic
5.執(zhí)行
當(dāng)前工作空間下,啟動兩個終端,終端1執(zhí)行發(fā)布程序,終端2執(zhí)行訂閱程序。
終端1輸入如下指令:
. install/setup.bash?
ros2 run cpp01_topic demo03_talker_stu
終端2輸入如下指令:
. install/setup.bash?
ros2 run cpp01_topic demo04_listener_stu
最終運行結(jié)果與案例2類似。
2.2.6 話題通信之自定義消息(Python)
準(zhǔn)備
Python文件中導(dǎo)入自定義消息相關(guān)的包時,為了方便使用,可以配置VSCode中settings.json文件,在文件中的python.autoComplete.extraPaths和python.analysis.extraPaths屬性下添加一行:"${workspaceFolder}/install/base_interfaces_demo/local/lib/python3.10/dist-packages"
添加完畢后,代碼可以高亮顯示且可以自動補齊,其他接口文件或接口包的使用也與此同理。
1.發(fā)布方實現(xiàn)
功能包py01_topic的py01_topic目錄下,新建Python文件demo03_talker_stu_py.py,并編輯文件,輸入如下內(nèi)容:
""" ?
? ?
? ?需求:以某個固定頻率發(fā)送文本學(xué)生信息,包含學(xué)生的姓名、年齡、身高等數(shù)據(jù)。?
"""
# 1.導(dǎo)包;
import rclpy
from rclpy.node import Node
from base_interfaces_demo.msg import Student
# 3.定義節(jié)點類;
class MinimalPublisher(Node):
? ?
?def __init__(self):
? ? ? ?
? ? super().__init__('stu_publisher_py') ? ? ? ?
? ? # 3-1.創(chuàng)建發(fā)布方;
? ? ? ?
? ? self.publisher_ = self.create_publisher(Student, 'topic_stu', 10)? ? ? ?
? ? # 3-2.創(chuàng)建定時器;
? ? ? ?
? ? timer_period = 0.5
? ? ? ?
? ? self.timer = self.create_timer(timer_period, self.timer_callback)? ? ? ? ? ? self.i = 0
? ?
? ? # 3-3.組織消息并發(fā)布。
? ?
? ? def timer_callback(self):
? ? ? ?
? ? ? ?stu = Student()
? ? ? ?
? ? ? ?stu.name = "李四"
? ? ? ?
? ? ? ?stu.age = self.i
? ? ? ?
? ? ? ?stu.height = 1.70
? ? ? ?
? ? ? ?self.publisher_.publish(stu)
? ? ? ?
? ? ? ?self.get_logger().info('發(fā)布的學(xué)生消息(py): name=%s,age=%d,height=%.2f' % (stu.name, stu.age, stu.height))
? ? ? ?
? ? ? ?self.i += 1
def main(args=None):
? ?
? # 2.初始化 ROS2 客戶端;
? ?
? rclpy.init(args=args) ? ?
? # 4.調(diào)用spin函數(shù),并傳入節(jié)點對象;
? ?
? minimal_publisher = MinimalPublisher()
? ?
? rclpy.spin(minimal_publisher) ? ?
? # 5.釋放資源。
? ?
? rclpy.shutdown()
if __name__ == '__main__':
? ?
? main()
2.訂閱方實現(xiàn)
功能包py01_topic的py01_topic目錄下,新建Python文件demo04_listener_stu_py.py,并編輯文件,輸入如下內(nèi)容:
""" ?
? ?
? 需求:訂閱發(fā)布方發(fā)布的學(xué)生消息,并輸出到終端。?
"""
# 1.導(dǎo)包;
import rclpy
from rclpy.node import Node
from base_interfaces_demo.msg import Student#?
3.定義節(jié)點類;
class MinimalSubscriber(Node):
? ?
?def __init__(self):
? ? ? ?
? ?super().__init__('stu_subscriber_py') ? ? ? ?
? ? # 3-1.創(chuàng)建訂閱方;
? ? ? ?
? ? self.subscription = self.create_subscription(
? ? ? ? ? ?
? ? ? ? Student, ? ? ? ? ? ?
? ? ? ? 'topic_stu',
? ? ? ? ? ?
? ? ? ? self.listener_callback, ? ? ? ? ? ?
? ? ? ? 10)
? ? ? ?
? ? ? ? self.subscription ?
? ?
?# 3-2.處理訂閱到的消息。
? ?
?def listener_callback(self, stu):
? ? ? ?
? ? ?self.get_logger().info('訂閱的消息(py): name=%s,age=%d,height=%.2f' % (stu.name, stu.age, stu.height))
def main(args=None):
? ?
? # 2.初始化 ROS2 客戶端;
? ?
? ?rclpy.init(args=args) ? ?
? # 4.調(diào)用spin函數(shù),并傳入節(jié)點對象;
? ?
? minimal_subscriber = MinimalSubscriber()
? ?
? rclpy.spin(minimal_subscriber) ? ?
? # 5.釋放資源。
? ?rclpy.shutdown()
if __name__ == '__main__':
? ?
? main()
3.編輯配置文件
package.xml無需修改,需要修改setup.py文件,entry_points
字段的console_scripts
中修改為如下內(nèi)容:
entry_points={
? ?
? 'console_scripts': [
? ? ? ?
? ? ?'demo01_talker_str_py = py01_topic.demo01_talker_str_py:main',? ? ? ? ? ? ?'demo02_listener_str_py = py01_topic.demo02_listener_str_py:main',? ? ? ? ? ?'demo03_talker_stu_py = py01_topic.demo03_talker_stu_py:main',? ? ? ? ? ?? 'demo04_listener_stu_py = py01_topic.demo04_listener_stu_py:main'
? ?
? ?],?
},
4.編譯
終端中進入當(dāng)前工作空間,編譯功能包:
colcon build --packages-select py01_topic
5.執(zhí)行
當(dāng)前工作空間下,啟動兩個終端,終端1執(zhí)行發(fā)布程序,終端2執(zhí)行訂閱程序。
終端1輸入如下指令:
. install/setup.bash?
ros2 run py01_topic demo03_talker_stu_py
終端2輸入如下指令:
. install/setup.bash?
?ros2 run py01_topic demo04_listener_stu_py
最終運行結(jié)果與案例2類似。

B站有完整的ros系列教程視頻,可以觀看完整內(nèi)容ros課程ROS2理論與實踐
更多內(nèi)容將在猛獅知識星球社區(qū)更新最新課程,后續(xù)將推出更多優(yōu)質(zhì)內(nèi)容——詳情可關(guān)注猛獅集訓(xùn)營公眾號和猛獅集訓(xùn)營官方網(wǎng)站。