ROS1学习第三弹
ROS1学习第二弹
本文纯属记录学习过程,所学教程来自B站古月居ROS入门21讲
tf工具的使用
命令行中
1.rosrun tf view_frames
生成当前各个坐标的结构图,导出pdf文件到当前终端所在文件夹下面2.rosrun rviz rviz -d
rospackage find turtle_tf
/rviz/turtle_rviz.rviz
在图中蓝色处要修改一下父亲坐标系
代码实现
广播:
#include<ros/ros.h>
#include<tf/transform_broadcaster.h>
#include<turtlesim/Pose.h>std::string turtle_name; // 存储乌龟名称(由命令行参数传入)// 回调函数:处理订阅的乌龟位姿消息,并发布TF变换
void poseCallback(const turtlesim::PoseConstPtr& msg)
{// 创建TF广播器(static确保只创建一次)static tf::TransformBroadcaster bro;// 创建变换对象,设置平移部分(x, y, z)tf::Transform transform;transform.setOrigin(tf::Vector3(msg->x, msg->y, 0.0));// 创建四元数对象,设置旋转部分(由欧拉角转换而来)tf::Quaternion q;q.setRPY(0, 0, msg->theta); // 绕Z轴旋转theta角度(乌龟朝向)transform.setRotation(q);// 发送带时间戳的变换:// 参数1:变换对象// 参数2:当前时间戳// 参数3:父坐标系名称("world")// 参数4:子坐标系名称(乌龟名称,如"turtle1")bro.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", turtle_name));
}int main(int argc, char **argv)
{// 初始化ROS节点,节点名称必须唯一// 注意:节点名称在命令行中通过__name:=参数指定,而非此处的硬编码名称ros::init(argc, argv, "my_tf_broadcaster");// 检查命令行参数(必须提供乌龟名称,如"turtle1"或"turtle2")if(argc != 2){ROS_ERROR("需要传入乌龟名称作为参数");return -1;}// 从命令行参数获取乌龟名称(argv[1])turtle_name = argv[1];// 创建节点句柄,用于与ROS系统通信ros::NodeHandle node;// 订阅乌龟的位姿话题(如"/turtle1/pose")// 回调函数poseCallback将在收到消息时被调用ros::Subscriber sub = node.subscribe(turtle_name + "/pose", 10, &poseCallback);// 进入循环等待回调(阻塞当前线程)ros::spin();return 0;
}
监听:
#include<ros/ros.h>
#include<tf/transform_listener.h>
#include<geometry_msgs/Twist.h>
#include<turtlesim/Spawn.h>int main(int argc, char **argv)
{// 初始化ROS节点,命名为"my_tf_listener"ros::init(argc, argv, "my_tf_listener");ros::NodeHandle node;// 等待/spawn服务可用(用于创建新乌龟)ros::service::waitForService("/spawn");// 创建服务客户端,连接到/spawn服务ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");// 准备Spawn服务请求(创建新乌龟)turtlesim::Spawn srv;// 注意:此处未设置srv.request参数,将使用默认值(可能导致重复创建同名乌龟)add_turtle.call(srv); // 调用服务创建乌龟// 创建速度指令发布者,发布到/turtle2/cmd_vel话题,控制turtle2移动ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel", 10);// 创建TF监听器,用于接收和缓存TF变换数据tf::TransformListener listener;// 设置循环频率为10Hzros::Rate rate(10.0);while(node.ok()) // 主循环,直到节点被关闭{tf::StampedTransform transform; // 存储变换结果的对象try{// 等待并查找turtle1相对于turtle2的变换// 参数1:目标坐标系(turtle2)// 参数2:源坐标系(turtle1)// 参数3:时间戳(ros::Time(0)表示最新可用变换)// 参数4:超时时间(等待3秒)listener.waitForTransform("/turtle2", "/turtle1", ros::Time(0), ros::Duration(3.0));listener.lookupTransform("/turtle2", "/turtle1", ros::Time(0), transform);}catch(tf::TransformException &ex){// 捕获变换异常并打印错误信息ROS_ERROR("%s", ex.what());ros::Duration(1.0).sleep(); // 暂停1秒后继续continue;}// 计算控制指令,使turtle2跟随turtle1geometry_msgs::Twist vel_msg;// 角速度控制(转向):// atan2计算turtle1相对于turtle2的角度,乘以4.0放大控制量vel_msg.angular.z = 4.0 * atan2(transform.getOrigin().y(), transform.getOrigin().x());// 线速度控制(前进):// 计算turtle1与turtle2的距离,乘以0.5作为前进速度vel_msg.linear.x = 0.5 * sqrt(pow(transform.getOrigin().x(), 2) + pow(transform.getOrigin().y(), 2));// 发布控制指令turtle_vel.publish(vel_msg);rate.sleep(); // 休眠以维持10Hz的频率}return 0;
}
launch文件
launch语法
<launch><node pkg="pkg_name", node="node_name", type="executable_name"/>
</launch>
参数设置(param)
用于设置ROS系统运行中的参数,存储在参数服务器中
<param name="output_frame" value="odom"/>
name
:参数名value
:参数值
参数文件加载(rosparam)
加载YAML文件中的多个参数
<rosparam file="params.yaml" command="load" ns="params"/>
局部变量(arg)
launch文件内部的局部变量,仅限于launch文件使用
<arg name="arg-name" default="arg-value"/>
name
:参数名default
:参数值(默认值)
局部变量调用方法
- 在param中调用:
<param name="foo" value="$(arg arg-name)"/>
- 在node中调用:
<node name="node" pkg="package" type="type" args="$(arg arg-name)"/>
重映射(remap)
重命名ROS计算图资源(如Topic、Service等)
<remap from="/turtlebot/cmd_vel" to="/cmd_vel"/>
from
:原始名称to
:映射后的新名称
包含其他launch文件(include)
在launch文件中包含另一个launch文件,类似C语言中的头文件包含
<include file="${dirname}/other.launch"/>
file
:被包含的launch文件路径(可使用$(find pkg)
或变量如${dirname}
)
其余可参考官网
工作空间中
以最简单的在同一终端下运行两个节点为例:
<launch><node pkg="learning_topic" type="person_subscriber" name="talker" output="screen" /><node pkg="learning_topic" type="person_publisher" name="listener" output="screen" />
</launch>
<launch><!-- 全局参数 --><param name="/turtle_number" value="2"/><!-- 节点1 --><node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node" output="screen"><!-- 节点自己的参数 --><param name="turtle_name1" value="Tom"/><param name="turtle_name2" value="Jerry"/><!-- 加载YAML参数文件 --><rosparam file="$(find learning_launch)/config/param.yaml" command="load" /></node><!-- 节点2 --><node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop_key" output="screen"/>
</launch>
调用之前的 learning_tf 功能包
<launch><node pkg="turtlesim" type="turtlesim_node" name="sim" /><node pkg="turtlesim" type="turtle_teleop_key" name="teleop" output="screen" /><node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle1" name="turtle1_tf_broadcaster" /><node pkg="learning_tf" type="turtle_tf_broadcaster" args="/turtle2" name="turtle2_tf_broadcaster" /><node pkg="learning_tf" type="learning_tf_listener" name="listener" /></launch>
可视化工具
rqt_bag
rqt_graph
rqt_plot
rqt_console
rqt_image_view
rqt_shell
rqt_dep
rqt_logger_level
rqt_topic
rosrun rviz rivz