前面介绍过ros2的相关的工具,这里主要介绍一些其他的用法
ROS 2中的launch系统负责帮助用户描述其系统的配置,然后按说明执行。系统的配置包括要运行的程序、运行它们的位置、传递它们的参数以及ROS特定的约定,这些约定通过为组件提供不同的配置,使得在整个系统中重用组件变得容易。它还负责监测启动过程的状态,并报告和/或对这些过程状态的变化作出反应。
用Python编写的启动文件可以启动和停止不同的节点,也可以触发和处理各种事件。提供这个框架的包是launch_ros,它使用下面的非ros特定的发布框架。
设计文件详细说明了ROS 2发射系统的设计目标(目前并非所有功能都可用)。
跟ros1 一样,我们通过ros2 launch
命令启动launch文件,我们可以通过ros2 pkg create
命令来创建一个包,这样会自动生成一个launch文件夹
python包的文件结构大概是这个样子
src/
my_package/
launch/
setup.py
setup.cfg
package.xml
python包有点特殊,需要在setup.py
的data_files
里加上对launch
文件的指定
这样:
import os
from glob import glob
from setuptools import setup
package_name = 'my_package'
setup(
# Other parameters ...
data_files=[
# ... Other data files
# Include all launch files. This is the most important line here!
(os.path.join('share', package_name), glob('launch/*.launch.py'))
]
)
python是在setup.py
里,c++是在CMakeLists.txt
,需要在install
命令里加上对launch
的指定
# Install launch files.
install(DIRECTORY
launch
DESTINATION share/${PROJECT_NAME}/
)
和ros1的launch文件差别挺大的,ros1是xml那种形式,roslaunch命令回去解析,roslaunch也是python写的,ros2 的launch文件,直接就是py文件了,可以推荐用个pyharm啥的
分了两个版本:foxy更新的是这样的:
import launch
import launch.actions
import launch.substitutions
import launch_ros.actions
def generate_launch_description():
return launch.LaunchDescription([
launch.actions.DeclareLaunchArgument(
'node_prefix',
default_value=[launch.substitutions.EnvironmentVariable('USER'), '_'],
description='Prefix for node names'),
launch_ros.actions.Node(
package='demo_nodes_cpp', node_executable='talker', output='screen',
node_name=[launch.substitutions.LaunchConfiguration('node_prefix'), 'talker']),
])
eloquent或者更老的版本:
import launch
import launch.actions
import launch.substitutions
import launch_ros.actions
def generate_launch_description():
return launch.LaunchDescription([
launch.actions.DeclareLaunchArgument(
'node_prefix',
default_value=[launch.substitutions.EnvironmentVariable('USER'), '_'],
description='Prefix for node names'),
launch_ros.actions.Node(
package='demo_nodes_cpp', node_executable='talker', output='screen',
node_name=[launch.substitutions.LaunchConfiguration('node_prefix'), 'talker']),
])
编译完成之后,通过ros2 launch
启动
ros2 launch my_package script.launch.py
可以看看这个例子:https://github.com/ros2/launch_ros/blob/master/launch_ros/examples/lifecycle_pub_sub_launch.py
这个介绍launch_ros
的节点启动,事件注册,通知功能
这样可以在有启动依赖的时候用到,当前值launch
启动导一定阶段,发布事件,启动后续步骤,可以不用通过延时这种不稳定的方式了
传送门:https://github.com/ros2/launch/blob/master/launch/doc/source/architecture.rst
主要是下面三个部分:
eloquent
之后需要加 --ros-args
标志,dashing
版本不需要ros2 run my_package node_executable --ros-args ...
dashing:
ros2 run my_package node_executable ...
说明文档的传送门在这:http://design.ros2.org/articles/ros_command_line_arguments.html
node内的名字( topics/services
)重定向通过命令 -r
实现. 节点的名字的和命名空间通过命令 -r __node:=
和-r __ns:=
.
重定向 node
:talker
-->my_talker
,topic
:my_topic
–>chatter
,namespace
:/demo/my_topic
–>/my_topic
eloquent 和更新的版本:
ros2 run demo_nodes_cpp talker --ros-args -r __ns:=/demo -r __node:=my_talker -r chatter:=my_topic
dashing版本:
ros2 run demo_nodes_cpp talker __ns:=/demo __node:=my_talker chatter:=my_topic
可以通过加上node
名称来指定具体的要重定向的节点,在执行多个节点的时候很重要
就是这个样子(修改talker
节点的node
的名字):
--ros-args -r talker:__node:=my_talker
命令行的话就是--log-level
这个参数的配置,具体的可以看看前面关于日志的介绍,这里就这样了
配置可以通过cli
和yaml
文件两种方式实现
dashing版本不支持
命令是这个样子:
ros2 run package_name executable_name --ros-args -p param_name:=param_value
前面改乌龟的颜色展示过
这个样子demo_params.yaml
:
parameter_blackboard:
ros__parameters:
some_int: 42
a_string: "Hello world"
some_lists:
some_integers: [1, 2, 3, 4]
some_doubles : [3.14, 2.718]
自省其实就是用来排查观察我们的程序
主要是介绍下命令行,可以通过ros2 --help
查看,我就不解释了
RQt
是一个图形用户界面框架,它以插件的形式实现各种工具和界面。可以在RQt
中将所有现有的GUI
工具作为可停靠窗口运行!这些工具仍然可以在传统的独立方法中运行,但是RQt
使得在一个屏幕布局中
管理所有不同的窗口更加容易。
这个可以通过前面cli
部分的RQT
部分回顾
在ROS 1
中,您可以将代码编写为ROS node
或ROS nodelet
。ROS 1 nodes
被编译成可执行文件。另一方面,ROS 1 nodelet
被编译成一个共享库,然后在运行时由容器进程加载。
在ros2
通过 Component
来实现
运行节点有两种方式:
IPC
,就是容易挂看demo的话来这里 https://github.com/ros2/demos.git
这个Component
最后生成的是so
,不包含main
函数,长这个样子
#include "composition/talker_component.hpp"
#include
#include
#include
#include
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::chrono_literals;
namespace composition
{
// Create a Talker "component" that subclasses the generic rclcpp::Node base class.
// Components get built into shared libraries and as such do not write their own main functions.
// The process using the component's shared library will instantiate the class as a ROS node.
Talker::Talker(const rclcpp::NodeOptions & options)
: Node("talker", options), count_(0)
{
// Create a publisher of "std_mgs/String" messages on the "chatter" topic.
pub_ = create_publisher<std_msgs::msg::String>("chatter", 10);
// Use a timer to schedule periodic message publishing.
timer_ = create_wall_timer(1s, std::bind(&Talker::on_timer, this));
}
void Talker::on_timer()
{
auto msg = std::make_unique<std_msgs::msg::String>();
msg->data = "Hello World: " + std::to_string(++count_);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", msg->data.c_str());
std::flush(std::cout);
// Put the message into a queue to be processed by the middleware.
// This call is non-blocking.
pub_->publish(std::move(msg));
}
} // namespace composition
#include "rclcpp_components/register_node_macro.hpp"
// Register the component with class_loader.
// This acts as a sort of entry point, allowing the component to be discoverable when its library
// is being loaded into a running process.
RCLCPP_COMPONENTS_REGISTER_NODE(composition::Talker)
一般是继承node
,通过最后一行的RCLCPP_COMPONENTS_REGISTER_NODE
来注册,只有注册之后,才能被系统发现
CMakelists.txt 也要跟着改一下
add_library(talker_component SHARED
src/talker_component.cpp)
rclcpp_components_register_nodes(talker_component "composition::Talker")
# To register multiple components in the same shared library, use multiple calls
# rclcpp_components_register_nodes(talker_component "composition::Talker2")
3种方式
命令:
ros2 component types
命令:
ros2 run rclcpp_components component_container
ros2 component load /ComponentManager composition composition::Talker
ros2 component load /ComponentManager composition composition::Listener
命令:
ros2 run rclcpp_components component_container
ros2 component load /ComponentManager composition composition::Server
ros2 component load /ComponentManager composition composition::Client
这个dlopen
就是加载so
用的
命令:
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so
实现的功能都一样,只是方法不一样
写导python的容器里
container = ComposableNodeContainer(
node_name='my_container',
node_namespace='',
package='rclcpp_components',
node_executable='component_container',
composable_node_descriptions=[
ComposableNode(
package='composition',
node_plugin='composition::Talker',
node_name='talker'),
ComposableNode(
package='composition',
node_plugin='composition::Listener',
node_name='listener')
],
output='screen',
)
命令:
ros2 launch composition composition_demo.launch.py
可以用unload
删除 components
,load
的逆操作
这个样子:
ros2 component unload /ComponentManager 1 2
节点同样可以重定向
动作是ROS中异步通信的一种形式。操作客户端向操作服务器发送目标请求。操作服务器向操作客户端发送目标反馈和结果。有关ROS操作的更多详细信息,请参阅设计文章。
此文档包含与操作相关的教程列表。作为参考,在完成所有教程之后,您应该期望有一个类似于包action_tutorials的ROS包。
具体的参考前面的教程
因为ROS2
引入了DDS
,所以在记录和播放bag
文件的时候需要考虑导Qos
配置的兼容性,可以通过参数--qos-profile-overrides-path
来指定新的qos配置文件
主要有以下配置:
history: [keep_all, keep_last]
depth: int
reliability: [system_default, reliable, best_effort, unknown]
durability: [system_default, transient_local, volatile, unknown]
deadline:
sec: int
nsec: int
lifespan:
sec: int
nsec: int
liveliness: [system_default, automatic, manual_by_node, manual_by_topic, unknown]
liveliness_lease_duration:
sec: int
nsec: int
avoid_ros_namespace_conventions: [true, false]
这个用到在看,现在除了记录demo
没啥意义
回顾整个命令行