前言
主要学习怎么使用客户端库来实现自己的功能,主要介绍C++ 和python 两种,其他的我不太会。。
因为卡面发现dashing版本有很多命令不能用,我这边切eloquent来学习
目前ros2 支持的库有以下这么多
rosidl_generator_java - 生成java的ros接口.
rosidl_generator_objc - 生成 Objective C 的ros接口.
rosidl_generator_cpp - 生成c++ 的ros 接口.
rosidl_generator_c - 生成C 的ros接口
rosidl - 提供ROS IDL(.msg)定义和代码生成的包。
rosidl_dds - 生成ROS接口的dds接口.
基于ros_tutorials 学习 ,可以从以下几个网站学习
https://github.com/ros/ros_tutorials
https://github.com/ros2/examples
https://github.com/ros2/demos
这边文章40000+字,很长,可以去我的专栏看
也可以跳转都转个文章来看:
创建工作空间
创建第一个自己的ROS 2 包
写一个简单的发布和订阅(C++)
写一个简单的发布和订阅(python)
写一个简单的服务端和客户端(C++)
写一个简单的服务端和客户端(python)
创建自定义的ROS2 msg 和 srv 文件
ros2doctor 入门
1 创建工作空间
① 背景
工作区是-个包含ROS 2包的目录。在使用ROS 2之前,必须在要使用的终端安装ros2的安装工作区(通过 source your_ws/install/setup.bash)。这样就可以使用ROS2 自带的软件包。
可以自己创建自定义的工作空间,官方推荐,可以创建一个底层工作空间,和业务工作空间,底层包含所有业务的依赖,也就是模块化
② 前提
已经有的不用重复安装
安装ros2
安装colcon sudo apt install python3-colcon-common-extensions
安装git sudo apt install git
安装小乌龟 sudo apt install ros-<distro>-turtlesim
安装rosdep sudo apt-get install python-rosdep
基本的linux 命令,不会的这里查http://www.ee.surrey.ac.uk/Teaching/Unix/
文本编辑器,我这边C++用的Clion, python用的pycharm
③ 任务
Ⅰ安装ros 2 环境
安装ros2 的环境,通过source
在终端执行:
source /opt/ros/<distro>/setup.bash
例子:
source /opt/ros/eloquent/setup.bash
推荐写入启动文件.bashrc里,这样就不用敲了,也不会忘记
echo "source /opt/ros/eloquent/setup.bash" >> ~/.bashrc
Ⅱ 创建文件夹
每个工作空间搞一个文件夹就行,最好名字显义
官方用的dev_ws
代表development workspace
mkdir dev_ws
mkdir dev_ws/src
cd dev_ws/src
自己的包就搞到src
下面去就行
Ⅲ clone 教程代码
在dev_ws/src
路径下执行命令:git clone https://github.com/ros/ros_tutorials.git -b eloquent-devel
下面这个样子就是下载好了
Ⅳ 解决依赖
以后自己使用别人的包的时候,也是这个解决依赖问题的
在~/dev_ws
路径下执行 sudo rosdep install -i --from-path src --rosdistro eloquent -y
报错的话,按照要求操作就行
sudo rosdep init
rosdep update
init 老是失败的话,自己想办法吧,网不好
Ⅴ 用colcon 编译在~/dev_ws路径下执行编译,命令是这个colcon build
colcon build的其他有用参数:
--packages-up-to 生成所需的包及其所有依赖项,但不是整个工作区(节省时间)
--symlink-install 避免了每次调整python脚本时都需要重新构建
--event-handlers console_direct+ 在生成时显示控制台输出
Ⅵ 安装自定义工作空间
默认 安装路径的setup.bash 是自动执行的,所以我们只需要执行dev_ws 下面的setup.bash
可以通过两种方式执行
. install/local_setup.bash
source install/local_setup.bash
自定义的工作空间的同名包会覆盖安装路径下的,这个看下研究下setup.bash 就知道了
Ⅶ 修改覆盖成
修改 ~/dev_ws/src/ros_tutorials/turtlesim/src/turtle_frame.cpp
52行,改成setWindowTitle("MyTurtleSim")
然后编译运行
所以环境不一样,执行的结果也不一样,左边是没有执行安装覆盖层的,右边是安装了的
④ 总结
在本教程中,您将主要的ROS 2发行版安装作为基础,并通过在新工作区中克隆和构建软件包来创建了覆盖层(自定义咯)。如您在修改过的turtlesim中所看到的,覆盖层位于路径的前面,并且优先于底层(/opt/ros)。
建议使用覆盖层来处理少量软件包,因此您不必将所有内容都放在同一工作区中,而不必在每编译都很费时间。
2 创建第一个自己的ROS 2 包
① 背景
Ⅰ什么手机ROS 2 包
可以将包视为ROS 2代码的容器。如果您希望能够安装代码或与他人共享代码,则需要将其组织在一个软件包中。使用软件包,您可以发布ROS 2的工作,并允许其他人轻松构建和使用它。
ROS 2中的软件包创建使用ament作为其构建系统,并使用colcon作为其构建工具。您可以使用官方支持的CMake或Python创建软件包,尽管确实存在其他构建类型
Ⅱ ROS 2 包有什么组成
cmake 包
package.xml 文件包含这个包里的元信息
CMakeLists.txt 怎么去编译这个包
最简单的长这个样子
my_package/
CMakeLists.txt
package.xml
python 包
package.xml
文件包含这个包里的元信息setup.py
如何按爪个软件包setup.cfg
保证ros2 可以找到这个包your_package_name
- ROS 工具能找到这个包
大概这个样子
my_package/
setup.py
package.xml
resource/my_package
Ⅲ ROS 2 包和工作空间的关系
一个工作空间可以包含n个包,但是不推荐放很多个,不然每次rebuild 的时候,就是该出去遛弯的时候了
大概长这个样子
workspace_folder/
src/
package_1/
CMakeLists.txt
package.xml
package_2/
setup.py
package.xml
resource/my_package
...
package_n/
CMakeLists.txt
package.xml
② 前提
准备号工作空间dev_ws
③ 任务
==下面每个都会有C++ 和 python 两种方式 ==
Ⅰ 创建包
确保到dev_ws/src路径下
一般我们创建包的命令这个样子就行:
C++ ros2 pkg create --build-type ament_cmake <package_name>
python ros2 pkg create --build-type ament_python <package_name>
我们现在加选项 --node-name 可以创建hello world 的包
C++ ros2 pkg create --build-type ament_cmake --node-name my_node_cpp my_package_cpp
python ros2 pkg create --build-type ament_python --node-name my_node_py my_package_py
Ⅱ 编译包
命令是 colcon build
也可以 选择只编译某一个包 colcon build --packages-select my_package_cpp
这里可能会报邮箱格式不正确的错误,把邮箱在xml里改一下,还是不行,就删除build/
文件夹重新来
Ⅲ 安装设置文件(setup.bash)
cd ~/dev_ws
. install/setup.bash
Ⅳ 运行包
因为用的–node_name 创建的,会有hello world出来
- C++
ros2 run my_package_cpp my_node_cpp
- python
ros2 run my_package_py my_node_py
-
Ⅴ 看看包里面都有啥子
-
C++
- python
-
-
Ⅵ 修改 package.xmldescription, license ,maintainer需要去改
- 这个样子
- python要改的话,setup.py要一致和package.xml
-
④ 总结
通过
pkg
来创建包,通过colcon
来编译,成功之后可以执行命令的说明
-
usage: ros2 pkg create [-h] [--package-format {2,3}] [--description DESCRIPTION] [--license LICENSE] [--destination-directory DESTINATION_DIRECTORY] [--build-type {cmake,ament_cmake,ament_python}] [--dependencies DEPENDENCIES [DEPENDENCIES ...]] [--maintainer-email MAINTAINER_EMAIL] [--maintainer-name MAINTAINER_NAME] [--node-name NODE_NAME] [--library-name LIBRARY_NAME] package_name Create a new ROS2 package positional arguments: package_name The package name optional arguments: -h, --help show this help message and exit --package-format {2,3}, --package_format {2,3} The package.xml format. --description DESCRIPTION The description given in the package.xml --license LICENSE The license attached to this package --destination-directory DESTINATION_DIRECTORY Directory where to create the package directory --build-type {cmake,ament_cmake,ament_python} The build type to process the package with --dependencies DEPENDENCIES [DEPENDENCIES ...] list of dependencies --maintainer-email MAINTAINER_EMAIL email address of the maintainer of this package --maintainer-name MAINTAINER_NAME name of the maintainer of this package --node-name NODE_NAME name of the empty executable --library-name LIBRARY_NAME name of the empty library
-
3 写一个简单的发布和订阅(C++)
① 背景
节点是通过ROS graph 进行通信的可执行程序。在本教程中,节点将通过主题以字符串消息的形式相互传递信息。这里使用的示例是一个简单的“talker”和“listener”系统。一个节点发布数据,另一个节点订阅该主题,以便它可以接收该数据② 前提
创建好工作空间③ 任务
Ⅰ 创建一个包
创建一个pubsub的包ros2 pkg create --build-type ament_cmake cpp_pubsub
-
改 xml文件里的邮箱别忘了
切换到dev_ws/src/cpp_pubsub/src
路径下Ⅱ 写发布节点
通过命令下载实例文件
-
wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/master/rclcpp/minimal_publisher/member_function.cpp
- 很大概率失败。。。这这这还是直接贴代码吧就算失败也会有个空文件
-
#include <chrono> #include <memory> #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" using namespace std::chrono_literals; /* This example creates a subclass of Node and uses std::bind() to register a * member function as a callback from the timer. */ class MinimalPublisher : public rclcpp::Node { public: MinimalPublisher() : Node("minimal_publisher"), count_(0) { publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10); timer_ = this->create_wall_timer( 500ms, std::bind(&MinimalPublisher::timer_callback, this)); } private: void timer_callback() { auto message = std_msgs::msg::String(); message.data = "Hello, world! " + std::to_string(count_++); RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str()); publisher_->publish(message); } rclcpp::TimerBase::SharedPtr timer_; rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; size_t count_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<MinimalPublisher>()); rclcpp::shutdown(); return 0; }
- 代码解析
- 修改 package.xml添加几行在里面
-
<depend>rclcpp</depend> <depend>std_msgs</depend>
- 修改 CMakeLists.txt在 find_package(ament_cmake REQUIRED)下面添加
-
find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED)
- 接着添加可执行文件的名字为 talker 一会用ros2 run 来运行
-
add_executable(talker src/publisher_member_function.cpp) ament_target_dependencies(talker rclcpp std_msgs)
- 最后添加 install(TARGETS…) 把可执行文件安装到install去
-
install(TARGETS talker DESTINATION lib/${PROJECT_NAME})
- 最后这个样子
-
cmake_minimum_required(VERSION 3.5) project(cpp_pubsub) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() 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) find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) # uncomment the following section in order to fill in # further dependencies manually. # find_package(<dependency> REQUIRED) add_executable(talker src/publisher_member_function.cpp) ament_target_dependencies(talker rclcpp std_msgs) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # uncomment the line when a copyright and license is not present in all source files #set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # uncomment the line when this package is not in a git repo #set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() install(TARGETS talker DESTINATION lib/${PROJECT_NAME}) ament_package()
- 可以尝试编译执行一下
- Ⅲ 写订阅节点通过命令下载实例文件,也是在dev_ws/src/cpp_pubsub/src路径下
-
wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/master/rclcpp/minimal_subscriber/member_function.cpp
- 很大概率失败。。。这这这还是直接贴代码吧就算失败也会有个空文件
-
#include <memory> #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" using std::placeholders::_1; class MinimalSubscriber : public rclcpp::Node { public: MinimalSubscriber() : Node("minimal_subscriber") { subscription_ = this->create_subscription<std_msgs::msg::String>( "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1)); } private: void topic_callback(const std_msgs::msg::String::SharedPtr msg) const { RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str()); } rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<MinimalSubscriber>()); rclcpp::shutdown(); return 0; }
- 代码解析
-
修改 package.xml
基于前面的pub没有新增依赖,所以不用修改
修改 CMakeLists.txt
增加可执行文件和Taeget
-
add_executable(listener src/subscriber_member_function.cpp) ament_target_dependencies(listener rclcpp std_msgs) install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME})
- 最后应是这样:
-
cmake_minimum_required(VERSION 3.5) project(cpp_pubsub) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() 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) find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) # uncomment the following section in order to fill in # further dependencies manually. # find_package(<dependency> REQUIRED) add_executable(talker src/publisher_member_function.cpp) ament_target_dependencies(talker rclcpp std_msgs) add_executable(listener src/subscriber_member_function.cpp) ament_target_dependencies(listener rclcpp std_msgs) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # uncomment the line when a copyright and license is not present in all source files #set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # uncomment the line when this package is not in a git repo #set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME}) ament_package()
- Ⅳ 编译和运行养成好习惯,编译前先解决依赖,切换到dev_ws下
-
sudo rosdep install -i --from-path src --rosdistro eloquent -y
- 只编译新增的包
-
colcon build --packages-select cpp_pubsub
- 安装环境
-
. install/setup.bash
- 运行两个节点
-
ros2 run cpp_pubsub talker ros2 run cpp_pubsub listener
-
④ 总结
您创建了两个节点来发布和订阅主题上的数据。在编译和运行它们之前,您已将它们的依赖项和可执行文件添加到包配置文件中。
这些例子中使用的代码可以在这里找到。4 写一个简单的发布和订阅(python)
① 背景
节点是通过ROS graph 进行通信的可执行程序。在本教程中,节点将通过主题以字符串消息的形式相互传递信息。这里使用的示例是一个简单的“talker”和“listener”系统。一个节点发布数据,另一个节点订阅该主题,以便它可以接收该数据,基于python② 前提
创建好工作空间③ 任务
Ⅰ 创建一个包
创建一个pubsub的包
切换到dev_ws/src路径下ros2 pkg create --build-type ament_python py_pubsub
- Ⅱ 写发布节点切换到dev_ws/src/py_pubsub/py_pubsub 路径下执行命令下载demo
-
wget https://raw.githubusercontent.com/ros2/examples/master/rclpy/topics/minimal_publisher/examples_rclpy_minimal_publisher/publisher_member_function.py
- 代码在这:
-
import rclpy from rclpy.node import Node from std_msgs.msg import String class MinimalPublisher(Node): def __init__(self): super().__init__('minimal_publisher') self.publisher_ = self.create_publisher(String, 'topic', 10) timer_period = 0.5 # seconds self.timer = self.create_timer(timer_period, self.timer_callback) self.i = 0 def timer_callback(self): msg = String() msg.data = 'Hello World: %d' % self.i self.publisher_.publish(msg) self.get_logger().info('Publishing: "%s"' % msg.data) self.i += 1 def main(args=None): rclpy.init(args=args) minimal_publisher = MinimalPublisher() rclpy.spin(minimal_publisher) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) minimal_publisher.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
- 代码解析
- 修改 package.xml修改描述
-
<description>Examples of minimal publisher/subscriber using rclpy</description> <maintainer email="you@email.com">Your Name</maintainer> <license>Apache License 2.0</license>
- 增加依赖
-
<exec_depend>rclpy</exec_depend> <exec_depend>std_msgs</exec_depend>
- 修改 setup.py这个和xml一样的
-
maintainer='YourName', maintainer_email='you@email.com', description='Examples of minimal publisher/subscriber using rclpy', license='Apache License 2.0',
- 在 entry_points 下的 console_scripts 里添加,这是个list可以写无数个
-
entry_points={ 'console_scripts': [ 'talker = py_pubsub.publisher_member_function:main', ], },
- 检查 setup.cfg是这样
-
[develop] script-dir=$base/lib/py_pubsub [install] install-scripts=$base/lib/py_pubsub
-
这个配置是说明 可执行文件是在
lib
下面,ros2 run 的时候会到目录下查找写订阅节点
在
dev_ws/src/py_pubsub/py_pubsub
下载demo -
wget https://raw.githubusercontent.com/ros2/examples/master/rclpy/topics/minimal_subscriber/examples_rclpy_minimal_subscriber/subscriber_member_function.py
- 下载失败的话,从我这边粘贴吧
代码: -
import rclpy from rclpy.node import Node from std_msgs.msg import String class MinimalSubscriber(Node): def __init__(self): super().__init__('minimal_subscriber') self.subscription = self.create_subscription( String, 'topic', self.listener_callback, 10) self.subscription # prevent unused variable warning def listener_callback(self, msg): self.get_logger().info('I heard: "%s"' % msg.data) def main(args=None): rclpy.init(args=args) minimal_subscriber = MinimalSubscriber() rclpy.spin(minimal_subscriber) # Destroy the node explicitly # (optional - otherwise it will be done automatically # when the garbage collector destroys the node object) minimal_subscriber.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
- 代码解析
-
修改 package.xml
基于前面的pub没有新增依赖,所以不用修改
修改 setup.py
在刚刚的entry_points 在加一行变成这样
-
entry_points={ 'console_scripts': [ 'talker = py_pubsub.publisher_member_function:main', 'listener = py_pubsub.subscriber_member_function:main', ], },
- Ⅳ 编译和运行养成好习惯,编译前先解决依赖,切换到dev_ws下
-
sudo rosdep install -i --from-path src --rosdistro eloquent -y
- 只编译新增的包
-
colcon build --packages-select py_pubsub
- 安装环境
-
. install/setup.bash
- 运行两个节点
-
ros2 run py_pubsub talker ros2 run py_pubsub listener
-
④ 总结
您创建了两个节点来发布和订阅主题上的数据。在编译和运行它们之前,您已将它们的依赖项和可执行文件添加到包配置文件中。
这些例子中使用的代码可以在这里找到。5 写一个简单的服务端和客户端(C++)
① 背景
当节点使用服务进行通信时,发送数据请求的节点称为客户机节点,响应请求的节点称为服务节点。请求和响应的结构由.srv文件决定。这里使用的示例是一个简单的整数加法系统;一个节点请求两个整数的和,另一个节点用结果响应。
其实简单得功能函数就可以解决了,这个解决了数据交互的问题,比如服务端返回客户端需要的,但是不用在客户端管理的数据或者状态,还是模块化
② 前提
学会创建工作空间和包③ 任务
Ⅰ 创建一个包
打开终端,准备好环境,切换到dev_ws/src 路径下来创建包 -
ros2 pkg create --build-type ament_cmake cpp_srvcli --dependencies rclcpp example_interfaces
- 然后出一个文件夹
-
utry@utry:~/dev_ws/src$ tree cpp_srvcli/ cpp_srvcli/ ├── CMakeLists.txt ├── include │ └── cpp_srvcli ├── package.xml └── src
- 加了
--dependencies rclcpp example_interfaces
之后,创建包的时候,会自动把package.xml
和CMakeLists.txt
依赖加上,省事,example_interfaces
包含例子srv
文件 -
utry@utry:~/dev_ws/src/cpp_srvcli$ ros2 interface show example_interfaces/ example_interfaces/action/Fibonacci example_interfaces/srv/AddTwoInts utry@utry:~/dev_ws/src/cpp_srvcli$ ros2 interface show example_interfaces/srv/AddTwoInts int64 a int64 b --- int64 sum
-
修改 package.xml
这三个部分还是要改一下的,依赖已经在创建的时候解决了,所以,不用关心依赖了
-
<description>C++ client server tutorial</description> <maintainer email="you@email.com">Your Name</maintainer> <license>Apache License 2.0</license>
-
Ⅱ 写服务节点
到
dev_ws/src/cpp_srvcli/src
下新建文件add_two_ints_server.cpp
demo 代码:
-
#include "rclcpp/rclcpp.hpp" #include "example_interfaces/srv/add_two_ints.hpp" #include <memory> void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request, std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response) { response->sum = request->a + request->b; RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld", request->a, request->b); RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum); } int main(int argc, char **argv) { rclcpp::init(argc, argv); std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server"); rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service = node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add); RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints."); rclcpp::spin(node); rclcpp::shutdown(); }
-
代码解析
这个就是
add
函数,把request
的两个数加起来,通过response
返回客户端,顺便打了两行日志 -
void add(const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request, std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response) { response->sum = request->a + request->b; RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld", request->a, request->b); RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum); }
- 初始化
-
rclcpp::init(argc, argv);
- 创建node
-
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_server");
- 创建一个服务,名字叫add_two_ints,并通过add函数发不出去
-
rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr service = node->create_service<example_interfaces::srv::AddTwoInts>("add_two_ints", &add);
- 例行日志
-
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");
- 死循环,不能让进程结束了
-
rclcpp::spin(node);
-
添加可执行文件
就是修改
CMakeLists.txt
编译的选项 -
add_executable(server src/add_two_ints_server.cpp) ament_target_dependencies(server rclcpp example_interfaces)
- 安装的选项,在ament_package() 这行前面
-
install(TARGETS server DESTINATION lib/${PROJECT_NAME})
-
Ⅲ 写客户端节点
到
dev_ws/src/cpp_srvcli/src
下新建文件add_two_ints_client.cpp
代码这样子: -
#include "rclcpp/rclcpp.hpp" #include "example_interfaces/srv/add_two_ints.hpp" #include <chrono> #include <cstdlib> #include <memory> using namespace std::chrono_literals; int main(int argc, char **argv) { rclcpp::init(argc, argv); if (argc != 3) { RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_two_ints_client X Y"); return 1; } std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client"); rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client = node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints"); auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>(); request->a = atoll(argv[1]); request->b = atoll(argv[2]); while (!client->wait_for_service(1s)) { if (!rclcpp::ok()) { RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting."); return 0; } RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again..."); } auto result = client->async_send_request(request); // Wait for the result. if (rclcpp::spin_until_future_complete(node, result) == rclcpp::executor::FutureReturnCode::SUCCESS) { RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum); } else { RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints"); } rclcpp::shutdown(); return 0; }
- 代码解析创建节点和client
-
std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_two_ints_client"); rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedPtr client = node->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints");
- 创建request,这个结构体是那个依赖里面的,
-
auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>(); request->a = atoll(argv[1]); request->b = atoll(argv[2]);
- 等待 服务起来
-
while (!client->wait_for_service(1s)) { if (!rclcpp::ok()) { RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting."); return 0; } RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again..."); }
- 发送请求
-
auto result = client->async_send_request(request);
- 等待服务端返回,并打印
-
if (rclcpp::spin_until_future_complete(node, result) == rclcpp::executor::FutureReturnCode::SUCCESS) { RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum); } else { RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints"); }
- 添加可执行文件CMakeLists.txt改成这样:
-
cmake_minimum_required(VERSION 3.5) project(cpp_srvcli) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() 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) find_package(rclcpp REQUIRED) find_package(example_interfaces REQUIRED) add_executable(server src/add_two_ints_server.cpp) ament_target_dependencies(server rclcpp example_interfaces) add_executable(client src/add_two_ints_client.cpp) ament_target_dependencies(client rclcpp example_interfaces) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # uncomment the line when a copyright and license is not present in all source files #set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # uncomment the line when this package is not in a git repo #set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() install(TARGETS server client DESTINATION lib/${PROJECT_NAME}) ament_package()
- Ⅳ 编译和运行养成好习惯,编译前先解决依赖,切换到dev_ws下
-
sudo rosdep install -i --from-path src --rosdistro eloquent -y
- 只编译新增的包
-
colcon build --packages-select cpp_srvcli
- 安装环境
-
. install/setup.bash
- 运行两个节点
-
ros2 run cpp_srvcli server ros2 run cpp_srvcli client 2 3
-
④ 总结
您创建了两个节点来通过服务请求和响应数据。您将它们的依赖项和可执行文件添加到程序包配置文件中,可以构建和运行它们,并看到的服务/客户端系统运行起来。6 写一个简单的服务端和客户端(python)
① 背景
当节点使用服务进行通信时,发送数据请求的节点称为客户机节点,响应请求的节点称为服务节点。请求和响应的结构由.srv文件决定。这里使用的示例是一个简单的整数加法系统;一个节点请求两个整数的和,另一个节点用结果响应。这儿用python写的
其实简单得功能函数就可以解决了,这个解决了数据交互的问题,比如服务端返回客户端需要的,但是不用在客户端管理的数据或者状态,还是模块化
② 前提
学会创建工作空间和包③ 任务
Ⅰ 创建一个包
打开终端,准备好环境,切换到dev_ws/src 路径下来创建包ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces
- 然后出一个文件夹
-
utry@utry:~/dev_ws/src$ tree py_srvcli/ py_srvcli/ ├── package.xml ├── py_srvcli │ └── __init__.py ├── resource │ └── py_srvcli ├── setup.cfg ├── setup.py └── test ├── test_copyright.py ├── test_flake8.py └── test_pep257.py 3 directories, 8 files
- 加了
--dependencies rclcpp example_interfaces
之后,创建包的时候,会自动把package.xml
和CMakeLists.txt
依赖加上,省事,example_interfaces
包含例子srv
文件 -
utry@utry:~/dev_ws/src/cpp_srvcli$ ros2 interface show example_interfaces/ example_interfaces/action/Fibonacci example_interfaces/srv/AddTwoInts utry@utry:~/dev_ws/src/cpp_srvcli$ ros2 interface show example_interfaces/srv/AddTwoInts int64 a int64 b --- int64 sum
-
修改 package.xml
这三个部分还是要改一下的,依赖已经在创建的时候解决了,所以,不用关心依赖了
-
<description>Python client server tutorial</description> <maintainer email="you@email.com">Your Name</maintainer> <license>Apache License 2.0</license>
- 修改 setup.py
-
maintainer='Your Name', maintainer_email='you@email.com', description='Python client server tutorial', license='Apache License 2.0',
-
Ⅱ 写服务节点
到
dev_ws/src/py_srvcli/py_srvcli
下新建文件service_member_function.py
demo 代码:
-
from example_interfaces.srv import AddTwoInts import rclpy from rclpy.node import Node class MinimalService(Node): def __init__(self): super().__init__('minimal_service') self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback) def add_two_ints_callback(self, request, response): response.sum = request.a + request.b self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) return response def main(args=None): rclpy.init(args=args) minimal_service = MinimalService() rclpy.spin(minimal_service) rclpy.shutdown() if __name__ == '__main__': main()
- 解析代码导入消息类型,python 模块
-
from example_interfaces.srv import AddTwoInts import rclpy from rclpy.node import Node
- 初始化节点名字
minimal_service
,创建服务add_two_ints
绑定回调add_two_ints_callback
,并发布 -
def __init__(self): super().__init__('minimal_service') self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
- 这就是那个回调
-
def add_two_ints_callback(self, request, response): response.sum = request.a + request.b self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) return response
- 修改setup.py在console_scripts list里加上一行
-
'service = py_srvcli.service_member_function:main',
-
Ⅲ 写客户端节点
到
dev_ws/src/py_srvcli/py_srvcli
下新建文件client_member_function.py
demo 代码:
-
import sys from example_interfaces.srv import AddTwoInts import rclpy from rclpy.node import Node class MinimalClientAsync(Node): def __init__(self): super().__init__('minimal_client_async') self.cli = self.create_client(AddTwoInts, 'add_two_ints') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = AddTwoInts.Request() def send_request(self): self.req.a = int(sys.argv[1]) self.req.b = int(sys.argv[2]) self.future = self.cli.call_async(self.req) def main(args=None): rclpy.init(args=args) minimal_client = MinimalClientAsync() minimal_client.send_request() while rclpy.ok(): rclpy.spin_once(minimal_client) if minimal_client.future.done(): try: response = minimal_client.future.result() except Exception as e: minimal_client.get_logger().info( 'Service call failed %r' % (e,)) else: minimal_client.get_logger().info( 'Result of add_two_ints: for %d + %d = %d' % (minimal_client.req.a, minimal_client.req.b, response.sum)) break minimal_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
- 代码解析import sys 为了获取输入的参数初始化节点,创建客户端
-
super().__init__('minimal_client_async') self.cli = self.create_client(AddTwoInts, 'add_two_ints')
- 等待服务端启动
-
while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...')
- 发送请求
-
minimal_client.send_request()
- 等待返回,返回之后打印出来,包括了几个异常退出打印
-
while rclpy.ok(): rclpy.spin_once(minimal_client) if minimal_client.future.done(): try: response = minimal_client.future.result() except Exception as e: minimal_client.get_logger().info( 'Service call failed %r' % (e,)) else: minimal_client.get_logger().info( 'Result of add_two_ints: for %d + %d = %d' % (minimal_client.req.a, minimal_client.req.b, response.sum)) break
- 修改setup.py在console_scripts list里加上一行
-
'client = py_srvcli.client_member_function:main',
- 最后这个样子:
-
from setuptools import setup package_name = 'py_srvcli' 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='utry', maintainer_email='jinmenglei', description='TODO: Package description', license='TODO: License declaration', tests_require=['pytest'], entry_points={ 'console_scripts': [ 'service = py_srvcli.service_member_function:main', 'client = py_srvcli.client_member_function:main', ], }, )
- Ⅳ 编译和运行养成好习惯,编译前先解决依赖,切换到dev_ws下
-
sudo rosdep install -i --from-path src --rosdistro eloquent -y
- 只编译新增的包
-
colcon build --packages-select py_srvcli
- 安装环境
-
. install/setup.bash
- 运行两个节点
-
ros2 run py_srvcli server ros2 run py_srvcli client 2 3
-
④ 总结
您创建了两个节点来通过服务请求和响应数据。您将它们的依赖项和可执行文件添加到程序包配置文件中,可以构建和运行它们,并看到的服务/客户端系统运行起来。这里面有个注意点就是客户端的调用方式,有两种,一个是同步call(self.req)一个是异步call_async(self.req),同步就是一直阻塞到有结果,很容易就死锁了,推荐异步
7 创建自定义的ROS2 msg 和 srv 文件
① 背景
在以前的教程中,使用消息和服务接口了解主题、服务以及简单的发布者/订阅者和服务/客户端节点。在这些情况下,您使用的接口是系统的。系统接口可以满足绝大多数的使用场景,但可能还需要定义自己的消息和服务。本教程将介绍创建自定义接口定义的最简单方法。
② 前提
准备ros2 的工作空间
创建新的srv和msg,会通过topic和service来测试③ 任务
Ⅰ 创建包
切换到dev_ws/src)路径下ros2 pkg create --build-type ament_cmake tutorial_interfaces
- 切换到dev_ws/src/tutorial_interfaces路径下创建两个文件夹
-
mkdir msg mkdir srv
-
Ⅱ 创建自定义接口
msg 定义
切换到
tutorial_interfaces/msg
路径下创建新文件Num.msg
,就写一行: -
int64 num
-
一个
int64
类型的变量,名字num
srv 定义
切换到
tutorial_interfaces/srv
路径下创建新文件AddThreeInts.srv
,就写下面这些: -
int64 a int64 b int64 c --- int64 sum
-
sum = a+b+c ,就这个意思
Ⅲ 修改 CMakeLists.txt
把接口转成 C 头文件和py库,需要用到
rosidl_default_generators
,添加: -
find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "msg/Num.msg" "srv/AddThreeInts.srv" )
- 文件内容是这样的:
-
cmake_minimum_required(VERSION 3.5) project(tutorial_interfaces) # Default to C99 if(NOT CMAKE_C_STANDARD) set(CMAKE_C_STANDARD 99) endif() # Default to C++14 if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() 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) find_package(rosidl_default_generators REQUIRED) rosidl_generate_interfaces(${PROJECT_NAME} "msg/Num.msg" "srv/AddThreeInts.srv" ) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) # the following line skips the linter which checks for copyrights # uncomment the line when a copyright and license is not present in all source files #set(ament_cmake_copyright_FOUND TRUE) # the following line skips cpplint (only works in a git repo) # uncomment the line when this package is not in a git repo #set(ament_cmake_cpplint_FOUND TRUE) ament_lint_auto_find_test_dependencies() endif() ament_package()
-
Ⅳ 修改 package.xml
前面的
Cmakelist
文件是添加了rosidl_default_generators
,需要在xml里声明下 -
<build_depend>rosidl_default_generators</build_depend> <exec_depend>rosidl_default_runtime</exec_depend> <member_of_group>rosidl_interface_packages</member_of_group>
- 这个样子:
-
<?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>tutorial_interfaces</name> <version>0.0.0</version> <description>TODO: Package description</description> <maintainer email="jinmenglei@utry.cn">utry</maintainer> <license>TODO: License declaration</license> <buildtool_depend>ament_cmake</buildtool_depend> <test_depend>ament_lint_auto</test_depend> <test_depend>ament_lint_common</test_depend> <build_depend>rosidl_default_generators</build_depend> <exec_depend>rosidl_default_runtime</exec_depend> <member_of_group>rosidl_interface_packages</member_of_group> <export> <build_type>ament_cmake</build_type> </export> </package>
- Ⅴ 编译 tutorial_interfaces在~/dev_ws路径下执行命令:
-
colcon build --packages-select tutorial_interfaces
-
Ⅵ 确认下msg和srv
安装环境,
dev_ws
路径下执行 -
. install/setup.bash
- 通过下面命令来查看刚刚的msg 和 srv
-
ros2 interface show tutorial_interfaces/msg/Num ros2 interface show tutorial_interfaces/srv/AddThreeInts
-
utry@utry:~/dev_ws$ ros2 interface show tutorial_interfaces/msg/Num int64 num utry@utry:~/dev_ws$ ros2 interface show tutorial_interfaces/srv/AddThreeInts int64 a int64 b int64 c --- int64 sum
-
Ⅶ 测试新的接口
这里面都用之前的包,稍作修改之后测试
使用 pub/sub测试 num.msg
就是改下之前用的
string
,改成num
C++ 版本
修改
cpp_pubsub
的包
直接贴代码了:
Publisher: -
#include <chrono> #include <memory> #include "rclcpp/rclcpp.hpp" #include "tutorial_interfaces/msg/num.hpp" // CHANGE using namespace std::chrono_literals; class MinimalPublisher : public rclcpp::Node { public: MinimalPublisher() : Node("minimal_publisher"), count_(0) { publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10); // CHANGE timer_ = this->create_wall_timer( 500ms, std::bind(&MinimalPublisher::timer_callback, this)); } private: void timer_callback() { auto message = tutorial_interfaces::msg::Num(); // CHANGE message.num = this->count_++; // CHANGE RCLCPP_INFO(this->get_logger(), "Publishing: '%d'", message.num); // CHANGE publisher_->publish(message); } rclcpp::TimerBase::SharedPtr timer_; rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr publisher_; // CHANGE size_t count_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<MinimalPublisher>()); rclcpp::shutdown(); return 0; }
- Subscriber:
-
#include <memory> #include "rclcpp/rclcpp.hpp" #include "tutorial_interfaces/msg/num.hpp" // CHANGE using std::placeholders::_1; class MinimalSubscriber : public rclcpp::Node { public: MinimalSubscriber() : Node("minimal_subscriber") { subscription_ = this->create_subscription<tutorial_interfaces::msg::Num>( // CHANGE "topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1)); } private: void topic_callback(const tutorial_interfaces::msg::Num::SharedPtr msg) const // CHANGE { RCLCPP_INFO(this->get_logger(), "I heard: '%d'", msg->num); // CHANGE } rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr subscription_; // CHANGE }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<MinimalSubscriber>()); rclcpp::shutdown(); return 0; }
- CMakeLists.txt:
-
find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(tutorial_interfaces REQUIRED) # CHANGE add_executable(talker src/publisher_member_function.cpp) ament_target_dependencies(talker rclcpp tutorial_interfaces) # CHANGE add_executable(listener src/subscriber_member_function.cpp) ament_target_dependencies(listener rclcpp tutorial_interfaces) # CHANGE install(TARGETS talker listener DESTINATION lib/${PROJECT_NAME}) ament_package()
- package.xml:
-
<exec_depend>tutorial_interfaces</exec_depend>
- 编译
-
colcon build --packages-select cpp_pubsub
- 安装环境
-
. install/setup.bash
- 运行:
-
ros2 run cpp_pubsub talker ros2 run cpp_pubsub listener
- python 版本修改 py_pubsub包Publisher:
-
import rclpy from rclpy.node import Node from tutorial_interfaces.msg import Num # CHANGE class MinimalPublisher(Node): def __init__(self): super().__init__('minimal_publisher') self.publisher_ = self.create_publisher(Num, 'topic', 10) # CHANGE timer_period = 0.5 self.timer = self.create_timer(timer_period, self.timer_callback) self.i = 0 def timer_callback(self): msg = Num() # CHANGE msg.num = self.i # CHANGE self.publisher_.publish(msg) self.get_logger().info('Publishing: "%d"' % msg.num) # CHANGE self.i += 1 def main(args=None): rclpy.init(args=args) minimal_publisher = MinimalPublisher() rclpy.spin(minimal_publisher) minimal_publisher.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
- Subscriber:
-
import rclpy from rclpy.node import Node from tutorial_interfaces.msg import Num # CHANGE class MinimalSubscriber(Node): def __init__(self): super().__init__('minimal_subscriber') self.subscription = self.create_subscription( Num, # CHANGE 'topic', self.listener_callback, 10) self.subscription def listener_callback(self, msg): self.get_logger().info('I heard: "%d"' % msg.num) # CHANGE def main(args=None): rclpy.init(args=args) minimal_subscriber = MinimalSubscriber() rclpy.spin(minimal_subscriber) minimal_subscriber.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
- 修改 package.xml,增加一行
-
<exec_depend>tutorial_interfaces</exec_depend>
- 编译
-
colcon build --packages-select py_pubsub
- 安装环境
-
. install/setup.bash
- 运行:
-
ros2 run py_pubsub talker ros2 run py_pubsub listener
-
AddThreeInts.srv 通过 service/client 测试
通过改之前的包,这样快一点
C++ 版本
修改的是cpp_srvcli这个包
Service: -
#include "rclcpp/rclcpp.hpp" #include "tutorial_interfaces/srv/add_three_ints.hpp" // CHANGE #include <memory> void add(const std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Request> request, // CHANGE std::shared_ptr<tutorial_interfaces::srv::AddThreeInts::Response> response) // CHANGE { response->sum = request->a + request->b + request->c; // CHANGE RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld" " c: %ld", // CHANGE request->a, request->b, request->c); // CHANGE RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum); } int main(int argc, char **argv) { rclcpp::init(argc, argv); std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_server"); // CHANGE rclcpp::Service<tutorial_interfaces::srv::AddThreeInts>::SharedPtr service = // CHANGE node->create_service<tutorial_interfaces::srv::AddThreeInts>("add_three_ints", &add); // CHANGE RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add three ints."); // CHANGE rclcpp::spin(node); rclcpp::shutdown(); }
- Client:
-
#include "rclcpp/rclcpp.hpp" #include "tutorial_interfaces/srv/add_three_ints.hpp" // CHANGE #include <chrono> #include <cstdlib> #include <memory> using namespace std::chrono_literals; int main(int argc, char **argv) { rclcpp::init(argc, argv); if (argc != 4) { // CHANGE RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: add_three_ints_client X Y Z"); // CHANGE return 1; } std::shared_ptr<rclcpp::Node> node = rclcpp::Node::make_shared("add_three_ints_client"); // CHANGE rclcpp::Client<tutorial_interfaces::srv::AddThreeInts>::SharedPtr client = // CHANGE node->create_client<tutorial_interfaces::srv::AddThreeInts>("add_three_ints"); // CHANGE auto request = std::make_shared<tutorial_interfaces::srv::AddThreeInts::Request>(); // CHANGE request->a = atoll(argv[1]); request->b = atoll(argv[2]); request->c = atoll(argv[3]); // CHANGE while (!client->wait_for_service(1s)) { if (!rclcpp::ok()) { RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting."); return 0; } RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again..."); } auto result = client->async_send_request(request); // Wait for the result. if (rclcpp::spin_until_future_complete(node, result) == rclcpp::executor::FutureReturnCode::SUCCESS) { RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum); } else { RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_three_ints"); // CHANGE } rclcpp::shutdown(); return 0; }
- 修改 CMakeLists.txt:
-
... find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(tutorial_interfaces REQUIRED) # CHANGE add_executable(server src/add_two_ints_server.cpp) ament_target_dependencies(server rclcpp tutorial_interfaces) #CHANGE add_executable(client src/add_two_ints_client.cpp) ament_target_dependencies(client rclcpp tutorial_interfaces) #CHANGE install(TARGETS server client DESTINATION lib/${PROJECT_NAME}) ament_package()
- 修改package.xml:添加依赖
-
<exec_depend>tutorial_interfaces</exec_depend>
- 编译:
-
colcon build --packages-select cpp_srvcli
- 安装环境:
-
. install/setup.bash
- 运行:
-
ros2 run cpp_srvcli server ros2 run cpp_srvcli client 2 3 1
-
python 版本
这个改的是py_pubsub这个包
Service: -
from tutorial_interfaces.srv import AddThreeInts # CHANGE import rclpy from rclpy.node import Node class MinimalService(Node): def __init__(self): super().__init__('minimal_service') self.srv = self.create_service(AddThreeInts, 'add_three_ints', self.add_three_ints_callback) # CHANGE def add_three_ints_callback(self, request, response): response.sum = request.a + request.b + request.c # CHANGE self.get_logger().info('Incoming request\na: %d b: %d c: %d' % (request.a, request.b, request.c)) # CHANGE return response def main(args=None): rclpy.init(args=args) minimal_service = MinimalService() rclpy.spin(minimal_service) rclpy.shutdown() if __name__ == '__main__': main()
- Client:
-
from tutorial_interfaces.srv import AddThreeInts # CHANGE import sys import rclpy from rclpy.node import Node class MinimalClientAsync(Node): def __init__(self): super().__init__('minimal_client_async') self.cli = self.create_client(AddThreeInts, 'add_three_ints') # CHANGE while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = AddThreeInts.Request() # CHANGE def send_request(self): self.req.a = int(sys.argv[1]) self.req.b = int(sys.argv[2]) self.req.c = int(sys.argv[3]) # CHANGE self.future = self.cli.call_async(self.req) def main(args=None): rclpy.init(args=args) minimal_client = MinimalClientAsync() minimal_client.send_request() while rclpy.ok(): rclpy.spin_once(minimal_client) if minimal_client.future.done(): try: response = minimal_client.future.result() except Exception as e: minimal_client.get_logger().info( 'Service call failed %r' % (e,)) else: minimal_client.get_logger().info( 'Result of add_three_ints: for %d + %d + %d = %d' % # CHANGE (minimal_client.req.a, minimal_client.req.b, minimal_client.req.c, response.sum)) # CHANGE break minimal_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main()
- 修改package.xml:插入依赖
-
<exec_depend>tutorial_interfaces</exec_depend>
- 编译:
-
colcon build --packages-select py_srvcli
- 安装环境:
-
. install/setup.bash
- 运行:
-
ros2 run py_srvcli server ros2 run py_srvcli client 2 3 1
-
④ 总结
在本文章中,学习创建自定义接口,以及如何从其他包中利用这些接口。这是一种简单的创建和使用接口的方法。ROS 推荐使用rosidl工具,您可以在这里学习。
.action文件是另一个可以自定义的ROS 2接口。这个以后在学吧。
8 ros2doctor 入门
① 背景
ros2doctor可以用来ros2的一些问题
ros2doctor 检查ROS 2的所有方面,包括平台,版本,网络,环境,运行的系统等,并警告您可能的错误和问题原因。还在开发中,谨慎使用,估计会有点问题
② 前提
确保安装了ros2doctor,ros2cli的组成部分
使用小乌龟包来测试③ 任务
Ⅰ 检查设置
这个是ros2doctor的帮助文档utry@utry:~/dev_ws$ ros2 doctor -h usage: ros2 doctor [-h] [--report | --report-failed] [--include-warnings] Check ROS setup and other potential issues optional arguments: -h, --help show this help message and exit --report, -r Print all reports. --report-failed, -rf Print reports of failed checks only. --include-warnings, -iw Include warnings as failed checks. Warnings are ignored by default.
- 执行ros2 doctor会检查所有的设置模块
-
utry@utry:~$ ros2 doctor All 3 checks passed utry@utry:~$
- 我的是没啥错警告的格式是这个样子的:
-
<path>: <line>: UserWarning: <message>
- 比如使用不稳定版本
-
UserWarning: Distribution <distro> is not fully supported or tested. To get more consistent features, download a stable version at https://index.ros.org/doc/ros2/Installation/
- 把网络关了就这个提示
-
utry@utry:~/dev_ws$ ros2 doctor /opt/ros/eloquent/lib/python3.6/site-packages/ros2doctor/api/format.py: 76: UserWarning: Fail to call PlatformCheck class functions. All 2 checks passed
- 也可能出现error
-
1/3 checks failed Failed modules: network
- 出现问题解决问题
- Ⅱ 检查系统可以检测正在运行的系统,这里运行小乌龟
-
ros2 run turtlesim turtlesim_node ros2 run turtlesim turtle_teleop_key
- 主要是一些,publisher without subscriber 或者subscriber without publisher
- Ⅲ 生成全面的报告使用这个命令就行
-
ros2 doctor --report
- 生成的结果可以复制出来一个一个处理这个是我生成结果
-
utry@utry:~$ ros2 doctor --report NETWORK CONFIGURATION inet : 192.168.241.131 inet4 : ['192.168.241.131'] ether : 00:0c:29:67:0e:2f inet6 : ['fe80::89a1:b2ed:4240:258f'] netmask : 255.255.255.0 device : ens33 flags : 4163<UP,BROADCAST,RUNNING,MULTICAST> mtu : 1500 broadcast : 192.168.241.255 inet : 127.0.0.1 inet4 : ['127.0.0.1'] inet6 : ['::1'] netmask : 255.0.0.0 device : lo flags : 73<UP,LOOPBACK,RUNNING> mtu : 65536 PLATFORM INFORMATION system : Linux platform info : Linux-5.3.0-45-generic-x86_64-with-Ubuntu-18.04-bionic release : 5.3.0-45-generic processor : x86_64 RMW MIDDLEWARE middleware name : rmw_fastrtps_cpp ROS 2 INFORMATION distribution name : eloquent distribution type : ros2 distribution status : active release platforms : {'ubuntu': ['bionic']} TOPIC LIST topic : /turtle1/cmd_vel publisher count : 1 subscriber count : 1 topic : /turtle1/color_sensor publisher count : 1 subscriber count : 1 topic : /turtle1/pose publisher count : 1 subscriber count : 1 topic : /turtle1/rotate_absolute/_action/feedback publisher count : 1 subscriber count : 1 topic : /turtle1/rotate_absolute/_action/status publisher count : 1 subscriber count : 1 utry@utry:~$
-
④ 总结
ros2doctor会告诉您ROS 2安装和运行系统中的问题。您可以使用–report参数更深入地了解这些警告背后的信息。
请记住,ros2doctor它不是调试工具;它不会帮助解决代码或系统实现方面的错误。
-
评论(0)
您还未登录,请登录后发表或查看评论