ROS2通过C++发布静态坐标变换

目录

1、坐标变换

1.1 核心作用:统一时空参考系

1.2 关键应用场景

2、使用命令行

3、使用功能包

 步骤1:建立功能包

步骤2:编写代码

步骤3:完善CMakeLists.txt

步骤4:编译运行

步骤5:验证节点

4、高级用法:

4.1发布多个静态坐标变换

4.2 从参数服务器读取配置,用于坐标变换


1、坐标变换

由于坐标变换在机器人开发之中非常重要,因此ROS2就开发了一个基于话题通讯的工具,即坐标变换工具,即TF。

1.1 核心作用:统一时空参考系

  • 建立坐标系关联:将机器人各部件的局部坐标系(如激光雷达、摄像头、机械臂等)与全局坐标系(如地图map或里程计odom)动态关联

  • 解决"在哪里"的问题:让系统始终知道每个传感器/执行机构相对于机器人本体和环境的实时位置


1.2 关键应用场景

(1) 多传感器融合

  • 示例:激光雷达(lidar_link)和摄像头(camera_link)的数据对齐

  • 实现方式

 # 发布静态变换:base_link → lidar_link static_transform_publisher x=0.1 y=0 z=0.5 q=0 0 0 1

(2) 自主导航与定位

  • SLAM:实时发布mapodombase_link的变换链

graph LR map-->odom-->base_link-->sensor_frames

  • 路径规划:所有障碍物坐标统一转换到map帧下计算

(3) 机械臂控制

  • 手眼标定:发布相机到机械臂末端的固定变换

  • 运动学计算:实时更新各关节坐标系关系

(4) 可视化调试

  • RViz显示:依赖TF树渲染机器人模型和各传感器数据

    ros2 run rviz2 rviz2 -d <config.rviz>

下面介绍其如何通过C++发布坐标变换:

2、使用命令行

在 ROS2 中,可以通过命令行工具 ros2 run 来发布坐标变换(Transforms)。这通常使用 tf2_ros 包中的 StaticTransformBroadcaster 来完成。

ros2 run tf2_ros static_transfrom_publisher --x 0.1 --y 0.0 --z 0.2 --roll 0.0 --pitch 0.0 --yaw 0.0 --frame-id base_link --child-frame-id base_laser

3、使用功能包

 步骤1:建立功能包

ros2 pkg create demo_cpp_tf --build-type ament_cmake --dependencies rclcpp tf2 tf2_ros geometry_msgs tf2_geometry_msgs --license Apache-2.0

步骤2:编写代码

在功能包下创建src文件夹,在src目录下建立static_tf_broadacaster.cpp文件,编写如下代码:

#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/static_transform_broadcaster.h>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <tf2/LinearMath/Quaternion.h>
#include <cmath>

class StaticTfPublisher : public rclcpp::Node {
public:
  StaticTfPublisher() : Node("static_tf_publisher") {
    // 创建静态TF广播器
    tf_pub_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);
    
    // 发布静态变换
    publish_static_transform();
  }

private:
  void publish_static_transform() {
    // 创建TransformStamped消息
    geometry_msgs::msg::TransformStamped transform;

    // 设置时间戳
    transform.header.stamp = this->now();
    transform.header.frame_id = "base_link";  // 父坐标系
    transform.child_frame_id = "laser";      // 子坐标系
    
    // 设置平移 (x, y, z)
    transform.transform.translation.x = 0.1;
    transform.transform.translation.y = 0.0;
    transform.transform.translation.z = 0.2;
    
    // 设置旋转 (四元数)
    tf2::Quaternion q,q1;
    q.setRPY(0, 0, 0);  // 无旋转 (roll=0, pitch=0, yaw=0)
    transform.transform.rotation.x = q.x();
    transform.transform.rotation.y = q.y();
    transform.transform.rotation.z = q.z();
    transform.transform.rotation.w = q.w();
    
    // 发布变换
    tf_pub_->sendTransform(transform);
    RCLCPP_INFO(this->get_logger(), "Published static transform: base_link → laser");

  }

  std::shared_ptr<tf2_ros::StaticTransformBroadcaster> tf_pub_;
};

int main(int argc, char** argv) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<StaticTfPublisher>());
  rclcpp::shutdown();
  return 0;
}

步骤3:完善CMakeLists.txt

add_executable(static_tf_broadcaster src/static_tf_broadcaster.cpp)
ament_target_dependencies(static_tf_broadcaster rclcpp tf2_ros geometry_msgs tf2 tf2_geometry_msgs)
install(TARGETS
static_tf_broadcaster 
  DESTINATION lib/${PROJECT_NAME})

附加:如果使用launch文件进行启动节点,则需要在src/static_tf_broadcaster下建立launch文件夹,并创建static_tf.launch.py,在里面编写代码:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():

    return LaunchDescription([
        Node(
            package='demo_cpp_tf',
            executable='static_tf_broadcaster',
            name='static_tf_publisher',
            parameters=[]
        )
    ])

参数

说明

pkg

节点所属的ROS2包名(需与package.xml中的包名一致)

exec

可执行文件名称(通常是Python脚本或C++节点编译后的名称)

name

节点在ROS2系统中的名称(可通过ros2 node list查看)

output

(可选)日志输出方式,如output="screen"表示打印到终端

注意:

如果使用launch,则需要在CMakeLists.txt之中添加如下指令:

install(DIRECTORY launch

DESTINATION share/${PROJECT_NAME}/),否则运行时在share/功能包目录下找不到.launch文件

步骤4:编译运行

编译:

colcon build
source install/setup.bash

运行:

ros2 run cpp_static_tf static_tf_publisher
# 或通过launch文件启动
ros2 launch cpp_static_tf static_tf.launch.py

步骤5:验证节点

(1) 验证节点是否启动:

ros2 node list

(2) 验证TF树:

可以使用以下命令查看TF树:

ros2 run tf2_tools view_frames

或者查看特定变换:

ros2 run tf2_ros tf2_echo base_link lidar_link

(3) 使用RViz2查看TF变换:

ros2 run rviz2 rviz2

4、高级用法:

4.1 发布多个静态坐标变换

.cpp代码修改为:

#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/static_transform_broadcaster.h>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <tf2/LinearMath/Quaternion.h>
#include <cmath>

class StaticTfPublisher : public rclcpp::Node {
public:
  StaticTfPublisher() : Node("static_tf_publisher") {
    // 创建静态TF广播器
    tf_pub_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);
    
    // 发布静态变换
    publish_static_transform();
  }

private:
  void publish_static_transform() {
    // 创建TransformStamped消息
    geometry_msgs::msg::TransformStamped transform;
    geometry_msgs::msg::TransformStamped transform1;

    // 设置时间戳
    transform.header.stamp = this->now();
    transform.header.frame_id = "base_link";  // 父坐标系
    transform.child_frame_id = "laser";      // 子坐标系

    transform1.header.stamp = this->now();
    transform1.header.frame_id = "base_link";  // 父坐标系
    transform1.child_frame_id = "camera";      // 子坐标系

    // 设置平移 (x, y, z)
    transform.transform.translation.x = 0.1;
    transform.transform.translation.y = 0.0;
    transform.transform.translation.z = 0.2;
    
    transform1.transform.translation.x = 5.0;
    transform1.transform.translation.y = 5.0;
    transform1.transform.translation.z = 5.0;

    // 设置旋转 (四元数)
    tf2::Quaternion q,q1;
    q.setRPY(0, 0, 0);  // 无旋转 (roll=0, pitch=0, yaw=0)
    transform.transform.rotation.x = q.x();
    transform.transform.rotation.y = q.y();
    transform.transform.rotation.z = q.z();
    transform.transform.rotation.w = q.w();

    double roll = 180.0 * M_PI / 180.0;  // 转换为弧度
    double pitch = 120.0 * M_PI / 180.0; // 转换为弧度
    double yaw = 40.0 * M_PI / 180.0;    // 转换为弧度
    q1.setRPY(roll, pitch, yaw);  // 设置旋转 (roll, pitch, yaw)
    transform1.transform.rotation.x = q1.x();
    transform1.transform.rotation.y = q1.y();
    transform1.transform.rotation.z = q1.z();
    transform1.transform.rotation.w = q1.w();
    
    // 发布变换
    tf_pub_->sendTransform(transform);
    RCLCPP_INFO(this->get_logger(), "Published static transform: base_link → laser");
    tf_pub_->sendTransform(transform1);

    RCLCPP_INFO(this->get_logger(), "Published static transform: base_link → camera");

  }

  std::shared_ptr<tf2_ros::StaticTransformBroadcaster> tf_pub_;
};

int main(int argc, char** argv) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<StaticTfPublisher>());
  rclcpp::shutdown();
  return 0;
}

4.2 从参数服务器读取配置,用于坐标变换

在src/demo_cpp_tf目录下建立config文件夹,创建params.yaml文件,编写代码:

static_tf_publisher:
  ros__parameters:
    parent_frame: "base_link"
    child_frame: "lidar"  
    translation_x: 0.15  
    translation_y: -0.05
    translation_z: 0.3
    rotation_x: 0.0
    rotation_y: 0.1           
    rotation_z: 0.0

注:第一行是节点名,不是可执行文件名!

publish_static_transform函数修改为:

  void publish_static_transform() {
    // 创建TransformStamped消息

    // 设置坐标系
    this->declare_parameter("parent_frame", "base_link");
    this->declare_parameter("child_frame", "laser");
    this->declare_parameter("translation_x", 0.1);
    this->declare_parameter("translation_y", 0.0);
    this->declare_parameter("translation_z", 0.2);
    this->declare_parameter("rotation_x", 0.0);
    this->declare_parameter("rotation_y", 10.0* M_PI / 180.0);
    this->declare_parameter("rotation_z", 0.0);

    geometry_msgs::msg::TransformStamped transform;
    transform.header.frame_id = this->get_parameter("parent_frame").as_string();  // 父坐标系
    transform.child_frame_id = this->get_parameter("child_frame").as_string();      // 子坐标系

    // 设置平移 (x, y, z)
    transform.transform.translation.x = this->get_parameter("translation_x").as_double();
    transform.transform.translation.y = this->get_parameter("translation_y").as_double();
    transform.transform.translation.z = this->get_parameter("translation_z").as_double();

    // 设置旋转 (四元数)
    tf2::Quaternion q;
    q.setRPY(this->get_parameter("rotation_x").as_double(), 
            this->get_parameter("rotation_y").as_double(), 
            this->get_parameter("rotation_z").as_double());
    transform.transform.rotation.x = q.x();
    transform.transform.rotation.y = q.y();
    transform.transform.rotation.z = q.z();
    transform.transform.rotation.w = q.w();

    // 发布变换
    tf_pub_->sendTransform(transform);
    RCLCPP_INFO(this->get_logger(), "Published static transform: base_link → laser");
  }

launch文件修改为:

from launch import LaunchDescription
from launch_ros.actions import Node
//新增两个库文件
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
//新增params.yaml路径
    package_dir = get_package_share_directory('demo_cpp_tf')
    params_file = os.path.join(package_dir, 'config', 'params.yaml')
    return LaunchDescription([
        Node(
            package='demo_cpp_tf',
            executable='static_tf_broadcaster',
            name='static_tf_publisher',
            parameters=[params_file] //导入参数
        )
    ])

CMakeLists.txt之中添加如下内容:

install(DIRECTORY config
DESTINATION share/${PROJECT_NAME}/)

验证参数是否有效:

        启动节点        :ros2 launch demo_cpp_tf static_tf.launch.py

        列出节点参数:ros2 param list static_tf_publisher

child_frame
parent_frame

rotation_x
rotation_y
rotation_z

translation_x
translation_y
translation_z

        获取参数值:ros2 param get static_tf_publisher child_frame

String value is: lidar

        其他参数指令

描述参数
ros2 param describe [node_name] [parameter_name]

设置参数

ros2 param set [node_name] [parameter_name] [value]

转储参数

ros2 param dump [node_name] > params.yaml

加载参数

ros2 param load [node_name] params.yaml

在启动时设置参数

ros2 run [package_name] [node_name] --ros-args -p [parameter_name]:=value

<think>嗯,用户想知道如何在ROS中广播静态坐标变换。我之前学过一些ROS的基础知识,但需要仔细回忆具体的步骤和相关工具。首先,静态坐标变换应该是不随时间变化的,比如固定传感器之间的相对位置。我记得ROS中有static_transform_publisher这个工具,可以用来发布静态坐标变换。 然后,可能需要使用tf库或者tf2库。用户提到的是ROS1还是ROS2呢?不过问题中没有明确说明,但提供的引用中有roslaunch和CMakeLists.txt的示例,这通常出现在ROS1中。所以应该以ROS1的教程来回答。 静态坐标变换发布方法有两种:命令行方式和在launch文件中配置。命令行方式是通过运行static_transform_publisher节点,参数包括平移的x、y、z和旋转的四元数或欧拉角,以及父坐标系和子坐标系。例如: rosrun tf static_transform_publisher x y z yaw pitch roll frame_id child_frame_id period_in_ms 或者使用四元数: rosrun tf static_transform_publisher x y z qx qy qz qw frame_id child_frame_id period_in_ms 另外,用户可能需要在launch文件中方便地启动这个静态变换,比如使用<node>标签来调用static_transform_publisher。例如: <node pkg="tf" type="static_transform_publisher" name="link1_broadcaster" args="0.5 0 0 0 0 0 base_link link1 100" /> 此外,用户可能需要使用tf2_ros提供的StaticTransformBroadcaster在C++代码中发布静态变换。需要包含相应的头文件,创建StaticTransformBroadcaster对象,构造geometry_msgs::TransformStamped消息,并填充平移和旋转信息,然后发送。 验证的话,用户可以使用tf_echo工具来检查两个坐标系之间的变换是否正确。例如: rosrun tf tf_echo base_link link1 还有,可能需要确保所有坐标系的关系正确,避免出现断开的情况。比如在RViz中查看坐标系结构是否合理。 用户提供的引用中提到了tf_echo、moveit配置以及CMakeLists.txt中的add_executable,所以可能还需要指导如何将这些静态变换集成到实际项目中,比如在launch文件中包含static_transform_publisher节点,或者在C++节点中发布静态变换。 需要确保回答结构清晰,分步骤说明不同的方法,并提到验证工具。同时,生成相关问题,比如如何验证静态变换、在代码中动态发布的方法、常见错误等。</think>在ROS中广播静态坐标变换主要通过`static_transform_publisher`工具实现,以下是详细步骤: ### 方法1:命令行直接发布 ```bash rosrun tf static_transform_publisher x y z yaw pitch roll parent_frame child_frame period_ms ``` - **参数说明**: - `x y z`:子坐标系相对于父坐标系的平移(单位:米) - `yaw pitch roll`:欧拉角表示的旋转(单位:弧度) - `parent_frame`:父坐标系名称(如`base_link`) - `child_frame`:子坐标系名称(如`sensor_frame`) - `period_ms`:发布间隔(建议设为1000ms) 示例发布一个Z轴偏移0.5米的静态变换: ```bash rosrun tf static_transform_publisher 0 0 0.5 0 0 0 base_link lidar 1000 ``` ### 方法2:在launch文件中集成 ```xml <launch> <node pkg="tf" type="static_transform_publisher" name="sensor_transform" args="0.2 0 0.1 0 0 0 base_link camera 1000"/> </launch> ``` 此方法通过XML参数传递实现持久化发布[^4]。 ### 方法3:C++代码实现(基于tf2_ros) ```cpp #include <tf2_ros/static_transform_broadcaster.h> int main(int argc, char** argv){ ros::init(argc, argv, "static_tf_broadcaster"); static tf2_ros::StaticTransformBroadcaster static_broadcaster; geometry_msgs::TransformStamped static_transform; static_transform.header.stamp = ros::Time::now(); static_transform.header.frame_id = "base_link"; static_transform.child_frame_id = "imu"; static_transform.transform.translation.x = 0.1; static_transform.transform.translation.y = 0; static_transform.transform.translation.z = 0.2; tf2::Quaternion quat; quat.setRPY(0, 0, 0); // 无旋转 static_transform.transform.rotation.x = quat.x(); static_transform.transform.rotation.y = quat.y(); static_transform.transform.rotation.z = quat.z(); static_transform.transform.rotation.w = quat.w(); static_broadcaster.sendTransform(static_transform); ros::spin(); return 0; } ``` 需要修改CMakeLists.txt添加依赖: ```cmake find_package(catkin REQUIRED COMPONENTS roscpp tf2_ros geometry_msgs) ``` ### 验证方法 1. 使用`tf_echo`工具检查变换关系: ```bash rosrun tf tf_echo base_link camera ``` 2. 在RViz中查看坐标系树结构: ```bash rosrun rviz rviz -d `rospack find tf2_tools`/rviz/tf2_monitor.rviz ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值