ROS2教程(10) - 编写接收程序、添加frame - Linux
- 注意 : 本篇文章接上节 (点击此处跳转到上节)
编写接收程序
cpp
- <the_work_ws>/src/learning_tf2_cpp/src/turtle_tf2_listener.cpp
#include <chrono>
#include <functional>
#include <memory>
#include <string>#include "geometry_msgs/msg/transform_stamped.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2/exceptions.h"
#include "tf2_ros/transform_listener.h"
#include "tf2_ros/buffer.h"
#include "turtlesim/srv/spawn.hpp"using namespace std::chrono_literals;class FrameListener : public rclcpp::Node
{
public:FrameListener(): Node("turtle_tf2_frame_listener"),turtle_spawning_service_ready_(false),turtle_spawned_(false){// 声明并获取参数target_frametarget_frame_ = this->declare_parameter<std::string>("target_frame", "turtle1");tf_buffer_ = std::make_unique<tf2_ros::Buffer>(this->get_clock());// 这里我们创建一个对象。 一旦创建了侦听器,它就开始通过网络接收tf2转换,并将它们缓冲至多10秒。tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);// 创建一个客户端 来生成乌龟spawner_ = this->create_client<turtlesim::srv::Spawn>("spawn");// 创建 turtle2 速度发布器publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("turtle2/cmd_vel", 1);// 每秒调用一次定时器timer_ = this->create_wall_timer(1s, [this]() {return this->on_timer();});}private:void on_timer(){// 将frame names 储存,以便计算变换std::string fromFrameRel = target_frame_.c_str();std::string toFrameRel = "turtle2";if (turtle_spawning_service_ready_) {if (turtle_spawned_) {geometry_msgs::msg::TransformStamped t;// 查找 target_frame 和 turtle2 frames 之间的转换,并发送指令使 turtle2 到达目标范围try {// 向侦听器查询特定的转换// 参数:Target frame(目标frame)、Source frame(源frame)、The time we want to transform(想要变换的时间)t = tf_buffer_->lookupTransform(toFrameRel, fromFrameRel, tf2::TimePointZero);} catch (const tf2::TransformException & ex) {RCLCPP_INFO(this->get_logger(), "Could not transform %s to %s: %s",toFrameRel.c_str(), fromFrameRel.c_str(), ex.what());return;}geometry_msgs::msg::Twist msg;static const double scaleRotationRate = 1.0;msg.angular.z = scaleRotationRate * atan2(t.transform.translation.y,t.transform.translation.x);static const double scaleForwardSpeed = 0.5;msg.linear.x = scaleForwardSpeed * sqrt(pow(t.transform.translation.x, 2) +pow(t.transform.translation.y, 2));publisher_->publish(msg);} else {RCLCPP_INFO(this->get_logger(), "Successfully spawned");turtle_spawned_ = true;}} else {// 检查服务器是否准备就绪if (spawner_->service_is_ready()) {// 初始化// 请注意 x, y 和 在 turtlesim/srv/Spawn 中是 float 类型auto request = std::make_shared<turtlesim::srv::Spawn::Request>();request->x = 4.0;request->y = 2.0;request->theta = 0.0;request->name = "turtle2";// Call request(请求)using ServiceResponseFuture =rclcpp::Client<turtlesim::srv::Spawn>::SharedFuture;auto response_received_callback = [this](ServiceResponseFuture future) {auto result = future.get();if (strcmp(result->name.c_str(), "turtle2") == 0) {turtle_spawning_service_ready_ = true;} else {RCLCPP_ERROR(this->get_logger(), "Service callback result mismatch");}};auto result = spawner_->async_send_request(request, response_received_callback);} else {RCLCPP_INFO(this->get_logger(), "Service is not ready");}}}// 是否有生成海龟的服务的bool值bool turtle_spawning_service_ready_;// 如果生成海龟成功bool turtle_spawned_;rclcpp::Client<turtlesim::srv::Spawn>::SharedPtr spawner_{nullptr};rclcpp::TimerBase::SharedPtr timer_{nullptr};rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_{nullptr};std::shared_ptr<tf2_ros::TransformListener> tf_listener_{nullptr};std::unique_ptr<tf2_ros::Buffer> tf_buffer_;std::string target_frame_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<FrameListener>());rclcpp::shutdown();return 0;
}
CMakeLists.txt
- <the_work_ws>/src/learning_tf2_cpp/CMakeLists.txt
# add
add_executable(turtle_tf2_listener src/turtle_tf2_listener.cpp)
ament_target_dependencies(turtle_tf2_listenergeometry_msgsrclcpptf2tf2_rosturtlesim
)
install(TARGETSturtle_tf2_listenerDESTINATION lib/${PROJECT_NAME})
launch
- <the_work_ws>/src/learning_tf2_cpp/launch/turtle_tf2_demo.launch.py
# 更新为以下内容
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfigurationfrom launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',executable='turtlesim_node',name='sim'),Node(package='learning_tf2_cpp',executable='turtle_tf2_broadcaster',name='broadcaster1',parameters=[{'turtlename': 'turtle1'}]),DeclareLaunchArgument('target_frame', default_value='turtle1',description='Target frame name.'),Node(package='learning_tf2_cpp',executable='turtle_tf2_broadcaster',name='broadcaster2',parameters=[{'turtlename': 'turtle2'}]),Node(package='learning_tf2_cpp',executable='turtle_tf2_listener',name='listener',parameters=[{'target_frame': LaunchConfiguration('target_frame')}]),])
run
# 终端1
rosdep install -i --from-path src --rosdistro humble -y
colcon build --packages-select learning_tf2_cpp
. install/setup.bash
ros2 launch learning_tf2_cpp turtle_tf2_demo.launch.py # 我们看到有两只海龟# 终端2ros2 run turtlesim turtle_teleop_key # 控制海龟1运动,发现海龟2跟随运动
添加frame
在之前的教程中,我们通过编写tf2广播器和tf2侦听器来重新创建海龟演示。 本教程将教你如何向转换树添加额外的固定和动态frame。 事实上,在tf2中添加一个frame与创建tf2广播器非常相似,但是这个示例将向您展示tf2的一些附加功能。
静态frmae广播器
我们将编写固定frame广播程序,基于前面的海龟跟随示例,我们将添加一个坐标系carrot1,它是turtle1的子坐标系,并将作为第二只海龟的目标。
cpp
- <the_work_ws>/src/learning_tf2_cpp/src/fixed_frame_tf2_broadcaster.cpp
#include <chrono>
#include <functional>
#include <memory>#include "geometry_msgs/msg/transform_stamped.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2_ros/transform_broadcaster.h"using namespace std::chrono_literals;class FixedFrameBroadcaster : public rclcpp::Node
{
public:FixedFrameBroadcaster(): Node("fixed_frame_tf2_broadcaster"){tf_broadcaster_ = 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(){// 坐标系转换geometry_msgs::msg::TransformStamped t;t.header.stamp = this->get_clock()->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_broadcaster_->sendTransform(t);}rclcpp::TimerBase::SharedPtr timer_;std::shared_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
};int main(int argc, char * argv[])
{rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<FixedFrameBroadcaster>());rclcpp::shutdown();return 0;
}
CMakeLists.txt
- <the_work_ws>/src/learning_tf2_cpp/CMakeLists.txt
# add
add_executable(fixed_frame_tf2_broadcaster src/fixed_frame_tf2_broadcaster.cpp)
ament_target_dependencies(fixed_frame_tf2_broadcastergeometry_msgsrclcpptf2_ros
)
install(TARGETSfixed_frame_tf2_broadcasterDESTINATION lib/${PROJECT_NAME})
launch
- <the_work_ws>/src/learning_tf2_cpp/launch/turtle_tf2_fixed_frame_demo.launch.py
import osfrom ament_index_python.packages import get_package_share_directoryfrom launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSourcefrom launch_ros.actions import Nodedef 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',),])
run
# 终端1
rosdep install -i --from-path src --rosdistro humble -y
colcon build --packages-select learning_tf2_cpp
. install/setup.bash
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo.launch.py
如果您驾驶第一只海龟,您应该注意到,尽管我们添加了一个新frame,但其行为与上一教程相比并没有改变。 这是因为添加一个额外的frame不会影响其他帧,我们的监听器仍然使用先前定义的frame。
因此,如果我们想让第二只乌龟跟随carrot而不是第一只乌龟,我们需要改变target_frame的值。 这可以通过两种方式实现。 一种方法是直接从控制台将参数传递给启动文件:
ros2 launch learning_tf2_cpp turtle_tf2_fixed_frame_demo.launch.py target_frame:=carrot1
第二种方法是更新启动文件turtle_tf2_fixed_frame_demo.launch.py:
def generate_launch_description():demo_nodes = IncludeLaunchDescription(...,launch_arguments={'target_frame': 'carrot1'}.items(),)
现在重新构建包,重新启动,您将看到第二只乌龟跟随carrot而不是第一只乌龟!
动态frame广播器
cpp
- <the_work_ws>/src/learning_tf2_cpp/src/dynamic_frame_tf2_broadcaster.cpp
#include <chrono>
#include <functional>
#include <memory>#include "geometry_msgs/msg/transform_stamped.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2_ros/transform_broadcaster.h"using namespace std::chrono_literals;const double PI = 3.141592653589793238463;class DynamicFrameBroadcaster : public rclcpp::Node
{
public:DynamicFrameBroadcaster(): Node("dynamic_frame_tf2_broadcaster"){tf_broadcaster_ = 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_broadcaster_->sendTransform(t);}rclcpp::TimerBase::SharedPtr timer_;std::shared_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;
}
CMakeLists.txt
- <the_work_ws>/src/learning_tf2_cpp/CMakeLists.txt
# add
add_executable(dynamic_frame_tf2_broadcaster src/dynamic_frame_tf2_broadcaster.cpp)
ament_target_dependencies(dynamic_frame_tf2_broadcastergeometry_msgsrclcpptf2_ros
)
install(TARGETSdynamic_frame_tf2_broadcasterDESTINATION lib/${PROJECT_NAME})
launch
- <the_work_ws>/src/learning_tf2_cpp/launch/turtle_tf2_dynamic_frame_demo.launch.py
import osfrom ament_index_python.packages import get_package_share_directoryfrom launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSourcefrom launch_ros.actions import Nodedef 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',),])
run
rosdep install -i --from-path src --rosdistro humble -y
colcon build --packages-select learning_tf2_cpp
. install/setup.bash
ros2 launch learning_tf2_cpp turtle_tf2_dynamic_frame_demo.launch.py
相关文章:
ROS2教程(10) - 编写接收程序、添加frame - Linux
注意 : 本篇文章接上节 (点击此处跳转到上节) 编写接收程序 cpp <the_work_ws>/src/learning_tf2_cpp/src/turtle_tf2_listener.cpp #include <chrono> #include <functional> #include <memory> #include <string>#include "geometry_…...
Arraylist与LinkedList的区别
Arraylist 概念 Arraylist非线程安全Arraylist 底层使用的是Object数组ArrayList 采用数组存储,插入和删除元素的时间复杂度受元素位置的影响ArrayList 支持快速随机访问,就是通过元素的序号快速获取元素对象ArrayList的空间浪费主要体现在列表的结尾会预留一定的容…...
Nestjs使用Redis的最佳实践
前几天在项目中有用到Redis JWT实现服务端对token的主动删除(退出登录功能)。故此介绍下如何在Nestjs中使用Redis,并做下总结。 知识准备 了解Redis - 网上很多简介。了解Nestjs如何使用jwt生成token - 可移步看下我之前的文章 效果展示 一、mac安装与使用 示…...
Cadence23学习笔记(十四)
ARC就是圆弧走线的意思: 仅打开网络的话可以只针对net进行修改走线的属性: 然后现在鼠标左键点那个走线,那个走线就会变为弧形: 添加差分对: 之后,分别点击两条线即可分配差分对: 选完差分对之后…...
socket 编程
1. socket 套接字 Socket 是一个用于网络通信的技术。Socket 通信允许客户端——服务器之间进行双向通信。它可以使任何客户端机器连接到任何服务器,安装在客户端和服务器两侧的程序就可以实现双向的通信。Socket的作用就是把连接两个计算机的通信软件“中间接”起来…...
如何使用 HTTPie 进行高效的 HTTP 请求
如何使用 HTTPie 进行高效的 HTTP 请求 引言 HTTPie 是一个命令行 HTTP 客户端,它以其简洁的语法和人性化的输出格式赢得了广大开发者的喜爱。与 curl 相比,HTTPie 提供了更加直观和用户友好的接口,使得执行 HTTP 请求变得轻松愉快。本文将…...
Lingo求解器百度云下载 ling 8.0/lingo 18安装包资源分享
如大家所熟悉的,Lingo是Linear Interaction and General Optimizer的缩写,中文名称为“交互式线性和通用优化求解器”,是一套专门用于求解最优化问题的软件包。 在大部分人认知里,Lingo可用于求解线性规划、二次规划、整数规划、…...
文献综述如何为研究的理论框架做出贡献
VersaBot一键生成文献综述 文献综述在几个关键方面对塑造和巩固研究的理论框架起着至关重要的作用; 1. 识别相关理论和概念: 通过对现有研究的探索,您将遇到与您的主题相关的突出理论和概念。这些可以作为您自己的理论框架的构建块。 2. 理…...
FastAPI(七十九)实战开发《在线课程学习系统》接口开发-- 加入课程和退出课程
源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 加入课程 我们先看下加入课程 1.是否登录 2.课程是否存在 3.是否已经存在 4.添加 首先实现逻辑 def get_student_course(db: Session, course: int…...
【赛事推荐】2024中国高校计算机大赛人工智能创意赛
“中国高校计算机大赛”(China Collegiate Computing Contest,简称C4)是面向全国高校各专业在校学生的科技类竞赛活动,于2016年由教育部高等学校计算机类专业教学指导委员会、教育部高等学校大学软件工程专业教学指导委员会、教育…...
C++沉思:预处理和编译
预处理和编译 条件编译源代码使用方式典型示例原理 使用static_assert执行编译时断言检查使用方式原理 在C中,编译是将源代码转换为机器代码并组织在目标文件中,然后将目标文件链接在一起生成可执行文件的过程。编译器实际上一次只处理一个文件ÿ…...
交通数据处理-计算途径某些路段的车辆数
根据车辆的运行轨迹,计算先经过某些路段,再经过某些路段的车辆数。 欢迎关注本人公众号--交通数据探索师 如下表, 其中:vehicle: 车辆编号;route: 车辆轨迹。 以第一行为例,车辆car1按顺序经过了路段123…...
从0到1入门系列 | 崖山公开课再加码,三小时带你入门崖山数据库!
对不断更新的技术心生迷茫 不知如何正确的提升自己? 对新兴的国产数据库领域充满好奇 却不知从何入手? 崖山专家团队精心筹备 《从0到1入门》系列直播课 6节课 三小时 助力数据库小白变身技术高手 掌握最前沿的数据库技术 现在开始 开启职场“金…...
Powershell自定义带参数的别名
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、函数二、使用步骤总结 前言 之前写了一篇文章定义别名让powershell尽可能接近Unix风格,增强两者的互操作性,今天给出方法可以定义带…...
文件操作相关的精讲
目录: 思维导图 一. 文件定义 二. 文件的打开和关闭 三. 文件的顺序读写操作 四. 文件的随机读写操作 五. 文本文件和二进制文件 六. 文件读取结束的判断 七.文件缓冲区 思维导图: 一. 文件定义 1.文件定义 C语言中,文件是指一组相…...
05 循环神经网络
目录 1. 基本概念 2. 简单循环网络 2.1 简单循环网络 2.2 长程依赖问题 3. 循环神经网络的模式与参数学习 3.1 循环神经网络的模式 3.2 参数学习 4. 基于门控的循环神经网络 4.1 长短期记忆网络 4.2 LSTM网络的变体网络 4.3 门控循环单元网络 5. 深层循环神经网络…...
C#初级——条件判断语句、循环语句和运算符
条件判断语句 简单的条件判断语句,if()里面进行条件判断,如果条件判断正确就执行语句块1,如果不符合就执行语句块2。 if (条件判断) { 语句块1 } else { 语句块2 } int age 18;if (age < 18){Console.WriteLine("未…...
Laravel路由模型绑定:简化依赖注入的艺术
Laravel路由模型绑定:简化依赖注入的艺术 引言 在现代Web应用开发中,Laravel框架以其优雅和简洁的代码而闻名。Laravel的路由模型绑定(Route Model Binding)是框架提供的一项强大功能,它允许开发者在路由处理中自动注…...
【vue前端项目实战案例】之Vue仿饿了么App
本文将介绍一款仿“饿了么”商家页面的App。该案例是基于 Vue2.0 Vue Router webpack ES6 等技术栈实现的一款外卖类App,适合初学者进行学习。 项目源码下载链接在文章末尾 1 项目概述 该项目是一款仿“饿了么”商家页面的外卖类App,主要有以下功能…...
冷热分离——Java全栈知识(36)
之前在面试的时候有老师问: 我看你使用了水平分表,但是如果有些 1%的数据占了访问量的 90%,而剩下 99%的数据只占了访问量的 10%。这种情况怎么处理。 1 、冷热分离 1.1、什么是冷热分离 冷热分离指的是在处理数据时将数据库分为冷库和热库…...
XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
