ROS2与tf2入门教程-添加框架 (C++)
说明:
- 介绍如何用C++添加框架
概述:
在之前的教程中,我们通过编写一个 tf2 广播器和一个 tf2 监听器重新创建了海龟演示。
本教程将教您如何向转换树添加额外的固定和动态帧。
实际上,在 tf2 中添加框架与创建 tf2 广播器非常相似,但是这个示例将向您展示 tf2 的一些附加功能。
对于许多与转换相关的任务,在本地框架内思考会更容易。
例如,最容易推断激光扫描仪中心框架中的激光扫描测量值。
tf2 允许您为系统中的每个传感器、链接或关节定义一个本地框架。
当从一帧转换到另一帧时,tf2 将处理所有引入的隐藏中间帧转换。
TF树:
- tf2 建立了框架的树状结构,因此不允许框架结构中出现闭环。
- 这意味着一个框架只有一个父级,但它可以有多个子级。
- 目前,我们的 tf2 树包含三个框架:world、turtle1和turtle2。
- 两个海龟框架turtle1和turtle2是框架world的子框架。
- 如果我们想在tf2中添加一个新的frame,三个已经存在的frame中的一个需要是父frame,新的frame会成为它的子frame
固定帧广播器:
- carrot1坐标系默认位于turtle1坐标系的右侧2米位置
- 下载固定帧广播器代码
cd ~/tf2_ws/src/learning_tf2_cpp/src
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/fixed_frame_tf2_broadcaster.cpp
- 打开文件fixed_frame_tf2_broadcaster.cpp
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/transform_broadcaster.h>
#include <memory>
using namespace std::chrono_literals;
class FixedFrameBroadcaster : public rclcpp::Node
{
public:
FixedFrameBroadcaster()
: Node("fixed_frame_tf2_broadcaster")
{
tf_publisher_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);
timer_ = this->create_wall_timer(
100ms, std::bind(&FixedFrameBroadcaster::broadcast_timer_callback, this));
}
private:
void broadcast_timer_callback()
{
rclcpp::Time now = this->get_clock()->now();
geometry_msgs::msg::TransformStamped t;
t.header.stamp = now;
t.header.frame_id = "turtle1";
t.child_frame_id = "carrot1";
t.transform.translation.x = 0.0;
t.transform.translation.y = 2.0;
t.transform.translation.z = 0.0;
t.transform.rotation.x = 0.0;
t.transform.rotation.y = 0.0;
t.transform.rotation.z = 0.0;
t.transform.rotation.w = 1.0;
tf_publisher_->sendTransform(t);
}
rclcpp::TimerBase::SharedPtr timer_;
std::shared_ptr<tf2_ros::TransformBroadcaster> tf_publisher_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<FixedFrameBroadcaster>());
rclcpp::shutdown();
return 0;
}
- 修改CMakeLists.txt文件,再原来的内容基础上添加fixed_frame_tf2_broadcaster.cpp的内容
add_executable(fixed_frame_tf2_broadcaster src/fixed_frame_tf2_broadcaster.cpp)
ament_target_dependencies(
fixed_frame_tf2_broadcaster
geometry_msgs
rclcpp
tf2
tf2_ros
turtlesim
)
install(TARGETS
fixed_frame_tf2_broadcaster
DESTINATION lib/${PROJECT_NAME})
- 创建一个启动文件
cd ~/tf2_ws/src/learning_tf2_cpp/launch
vim turtle_tf2_fixed_frame_demo.launch.py
- 内容如下:
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
def generate_launch_description():
demo_nodes = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_tf2_cpp'), 'launch'),
'/turtle_tf2_demo.launch.py']),
)
return LaunchDescription([
demo_nodes,
Node(
package='learning_tf2_cpp',
executable='fixed_frame_tf2_broadcaster',
name='fixed_broadcaster',
),
])
- 构建
colcon build --symlink-install --packages-select learning_tf2_cpp
- 加载工作空间
. ~/tf2_ws/install/local_setup.bash
测试:
- 启动海龟广播器演示
. ~/tf2_ws/install/local_setup.bash
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo.launch.py
- 新carrot1框架出现在转换树中
通过参数指定跟随不同坐标系
- 通过参数传递给启动文件,可以这样运行
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo.launch.py target_frame:=carrot1
- 更改启动文件为
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
def generate_launch_description():
target_frame = LaunchConfiguration('target_frame')
declare_target_frame_cmd = DeclareLaunchArgument(
'target_frame',
default_value='carrot1',
description='target_frame')
demo_nodes = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_tf2_cpp'), 'launch'),
'/turtle_tf2_demo.launch.py']),
launch_arguments={'target_frame': target_frame}.items()
)
return LaunchDescription([
declare_target_frame_cmd,
demo_nodes,
Node(
package='learning_tf2_cpp',
executable='fixed_frame_tf2_broadcaster',
name='fixed_broadcaster',
),
])
- 构建
colcon build --symlink-install --packages-select learning_tf2_cpp
- 加载工作空间
. ~/tf2_ws/install/local_setup.bash
- 默认跟随carrot1,执行
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo.launch.py
- 如果要跟随turtle1,执行
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo.launch.py target_frame:=turtle1
动态帧广播器
- 下载动态帧广播器代码
cd ~/tf2_ws/src/learning_tf2_cpp/src
wget https://raw.githubusercontent.com/ros/geometry_tutorials/ros2/turtle_tf2_cpp/src/dynamic_frame_tf2_broadcaster.cpp
- 打开文件dynamic_frame_tf2_broadcaster.cpp
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/transform_broadcaster.h>
#include <memory>
using namespace std::chrono_literals;
const double PI = 3.141592653589793238463;
class DynamicFrameBroadcaster : public rclcpp::Node
{
public:
DynamicFrameBroadcaster()
: Node("dynamic_frame_tf2_broadcaster")
{
tf_publisher_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);
timer_ = this->create_wall_timer(
100ms, std::bind(&DynamicFrameBroadcaster::broadcast_timer_callback, this));
}
private:
void broadcast_timer_callback()
{
rclcpp::Time now = this->get_clock()->now();
double x = now.seconds() * PI;
geometry_msgs::msg::TransformStamped t;
t.header.stamp = now;
t.header.frame_id = "turtle1";
t.child_frame_id = "carrot1";
t.transform.translation.x = 10 * sin(x);
t.transform.translation.y = 10 * cos(x);
t.transform.translation.z = 0.0;
t.transform.rotation.x = 0.0;
t.transform.rotation.y = 0.0;
t.transform.rotation.z = 0.0;
t.transform.rotation.w = 1.0;
tf_publisher_->sendTransform(t);
}
rclcpp::TimerBase::SharedPtr timer_;
std::shared_ptr<tf2_ros::TransformBroadcaster> tf_publisher_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<DynamicFrameBroadcaster>());
rclcpp::shutdown();
return 0;
}
- 修改CMakeLists.txt文件,再原来的内容基础上添加dynamic_frame_tf2_broadcaster.cpp的内容
add_executable(dynamic_frame_tf2_broadcaster src/dynamic_frame_tf2_broadcaster.cpp)
ament_target_dependencies(
dynamic_frame_tf2_broadcaster
geometry_msgs
rclcpp
tf2
tf2_ros
turtlesim
)
install(TARGETS
dynamic_frame_tf2_broadcaster
DESTINATION lib/${PROJECT_NAME})
- 创建一个启动文件
cd ~/tf2_ws/src/learning_tf2_cpp/launch
vim turtle_tf2_dynamic_frame_demo.launch.py
- 内容如下
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import Node
def generate_launch_description():
demo_nodes = IncludeLaunchDescription(
PythonLaunchDescriptionSource([os.path.join(
get_package_share_directory('learning_tf2_cpp'), 'launch'),
'/turtle_tf2_demo.launch.py']),
launch_arguments={'target_frame': 'carrot1'}.items(),
)
return LaunchDescription([
demo_nodes,
Node(
package='learning_tf2_cpp',
executable='dynamic_frame_tf2_broadcaster',
name='dynamic_broadcaster',
),
])
- 构建
colcon build --symlink-install --packages-select learning_tf2_cpp
- 加载工作空间
. ~/tf2_ws/install/local_setup.bash
- 启动turtle_tf2_dynamic_frame_demo.launch.py启动文件
ros2 launch learning_tf2_cpp turtle_tf2_dynamic_frame_demo.launch.py
- 看到第二只乌龟跟随不断变化的位置
获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号