ROS
1. ROS测试
启动ros master 打开一个终端roscore
启动小海龟仿真器 新打开一个终端rosrun turtlesim turtlesim_node
启动海龟控制节点 新打开一个终端rosrun turtlesim turtle_teleop_key
2. ROS是什么
一套为机器人所做的操作系统=通信机制+开发工具+应用功能+生态系统
通信机制:松耦合分布式通信
开发工具:命令行、TF坐标变换、Rviz、Qt工具箱、Gazebo
应用功能:Navigation、SLAM、Moveit
生态系统:发行版、软件源、ROS wiki、邮件列表、ROS Answers、博客
3. Ros中的核心概念
节点(Node)——执行单元
- 执行具体任务的进程、独立运行的可执行文件
- 不同节点可使用不同的编程语言,可分布式运行在不同的主机
- 节点在系统中的名称必须是唯一的
节点管理器(ROS Master)——控制中心
- 为节点提供命名和注册服务
- 跟踪和记录话题/服务通信,辅助节点相互查找、建立连接
- 提供参数服务器,节点使用此服务器存储和检索运行时的参数
话题(Topic)——异步通信机制
- 节点间用来传输数据的重要总线
- 使用发布/订阅模型,数据由发布者传输到订阅者,同一个话题的订阅者或发布者可以不唯一
消息(Message)——话题数据
- 具有一定的类型和数据结构,包括ROS提供的标准类型和用户自定义类型
- 使用编程语言无关的.msg文件定义,编译过程中生成对应的代码文件
服务(Service)——同步通信机制
- 使用客户端/服务器(C./S)模型,可无端发送请求数据,服务器完成处理后返回应答数据
- 使用编程语言无关的.srv文件定义请求和应答数据结构,编译过程中生成对应的代码文件
| 话题 | 服务 |
---|
同步性 | 异步 | 同步 |
通信模型 | 发布/订阅 | 服务端/客户端 |
底层协议 | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
反馈机制 | 无 | 有 |
缓冲区 | 有 | 无 |
实时性 | 弱 | 强 |
节点关系 | 多对多 | 一对多 |
适用场景 | 数据传输 | 逻辑处理 |
参数(Parameter)——全局共享字典
- 可通过网络访问的共享、多变量字典
- 节点使用此服务器来存储和检索运行时的参数
- 适合存储静态、非二进制的配置参数,不适合存储动态配置的数据
功能包(Package)
- ROS软件中的基本单元,包含节点源码、配置文件、数据定义等
功能包清单(Package manifest)
- 记录功能包的基本信息,包含作者信息、许可信息、依赖选项、编译标志等
元功能包(Meta Packages)
4. ROS命令行工具的使用
4.1 常用命令
- rostopic
- rosservice
- rosnode
- rosparam
- rosmsg
- rossrv
- rosrun 功能包 功能包节点
4.2 rqt_graph
4.3 rosnode
rosnode list 把系统所有节点显示出来
rosnode info /turtlesim 答打印节点信息
4.4 rostopic(两下Tab补全)
rostopic list 系统的所有话题
rostopic pub -r 10 (发布频率) /turtle1/cmd_vel(话题名) XXXXX(发送数据内容) 可以发布的内容
4.5 rosmsg
rosmsg show 消息
4.6 rosservice
rosservice list
rosservice call /spawn (可以再开一个服务)
4.7 rosbag
应用场景:保存一次飞行记录,然后复现
话题记录rosbag record -a -O cmd_record
话题浮现rosbag play cmd_record.bag
5. 创建工作空间与功能包
5.1 工作空间(workspace)
工作空间是一个存放工程开发相关的文件夹
- src:代码空间
- build:编译空间
- devel:开发空间
- Install:安装空间
创建工作空间
mkdir -p ./catkin_ws/src
cd ./catkin_ws/src
catkin_init_workspace
编译工作空间
cd ./catkin_ws/
catkin_make
catkin_install
设置环境变量
source devel/setup.bash
检查环境变量
echo $ROS_PACKAGE_PATH
5.2 功能包
catkin_create_pkg<package_name>[depend1][depend2][depend3](库)
创建功能包
cd ./catkin_ws/src
catkin_create_pkg test_pkg msgs rospy roscpp
编译功能包
cd ./catkin_ws
catkin_make
source ./catkin_ws/devel/setup.bash
6. 发布者Publicsher的编程实现
1.创建功能包
cd ./catkin_ws/src
catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim
2.在src下写cpp
如何实现一个发布者
- 初始化ROS节点
- 向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型
- 创建消息数据
- 按照一定频率循环发布消息
文件名 velocity_publisher.cpp
/**
*该例程将发布turtle1/cmd_vel话题,消息类型为geometry_msgs::Twist
*/
#include <ros/ros.h>
#include <geometry/Twist.h>
int main(int argc, char **argv)
{
//ROS节点初始化
ros::init(argc,argv,"velocity_publisher");
//创建节点句柄
ros::NodeHandle n;
//创建一个Publisher,发布名为/turtle/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度为10
ros::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类型的消息
gepmetry_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[%o.2f m/s,%0.2 rad/s]",vel_msg.linear.x,vel_msg.angual.z);
//按照循环频率延时
loop_rate.sleep();
}
return 0;
}
3.配置CMaklelists.txt中的编译规则
放在Build一栏
add_executable(velocity_publisher src/velocity_publisher.cpp)
target_link_libraries(velocity_publisher${catkin_LIBRARIES})
4.编译并运行发布者
cd ./catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_topic velocity_publisher
7. 订阅者Subscriber的编程实现
1.上一讲的learning_topic功能包
2.在src下写cpp
如何实现一个订阅者
- 初始化ROS节点
- 订阅需要的话题
- 循环等待话题消息,接收到消息后进入回调函数
- 在会点函数中完成消息处理
文件名 pose_subscriber.cpp
/**
*该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
*/
#include <ros/ros.h>
#include <turtlesim/Pos.h>
void poseCallback(const turtlesim::Pose::ConstPtr& msg)
{
ROS_INFO("Turtle pose: x:%0.2f , y:%0.2f",msg->x,msg->y);
}
int main(int argc, char **argv)
{
//初始化ROS节点
ros::init(argc,argv,"pose_subscriber");
//创建句柄
rose::NodeHandle n;
//创建一个Subscriber,订阅名为turtle1/pose的topic,注册回调函数poseCallback
rose::Subscriber pos_pub=n.subscribe("/turtle1/pose",10,poseCallback);
//循环等待回调函数
ros::spin();
return 0;
}
3.配置CMaklelists.txt中的编译规则
add_executable(pose_subcriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber${catkin_LIBRARIES})
4.编译并运行订阅者
cd ./catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun turtlesim turtlesim_node
rosrun learning_topic velocity_publisher
rosrun learning_topic pose_subcriber
8. 话题消息的定义与使用
上面两个都是源码文件中有的消息类型,这节主要是自定义数据类型
1.定义msg文件
在learning topic中创建msg文件夹,所有msg都放在这里
在其中touch Person.msg
string name
uint8 sex
uint8 sex
uint unknown=0
uint male=1
uint female=2
2.在package.xml中添加功能包依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
3.CMakeLists.txt添加编译选项
find_package(...... ){
xxxxx
xxxxx
message_generation
}
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std msgs)
catkin package(){
message runtime
}
4.测试
person_publisher.cpp
/**
*该例程将发布/Person_info话题,消息类型为learning_topic::Person
*/
#include <ros/ros.h>
#include <learning_topic/Person.h>
int main(int argc, char **argv)
{
//ROS节点初始化
ros::init(argc,argv,"person_publisher");
//创建节点句柄
ros::NodeHandle n;
//创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度为10
ros::Publisher turtle_vel_pub=n.advertise<learning_topic::Person>("/turtle1/cmd_vel",10);
//设置循环频率
ros::Rate loop_rate(1);
int count=0;
while(ros::ok())
{
//初始化learning_topic::Person类型的消息
learning_topic::Person person_msg;
person_msg.name="tina";
person_msg.age=18;
person_msg.sex= learning_topic::Person::male;
//发布消息
turtle_vel_pub.publish(person_msg);
ROS_INFO("Publish Person_Info:name:%s age:%d sex:%d",person_msg.name,person_msg.age,person_msg.sex);
//按照循环频率延时
loop_rate.sleep();
}
return 0;
}
person_subscriber.cpp
/**
*该例程将订阅/person_info话题,消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include <learning_topic/Person.h>
void personCallback(const learning_topic::Person::ConstPtr& msg)
{
ROS_INFO("Subcriber person_info::name:%s age:%d sex:%d",msg->name,msg->age,msg->sex);
}
int main(int argc, char **argv)
{
//初始化ROS节点
ros::init(argc,argv,"person_subscriber");
//创建句柄
rose::NodeHandle n;
//创建一个Subscriber,订阅名为person_info的topic,注册回调函数personCallback
rose::Subscriber person_info_pub=n.subscribe("/person_info",10,personCallback);
//循环等待回调函数
ros::spin();
return 0;
}
5.编译并执行
配置CMaklelists.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)
add_executable(person_subscriber src/person_subscriber.cpp)
target_link_libraries(person_subscriber${catkin_LIBRARIES})
add_dependencies(person_subscriber${PROJECT_NAME}_generate_messages_cpp)
cd ./catkin_ws
catkin_make
source devel/setup.bash
roscore
rosrun learning_topic person_publisher
rosrun learning_topic person_subscriber
6.关闭rosmaster仍能运行,因为已经帮助建立连接了
9. 客户端Client的编程实现
1.创建功能包
cd ./catkin_ws/src
catkin_creat_pkg learning_service roscpp rospy std_msgs geometry_msgs turtlesim
2.在learning_service/src下实现一个客户端
- 初始化ROS节点
- 创建一个Client实例
- 发布服务请求数据
- 等待Server处理后的应答结果
turtle_spawn.cpp
/**
*该例程将请求/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的service
ros::service::waitForservice("/spawn");
ros::serviceClient add_turtle=node.serviceClient<turtles::Spaen>("/spawn");
//初始化turtlesim::Spawn的service
turtlesim::Spawn srv;
srv.request.x=2.0;
srv.request.y=2.0;
srv.request.name="turtle2";
//请求服务调用
ROS_INFO("Call service to spawn 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("Spawn turtle successfully[name:%s]".srv.respomse.name.c_str());
return 0;
}
3.cmakelist.txt
add_executable(turtle_spawn src/turtle_spawn.cpp.cpp)
target_link_libraries(turtle_spawn${catkin_LIBRARIES})
10.服务端Server的编程实现
2.在learning_service/src下实现一个客户端
- 初始化ROS节点
- 创建一个Server实例
- 循环等待服务请求,进入回调函数
- 在回调函数中完成服务功能的处理,并反馈应答数据
turtle_command_server.cpp
/**
*该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger
*/
#include <ros/ros.h>
#include <geometry/Twist.h>
#include <std_srvs/Trigger.h>
ros::Publisher turtle_vel_pub
bool pubCommand =fasle;
//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=ture?"Yes":"No");
//设置反馈数据
res.sucess=true;
res.message="change turtle command state!";
return ture;
}
int main(int argc,char **argv)
{
//ROS节点初始化
ros::init(argc,argv,"turtle_command_server");
//创建节点句柄
ros::NodeHandle n;
//创建一个名为/turtle_command的server,注册回调函数commandCallback
ros::ServiceServer command_service = n.advertiseService("/turtle_command",commandCallback);
//创建一个Publicsher,发布名为/turtle1/cmd_vel,消息类型为geometry_msgs::Twist,队列长度10
turtle_vel_pub=n.advertise<gemotry_msgs::Twist>("/turtle1/cmd_vel",10);
//循环等待回调函数
ROS_INFO("Ready to receive turtle command");
//设置循环的频率
ros::Rate loop_rate(10);
while(ros::ok())
{
//查看一次回调函数队列
ros::spinOnce();
//如果标志为ture,则发布速度指令
if(pubCommand)
{
geometry_msgs::Twist vel_msg;
vel_msg.liner.x=0.5;
vel_msg.angular.z=0.2;
turtle_vel_pub.publish(vel_msg);
}
//按照循环频率延时
loop_rate.sleep();
}
}
3.cmakelist.txt
加到上节那个
add_executable(turtle_command_server src/turtle_command_server.cpp)
target_link_libraries(turtle_command_server${catkin_LIBRARIES})
11. 服务数据的定义与使用
1.定义src文件
在src中创建文件夹srv
Person.srv
string name
uint8 age
uint8 sex
uint8 unknown=0
uint8 male=1
uint8 female=2
---
string result
2.在package.xml中添加功能包依赖
<build_depend>message_generation<build_depend>
<exec_depend>message_runtime</exec_denpend>
3.在cmakelists.txt
find_package
{
....
message_generation
}
add_service_files(FILES Person.srv)
generate_messages(DEPENDENCIES std_msgs)
catkin_package(.... message_runtime)
4.client与server
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;
//发现/spawn服务后,创建一个服务客户端,连接为/show_spawn的service
ros::service::waitForservice("/show_spawn");
ros::serviceClient person_client=node.serviceClient<learning_service::Person>("/show_spawn");
//初始化learning_service::Person的请求数据
learning_service::Person srv;
srv.request.name="TOM";
srv.request.age=28;
srv.request.sex=learning_service::Person::Request::male;
//请求服务调用
ROS_INFO("Call service to show_spawn [name:%s,age:%d,sex:%d]",srv.request.name,srv.request.age,rv.request.sex);
person_client.call(srv);
//显示的是服务调用结果
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 ture;
}
int main(int argc,char **argv)
{
//ROS节点初始化
ros::init(argc,argv,"person_server");
//创建节点句柄
ros::NodeHandle n;
//创建一个名为/show_person的server,注册回调函数personCallback
ros::ServiceServer person_service = n.advertiseService("/show_person",personCallback);
//循环等待回调函数
ROS_INFO("Ready to show person information");
ros::spin();
return 0;
}
5.cmakelist.txt
12. 参数的使用与编程方法
1.创建功能包
cd ./catkin_ws/src
catkin_create_pkg learning_parameter roscpp rospy std_srvs
2.rosparam
-
rosparam list
列出当前多少参数
-
rosparam get param_key
显示某个参数值
-
rosparam set param_key param_value
保存参数到文件
-
rosparam dump file_name.yaml
从文件读取参数
-
rosparam load file_name
删除参数
-
rosparam delete param_key
3.程序实现
13.ROS中的坐标系管理系统
TF功能包能干什么?
- 五秒钟之前,机器人的头部坐标系相对于全局坐标系的关系事怎样的?
- 机器人夹取的物体相对于机器人中心坐标系的位置在哪里?
- 机器人中心坐标系相对于全局坐标系的位置在哪里
TF坐标变换如何实现?
机器人中的坐标变换
sudo apt_get install ros-melodic-turtle-tf
roslaunch turtle_tf turtle_tf_demo.lauch
rosrun turtlesim turtle_teleop_key
rosrun tf view_frames
14. tf坐标系广播与监听的编程实现
1.创建功能包
cd ./catkin_ws/src
catkin_create_pkg learning_tf roscpp rospy tf_turtlesim
2.程序tf广播实现
- 定义TF广播器(TransformBroadcaster)
- 创建坐标变换值
- 发布坐标变换(sendTransform)
- turtle_tf_broadcaster.cpp
/**
*该例程产生tf数据,并计算、发布Turtle2的速度指令
*/
#include <ros/ros.h>
#include <tf/transform_broadcaster.h>
#include <turtlesim/Pose.h>
std::string turtle_name;
void poseCallback(const turtlesim::posConstPtr& msg)
{
//创建tf的广播器
static tf::TransformBroadcaster br;
//初始化tf数据
tf::Tramsform transform;
//tf中tramsform就保存了trtule相对于world的平面坐标平移关系
transform.setOrigin(tf::vector3(msg->x,msg->y,0.0));
tf::Quaternion q;
//tf中Quaternion就保存了Trtule相对于world的旋转关系
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+"/pos",10,&poseCallback);
ros::spin();
return 0;
}
3.如何实现一个TF监听器
/**
*该例程监听tf数据,并计算、发布turtle2的速度指令
*/
#include <ros/ros.h>
#include <tf/tramsform_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;
//请求产生turtle2
ros::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;
tf::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.liner.x=0.5*sqrt(pow(transform.getOrigin().x(),2)+pow(transform.getOrigin().y(),2));
turtle_vel.publish(vel_msg);
rate.sleep();
}
return 0;
}
4.cmakelist.txt
5.编译运行
cd ./catkin_ws
catkin_make
source devel/setup.bash
rescore
rosrun turtlesim turtlesim_node
rosrun learning_tf turtle_tf_broadcaster__name:=turtbroadcaster/turtle1
rosrun learning_tf turtle_tf_broadcaster__name:=turtbroadcaster/turtle2
rosrun learning_tf turtle_tf_listener
rosrun turtlesim turtle_teleop_key
15.launch启动文件的使用方法
launch文件:通过XML文件实现多节点的配置和启动(可自动启动ROS Master)
15.1 launch文件语法
<launch>
<node pkg="turtlesim" name="sim1" type="turtlesim_node"/>
<node pkg="turtlesim" name="sim2" type="turtlesim_node"/>
<launch>
//<launch> launch文件中的根元素采用<launch>标签定义
//<node>启动节点
//<node pkg="package_name" name="executable_name" type="node_name"/>
pkg:节点所在的功能包的名称
type:节点的可执行文件名称
name:节点运行时的名称 //这个会取代ros::init时的命的名字
output(是否打印到当前终端),respawn(重启),required,ns,args
<param name="output_frame" value="odom"/>
//<param>/<rosparam>设置ROS系统运行中的参数,存储在参数服务器中
name:参数名字
value:参数值
加载参数文件中的多个参数
<rosparam file="params.yaml" command="load" ns="params"/>
<arg name="arg_name" default="arg_value"/>
//<arg>Launch文件内部的局部变量,仅限于launch文件使用
name:参数名
value:参数值
调用:
<param name="foo" value="$(arg arg-name)"/>
<node name="node" pkg="package" type="type" args="$(arg arg_name)"/>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)