ROS(快速初步入门)

目录

一、节点与节点管理器

二、通信方式

三、参数

四、文件系统

五、ROS命令行工具

六、创建工作空间与功能包

1)工作空间

2)创建功能包

七、发布者Publisher的代码实现

八、订阅者Subscriber的编程实现

九、自定义话题并使用

十、Client客户端的编程实现

1.创建功能包

2.配置CMakeLists.txt的编译规则

十一、Server的编程实现

十二、服务数据的定义与使用

1.定义服务数据

2.创建服务器代码

十三、参数的使用与编程方法

1.参数模型

2.创建功能包

3.参数命令行的使用

4.代码实现

十四、ROS中的坐标管理系统

1.机器人坐标变换例程

2.tf坐标系广播与监听的编程实现

十五、launch文件的使用方法

1.launch文件

2.核心语法

十六、常用可视化工具的使用


一、节点与节点管理器

二、通信方式

三、参数

全局共享的字典,里面存储的是整个全局所需要用到的参数,适合存储静态,非二进制类型。

四、文件系统

五、ROS命令行工具

  1. 启动ROS Master:
$ roscore

     2.启动小海龟仿真器:

$ rosrun turtlesim turtlesim_node

     3.启动海龟控制节点:

$ rosrun turtlesim turtle_teleop_key

注意:以上三个步骤分别需要在三个不同的终端中执行。

$ rqt_graph

这一代码用于显示系统计算图。

控制器将数据打包发给订阅了相关话题的节点,该节点接收到数据后会执行对应操作。

与节点相关:

rosnode //显示节点信息的指令。 
rosnode list //列出节点 
rosnode info 节点 //查看具体节点的信息

与话题相关:

rostopic //显示话题相关信息 
rostopic list //列出话题 
rostopic pub 话题/输入指令的数据结构 “具体数据” //发送话题的消息数据内容 
rostopic pub -r 话题 //-r表示频率,代表一秒发送多少次 
rosmsg show geometry_msgs/twist //查看这一指令的数据结构

与服务相关:

rosservice list //列出服务 
rosservice call 服务名字 按tab键补全 //访问这一服务 //例如,访问/spawn,用于产生新的海龟,不过新海龟只能用
rostopic pub操作运动 
rosservice call /spawn tab键补全

话题记录工具:

rosbag record -a -O cmd_record //话题记录。-a表示记录所有数据,-O表示把记录的数据压缩成压缩包 
rosbag play cmd_record.bag //话题复现,即让仿真器再次完成之前记录数据代表的操作

六、创建工作空间与功能包

1)工作空间

存放工程开发相关文件的文件夹。

  • src:代码空间
  • build:编译空间
  • devel:开发空间
  • install:安装空间
//创建工作空间 
mkdir -p ~/catkin_ws/src 
cd ~/catkin_ws/src cakin_init_workspace 
//编译工作空间 
cd ~/catkin_ws/ catkin_make catkin_make install 
//设置环境变量 
source devel/setup.bash 
//检查环境变量 
echo $ROS_PACKAGE_PATH

完成第九行代码后,你的catkin_ws文件夹(工作空间文件夹名字可以自己定)就会出现如下图样式

2)创建功能包

//创建功能包 
cd ~/catkin_ws/src catkin_create_pkg test_pkg std_msgs rospy roscpp 
catkin_create_pkg <package_name> [depend1] [depend2] [depend3] //<>里面的是船舰的功能包的名字,[]里面的是创建的功能包依赖ROS里面的哪些文件 
//编译功能包 
cd ~/catkin_ws catkin_make source ~/catkin_ws/devel/setup.bash

CMakelists中存写的是功能包代码编译的一些条件,协议等等。

package中存放的是功能包的相关信息。

任意两个功能包都会包含以上两个文件,只有拥有这两个文件,才代表这个文件夹是一个功能包。

注意:同一个工作空间(catkin_ws)中,不允许存在同名功能包,例如我们以上代码,在./src中创建了名叫test_pkg的功能包,则不能再次创建同名功能包。

七、发布者Publisher的代码实现

cd ~/catkin_ws/src 
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim

创建发布者代码:

C++代码:

/*** publish turtle1/cmd_vel topic, the type of message is geometry_msgs::Twist*/#include <ros/ros.h>
#include <geometry_msgs/Twist.h>int main(int argc,char **argv)
{//ROS节点初始化 ros::init(argc,argv,"velocity_publisher");//创建节点句柄ros::NodeHandle n;//创建一个Publisher,发布名为turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",10);//设置循环频率ros::Rate loop_rate(10);int count = 0;while(ros::ok()){//初始化geometry_msgs::Twist类型的消息,避免之前的数据造成的影响,设定基本参数geometry_msgs::Twist vel_msg;vel_msg.linear.x=0.5;vel_msg.angular.z=0.2;//发布消息turtle_vel_pub.publish(vel_msg);ROS_INFO("Publish turtle velocity command[%0.2f m/s, %0.2f rad/s]",vel_msg.linear.x,vel_msg.angular.z);//按照循环频率延时loop_rate.sleep();}return 0;
}

Python代码:(不需要像C++一样重编译成可执行文件,直接就可以执行,但需要勾选权限,详见第八节末尾注意)

#1 /usr/bin/env python
# -*- coding: utf-8 -*-
#该例程将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twistimport rospy
from geometry_msgs.msg import Twistdef velocity_publisher():#ROS节点初始化rospy.init_node('velocity_publisher',anonymous=True)#创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10turtle_vel_pub=rospy.Publisher('/turtle1/cmd_vel',Twist,queue_size=10)#设置循环频率rate = rospy.Rate(10)while not rospy.is_shutdown():#初始化geometry_msgs::Twist类型的消息,即初始化传输什么数据vel_msg = Twist()vel_msg.linear.x = 0.5vel_msg.angular.z = 0.2#发布消息turtle_vel_pub.publish(vel_msg)rospy.loginfo("Publish turtle velocity command[%0.2f m/s,%0.2f rad/s]",vel_msg.linear.x,vel_msg.angular.z)#按照循环频率延时,执行完一次,延时再继续rate.sleep()if _name_ == '_main_':try:velocity_publisher()except rospy.ROSInterruptException:pass

思路:

  1. 初始化ROS节点,使得发送的数据对这个节点起作用,例如,小车轮子接收到速度并以这个速度转动,轮子就是节点。
  2. 向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型。
  3. 创建消息数据。
  4. 按照一定的频率循环发送。

配置发布者代码编译规则:

add_executable(velocity_publisher src/velocity_publisher.cpp) //将后一个.cpp文件编译成前面那个可执行文件 //转换成可执行文件后,才能执行文件相应操作,.cpp文件只是代码要想电脑执行则必须转换成可执行文件 
target_link_libraries(velocity_publisher ${catkin_LIBRARIES}) //把可执行文件和ROS的一些库作链接

编译并运行发布者:

//回到catkin_ws的根目录下执行 
cd ~/catkin_ws 
catkin_make 
source devel/setup.bash 
roscore 
rosrun turtlesim turtlesim_node 
rosrun learning_topic velocity_publisher

对于第六行代码,可以在根目录下按住ctrl+h显示隐藏文件,打开.bashrc,在文件末尾加上绝对路径即可。

最终编译形成的可执行文件velocity_publisher存放在/catkin_ws/devel/lib/learning_topic目录下。

八、订阅者Subscriber的编程实现

实现步骤:

  • 初始化ROS节点;
  • 订阅需要的话题;
  • 循环等待话题消息,接收到消息后进入回调函数;
  • 在回调函数中完成消息处理。

C++代码:

/***该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose*/#include <ros/ros.h>#include "turtlesim/Pose.h"//接收到订阅的信息后,会进入信息回调函数//ConsPtr&是长指针类型,msg会指向收到的消息void poseCallback(const turtlesim::Pose::ConstPtr& msg){//将接收到的信息打印出来,类似与printfROS_INFO("Turtle pose: x:%0.6f, y:%0.6f",msg->x, msg->y);           }int main(int argc,char **argv){//初始化ROS节点ros::init(argc, argv, "pose_subscriber");//创建节点句柄ros::NodeHandle n;//创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback,打印相应接收到的信息ros::Subscriber pose_sub = n.subscribe("/turtle1/pose",10,poseCallback);//循环等待回调函数 ros::spin();return 0;}

Python代码:(不需要像C++一样重编译成可执行文件,直接就可以执行,但需要勾选权限,详见第八节末尾注意)

#!/user/bin/env python
# -*- coding: utf-8 -*-
#该例程将订阅/turtle1/pose话题,消息类型turtlesim::Poseimport rospy
from turtlesim.msg import Pose
def poseCallback(msg):rospy.loginfo("Turtle pose: x:%0.6f, y:%0.6f",msg.x, msg.y)def pose_subscriber():#初始化ROS节点rospy.init_node('pose_subscriber', n=anonymous=True)#创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback,打印相应接收到的信息rospy.Subscriber(/turtle1/pose", Pose, poseCallback)#循环等待回调函数 rospy.spin()#上面两个是定义函数,下面这个是用于执行主程序,类似C中的main函数
if __name__ == '__main__':pose_subscriber()

配置订阅者代码编译规则:

add_executable(pose_subscriber src/pose_subscriber.cpp)
//将后一个.cpp文件编译成前面那个可执行文件target_link_libraries(pose_subscriber ${catkin_LIBRARIES})
//把可执行文件和ROS的一些库作链接

后续操作同上发布者一样。

编译并运行订阅者:

//回到catkin_ws的根目录下执行
cd ~/catkin_wscatkin_makesource devel/setup.bashroscorerosrun turtlesim turtlesim_noderosrun learning_topic pose_subscriber

注意:若是Python作为可执行文件,则需要在文件属性的权限栏中勾选“允许作为程序可执行文件”。

九、自定义话题并使用

创建一个msg文件夹,存放msg文件。

在Person.msg文件中输入蓝色框框里面的内容。

在package。xml中输入以下内容:

添加Person.msg为编译的接口,当编译的时候会自动识别并针对文件作为编译的接口。

添加编译Person.msg时需要的ros库。

自定义话题的C++代码: 

/*** publish /person_info topic, the type of message is learning_topic::Person*/#include <ros/ros.h>
#include "learning_topic/Person.h"//包含我们自己定义的消息类型learning_topic::Person的头文件int main(int argc,char **argv)
{//ROS节点初始化 ros::init(argc,argv,"person_publisher");//创建节点句柄ros::NodeHandle n;//创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info",10);//设置循环频率ros::Rate loop_rate(1);int count = 0;while(ros::ok()){//初始化learning_topic::Person类型的消息,避免之前的数据造成的影响,设定基本参数learning_topic::Person person_msg;person_msg.name = "Aurora";person_msg.age = 18;person_msg.sex = learning_topic::Person::male;//调用在头文件中定义性别的宏                   //发布消息person_info_pub.publish(person_msg);ROS_INFO("Publish Person Info: name: %s, age: %d, sex: %d",person_msg.name.c_str(),person_msg.age,person_msg.sex);//按照循环频率延时loop_rate.sleep();}return 0;
}
/***该例程将订阅/person_info话题,消息类型learning_topic::Person*/#include <ros/ros.h>#include "learning_topic/Person.h"//接收到订阅的信息后,会进入信息回调函数//ConsPtr&是长指针类型指向learning_topic::Person类型的数据,msg会指向收到的消息void poseCallback(const learning_topic::Person::ConstPtr& msg){//将接收到的信息打印出来,类似与printfROS_INFO("Subscribe Person Info: name:%s, age:%d, sex:%d",msg->name.c_str(), msg->age,msg->sex);           //name.c_str()将name的字符串转换为C语言的风格}int main(int argc,char **argv){//初始化ROS节点,相当于此文件的执行文件,中断调用时,就是在调用并执行这个文件ros::init(argc, argv, "person_subscriber");//创建节点句柄ros::NodeHandle n;//创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback,打印相应接收到的信息ros::Subscriber person_info_sub = n.subscribe("/person_info",10,poseCallback);//循环等待回调函数 ros::spin();return 0;}

配置CmakeLists.txt中的编译规则:

add_executable(person_publisher src/person_publisher.cpp)
target_link_libraries(person_publisher ${catkin_LIBRARIES})
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
//第三行代码是用于跟生成的头文件Person.h进行连接用的
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)

ROS里面的程序有运行对象这一选项,roscore帮助两个节点也就是运行程序找到对象就不在对两个节点其作用了。

在我看来,其实是两个程序相互联系后,基于共同topic和消息类型来执行程序并最终实现相应结果。

十、Client客户端的编程实现

ROS(Robot Operating System)客户端是指在ROS框架中,使用服务(Service)通信模式的节点,它发送请求给服务端(Server),并等待服务端处理请求后的响应。

相当于一个开关。

1.创建功能包

用户命令在指定位置显现出一直海归。

C++代码:

/***该例程将请求/spawn服务,服务数据类型turtlesim::Spawn*/#include <ros/ros.h>#include <turtlesim/Spawn.h>int main(int argc,char** argv){//初始化ROS节点ros::init(argc,argv,"turtle_spawn");//创建节点句柄ros::NodeHandle node;//发现/spawn服务后,创建一个服务客户端,连接名为/spawn的serviceros::service::waitForService("/spawn");ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");//初始化turtlesim::Spawn的请求数据//这里相当于用户命令的封装turtlesim::Spawn srv;srv.request.x=2.0;srv.request.y=2.0;srv.request.name="turtle2";//请求服务调用ROS_INFO("Call service to spwan turtle[x:%0.6f, y:%0.6f, name:%s]",srv.request.x,srv.request.y,srv.request.name.c_str());add_turtle.call(srv);//显示服务调用结果ROS_INFO("Spwan turtle successfully [name:%s]",srv.request.name.c_str());return 0; };

Python代码:

#1 /usr/bin/env python
# -*- coding: utf-8 -*-
#该例程将请求/spawn服务,服务数据类型turtlesim::Spawnimport sys
import rospy
from turtlesim.srv import Spawndef turtle_spawn():#ROS节点初始化rospy.init_node('turtle_spawn')#发现/spawn服务后,创建一个服务客户端,连接名为/spawn的servicerospy.wait_for_service('/spawn')		try:#客户端是add_turtle,服务连接名是/spawn,数据类型是Spawnadd_turtle = rospy.ServiceProxy('/spawn',Spawn)#请求服务调用,输入请求数据,直接将指定数据添加到客户端后面#调用程序让turtle2小海龟在所给数据指定位置处出现#add_turtle等待海龟是否产生成功,然后返回值给response,产生成功返回海龟名,不成功则执行except里面的代码response = add_turtle(2.0,2.0,0.0,"turtle2")return response.nameexcept rospy.ServiceException, e:print "Service call failed: %s"%eif __name__ == "__main__":#服务调用并显示调用结果print "Spawn turtle successfully [name:%s]" %(turtle_spawn())

实现一个客户端的流程:

  1. 初始化ROS节点;
  2. 创建一个Client实例;
  3. 发布服务请求数据;
  4. 等待Server处理之后的应答结果;

2.配置CMakeLists.txt的编译规则

  • 设置需要编译的代码和生成可执行文件;
  • 设置链接库;
add_executable(turtle_spawn src/turtle_spawn.cpp)
target_link_libraries(turtle_spawn ${catkin_LIBRARIES})//运行
roscore
rosrun turtlesim turtlesim_node
rosrun learning_service turtle_spawn 

十一、Server的编程实现

发送具体海龟运动的指令。

C++代码:

/***该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger*/#include <ros/ros.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Trigger.h>ros::Publisher turtle_vel_pub;
bool pubCommand = false;//service回调函数,输入参数req,输出参数res
bool commandCallback(std_srvs::Trigger::Request &req,std_srvs::Trigger::Response &res)
{pubCommand = !pubCommand;//显示请求数据ROS_INFO("Publish turtle velocity command [%s]",pubCommand==true?"Yes":"No");//设置反馈数据res.success = true;res.message = "Change turtle command state!";return true;                                                                                                 
}int main(int argc,char **argv)
{//ROS节点初始化ros::init(argc,argv,"turtle_command_server");//创建节点句柄ros::NodeHandle n;//创建一个名为/turtle_command的server,注册回调函数commandCallbackros::ServiceServer command_service = n.advertiseService("/turtle_command",commandCallback);//创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",10);//循环等待回调函数ROS_INFO("Ready to receive turtle command.");//设置循环频率ros::Rate loop_rate(10);while(ros::ok()){//查看一次回调函数队列ros::spinOnce();//如果标志位为true,则发布速度命令if(pubCommand){geometry_msgs::Twist vel_msg;vel_msg.linear.x = 0.5;vel_msg.angular.z = 0.2;turtle_vel_pub.publish(vel_msg);         }         //按照循环频率延时 loop_rate.sleep();                            }return 0;
}

展示数据类型:

十二、服务数据的定义与使用

1.定义服务数据

  1. 定义srv文件

三个横线之上是request数据,三个横线之下是response的数据。

  1. 在package.xml中添加功能依赖包
<build_depend>message_generation</build_depend> 
<exec_depend>message_runtime</exec_depend>
  1. 在CMakeLists.txt中添加编译选项
find_package(...... message_generation)add_service_files(FILES Person.srv)
//根据上一行所加的Person.srv文件生成对应头文件
generate_messages(DEPENDENCIES std_msgs)catkin_package(...... message_runtime)
  1. 编译生成语言相关文件

---上面的内容在PersonRequest里面,---下面的内容在PersonRespense里面,Person.h头文件包含这两个,所以在使用的时候,只用包含这一个就可以。

2.创建服务器代码

C++代码:

person_client.cpp

/**该例程将请求/show_person服务,服务数据类型learning_service::Person*/#include <ros/ros.h>#include "learning_service/Person.h"int main(int argc,char** argv){//初始化ROS节点ros::init(argc,argv,"person_client");//创建节点句柄ros::NodeHandle node;//发现/show_person服务后,创建一个服务客户端,连接名为/show_person的serviceros::service::waitForService("/show_person");ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");//初始化learning_service::Person的请求数据learning_service::Person srv;srv.request.name = "Aurora";srv.request.age = 18;srv.request.sex = learning_service::Person::Request::male;//请求服务调用,显示request发送的数据srvROS_INFO("Call service to show person[name:%s, age:%d, sex:%d]", srv.request.name.c_str(),srv.request.age,srv.request.sex);person_client.call(srv);//显示服务调用结果,即response这个返回值ROS_INFO("Show person result: %s",srv.response.result.c_str());return 0; }

person_server.cpp代码:

/**该例程将执行/show_person服务,服务数据类型learning_service::Person*/#include <ros/ros.h>
#include "learning_service/Person.h"//service回调函数,输入参数req,输出参数res
bool personCallback(learning_service::Person::Request &req,learning_service::Person::Response &res)
{//显示请求数据ROS_INFO("Person: name:%s, age:%d, sex:%d",req.name.c_str(),req.age,req.sex);//设置反馈函数res.result = "OK";return true;                                                                                                                                                                             
}        int main(int argc,char** argv)
{//ROS节点初始化ros::init(argc,argv,"person_server");//创建节点句柄ros::NodeHandle n;//创建一个名为/show_person的server,注册会调函数personCallback//数据传输的通道是/show_person或者说两个连接到/show_person节点相互通信ros::ServiceServer person_service = n.advertiseService("/show_person",personCallback);//循环等待回调函数ROS_INFO("Ready to show person information.");ros::spin();return 0;
}                        

实现步骤:

  • 初始化ROS节点
  • 创建Server实例
  • 循环等待服务请求,进入回调函数
  • 在回调函数中完成服务功能的处理,并反馈应答数据

(Person.srv定义的是一个数据类型)

CMakeLists.txt中添加

add_executable(person_server src/person_server.cpp)
target_link_libraries(person_server ${catkin_LIBRARIES})
add_dependencies(person_server ${PROJECT_NAME}_gencpp)add_executable(person_client src/person_client.cpp)
target_link_libraries(person_client ${catkin_LIBRARIES})
add_dependencies(person_client ${PROJECT_NAME}_gencpp)
//这通常用于确保在编译person_client之前,相关的代码生成步骤已经完成,生成了必要的源文件。
//可执行文件中肯定包含其它需要执行的文件,需要顺利执行可执行文件,这些源文件必不可少

配置规则:

  • 设置要编译的代码和生成可执行的文件
  • 设置链接库
  • 添加依赖项

服务模型:

client:用户操控部分,用户端,用户下达命令或请求的地方。

server:响应用户命令和请求,并作出相应显示的部分。

注意:运行多个程序时都需要开启rosccore,这时roscore中可能就会包含多个同名参数,所以最好每次运行新程序时,先关闭roscore再打开。

十三、参数的使用与编程方法

1.参数模型

Parameter Server是一个全局字典(参数服务器),用于保存各个节点间的参数。

2.创建功能包

cd ~/catkin_ws/src
catkin_create_pkg learning_parameter roscpp rospy std_srvs

3.参数命令行的使用

参数存放在roscore中,所以要先打开roscore才能用rosparam查看当前在roscore中的参数。

dump和load都是默认存储或读取到当前终端所在路径。

4.代码实现

C++代码:

parameter_config.cpp

/**该例程设置/读取海龟例程中的参数 */#include <string>
#include <ros/ros.h>
#include <std_srvs/Empty.h>int main(int argc ,char** argv)
{int red,green,blue;//ROS节点化ros::init(argc,argv,"parameter_config");//创建节点句柄,用于与ROS通信ros::NodeHandle node;//读取背景颜色参数ros::param::get("/background_r",red);ros::param::get("/background_g",green);ros::param::get("/background_b",blue);ROS_INFO("Get Background Color[%d %d %d]",red,green,blue);//设置背景颜色参数ros::param::set("/background_r",255);ros::param::set("/background_g",255);ros::param::set("/background_b",255);   ROS_INFO("Set Background Color[255 255 255]");//读取背景颜色参数ros::param::get("/background_r",red);ros::param::get("/background_g",green);ros::param::get("/background_b",blue);    ROS_INFO("R-Get Background Color[%d %d %d]",red,green,blue);                            //调用回调函数,刷新背景颜色//这里把client和service的代码整合到一起//service等待/clear,接收到以后会执行相应程序//clear_background这个客户端发送/clear这个命令,这个命令后面所跟的数据类型是std_srvs::Empty,即数据是空的ros::service::waitForService("/clear");//ros::ServiceClient用于创建客户端,node.serviceClient用于告诉ROS创建了客户端ros::ServiceClient clear_background = node.serviceClient<std_srvs::Empty>("/clear");//定义一个空数据std_srvs::Empty srv;//clear_background客户端发出命令clear,再发送空数据给serviceclear_background.call(srv);sleep(1);return 0;
}

ROS中客户端发送命令后往往会跟着需要发送到的数据,这里不需要发送数据,所以定义数据类型为空。

流程:

  • 初始化ROS节点
  • get函数获取参数
  • set函数设置参数

CMakeLists.txt中编译

add_executable(parameter_config src/parameter_config.cpp) 
target_link_libraries(parameter_config ${catkin_LIBRARIES})

十四、ROS中的坐标管理系统

1.机器人坐标变换例程

sudo apt-get install ros-自己ROS的版本(例如我的是noetic)-turtle-tf
roslaunch turtle_tf turtle_tf_demo.launch
rosrun turtlesim turtle_teleop_key
rosrun tf view_frames

运行第一行代码时不知道自己的版本号,可以用下面代码查询:

rosversion -d

运行第二行代码可能会遇到以下问题:

这是缺少功能包,输入以下指令安装即可:

sudo apt install python-is-python3

正常显示结果:

运行第三行代码时可能会有以下报错:

直接输入:

sudo gedit /opt/ros/noetic/lib/tf/view_frames /opt/ros/noetic/lib/tf/view_frames是上面报错的路径。

等待这个文件打开,在88行如下图所示输入:

成功结果输出如下:

运行成功后在根目录下会有 以下两个文件:

注意:我这里是在根目录下运行的这行代码,所以默认存放到根目录下。

生成文件:

world是你这个海龟界面的基本坐标系,以左下角为原点。

turtle1:是第一只海龟的坐标。

turtel2:是第二只海龟的坐标。

打开三维坐标图:(先运行roscore)

rosrun rviz rviz -d `rospack find turtle_tf` /rviz/turtle_rviz.rviz

打不开先安装一下:

sudo apt-get install ros-noetic-rviz

2.tf坐标系广播与监听的编程实现

创建功能包

cd ~/catkin_ws/src 
catkin_create_pkg learning_tf roscpp rospy tf turtlesim

如何实现一个tf广播器:

  • 定义TF广播器
  • 创建坐标变换值
  • 发布坐标变换

C++代码:

/**该例程产生tf数据,并计算、发布turtle2的速度指令*/#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>std::string turtle_name;void poseCallback(const turtlesim::PoseConstPtr& msg)
{//创建tf的广播器static tf::TransformBroadcaster br;//初始化tf数据tf::Transform transform;transform.setOrigin(tf::Vector3(msg->x,msg->y,0.0));tf::Quaternion q;q.setRPY(0,0,msg->theta);transform.setRotation(q);//广播world与海龟坐标系之间的tf数据br.sendTransform(tf::StampedTransform(transform,ros::Time::now(),"world",turtle_name));               
}int main(int argc,char** argv)
{//初始化ROS节点ros::init(argc,argv,"my_tf_broadcaster");//输入参数作为海龟的名字if(argc != 2){ROS_ERROR("need turtle name as argument");return -1;    }turtle_name = argv[1];//订阅海龟的位置话题ros::NodeHandle node;ros::Subscriber sub = node.subscribe(turtle_name+"/pose",10,&poseCallback);//循环等待回调函数ros::spin();return 0;
};

如何实现一个TF监听器:

  • 定义TF监听器
  • 查找坐标变换
/***该例程监听tf数据,并计算、发布turtle2的速度指令*/#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节点ros::init(argc,argv,"my_tf_listener");//创建节点句柄ros::NodeHandle node;//请求产生turtle2ros::service::waitForService("/spawn");ros::ServiceClient add_turtle = node.serviceClient<turtlesim::Spawn>("/spawn");turtlesim::Spawn srv;add_turtle.call(srv);//创建发布turtle2速度控制指令的发布者ros::Publisher turtle_vel = node.advertise<geometry_msgs::Twist>("/turtle2/cmd_vel",10);//创建tf的监听器tf::TransformListener listener;ros::Rate rate(10.0);while(node.ok()){//获取turtle1与turtle2坐标系之间的tf数据tf::StampedTransform transform;try{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();continue;       }//根据turtle1与turtle2坐标系之间的位置关系,发布turtle2的速度指令geometry_msgs::Twist vel_msg;vel_msg.angular.z=4.0*atan2(transform.getOrigin().y(),transform.getOrigin().x());vel_msg.linear.x=0.5*sqrt(pow(transform.getOrigin().x(),2)+pow(transform.getOrigin().y(),2));                                    turtle_vel.publish(vel_msg);rate.sleep();    }return 0;
};

配置CMakeLists.txt中的编译规则。

add_executable(turtle_tf_broadcaster src/turtle_tf_broadcaster.cpp)
target_link_libraries(turtle_tf_broadcaster ${catkin_LIBRARIES})add_executable(turtle_tf_listener src/turtle_tf_listener.cpp)
target_link_libraries(turtle_tf_listener ${catkin_LIBRARIES})

编译并运行:

这里用到了节点名重映射,因为一个ROS中的节点名是不能重复的,但是同一个程序要针对两只乌龟运行两次,那么这样就会导致出现对同一个节点名操作两次的结果,所以要用重映射分别来处理,而不是对一个来处理。

十五、launch文件的使用方法

1.launch文件

通过XML文件实现多节点的配置和启动(可自动启动ROS Master)

node代表一个ROS节点,而用一个launch就可以启动所有的节点,不需要单一的一个一个去分别启动。

2.核心语法

:launch文件中的根元素采用标签定义。

:表示配置结束。

:启动节点。

<node pkg="package-name" type="executable-name" name="node-name"/>
pkg:节点所在的功能包的名称
type:节点的可执行文件名称
name:节点运行时的名称
output、respawn、required、ns、args

如上图,对于同一个执行文件,在执行的时候需要针对两个不同对象执行同一个程序,这时候如果不更改节点名,它就会对同一个节点操作,那么最终结果也只有第一个对象改变。所以需要初始化两个节点名,这样虽然执行的程序是同一个,但是执行的对象有两个,互不冲突。

param:保存一个参数的名称和值。

rosparam:保存一个参数文件到ROS服务器里面。

arg:在launch内部定义一个变量,这个变量仅限于在launch内部使用。

通过上述最下面两行代码,就可以调用arg定义的局部变量。

重映射:利用remap把ROS中的计算图资源重命名。

注意:重映射后,原来的名字就没有了。

嵌套:可以将一个launch文件分成不同的文件,在同样一个文件中包含就可以了。

simple.launch文件

<launch><node pkg="learning_topic" type="person_subscriber" name="talker" output="screen"/><node pkg="learning_topic" type="person_publisher" name="listener" output="screen"/>
</launch>//output是输出到哪
//节点是可执行文件,节点名称是name里面的,虽然名字改变了,但是这个节点执行的还是同一个文件,完成相同操作

roslaunch learning_launch simple.launch

运行launch文件。

turtlesim_parameter_config.launch文件

<launch><param name="/turtle_number" value="2"/><node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node">//这里缩进了,并且写到了node里面,这是在这个节点里面配置参数<param name="turtle_name1" value="Tom"/><param name="turtle_name2" value="Jerry"/><rosparam file="$(find learning_launch)/config/param.yaml" command="load"/>//$(find learning_launch)/config/param.yaml是找到learning_launch功能包,找到里面的param.yaml文件</node><node pkg="turtlesim" type="turtle_teleop_key" name="turtle_teleop_key" output="screen"/></launch>

param.yaml文件

A: 123
B: "hello"group:C: 456D: "hello"

注意:C和D之前不能用制表符tab,必须用空格键。否则会出错。

/turtlesim_node/turtle_name2的前缀代表这个是turtlesim_node这个节点的参数。

start_tf_demo_c++.launch文件

<launch><!-- Turtlesim Node--><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"/>//args是配置启动文件里面的参数<node pkg="learning_tf" type="turtle_tf_listener" name="listener"/></launch>

turtlesim_remap.launch文件

<launch><include file="$(find learning_launch)/launch/simple.launch"/><node pkg="turtlesim" type="turtlesim_node" name="turtlesim_node"><remap from="/turtle1/cmd_vel" to="/cmd_vel"/></node>
</launch>//<node></node>分别代表节点定义的开始和结束,如果要更改其它参数,则第一个《node》后面不用加/,在《/node》这里的/会自动完成定义

十六、常用可视化工具的使用

rqt_console:用于日志的接收。

(上面接收到的日志是因为海龟撞到墙而发出的)

rqt_plot:用于对于节点参数的读取。

(这里是对海龟速度的一个读取。)

rqt_image_view:摄像头图像的显示。

rqt:所有插件的一个集合,可以在这里面使用其它插件。

plugins里面是其它rqt插件。

Rviz:机器人开发的可视化平台。

Gazebo:一款功能强大的三维物理仿真平台。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/461073.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode-62-不同路径

题解&#xff1a; 1、设dp[i][j]代表到达(i,j)点最多的路径&#xff1b;题目要求机器人每次只能向右或向下走一步&#xff0c;所以到达(i,j)点的最多路径为到达(i-1,j)的最多路径与到达(i,j-1)的最多路径之和。即dp[i][j]dp[i-1][j]dp[i][j-1]。 2、初始化一个M*N的矩阵dp,将…

【问题解决】pnpm : 无法将“pnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。

今天配置完poetry环境变量之后pnpm不能用了 具体报错 pnpm : 无法将“pnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。 所在位置 行:1 字符: 1pnpm run dev~~~~ Ca…

【设计模式】如何用C++实现依赖倒置

【设计模式】如何用C实现依赖倒置 一、什么是依赖倒置&#xff1f; 依赖倒置原则&#xff08;Dependency Inversion Principle&#xff0c;DIP&#xff09;是SOLID面向对象设计原则中的一项。它的核心思想是&#xff1a; 高层模块不应该依赖于低层模块&#xff0c;两者都应该…

达梦数据迁移工具DTS使用实践

1、环境描述 2、DTS概述 1.支持视图、存储过程/函数、包、类、同义词、触发器等对象迁移&#xff1b; 2.支持数据类型的自动映射&#xff0c;编码转换&#xff1b; 3.支持根据条件自定义迁移部分数据&#xff1b; 4.向导式迁移步骤&#xff0c;上手简单&#xff1b; 5.支持 we…

瑞格智慧心理服务平台 NPreenSMSList.asmx sql注入漏洞复现

0x01 产品描述&#xff1a; ‌ 瑞格智慧心理服务平台‌是一个集心理测评、心理咨询、心理危机干预、心理放松训练等功能于一体的综合性心理健康服务平台。该平台由北京瑞格心灵科技有限公司开发&#xff0c;旨在为用户提供全方位的心理健康服务。0x02 漏洞描述&#xff1a;…

【经典论文阅读11】ESMM模型——基于贝叶斯公式的CVR预估

传统的CVR模型&#xff08;也就是直接对conversion rate建模的模型&#xff09;在实际应用中面临两个问题&#xff08;样本选择偏差与数据稀疏性问题&#xff09;。为了解决这两个问题&#xff0c;本文提出ESMM模型。该模型巧妙地利用用户行为序列去建模这个问题&#xff0c;从…

OpenCV基础01

目录 一、环境安装 二、显示窗口 三、创建图片 四、图片保存 五、图像裁剪 六、调整图片大小 七、图像绘制 1、绘制圆形 2、绘制矩形 3、绘制文本 4、绘制直线 5、中文文本 八、控制鼠标 九、鼠标事件 十、视频处理 OpenCV作为C和C语言的源代码文件&#xff0c;…

git:将多个提交合并为一个

如何将第一至第五次提交合并为一个&#xff1f; 1. 使用 git log -n 命令查看spring boot admin的commit-id&#xff0c;本例n6&#xff0c;命令如下&#xff1a; PS E:\liguogang\spring-cloud> git log -62. 使用 git reset --soft commit-id 命令将前五次提交重置到工作…

Leetcode 二叉树中的最大路径和

算法思想 这道题要求在一棵二叉树中找到路径和最大的路径。路径可以从树中任意一个节点开始&#xff0c;到任意一个节点结束&#xff0c;但路径上的节点必须是连续的。 算法使用递归的方式来遍历树中的每个节点&#xff0c;并在遍历过程中计算包含当前节点的最大路径和。具体…

Python 变量在函数中的作用域

什么是局部变量&#xff1f; 作用范围在函数内部&#xff0c;在函数外部无法使用 什么是全局变量&#xff1f; 在函数内部和外部均可使用 如何将函数内定义的变量声明为全局变量&#xff1f; 使用global关键字&#xff0c; global变量 练习&#xff1a; 演示局部变量 #…

【Android】Kotlin教程(4)

文章目录 1.field2.计算属性3.主构造函数4.次构造函数5.默认参数6.初始化块7.初始化顺序7.延迟初始化lateinit8.惰性初始化 1.field field 关键字通常与属性的自定义 getter 和 setter 一起使用。当你需要为一个属性提供自定义的行为时&#xff0c;可以使用 field 来访问或设置…

SSH登录介绍

说明&#xff1a;一般登录服务器&#xff0c;我们可以用远程连接工具&#xff0c;如XShell、Windterm等&#xff0c;或者通过公司搭建的JumpServer&#xff08;跳板机、堡垒机&#xff09;来连接。前者是点对点登录&#xff0c;输入主机、端口&#xff0c;通过SSH协议登录&…

unity中预制体的移动-旋转-放缩

unity中预制体的移动-旋转-放缩 左上侧竖栏图标介绍Tools(手形工具)Move Tool(移动工具&#xff0c;单位米)Rotate Tool(旋转工具&#xff0c;单位角度)Scale Tool(缩放工具&#xff0c;单位倍数)Rect Tool(矩形工具)Transform Tool(变换工具)图标快捷键对照表工具使用的小技巧…

HarmonyOS开发 - 本地持久化之实现LocalStorage实例

用户首选项为应用提供Key-Value键值型的数据处理能力&#xff0c;支持应用持久化轻量级数据&#xff0c;并对其修改和查询。数据存储形式为键值对&#xff0c;键的类型为字符串型&#xff0c;值的存储数据类型包括数字型、字符型、布尔型以及这3种类型的数组类型。 说明&#x…

java程序打包为一个exe程序

ok&#xff0c;最近学到了一个有意思的东西 那就是如何将自己写好的java程序打包成一个exe程序&#xff0c;发给别人&#xff0c;然后运行。 那么开始之前&#xff0c;请先安装整个工具&#xff1a; exe4j&#xff1a;https://www.ej-technologies.com/exe4j/download&#…

高并发设计模式之ForkJoin模式

分而治之是一种思想,所谓分而治之就是把一个复杂的算法问题按一定的分解方法分为规模较小的若干部分,然后逐个解决,分别找出各部分的解,最后把各部分的解在整合成整个问题的解.ForkJoin模式就是分而治之思想的另一种应用. ForkJoin模式的原理 ForkJoin模式先把一个大任务分解…

AMD XILINX 20nm器件价格上调25%

随着市场回暖&#xff0c;台积电也在调整价格策略&#xff0c;近期台积电上调了20nm的出厂价格。 据相关消息显示&#xff0c;AMD为了保障持续的供货和服务&#xff0c;也计划将20nm器件的价格统一上调25%&#xff0c;预计将于11月发布正式的涨价通知&#xff0c;并于2025年Q1开…

EfficientNet-B6模型实现ISIC皮肤镜图像数据集分类

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 《------往期经典推荐------》 项目名称 1.【基于opencv答题卡识别判卷】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【G…

为何我们要将测试左移?回到过去的美好时光

以下为作者观点&#xff1a; 为何我们将测试左移&#xff1f;在传统的开发周期中&#xff0c;测试通常在功能完成后甚至在开发阶段结束时进行。左移测试通过从开发过程开始到整个开发过程整合测试活动来挑战这一点。 让我们首先讨论一下为什么我们选择“左移”&#xff0c;因…

java项目之基于智能推荐的卫生健康系统(springboot)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的基于智能推荐的卫生健康系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于智能推荐…