ROS2话题通讯基础
- 2.5 自定义话题通讯
- 2.5.1 自定义话题通讯的一般流程
- 2.5.2 创建自定义话题消息简单例子
- 2.5.3 快速创建C/C++和Python自定义话题通讯的Student.msg
- 2.6 使用C/C++实现自定义话题通讯
- 2.6.1 创建C/C++自定义话题发布方功能包并编写节点文件
- 2.6.2 配置C/C++自定义话题发布方功能包
- 2.6.3 编译并运行C/C++自定义话题发布方功能包
- 2.6.4 创建C/C++自定义话题订阅方功能包并编辑节点文件
- 2.6.5 编译并运行C/C++自定义话题订阅节点
- 2.7 使用Python实现自定义话题通讯
- 2.7.1 创建Python自定义话题订阅方节点并编写节点内容
- 2.7.2 配置Python自定义订阅方功能包
- 2.7.3 编译并运行Python自定义话题发布方
- 2.7.4 创建Python自定义话题订阅方
- 2.7.5 编译运行Python发布方节点
- 2.8 话题通讯小结
- 其他ROS2学习笔记: ROS2学习笔记
- 代码仓库:Github连接地址
- 欢迎各位互相学习交流
2.5 自定义话题通讯
参考内容
Creating custom msg and srv files
2.2.4_话题通信_自定义接口消息_接口文件
2.5.1 自定义话题通讯的一般流程
构建自定义话题其实就是利用
ament_cmake
工具构建出该自定义话题的.c
和.py
文件,编译好的自定义话题,也就可以和std_msgs
一样在创建包的时候,手动添加进去,后续即可不用再配置packages.xml
和CMakeLists.txt
-
- 创建ROS2功能包,功能包可以只用来作为存放自定义的
msg/srv
,不需要节点node的功能包,且--build-type
必须是ament_camke
,因为目前来看,Python的自定义的消息或者服务也需要通过cmake编译出来再调用,功能包必须是下划线的推荐命名方法,而不是大小写的驼峰,否则会报错,如下所示:
- 创建ROS2功能包,功能包可以只用来作为存放自定义的
--- stderr: msgDemoError
CMake Error at /opt/ros/humble/share/rosidl_adapter/cmake/rosidl_adapt_interfaces.cmake:59 (message):
execute_process(/usr/bin/python3.10 -m rosidl_adapter --package-name
msgDemoError --arguments-file
/mnt/hgfs/VMware/ROS2_DEMO/2_Chapter/code/build/msgDemoError/rosidl_adapter__arguments__msgDemoError.json
--output-dir
/mnt/hgfs/VMware/ROS2_DEMO/2_Chapter/code/build/msgDemoError/rosidl_adapter/msgDemoError
--output-file
/mnt/hgfs/VMware/ROS2_DEMO/2_Chapter/code/build/msgDemoError/rosidl_adapter/msgDemoError.idls)
returned error code 1:
Error processing 'String name' of 'msgDemoError/Student': ''msgDemoError'
is an invalid package name. It should have the pattern
'^(?!.*__)(?!.*_$)[a-z][a-z0-9_]*$''
Traceback (most recent call last):
File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
exec(code, run_globals)
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/__main__.py", line 19, in <module>
sys.exit(main())
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/main.py", line 53, in main
abs_idl_file = convert_to_idl(
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/__init__.py", line 19, in convert_to_idl
return convert_msg_to_idl(
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/msg/__init__.py", line 28, in convert_msg_to_idl
msg = parse_message_string(package_name, input_file.stem, content)
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/parser.py", line 520, in parse_message_string
Type(type_string, context_package_name=pkg_name),
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/parser.py", line 277, in __init__
super(Type, self).__init__(
File "/opt/ros/humble/local/lib/python3.10/dist-packages/rosidl_adapter/parser.py", line 201, in __init__
raise InvalidResourceName(
rosidl_adapter.parser.InvalidResourceName: 'msgDemoError' is an invalid
package name. It should have the pattern
'^(?!.*__)(?!.*_$)[a-z][a-z0-9_]*$'
Call Stack (most recent call first):
/opt/ros/humble/share/rosidl_cmake/cmake/rosidl_generate_interfaces.cmake:130 (rosidl_adapt_interfaces)
CMakeLists.txt:16 (rosidl_generate_interfaces)
---
Failed <<< msgDemoError [1.53s, exited with code 1]
-
- 在ROS2功能包内创建
msg
文件夹,里面存放自定义的.msg
消息文件,并且.msg
文件必须是大写开头的符合类的命名规则
- 在ROS2功能包内创建
-
- 配置
packages.xml
文件,都需要配置下面的内容(下面的配置项与.msg文件无关,是默认的固定配置)
- 配置
<!-- 这一项是针对在msg中可能用到的其他依赖项,例如geomtry_msgs等等,如过没有用到就不添加 -->
<depend>其他的depend</depend>
<!-- 下面三项是必须的 -->
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
-
- 配置
CMakeLists.txt
文件,find_package
和rosidl_generate_interfaces
- 配置
# 0. 是针对在msg中可能用到的其他依赖项,例如geomtry_msgs等等,则需要添加
# find_package(geometry_msgs REQUIRED)
# 1. rosidl_default_generators是必须添加的内容
find_package(rosidl_default_generators REQUIRED)
# 2. rosidl_generate_interfaces必须配置,里面添加msg文件位置
rosidl_generate_interfaces(${PROJECT_NAME}
# 2.1 添加自定义的msg位置,例如存放在功能包的msg文件夹下的xxx.msg
"msg/xxx.msg"
# 2.2 可选,如果xxx.msg依赖了其他的内容,例如依赖了geometry_msgs
DEPENDENCIES geometry_msgs
)
-
- 编译功能包:
colcon build --packages-select <功能包名>
- 编译功能包:
-
- 此时激活
install
目录下的setup.bash
如. install/setup.bash
,可以通过ros2 interface show <功能包名称>/msg/xxx.msg
查看到xxx.msg
内容,此时编译好的msg
的.c
文件存放在install/<功能包名>/include
下,.py
文件则存放在install/<功能包名>/local/
- 此时激活
2.5.2 创建自定义话题消息简单例子
-
创建自定义消息功能包
msg_demo
,采用ament_cmake
方式:ros2 pkg create msg_demo --build-type ament_cmake
-
在功能包内创建
msg
文件夹,其中存放Demo.msg
文件,文件内容如下:其中还依赖geometry_msgs
geometry_msgs/Point center
float64 radius
- 此时的功能包内部的目录结构如下:
tree -a
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code/msg_demo$ tree -a
.
├── CMakeLists.txt
├── include
│ └── msg_demo
├── msg
│ └── Demo.msg
├── package.xml
└── src
4 directories, 3 files
- 配置
packages.xml
添加依赖项,特别的是该Demo.msg
还需依赖gemotry_msgs
:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>msg_demo</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="pldz@R7000.com">pldz</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- 自定义Demo.msg中依赖geometry_msgs所以添加到depend -->
<depend>geometry_msgs</depend>
<!-- 构建自定义msg的必须依赖项 -->
<buildtool_depend>rosidl_default_generators</buildtool_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
- 配置
CMakeLists.txt
文件,添加构建自定义Demo.msg
的依赖:
cmake_minimum_required(VERSION 3.8)
project(msg_demo)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
# 1. 自定义的Demo.msg依赖geometry_msgs
find_package(geometry_msgs REQUIRED)
# 2. 必须添加的构建自定义包辅助包的位置
find_package(rosidl_default_generators REQUIRED)
# 3. 构建自定义msg
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Demo.msg" # Demo.msg的位置
DEPENDENCIES geometry_msgs # 创建Demo.msg需要的依赖
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()
-
构建功能包:
colcon build --packages-select msg_demo
-
查看自定义的消息:激活环境:
. install/setup.bash
,查看自定义消息ros2 interface show msg_demo/msg/Demo
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ colcon build --packages-select msg_demo
Starting >>> msg_demo
Finished <<< msg_demo [10.4s]
Summary: 1 package finished [10.7s]
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ . install/setup.bash
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ ros2 interface show msg_demo/msg/Demo
geometry_msgs/Point center
float64 x
float64 y
float64 z
float64 radius
- 查看
instll
下面的文件:其中.c
的class文件在install/<功能包名>/include/<功能包名>/<功能包名>/msg/**
,.py
在install/<功能包名>/local/lib/python3.10/dist-packages/<功能包名>/msg/**
2.5.3 快速创建C/C++和Python自定义话题通讯的Student.msg
-
新建功能包:
ros2 pkg create student_msg --build-type ament_cmake
-
创建
msg
文件夹和Student.msg
文件,写入如下内容:
string name
int32 exam
float64 score
- 不需要其他依赖项,配置
packages.xml
添加下面三项即可:
<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>
- 配置
CMakeLists.txt
:
find_package(rosidl_default_generators REQUIRED)
rosidl_generate_interfaces(${PROJECT_NAME}
"msg/Student.msg"
)
- 快速构建:
colcon build --packages-select student_msg
:
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ colcon build --packages-select student_msg
Starting >>> student_msg
Finished <<< student_msg [8.93s]
Summary: 1 package finished [9.21s]
2.6 使用C/C++实现自定义话题通讯
2.6.1 创建C/C++自定义话题发布方功能包并编写节点文件
-
创建ROS2功能包,这里为了省的创建node文件直接指定了
cpp_idl_pub
,指令如下:ros2 pkg create cpp_idl_pub --build-type ament_cmake --node-name cppIdlPubNode
, -
配置Vscode:配置Vscode的
settings.json
:
{
"C_Cpp.default.includePath": [
"/opt/ros/humble/include/**",
"./install/student_msg/include/**"
],
"python.analysis.extraPaths": [
"/opt/ros/humble/local/lib/python3.10/dist-packages/"
],
}
- 编写自定义节点文件
cppIdlPubNode.cpp
:
#include "rclcpp/rclcpp.hpp"
// 1. 添加自定义消息头文件,如果vscode出现下划线警告,则需要配置settings.json添加include路径
#include "student_msg/msg/student.hpp"
// C++14中的时间库
using namespace std::chrono_literals;
// 2. 引入Student.msg生成的Student类
using student_msg::msg::Student;
class CppIdlMsgPub: public rclcpp::Node
{
public:
/* 3. ROS2节点的构造函数,其中包括一个node对象(初始化时候没有给出节点的名称),
* 和属性exam_(初始化为0,类型为size_t),以及属性socre_(类型为double_t初始值为60.0) */
CppIdlMsgPub(const char* nodeName):Node(nodeName), exam_(0), score_(60.0)
{
// 4. 创建发布者,参数分别为话题名称myStudent,和发布队列的长队为10
publisher_ = this->create_publisher<Student>("myStudent", 10);
// 5. 创建定时器,设置发布的频率,并绑定定时执行的事件,这里给到的是CppIdlMsgPub类的函数
timer_ = this->create_wall_timer(500ms, std::bind(&CppIdlMsgPub::timer_callback, this));
}
private:
// 6. 定义回调函数
void timer_callback()
{
Student stu = Student();
// 6.1 姓名zhangsan
stu.name = "zhangsan";
// 6.2 考试次数递增
stu.exam = exam_++;
// 6.3 分数一直递增
stu.score = score_;
score_ += 0.1;
RCLCPP_INFO(this->get_logger(), "Student: '%s', exam time: %d, socre is : %f", stu.name.c_str(),stu.exam,stu.score);
publisher_->publish(stu);
}
// 5. 计时器、发布者和计数器字段的声明
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<Student>::SharedPtr publisher_;
size_t exam_;
double_t score_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
// 创建节点,给出构造的节点名为CppIdlMsgPubNode
rclcpp::spin(std::make_shared<CppIdlMsgPub>("CppIdlMsgPubNode"));
rclcpp::shutdown();
return 0;
}
2.6.2 配置C/C++自定义话题发布方功能包
- 配置
packages.xml
文件,添加依赖项有rclcpp
用于构建节点,和student_msg
用于构建Student.msg
:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>cpp_idl_pub</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="pldz@R7000.com">pldz</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<!-- 添加依赖项 -->
<depend>rclcpp</depend>
<depend>student_msg</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
- 配置
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.8)
project(cpp_idl_pub)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
# 1. 添加依赖项
find_package(rclcpp REQUIRED)
find_package(student_msg REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
# 2. 默认是已经创建了构建节点的文件配置
add_executable(cppIdlPubNode src/cppIdlPubNode.cpp)
# 3. 默认已经包括<INSTALL_INTERFACE>的路径也不用更改
target_include_directories(cppIdlPubNode PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
# 4. ament工具构建节点的依赖配置
ament_target_dependencies(
cppIdlPubNode
rclcpp
student_msg
)
target_compile_features(cppIdlPubNode PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
# 5. Install配置,默认ros2 run <包名>的配置
install(TARGETS cppIdlPubNode
DESTINATION lib/${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
# 6. 生成包的环境
ament_package()
2.6.3 编译并运行C/C++自定义话题发布方功能包
-
编译:
colcon build --packages-select cpp_idl_pub
-
激活环境:
. install/setup.bash
-
运行:
ros2 run cpp_idl_pub cppIdlPubNode
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ colcon build --packages-select cpp_idl_pub
Starting >>> cpp_idl_pub
Finished <<< cpp_idl_pub [10.0s]
Summary: 1 package finished [10.4s]
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ . install/setup.bash
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ ros2 run cpp_idl_pub cppIdlPubNode
[INFO] [1683041064.126034476] [CppIdlMsgPubNode]: Student: 'zhangsan', exam time: 0, socre is : 60.000000
[INFO] [1683041064.625514370] [CppIdlMsgPubNode]: Student: 'zhangsan', exam time: 1, socre is : 60.100000
[INFO] [1683041065.125571561] [CppIdlMsgPubNode]: Student: 'zhangsan', exam time: 2, socre is : 60.200000
^C[INFO] [1683041082.271152379] [rclcpp]: signal_handler(signum=2)
2.6.4 创建C/C++自定义话题订阅方功能包并编辑节点文件
-
创建ROS2功能包,包名为
cpp_idl_sub
,节点名称为cppIdlSubNode
,并直接指定依赖项为rclcpp
和student_msg
,如下所示:ros2 pkg create cpp_idl_sub --build-type ament_cmake --node-name cppIdlSubNode --dependencies rclcpp student_msg
-
编辑节点文件:订阅
Student
类型的话题,话题名为myStudent
:
#include "rclcpp/rclcpp.hpp"
// 1. 添加自定义消息头文件,如果vscode出现下划线警告,则需要配置settings.json添加include路径
#include "student_msg/msg/student.hpp"
// 占位符,结合std::bind函数的绑定使用
using std::placeholders::_1;
// 2. 引入Student.msg生成的Student类
using student_msg::msg::Student;
class CppIdlMsgSub: public rclcpp::Node
{
public:
/* 3. ROS2节点的构造函数,其中包括一个node对象(初始化时候没有给出节点的名称),
* 和属性exam_(初始化为0,类型为size_t),以及属性socre_(类型为double_t初始值为60.0) */
CppIdlMsgSub(const char* nodeName):Node(nodeName)
{
subscription_ = this->create_subscription<Student>(
"myStudent", 10, std::bind(&CppIdlMsgSub::topic_callback, this, _1));
}
private:
// 4. 定义订阅到消息的回调函数
void topic_callback(const Student & stu) const
{
RCLCPP_INFO(this->get_logger(), "I heard: %s, exam: %d, score: %f", stu.name.c_str(),stu.exam, stu.score);
}
// 5. 计时器、发布者和计数器字段的声明
rclcpp::Subscription<Student>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
// 创建节点,给出构造的节点名为CppIdlMsgSubNode
rclcpp::spin(std::make_shared<CppIdlMsgSub>("CppIdlMsgSubNode"));
rclcpp::shutdown();
return 0;
}
2.6.5 编译并运行C/C++自定义话题订阅节点
-
由于在创建包时候已经指定了依赖项,可以不用再进行配置
packages.xml
和CMakeLists.txt
文件,直接编译colcon build --packages-select cpp_idl_sub
-
激活环境:
. install/setup.bash
-
运行:
2.7 使用Python实现自定义话题通讯
2.7.1 创建Python自定义话题订阅方节点并编写节点内容
-
创建ROS2 Python发布方功能包,包名为
python_idl_pub
,节点名为pythonIdlPubNode
:ros2 pkg create python_idl_pub --build-type ament_python --node-name pythonIdlPubNode
-
配置Vscode环境:在
settings.json
中添加Python的依赖项位置,虽然编译不影响报错,但是vscode的警告看着也不习惯:
- 编写订阅节点:
import rclpy
from rclpy.node import Node
# 1. 导入包,如果vscode下划线警告,则需要配置settings.json
from student_msg.msg import Student
class PythonIdlPub(Node):
def __init__(self, nodeName):
# 1. 初始化父类构造函数,其中节点名需要创建时候指定
super().__init__(nodeName)
self.exam_ = 0
self.score_ = 60.0
# 2. 声明发布者,发布消息类型为Student,话题名为myStudent,队列大小为10
self.publisher_ = self.create_publisher(Student, 'myStudent', 10)
# 3. 创建定时器,其中更新频率为0.5秒,并绑定回调函数
self.timer = self.create_timer(0.5, self.timer_callback)
# 4. 定义回调函数
def timer_callback(self):
stu = Student()
stu.name = "zhangsan"
stu.exam = self.exam_
stu.score = self.score_
self.publisher_.publish(stu)
self.get_logger().info('Publishing: name: {}, exam:{}, score:{}'.format(stu.name,stu.exam,stu.score))
self.exam_ += 1
self.score_ += 0.1
def main(args=None):
rclpy.init(args=args)
pythonIdlPubNode = PythonIdlPub("pythonIdlPubNode")
rclpy.spin(pythonIdlPubNode)
# 销毁节点
pythonIdlPubNode.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
2.7.2 配置Python自定义订阅方功能包
- 配置
packages.xml
增加rclpy
和student_msg
两个依赖项,注意Python本身是可执行文件,因此它的为<exec_depend>
与C/C++
的<depend>
关键字不同:
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>python_idl_pub</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="pldz@R7000.com">pldz</maintainer>
<license>TODO: License declaration</license>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<!-- 添加依赖项 -->
<exec_depend>rclpy</exec_depend>
<exec_depend>student_msg</exec_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
- 配置
setup.py
文件,事实上,我们已经创建包的时候已经指定了节点名,其实应该是不用配置生成节点的main
入口的:
from setuptools import setup
package_name = 'python_idl_pub'
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='pldz',
maintainer_email='pldz@R7000.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
# 配置节点的main函数入口
entry_points={
'console_scripts': [
'pythonIdlPubNode = python_idl_pub.pythonIdlPubNode:main'
],
},
)
2.7.3 编译并运行Python自定义话题发布方
-
编译:
colcon build --packages-select python_idl_pub
-
激活环境:
. install/setup.bash
-
运行节点:
ros2 run python_idl_pub pythonIdlPubNode
pldz@pldz-pc:~/share/ROS2_DEMO/2_Chapter/code$ ros2 run python_idl_pub pythonIdlPubNode
[INFO] [1683044269.902750751] [pythonIdlPubNode]: Publishing: name: zhangsan, exam:0, score:60.0
[INFO] [1683044270.393748471] [pythonIdlPubNode]: Publishing: name: zhangsan, exam:1, score:60.1
[INFO] [1683044270.891683074] [pythonIdlPubNode]: Publishing: name: zhangsan, exam:2, score:60.2
2.7.4 创建Python自定义话题订阅方
-
创建Python功能包时,包名为
python_idl_sub
,节点名为pythonIdlSubNode
,直接指定依赖项rclpy
和student_msg
:ros2 pkg create python_idl_sub --build-type ament_python --node-name pythonIdlSubNode --dependencies rclpy student_msg
-
编写节点:
import rclpy
from rclpy.node import Node
# 1. 导入包,如果vscode下划线警告,则需要配置settings.json
from student_msg.msg import Student
class PythonIdlSub(Node):
def __init__(self, nodeName):
# 1. 构造函数,初始化node节点
super().__init__(nodeName)
# 2. 声明订阅者,订阅话题`myTopicNmae`,并绑定回调函数
self.subscription = self.create_subscription(Student,'myStudent',self.listener_callback,10)
# 3. 定义回调函数
def listener_callback(self, stu:Student):
self.get_logger().info('I heard: name:{}, exam:{}, score{}'.format(stu.name, stu.exam, stu.score))
def main(args=None):
rclpy.init(args=args)
pythonIdlSubNode = PythonIdlSub("pythonIdlSubNode")
rclpy.spin(pythonIdlSubNode)
pythonIdlSubNode.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
2.7.5 编译运行Python发布方节点
-
由于指定了依赖项,直接编译即可:
colcon build --packages-select python_idl_sub
-
激活环境:
. install/setup.bash
-
运行所有节点:
2.8 话题通讯小结
-
- 创建功能包时,如果能够直接指定节点名和依赖项,可以省去很大部分的配置工作
-
- C/C++手动配置功能包时,
packages.xml
配置依赖项,CMakeLists.txt
配置编译的内容,包括find_package
;add_executable
;target_include_directories
;ament_target_dependencies
;install
;ament_package
这六个部分
- C/C++手动配置功能包时,
-
- Python手动配置功能包时,
packages.xml
配置可执行的依赖项exec_depend
而不是depend
,并且在setup.py
中主要配置节点的main
函数入口
- Python手动配置功能包时,
-
- 自定义话题通讯,本质是创建一个C/C++的功能包,将里面的自定义消息编译成可以使用的
.c
和.py
文件,值得注意的是,生成好的中间文件是存放在 工作空间的install
文件夹下,如果这个这个工作空间的install
找不到或者不是一个,那是需要再进一步的手动配置的
- 自定义话题通讯,本质是创建一个C/C++的功能包,将里面的自定义消息编译成可以使用的