工具
命令
查找或展示 node、interface、topic、service、action、param和具体信息
-h
:帮助文档ros2 pkg
:查询功能包相关信息的参数
list 列出所有功能包
prefix 列出[指定]功能包路径
executables 输出指定功能包下的可执行程序。
xml 输出指定功能包的package.xml内容
ros2 node
:节点相关
info 输出节点信息
list 输出运行中的节点的列表
ros2 interface
:接口(msg、srv、action)消息相关
list 输出所有可用的接口消息
package 输出指定功能包下的
packages 输出包含接口消息的功能包
proto 输出接口消息原型
show 输出接口消息定义格式
ros2 topic
:话题通信相关
bw 输出话题消息传输占用的带宽
delay 输出带有 header 的话题延迟
echo 输出某个话题下的消息
find 根据类型查找话题
hz 输出消息发布频率
info 输出话题相关信息
list 输出运行中的话题列表
pub 向指定话题发布消息
type 输出话题使用的接口类型
ros2 service
:服务通信相关
call 向某个服务发送请求
find 根据类型查找服务
list 输出运行中的服务列表
type 输出服务使用的接口类型
ros2 action
:动作通信相关
info 输出指定动作的相关信息
list 输出运行中的动作的列表
send_goal 向指定动作发送请求
ros2 param
:参数服务相关,参数dump文件可被ros2 run
delete 删除参数
describe 输出参数的描述信息
dump 将节点参数写入到磁盘文件
get 获取某个参数
list 输出可用的参数的列表
load 从磁盘文件加载参数到节点
set 设置参数
ros2 bag
:录制和回放话题
convert 给定一个 bag 文件,写出一个新的具有不同配置的 bag 文件;
info 输出 bag 文件的相关信息;
list 输出可用的插件信息;
play 回放 bag 文件数据;
record 录制 bag 文件数据;
reindex 重建 bag 的元数据文件。
#include "rclcpp/rclcpp.hpp" #include "rosbag2_cpp/writer.hpp" #include "geometry_msgs/msg/twist.hpp" class BagRecorder : public rclcpp::Node { public: BagRecorder() : Node("bag_recorder") { writer_ = std::make_unique<rosbag2_cpp::Writer>(); writer_->open("my_bag"); subscription_ = create_subscription<geometry_msgs::msg::Twist>( "/turtle1/cmd_vel", 10, std::bind(&BagRecorder::topic_callback, this, _1)); } private: void topic_callback(std::shared_ptr<rclcpp::SerializedMessage> msg) const { rclcpp::Time time_stamp = this->now(); writer_->write(msg, "/turtle1/cmd_vel", "geometry_msgs/msg/Twist", time_stamp); } rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr subscription_; std::unique_ptr<rosbag2_cpp::Writer> writer_; }; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared<BagRecorder>()); rclcpp::shutdown(); return 0; } ---------------------------------------------------------------------- #include "rclcpp/rclcpp.hpp" #include "rosbag2_cpp/reader.hpp" #include "geometry_msgs/msg/twist.hpp" class BagPlayer : public rclcpp::Node { public: BagPlayer() : Node("bag_player") { reader_ = std::make_unique<rosbag2_cpp::Reader>(); reader_->open("my_bag"); while (reader_->has_next()) { geometry_msgs::msg::Twist twist = reader_->read_next<geometry_msgs::msg::Twist>(); RCLCPP_INFO(this->get_logger(),"%.2f ---- %.2f",twist.linear.x, twist.angular.z); } reader_->close(); } private: std::unique_ptr<rosbag2_cpp::Reader> reader_; }; int main(int argc, char const *argv[]) { rclcpp::init(argc,argv); rclcpp::spin(std::make_shared<BagPlayer>()); rclcpp::shutdown(); return 0; }
图形化
rqt:具有与ros2命令相同作用的图形化工具
rviz2:通信消息可视化
ros2 run rviz2 rviz2 -d [config]
launch
# 批量运行节点
from launch import LaunchDescription
from launch_ros.actions import Node
# 获取功能包下share目录路径-------
import os
from ament_index_python.packages import get_package_share_directory
# 封装终端指令相关类--------------
from launch.actions import ExecuteProcess
# from launch.substitutions import FindExecutable
# launch参数声明与获取-----------------
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
# 文件包含相关-------------------
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
# 分组相关----------------------
from launch_ros.actions import PushRosNamespace
from launch.actions import GroupAction
# 事件相关----------------------
from launch.event_handlers import OnProcessStart OnProcessExit
from launch.actions import RegisterEventHandler, LogInfo
def generate_launch_description():
turtle1 = Node(package="turtlesim", executable="turtlesim_node",
namespace="ns_1", name="t1", # 命名空间和节点重命名
exec_name="turtle_label", # 流程标签
respawn=True) # 崩溃后重启
turtle2 = Node(package="turtlesim", executable="turtlesim_node", name="t3",
remappings=[("/turtle1/cmd_vel","/cmd_vel")] #话题重映射
)
turtle3 = Node(package="turtlesim", executable="turtlesim_node", name="t2",
# 服务参数设置1
# parameters=[{"background_r": 0,"background_g": 0,"background_b": 0}],
# 服务参数设置2: 从 yaml 文件加载参数,yaml 文件所属目录需要在配置文件中安装。
parameters=[os.path.join(get_package_share_directory("cpp01_launch"),"config","t2.yaml")]
)
rviz = Node(package="rviz2", executable="rviz2",
# 节点启动时传参
arguments=["-d", os.path.join(get_package_share_directory("cpp01_launch"),"config","my.rviz")]
)
turtle4 = Node(package="turtlesim", executable="turtlesim_node",
# 节点启动时传参,相当于 arguments 传参时添加前缀 --ros-args
ros_arguments=["--remap", "__ns:=/t4_ns", "--remap", "__node:=t4"]
)
# 终端指令
spawn = ExecuteProcess(
# cmd 1
# cmd = [
# FindExecutable(name = "ros2"), # 不可以有空格
# " service call",
# " /spawn turtlesim/srv/Spawn",
# " \"{x: 8.0, y: 9.0,theta: 1.0, name: 'turtle2'}\""
# ],
# cmd 2
cmd=["ros2 service call /spawn turtlesim/srv/Spawn \"{x: 8.0, y: 9.0,theta: 0.0, name: 'turtle2'}\""],
output="both",
shell=True)
# launch参数动态设置
#ros2 launch cpp01_launch py03_args.launch.py background_r:=200 background_g:=80 background_b:=30
decl_bg_r = DeclareLaunchArgument(name="background_r",default_value="255")
decl_bg_g = DeclareLaunchArgument(name="background_g",default_value="255")
decl_bg_b = DeclareLaunchArgument(name="background_b",default_value="255")
turtle = Node(package="turtlesim", executable="turtlesim_node",
parameters=[{"background_r": LaunchConfiguration("background_r"), "background_g": LaunchConfiguration("background_g"), "background_b": LaunchConfiguration("background_b")}]
)
# 文件包含
include_launch = IncludeLaunchDescription(
launch_description_source= PythonLaunchDescriptionSource(
launch_file_path=os.path.join(
get_package_share_directory("cpp01_launch"),
"launch/py",
"py03_args.launch.py"
)
),
launch_arguments={
"background_r": "200",
"background_g": "100",
"background_b": "70",
}.items()
)
# 分组设置
turtlex = Node(package="turtlesim",executable="turtlesim_node",name="t1")
turtley = Node(package="turtlesim",executable="turtlesim_node",name="t2")
turtlez = Node(package="turtlesim",executable="turtlesim_node",name="t3")
g1 = GroupAction(actions=[PushRosNamespace(namespace="g1"),turtlex, turtley])
g2 = GroupAction(actions=[PushRosNamespace(namespace="g2"),turtlez])
# 添加事件
start_event = RegisterEventHandler(
event_handler=OnProcessStart(
target_action = turtle,
on_start = spawn
)
)
exit_event = RegisterEventHandler(
event_handler=OnProcessExit(
target_action = turtle,
on_exit = [LogInfo(msg = "turtlesim_node退出!")]
)
)
return LaunchDescription([turtle1, turtle2, turtle3, turtle4, rviz, decl_bg_r, decl_bg_g, decl_bg_b, turtle, start_event, exit_event, include_launch, g1, g2])
功能
功能包(下载、自定义)是相对独立的,可直接移植入工作空间
一些先进功能包:NAV2、OpenCV、MoveIt、Autoware、PX4、microROS…
重名问题
设备 -> 工作空间 -> 功能包 -> 节点 -> 话题,都可能存在重名;下面使用命令和源码解决,[launch](# launch)部分也介绍了如何解决重名问题
功能包重名
- 同一工作空间下不允许功能包重名
- 不同工作空间下功能包重名会产生功能包覆盖,这与
~/.bashrc
中不同工作空间的setup.bash
文件的加载顺序有关,先加载会被覆盖(ROS自带功能包优先加载易被覆盖) - 在实际工作中,要求避免功能包重名
节点重名
不同的节点可以有相同的节点名
节点重名可能导致通信逻辑上的混乱,要求避免节点重名,如下:
名称重映射
ros2 run [package_name] [program_name] --ros-args --remap __name:=[new_node_name]
// cpp Node (const std::string &node_name,...) # python Node(node_name,...)
命名空间,如
/namespace1
ros2 run [package_name] [program_name] --ros-args --remap __ns:=[namespace]
// cpp Node (const std::string &node_name, const std::string &namespace_, const NodeOptions &options=NodeOptions()) # python Node(node_name, namespace=None,...)
话题重名
不同的节点可以有相同的话题名
节点需要按情况而定是否重名,如下:
- 全局话题(与节点命名空间平级):定义时以
/
开头的名称,和命名空间、节点名称无关,名称为/[topic_name]
- 相对话题(与节点名称平级):非
/
开头的名称,参考命名空间设置话题名称,名称为/[namespace]/[topic_name]
- 私有话题(节点名称的子级):定义时以
~/
开头的名称,和命名空间、节点名称都有关系,名称为/[namespace]/[node_name]/[topic_name]
名称重映射
ros2 run [package_name] [program_name] --ros-args --remap [old_node_name]:=[new_node_name]
命名空间,要求话题非全局
ros2 run [package_name] [program_name] --ros-args --remap __ns:=[namespace]
- 全局话题(与节点命名空间平级):定义时以
TF坐标变换
实现不同坐标系之间的坐标帧的转换,由坐标变换广播方和坐标变换监听方两部分组成。每个坐标变换广播方一般会发布一组坐标系相对关系,而坐标变换监听方则会将多组坐标系相对关系融合为一棵坐标树(该坐标树有且仅有一个根坐标系),并可以实现任意坐标系之间或坐标点与坐标系的变换。
- 两个重要接口
# geometry_msgs/msg/TransformStamped
header:
stamp:
sec: 0
nanosec: 0
frame_id: ''
child_frame_id: ''
transform:
translation:
x: 0.0
y: 0.0
z: 0.0
rotation:
x: 0.0
y: 0.0
z: 0.0
w: 1.0
# geometry_msgs/msg/PointStamped
header:
stamp:
sec: 0
nanosec: 0
frame_id: ''
point:
x: 0.0
y: 0.0
z: 0.0
- 两个重要tf功能包工具
tf2_ros
static_transform_publisher:该节点用于广播静态坐标变换;
ros2 run tf2_ros static_transform_publisher --x [x] --y [y] --z [z] --yaw [yaw] --pitch [pitch] --roll [roll] --frame-id [frame_id] --child-frame-id [child_frame_id]
tf2_monitor:该节点用于打印所有或特定坐标系的发布频率与网络延迟;
tf2_echo:该节点用于打印特定坐标系的平移旋转关系。
tf2_tools
- view_frames:该节点可以生成显示坐标系关系的 pdf 文件,该文件包含树形结构的坐标系图谱。
静态tf广播
#include <rclcpp/rclcpp.hpp>
#include <tf2/LinearMath/Quaternion.h>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <tf2_ros/static_transform_broadcaster.h>
using std::placeholders::_1;
class StaticFrameBroadcaster : public rclcpp::Node {
public:
explicit StaticFrameBroadcaster(char * transformation[]): Node("static_frame_broadcaster"){
tf_publisher_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);
this->make_transforms(transformation);
}
private:
void make_transforms(char * transformation[]) {
geometry_msgs::msg::TransformStamped t;
rclcpp::Time now = this->get_clock()->now();
t.header.stamp = now;
t.header.frame_id = transformation[7];
t.child_frame_id = transformation[8];
t.transform.translation.x = atof(transformation[1]);
t.transform.translation.y = atof(transformation[2]);
t.transform.translation.z = atof(transformation[3]);
tf2::Quaternion q;
q.setRPY(
atof(transformation[4]),
atof(transformation[5]),
atof(transformation[6]));
t.transform.rotation.x = q.x();
t.transform.rotation.y = q.y();
t.transform.rotation.z = q.z();
t.transform.rotation.w = q.w();
tf_publisher_->sendTransform(t);
}
std::shared_ptr<tf2_ros::StaticTransformBroadcaster> tf_publisher_;
};
int main(int argc, char * argv[]) {
auto logger = rclcpp::get_logger("logger");
if (argc != 9) {
RCLCPP_INFO(logger, "x y z roll pitch yaw frame_id child_frame_id");
return 1;
}
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<StaticFrameBroadcaster>(argv));
rclcpp::shutdown();
return 0;
}
import sys
import rclpy
from rclpy.node import Node
import tf_transformations
from geometry_msgs.msg import TransformStamped
from tf2_ros.static_transform_broadcaster import StaticTransformBroadcaster
class StaticFrameBroadcaster(Node):
def __init__(self, transformation):
super().__init__('static_frame_broadcaster')
self._tf_publisher = StaticTransformBroadcaster(self)
self.make_transforms(transformation)
def make_transforms(self, transformation):
static_transformStamped = TransformStamped()
static_transformStamped.header.stamp = self.get_clock().now().to_msg()
static_transformStamped.header.frame_id = sys.argv[7]
static_transformStamped.child_frame_id = sys.argv[8]
static_transformStamped.transform.translation.x = float(sys.argv[1])
static_transformStamped.transform.translation.y = float(sys.argv[2])
static_transformStamped.transform.translation.z = float(sys.argv[3])
quat = tf_transformations.quaternion_from_euler(
float(sys.argv[4]), float(sys.argv[5]), float(sys.argv[6]))
static_transformStamped.transform.rotation.x = quat[0]
static_transformStamped.transform.rotation.y = quat[1]
static_transformStamped.transform.rotation.z = quat[2]
static_transformStamped.transform.rotation.w = quat[3]
self._tf_publisher.sendTransform(static_transformStamped)
def main():
logger = rclpy.logging.get_logger('logger')
if len(sys.argv) < 9:
logger.info('x y z roll pitch yaw frame_id child_frame_id')
sys.exit(0)
rclpy.init()
node = StaticFrameBroadcaster(sys.argv)
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
rclpy.shutdown()
if __name__ == '__main__':
main()
动态tf广播
#include <rclcpp/rclcpp.hpp>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2_ros/transform_broadcaster.h>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <turtlesim/msg/pose.hpp>
using std::placeholders::_1;
class DynamicFrameBroadcaster : public rclcpp::Node {
public:
DynamicFrameBroadcaster(): Node("dynamic_frame_broadcaster") {
tf_broadcaster_ = std::make_unique<tf2_ros::TransformBroadcaster>(*this);
std::string topic_name = "/turtle1/pose";
subscription_ = this->create_subscription<turtlesim::msg::Pose>(topic_name, 10,
std::bind(&DynamicFrameBroadcaster::handle_turtle_pose, this, _1));
}
private:
void handle_turtle_pose(const turtlesim::msg::Pose & msg) {
geometry_msgs::msg::TransformStamped t;
rclcpp::Time now = this->get_clock()->now();
t.header.stamp = now;
t.header.frame_id = "world";
t.child_frame_id = "turtle1";
t.transform.translation.x = msg.x;
t.transform.translation.y = msg.y;
t.transform.translation.z = 0.0;
tf2::Quaternion q;
q.setRPY(0, 0, msg.theta);
t.transform.rotation.x = q.x();
t.transform.rotation.y = q.y();
t.transform.rotation.z = q.z();
t.transform.rotation.w = q.w();
tf_broadcaster_->sendTransform(t);
}
rclcpp::Subscription<turtlesim::msg::Pose>::SharedPtr subscription_;
std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<DynamicFrameBroadcaster>());
rclcpp::shutdown();
return 0;
}
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import TransformStamped
from tf2_ros import TransformBroadcaster
import tf_transformations
from turtlesim.msg import Pose
class DynamicFrameBroadcaster(Node):
def __init__(self):
super().__init__('dynamic_frame_broadcaster')
self.br = TransformBroadcaster(self)
self.subscription = self.create_subscription(Pose, '/turtle1/pose', self.handle_turtle_pose,1)
def handle_turtle_pose(self, msg):
t = TransformStamped()
t.header.stamp=self.get_clock().now().to_msg()
t.header.frame_id = 'world'
t.child_frame_id = 'turtlel'
t.transform.translation.x= msg.x
t.transform.translation.y= msg.y
t.transform.translation.z=0.0
q=tf_transformations.quaternion_from_euler(0,0, msg.theta)
t.transform.rotation.x=q[0]
t.transform.rotation.y = q[1]
t.transform.rotation.z=q[2]
t.transform.rotation.w =q[3]
self.br.sendTransform(t)
def main():
rclpy.init()
node = DynamicFrameBroadcaster()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
rclpy.shutdown()
if __name__ == '__main__':
main()
坐标点发布
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/point_stamped.hpp"
using namespace std::chrono_literals;
class PointPublisher: public rclcpp::Node {
public:
PointPublisher():Node("point_publisher"),x(0.1) {
point_pub_= this->create_publisher<geometry_msgs::msg::PointStamped>("point",10);
timer_= this->create_wall_timer(0.1s,std::bind(&PointPublisher::on_timer, this));
}
private:
void on_timer(){
geometry_msgs::msg::PointStamped point;
point.header.frame_id = "laser";
point.header.stamp = this->now();
x += 0.004;
point.point.x=x;
point.point.y = 0.0;
point.point.z=0.1;
point_pub_->publish(point);
}
rclcpp::Publisher<geometry_msgs::msg::PointStamped>::SharedPtr point_pub_;
rclcpp::TimerBase::SharedPtr timer_;
double_t x;
};
int main(int argc, char const *argv[]) {
rclcpp::init(argc,argv);
rclcpp::spin(std::make_shared<PointPublisher>());
rclcpp::shutdown();
return 0;
}
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import PointStamped
class PointPublisher(Node):
def __init__(self):
super().__init__('point_publisher')
self.pub =self.create_publisher(PointStamped,'point', 10)
self.timer=self.create_timer(1.0,self.on_timer)
self.x=0.1
def on_timer(self):
ps =PointStamped()
ps.header.stamp=self.get_clock().now().to_msg()
ps.header.frame_id='laser'
self.x += 0.02
ps.point.x=self.x
ps.point.y = 0.0
ps.point.z=0.2
self.pub.publish(ps)
def main():
rclpy.init()
node =PointPublisher()
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
坐标系变换监听
#include "rclcpp/rclcpp.hpp"
#include "tf2_ros/buffer.h"
#include "tf2_ros/transform_listener.h"
#include "tf2/LinearMath/Quaternion.h"
using namespace std::chrono_literals;
class FrameListener : public rclcpp::Node {
public:
FrameListener():Node("frame_listener"){
tf_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());
transform_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
timer_ = this->create_wall_timer(1s, std::bind(&FrameListener::on_timer,this));
}
private:
void on_timer() {
try {
auto transformStamped = tf_buffer_->lookupTransform("camera","laser",tf2::TimePointZero);
RCLCPP_INFO(this->get_logger(),"frame_id:%s",transformStamped.header.frame_id.c_str());
RCLCPP_INFO(this->get_logger(),"child_frame_id:%s",transformStamped.child_frame_id.c_str());
RCLCPP_INFO(this->get_logger(),"坐标:(%.2f,%.2f,%.2f)",
transformStamped.transform.translation.x,
transformStamped.transform.translation.y,
transformStamped.transform.translation.z);
}
catch(const tf2::LookupException& e) {
RCLCPP_INFO(this->get_logger(),"坐标变换异常:%s",e.what());
}
}
std::shared_ptr<tf2_ros::TransformListener> transform_listener_;
rclcpp::TimerBase::SharedPtr timer_;
std::unique_ptr<tf2_ros::Buffer> tf_buffer_;
};
int main(int argc, char const *argv[]) {
rclcpp::init(argc,argv);
rclcpp::spin(std::make_shared<FrameListener>());
rclcpp::shutdown();
return 0;
}
import rclpy
from rclpy.node import Node
from tf2_ros.buffer import Buffer
from tf2_ros.transform_listener import TransformListener
from rclpy.time import Time
class TFListener(Node):
def __init__(self):
super().__init__("tf_listener")
self.buffer = Buffer()
self.listener = TransformListener(self.buffer,self)
self.timer = self.create_timer(1.0,self.on_timer)
def on_timer(self):
if self.buffer.can_transform("camera","laser",Time()):
ts = self.buffer.lookup_transform("camera","laser",Time())
self.get_logger().info(
"转换的结果,父坐标系:%s,子坐标系:%s,偏移量:(%.2f,%.2f,%.2f)"
% (ts.header.frame_id,ts.child_frame_id,
ts.transform.translation.x,
ts.transform.translation.y,
ts.transform.translation.z)
)
else:
self.get_logger().info("转换失败......")
def main():
rclpy.init()
rclpy.spin(TFListener())
rclpy.shutdown()
if __name__ == '__main__':
main()
坐标点变换监听
#include <geometry_msgs/msg/point_stamped.hpp>
#include <message_filters/subscriber.h>
#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/buffer.h>
#include <tf2_ros/create_timer_ros.h>
#include <tf2_ros/message_filter.h>
#include <tf2_ros/transform_listener.h>
// #ifdef TF2_CPP_HEADERS
// #include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
// #else
// #include <tf2_geometry_msgs/tf2_geometry_msgs.h>
// #endif
#include <tf2_geometry_msgs/tf2_geometry_msgs.hpp>
using namespace std::chrono_literals;
class MessageFilterPointListener : public rclcpp::Node {
public:
MessageFilterPointListener(): Node("message_filter_point_listener") {
target_frame_ = "base_link";
typedef std::chrono::duration<int> seconds_type;
seconds_type buffer_timeout(1);
// 创建tf缓存对象指针;
tf2_buffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
this->get_node_base_interface(),
this->get_node_timers_interface());
tf2_buffer_->setCreateTimerInterface(timer_interface);
// 创建tf监听器;
tf2_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf2_buffer_);
// 创建坐标点订阅方并订阅指定话题;
point_sub_.subscribe(this, "point");
// 创建消息过滤器过滤被处理的数据;
tf2_filter_ = std::make_shared<tf2_ros::MessageFilter<geometry_msgs::msg::PointStamped>>(
point_sub_, *tf2_buffer_, target_frame_, 100, this->get_node_logging_interface(),
this->get_node_clock_interface(), buffer_timeout);
// 为消息过滤器注册转换坐标点数据的回调函数。
tf2_filter_->registerCallback(&MessageFilterPointListener::msgCallback, this);
}
private:
void msgCallback(const geometry_msgs::msg::PointStamped::SharedPtr point_ptr) {
geometry_msgs::msg::PointStamped point_out;
try {
tf2_buffer_->transform(*point_ptr, point_out, target_frame_);
RCLCPP_INFO(
this->get_logger(), "坐标点相对于base_link的坐标:(%.2f,%.2f,%.2f)",
point_out.point.x,
point_out.point.y,
point_out.point.z);
}
catch (tf2::TransformException & ex) {
RCLCPP_WARN(this->get_logger(), "Failure %s\n", ex.what());
}
}
std::string target_frame_;
std::shared_ptr<tf2_ros::Buffer> tf2_buffer_;
std::shared_ptr<tf2_ros::TransformListener> tf2_listener_;
message_filters::Subscriber<geometry_msgs::msg::PointStamped> point_sub_;
std::shared_ptr<tf2_ros::MessageFilter<geometry_msgs::msg::PointStamped>> tf2_filter_;
};
int main(int argc, char * argv[]) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MessageFilterPointListener>());
rclcpp::shutdown();
return 0;
}
turtlebot3
# Start
sudo apt install ros-humble-gazebo-*
sudo apt install ros-humble-cartographer
sudo apt install ros-humble-cartographer-ros
sudo apt install ros-humble-navigation2
sudo apt install ros-humble-nav2-bringup
sudo apt install ros-humble-turtlebot3*
sudo apt install ros-humble-teleop-twist-keyboard
# Demo
export TURTLEBOT3_MODEL=[burger|waffle|waffle_pi]
source /usr/share/gazebo/setup.sh
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
ros2 run turtlebot3_teleop teleop_keyboard || ros2 run turtlebot3_gazebo turtlebot3_drive
# Rviz
ros2 launch turtlebot3_fake_node turtlebot3_fake_node.launch.py ||ros2 launch turtlebot3_bringup rviz2.launch.py
# Map Nav
ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=true
ros2 run nav2_map_server map_saver_cli -f ./maps/map
# navigation2.launch.py 基于 nav2_bringup/bring_up.launch.py 但 navigation2.launch.py的params_file存在问题
# ros2 launch turtlebot3_navigation2 navigation2.launch.py use_sim_time:=true map:=./maps/map.yaml
# 一个正确的params_file可以参考[nav2_bringup/params/nav2_params.yaml]
# ros2 launch turtlebot3_navigation2 navigation2.launch.py use_sim_time:=true map:=./maps/map.yaml params_file:=[nav2_bringup/params/nav2_params.yaml]
ros2 launch nav2_bringup bringup_launch.py use_sim_time:=true map:=./maps/map.yaml
rviz2 -d ... # 设置起始位姿,供nav2/amcl导航,后设置目标点...
社区
业务关注三个分析:节点、话题、网络
其他
时间API
Rate
:创建频率,常用于设置while频率Time
:创建时间戳,常用于控制时间的功能Duration
:创建时间差,多用于计算时间戳
分布式
以DDS域ID机制划分通信网络,实现主机间通信策略;默认域ID为0,只要保证在同一网络中即可实现分布式通信
# 将节点划分到域6,该节点不可与其他域节点通信
export ROS_DOMAIN_ID=6
# 0<= ROS_DOMAIN_ID <= 101, 每个域ID内节点数要求<=120(域101内节点数要求<=54)
元功能包
打包多个功能包为一个虚包,里面没有实质性内容,但依赖于被打包的功能包,用于方便用户安装。
# 创建一个空包
ros2 pkg create metadata_pkg
<!--- 修改 package.xml 文件,添加执行时所依赖的包 --->
<?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>metadata</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="longlong@todo.todo">longlong</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<!--- 添加执行时所依赖的包名 --->
<exec_depend>base_interface</exec_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>