参考古月居胡春旭ROS入门21讲视频等内容,跟着练习整理
ROS(Robot Operating System)是一个适用于机器人的开源的元操作系统。是一种新的标准化机器人软件框架,它提供了操作系统应有的服务,为机器人开发提供包括硬件抽象,底层驱动,消息传递,程序管理,应用原型等功能机制,并整合了许多第三方工具和库文件。ROS被设计为一种分布式结构,框架中每个功能模块都可以被单独设计、编译,并且运行时以松散耦合方式结合在一起,它也提供用于获取、编译、编写、和跨计算机运行代码所需的工具和库函数。
工作空间(workspace);是ROS存放工程开发相关文件的文件夹,工作空间中一般包括四个目录空间:src(代码空间):最常用的文件夹,用来储存所有ROS功能包的源码文件;build(编译空间):储存编译过程中的缓存信息和中间文件;devel(开发空间):放置编译生成的可执行文件;install(安装空间):编译成功后使用make install执行安装文件到该空间,运行该空间中的环境变量脚本,在终端运行这些可执行文件。
功能包(package):是ROS应用程序代码的组织单元,每个软件包都可以包含程序库、节点、可执行文件、脚本或者其它手动创建的东西。 创建了相应的软件包才能实现相应的功能。功能包中通常可包含如下文件夹:include(放置头文件)scripts(放置可直接运行的python文件)src(放置需要编译的C++文件)launch(放置启动文件)msg(放置自定义的消息类型)srv(放置自定义的服务类型)action(放置自定义的动作指令)CMakeLists.txt(编译器编译功能包的规则)package.xml(功能包清单:包括功能包名称,版本号,信息描述,作者信息,许可信息等,以及功能包中代码编译所依赖的其他功能包,运行功能包中可执行程序所依赖的其他功能包)
节点(Node):是 ROS 功能包中的一个可执行文件,一个系统通常由多个节点构成,节点之间可以通过 ROS 客户端库(如roscpp 、 rospy )相互通信。一个机器人控制系统由许多节点组成,这些节点各司其职,如,一个节点控制激光距离传感器,一个节点控制轮子的马达,一个节点执行定位,一个节点执行路径规划,一个节点提供系统的整个视图等。也就是能够执行特定工作任务的工作单元。
节点管理器(Master):顾名思义,使所有节点有条不紊执行的模块,节点通过与节点管理器通信来报告他们的注册信息,帮助节点之间相互查找,建立连接,同时还为系统提供参数服务器,管理全局参数,ROS master作为管理者,必不可少。
消息(Message):是一种ROS数据类型。节点之间做重要的通讯机制就是基于发布/订阅模型的消息(Message)通讯,一个节点向特定主题发布消息,从而将数据发送到另一个节点。消息具有一定的类型和数据结构,包括ROS提供的标准类型和用户自定义类型。消息与话题(发布 / 订阅模式)是紧密联系在一起的,msg文件实质上就是一个从结构上描述ROS中所使用消息类型的简单文本,描述的是消息的数据类型。
话题( Topic ): 是节点间用来传输数据的总线,通过主题进行消息路由不需要节点之间直接连接。节点可以发布消息到话题,也可以订阅话题以接收消息。一个话题可能对应有许多节点作为话题发布者和话题订阅者。当然,一个节点可以发布和订阅许多话题。一个节点对某一类型的数据感兴趣,它只需订阅相关话题即可。一般来说。话题发布者和话题订阅者不知道对方的存在。发布者将信息发布在一个全局的工作区内,当订阅者发现该信息是它所订阅的,就可以接收到这个信息。
服务: 当需要直接与节点通信并获得应答时,将无法通过话题来实现,而需要服务(Service)。可以看出,发布 / 订阅模式是一种多对多的传输方式,显然这种传输方式不适用于请求 / 回复的交互方式。请求 / 回复交互方式经常被用于分布式系统中。请求服务通过 Service 来进行,Service 被定义为一对消息结构:一个用于请求,一个用于回复。一个客户通过发送请求信息并等待响应来使用服务( ROS中的请求 / 回复交互方式类似于一个远程函数调用,srv文件类似于函数参数和返回值的定义)。
参数管理器( Parameter Server ): 是节点管理器的一部分,是一个可通过网络访问的共享的多变量字典。节点使用此服务器来存储和检索运行时参数。
消息记录包(Bag): 是一种用于保存和回放 ROS 消息数据的格式。消息记录包是检索传感器数据的重要机制,这些数据虽然很难收集,但是对于发展和测试算法很有必要。
清单(Manifest): 是对于功能包相关信息的描述,用于定义软件包相关元信息之间的依赖关系,这些信息包括版本、维护者和许可协议等。 也就是 package.xml文件。
脚本文件: CMakeLists.txt是一个Cmake的脚本文件,Cmake是一个符合工业标准的跨平台编译系统。这个文件包含了一系列的编译指令,包括应该生成哪种可执行文件,需要哪些源文件,以及在哪里可以找到所需的头文件和链接库。
ROS中有四种通讯方式:Topic(话题)、Service(服务)、Parameter Serivce(参数服务器)、Actionlib(动作库)。
roscore //开启rosmaster
catkin_create_pkg //创建功能包
rospack //获取功能包信息
rospack find [包名称] //找到一个软件包的目录
rospack list //显示出当前的包信息
rospack depends1 beginner_tutorials //显示当前包的一级依赖
rospack depends beginner_tutorials //显示当前包的所有依赖
catkin_make //编译工作空间中功能包
rosdep //自动安装功能包依赖的其他包
roscd [ 本地包名称[/子目录] ] //允许你直接切换(cd)工作目录到某个软件包或者软件包集当中
roscp //拷贝功能包中文件
rosed //编辑功能包中文件
rosrun [package_name] [node_name] //运行功能包中可执行文件
roslaunch //运行启动文件
rosnode 显示当前运行的ROS节点信息
rosnode list 获得运行节点列表
rosnode info node-name 获得特定节点的信息
rosnode ping node-name 测试节点是否连通
rosnode kill node-name 终止节点
rostopic //获取有关ROS话题的信息
rossrv //针对静态服务文件
rossrv show <pkg_name>/srv_name 显示服务类型的所有信息
rossrv list 显示所有服务类型信息
rossrv md5 <pkg_name>/srv_name 显示服务类型的md5sum信息
rossrv package <pkg_name> 列出一个程序包中的所有服务类型
rossrv packages <srv_name> 列出包含服务的所有程序包
rossevice
rossevice args //打印服务的参数
rosservice call //以命令行的形式调用服务,可以指定具体参数
rosservice find //根据服务类型寻找当前服务
rosservice info //打印服务信息
rosservice list //列出当前服务类型
rosservice type //打印服务类型
rosservice uri //打印服务的ROSRPC uri
rosmsg //查看消息的详细情况
工作空间:一个存放工程开发相关的文件夹
功能包package:是ROS软件的基本单元,包含ROS节点、库、配置文件等
Src:代码空间(source space)功能包代码,配置文件,launch文件
Build:编译空间(build space)
Devel:开发空间(development space)放置编译生成的可执行文件,一些库,脚本
Install:安装空间(install space)
mkdir –p~/catkin_ws/src
cd~/catkin_ws/src
catkin_init_workspace
cd ~/catkin_ws/
catkin_make
catkin _make install(创建安装空间)
catkin_create_pkg test_pkg roscpp rospy std_msgs
创建的功能包名为 test_pkg,依赖c++,python以及msgs等库,后期可添加,生成的test_pkg文件中包含src文件,用来存放代码,其中include用来存放头文件
source devel/setup.bash
echo $ROS_PACKAGE_PATH
roscore // 启动rosmaster 运行节点管理器
rosrun turtlesim turtlesim_node //运行海龟仿真器节点指令
rosrun turtlesim turtle_teleop_key //启动键盘操作节点
rqt_graph/rqt //是基于qt的可视化工具,rqt_graph为显示系统计算处理工具
Ros中默认单位为m和弧度
## 常用指令:
rosnode 显示系统中所有节点相关信息的指令;
rostopic 显示系统中所有话题相关的指令;
rosmsg 与消息相关的指令;
rosservice 显示与服务相关指令
rosbag
rosnode list 列出系统中所有节点;
rosnode info 节点名称 查看节点具体信息;
rostopic list 列出系统中所有话题;
发布话题:
rostopic pub /话题名/节点类型/数据结构“具体数据”可用tab键补齐
例子:rostopic pub –r 10 /turtle1/cmd_vel geometry_msgs/Twist "linear:
x: 0.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0"
其中-r 10 为频率,以10Hz每秒发送数据,具体数据包括:角速度和线速度。
查看发布话题的消息结构:rosmsg show geometry_msgs/Twist
eg:发布服务请求:rosservice call/spawn”x:5.0
Y:5.0
Theta:0.0
name:’turtle2’”
话题记录:rosbag record –a-O cmd_record/record记录,-a所有信息-o保存为压缩包+名字
话题复现:rosbag play 压缩包名字
# 话题通讯模型
## 创建功能包
cd~/catkin_ws/src
catkin_create_pkg learning_topic rospy std_msgs geometry_msgs turtlesim
## 发布者publisher编程实现
### 创建发布者代码
## 订阅者subscriber编程实现
初始化ROS节点
订阅需要的话题
循环等待话题消息,接收到消息后进入回调函数,在回调函数中完成消息处理
话题消息订阅使用
## 自定义话题消息
### 定义msg文件
创建msg文件夹,存放与消息有关的文件。使用touch person.msg命令创建文件,打开后编写msg文件
string name
uint8 sex
uint8 age
uint8 unknow = 0
uint8 male = 1
uint8 female = 2
### 在package.xml中添加功能包依赖
编译依赖message_generation 动态产生msg的功能包
执行依赖message_runtime
### 在CMakeList.txt添加编译选项
1.添加功能包依赖find…package(…message_generation)
2.把msg文件编译成对应的程序文件add_message_files(FILES person.msg)
3.声明编译msg文件时需要依赖ros已有的库generate_messages(DEPENDENCIES std_msgs)
4创建msg运行依赖
catkin_package(
INCLUDE_DIRS include
LIBRARIES lenrnking_topic
CATKIN_DEPENDS roscpp rospy std_msgs
DEPENDS system_lib
message_runtime
)
5编译catkin_make;根据msg文件生成include头文件,必须采用大写首字母命名
### 编译生成语言相关文件
创建subscriber和publisher文件
在CMakeList.txt中设置需要编译的代码和生成的可执行文件;
add_executable(person_publisher src/person_publisher.cpp)
add_executable(person_subscriber src/person_subscriber.cpp)
设置链接库;
target_link_libraries(person_publisher ${catkin_LIBRARIES})
target_link_libraries(person_subscriber ${catkin_LIBRARIES})
添加依赖项
add_dependencies(person_publisher ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(person_subscriber ${PROJECT_NAME}_generate_messages_cpp)
运行roscore;
运行rosrun learning_topic person_subscriber;
运行rosrun learning_topic person_publisher;
可观察到发布者发布信息,订阅者显示打印信息
# 服务通信模型
服务(services)是节点之间通讯的另一种方式。服务允许节点发送请求(request) 并获得一个响应(response)
## 在工作空间src目录下创建learning_service功能包并添加依赖
catkin_create_pkg learning_service roscpp rospy std_msg geometry_msg turtlesim
## 客户端client编程实现
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200602173756793.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzM2NTc1MQ==,size_16,color_FFFFFF,t_70)
创建客户端turtle_spawn节点,发布request给server(海龟仿真器)
当server收到request后会回馈一个response给客户端,client端与server端通过service消息结构建立联系
在创建的learning_service功能包的src中使用命令行创建文件
touch turtle_spawe.cpp
编写client端程序
```cpp
/**
请求/spawn服务,服务数据类型turtlesim::spawn
**/
#include
#include
int main(int argc, char** argv)
{
//初始化节点 节点名为turtle_spawn
ros::init(argc,argv,"turtle_spawn");
//创建节点句柄
ros::NodeHandle node;
//发现/spawn服务后,创建一个ServiceClient实例服务客户端,连接名为/spawn的service,请求的数据类型为turtlesim::Spawn
ros::service::waitForService("/spawn");//查询当前系统中是否有/spawn服务,阻塞型api,循环等待
ros::ServiceClient add_turtle = node.serviceClient("/spawn");
//封装初始化turtlesim::Spawn的请求数据
turtlesim::Spawn srv;//定义名为srv,类型为turtlesim::Spawn的请求数据
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);//调用call方法,把封装好的srv发出去,等待服务器反馈。
//显示服务调用结果
ROS_INFO("Spwan turtle successfully [name:%s]",srv.response.name.c_str());
return 0;
}
打开learning_service功能包中的CMakeList.txt,在build中声明C++可执行文件并指定库链接另一个库或一个可执行目标:
add_executable(turtle_spawn src/turtle_spawn.cpp) //把spawn.cpp代码编译为turtle_spawn规则
target_link_libraries(turtle_spawn ${catkin_LIBRARIES}) //把生成的turtle_spawn连接到ros中的库
返回到工作空间根目录进行编译
catkin_make
编译结束中,可在工作空间目录的devel文件夹中的lib文件夹可找到生成的learning_service功能包,包含代码编译生成的可执行文件turtle_spawn
cd ~/ros_novice
catkin_make
roscore
rosrun turtlesim tuetlesim_node
rosrun learning_service turtle_spawn
在功能包src文件夹下打开命令行 touch turtle_command_server.cpp 创建代码文件
/**
* 该例程将执行/turtle_command服务,服务数据类型std_srvs/Trigger
*/
#include
#include
#include
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,注册回调函数commandCallback
ros::ServiceServer command_service = n.advertiseService("/turtle_command", commandCallback);
// 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
turtle_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;
}
打开learning_service功能包中的CMakeList.txt,在build中声明C++可执行文件并指定库链接另一个库或一个可执行目标:
add_executable(turtle_command_server src/turtle_command_server.cpp) //把spawn.cpp代码编译为turtle_spawn规则
target_link_libraries(turtle_command_server ${catkin_LIBRARIES}) //把生成的turtle_spawn连接到ros中的库
cd ~/ros_novice
catkin_make
roscore
rosrun turtlesim tuetlesim_node
rosrun learning_service turtle_command_server
rosservice call/turtle_command
Client端request发布显示人信息的请求,把信息通过service端发布数据,server端收到service发布的数据
1、在功能包下创建srv文件夹,touch show_person.srv
定义.src文件;//—以上为request数据,—以下为response数据
string name
uint8 age
uint8 sex
uint8 unknown = 0
uint8 male =1
uint8 female = 2
string result
2、在package.xml中添加动态生成msg依赖的功能包
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
3、在CMakeList.txt添加编译选项
################################################
## Declare ROS messages, services and actions ##
################################################
在此目录下添加如下命令
add_service_files(FILES Person.srv)
##自动搜索srv下的文件
generate_messages(DEPENDENCIES std_msgs )
##根据文件定义和std_msgs依赖产生对应头文件
添加依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES learning_service
CATKIN_DEPENDS geometry_msg roscpp rospy std_msg turtlesim message_runtime
# DEPENDS system_lib
)
在工作目录中使用catkin_make编译生成头文件
/*
* 该例程用于将请求/show_person服务,服务数据类型learning_service::Person
*
*/
#include
#include
int main(int argc,char** argv)
{
//初始化节点
ros::init(argc,argv,"person_client");
//创建节点句柄
ros::NodeHandle node;
//发现/spawn服务后,创建一个服务客户端,连接名为/spawn的service
ros::service::waitForService("show_person");
ros::ServiceClient person_client = node.serviceClient<learning_service::Person>("/show_person");
//初始化learning_serviceL::Person请求数据
learning_service::Person srv;
srv.request.name = "Tom";
srv.request.age = 27;
srv.request.sex = learning_service::Person::Request::male;
//请求服务调用
ROS_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);
//显示服务器调用结果
ROS_INFO("show person result : %s",srv.response.result.c_str());
return 0 ;
}
/*
* 该例程将执行/show_person服务,服务数据类型learning_service::Person
* */
#include
#include
//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
ros::ServiceServer person_service = n.advertiseService("/show_person",personCallback);
//循环等待回调函数
ROS_INFO("Ready to show person inforntion");
ros::spin();
return 0;
}
add_executable(person_client src/person_client.cpp)
##把person_client.cpp代码编译为person_client可执行文件
target_link_libraries(person_client ${catkin_LIBRARIES})
##把生成的person_client连接到ros中的库
add_executable(person_server src/person_server.cpp)
##把person_server.cpp代码编译为person_server可执行文件
target_link_libraries(person_server ${catkin_LIBRARIES})
##把生成的person_server连接到ros中的库
add_dependencies(person_client ${PROJECT_NAME}_gencpp)
## 和动态生成的头文件做依赖
add_dependencies(person_server ${PROJECT_NAME}_gencpp)
## 和动态生成的头文件做依赖
在工作空间根目录使用catkin_make编译
roscore
rosrun learning_service person_server
rosrun learning_service person_client
可查看结果
运行server会发现等待show_person的数据
运行client请求service端show_person,与此同时可发现server端打印显示client发送的信息。
参数命令行使用
rosparam list 列出当前所有参数
rosparam get param_key 显示某个参数值
rosparam set param_key param_value 设置某个参数值
rosparam dump file_name 保存参数到文件
rosparam load file_name 从文件读取参数
rosparam delete param_key 删除参数
首先在src中创建参数功能包
catkin_create_pkg learning_parameter roscpp rospy std_srvs
创建parameter.cpp文件
/**
* 该例程设置/读取海龟例程中的参数
*/
#include
#include
#include
int main(int argc, char **argv)
{
int red, green, blue;
// ROS节点初始化
ros::init(argc, argv, "parameter_config");
// 创建节点句柄
ros::NodeHandle node;
// 读取背景颜色参数
ros::param::get("/background_r", red);
ros::param::get("/background_g", green);
ros::param::get("/background_b", blue);
ROS_INFO("Get Backgroud 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 Backgroud Color[255, 255, 255]");
// 读取背景颜色参数
ros::param::get("/background_r", red);
ros::param::get("/background_g", green);
ros::param::get("/background_b", blue);
ROS_INFO("Re-get Backgroud Color[%d, %d, %d]", red, green, blue);
// 调用服务,刷新背景颜色
ros::service::waitForService("/clear");
ros::ServiceClient clear_background = node.serviceClient<std_srvs::Empty>("/clear");
std_srvs::Empty srv;
clear_background.call(srv);
sleep(1);
return 0;
}
在CMakeList.txt中添加依赖
add_executable(parameter_config src/parameter_config.cpp)
target_link_libraries(parameter_config ${catkin_LIBRARIES})
在工作空间下catkin_make编译
运行测试
roscore
rosrun toutlesim turtlesim_node
rosrun learning_perameter parameter_config