ROS基本概念

ROS通信编程

一、话题编程

流程:

  1. 创建发布者
  2. 创建订阅者
  3. 添加编译选项
  4. 运行可执行程序

1.1 话题发布

方法:

  1. 初始化ROS节点。
  2. 向ROS master注册节点信息,包括发布的话题名和话题中的消息类型。
  3. 按照一定频率发布消息

代码: talker.cpp

#include <sstream>
#include "ros/ros.h"
#include "std_msgs/String.h"

int main(int argc, char **argv)
{
    //ROS节点初始化
    ros::init(argc, argv, "talker");
    
    //创建节点句柄
    ros::NodeHandle n;
    
    //创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String
    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter",1000);
    
    //设置循环的频率
    ros::Rate loop_rate(10);
    
    int count = 0;
    while(ros::ok)
    {
        //初始化std_msgs::String类型的消息
        std_msgs::String msg;
        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();
        
        //发布消息
        ROS_INFO("%s", msg.data.c_str());
        chatter_pub.publish(msg);
        
        //循环等待回调函数
        ros::spinOnce();
        
        //按照循环频率延时
        loop_rate.sleep();
        ++count;
    }
    return 0;
}

1.2 话题订阅

方法:

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

代码: listener.cpp

#include "ros/ros.h"
#include "std_msgs/String.h"

//接收到订阅的消息后,会进入消息回调函数
void chatterCallback(count std_msgs::String::ConstPtr& msg)
{
    //将接收到的消息打印出来
    ROS_INFO("I heard: {%s}", msg->data,c_str());
}

int main(int argc, char **argv)
{
    //初始化ROS节点
    ros::init(argc, argv, "listener");
    
    //创建节点句柄
    ros::NodeHandle n;
    
    //创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback
    ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
    
    //循环等待回调函数
    ros::spin();
    
    return 0;
}

1.3 代码编译

方法:

  1. 设置需要编译的代码和生成的可执行文件;
  2. 设置链接库
  3. 设置依赖

代码: CMakeLists.txt

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
# add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
# add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)

1.4 运行程序

rosrun learning_communication talker
rosrun learning_communication listener

1.5 自定义话题消息

1.5.1 定义msg文件

代码: Person.msg

string name
uint8 sex
uint8 age

uint8 unknown = 0
uint8 male = 1
uint8 female = 2

1.5.2 在package.xml中添加功能包依赖

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

1.5.3 在CMakeLists.txt添加编译选项

find_package(... message_generation)
catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs message_runtime)
add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)

部分ROS版本需将exec_depend改成run_depend

二、服务编程

流程:

  1. 创建服务器
  2. 创建客户端
  3. 添加编译选项
  4. 运行可执行程序

2.1 创建服务器

方法:

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

代码: server.cpp

#include "ros/ros.h"
#include "learning_communication/AddTwoInts.h"

//service 回调函数,输入参数req,输出参数res
bool add(learning_communication::AddTwoInts::Request &req,
        learning_communication::AddTwoInts:Response &res)
{
    //将输入参数中的请求数据相加,结果放到应答变量中
    res.sum = req.a + req.b;
    ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
    ROS_INFO("sending back response: [%ld]", (long int)res.sum);
    
    return true;    
}

int main(int argc, char **argv)
{
    //ROS节点初始化
    ros::init(argc, argv, "add_two_ints_server");
    
    //创建节点句柄
    ros::NodeHandle n;
    
    //创建一个名为add_two_ints的server,注册回调函数add()
    ros::ServiceServer service = n.advertiseService("add_two_ints", add);
    
    //循环等待回调函数
    ROS_INFO("Ready to add two ints"):
    ros::spin();
    
    return 0;    
}

2.2 创建客户端

方法:

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

代码:

#include <cstdlib>
#inlcude "ros/ros.h"
#include "learning_communication/AddTwoInts.h"

int main(int argc, char **argv)
{
    //ROS节点初始化
    ros::init(argc, argv, "add_two_ints_clients");
    
    //从终端命令行获取两个加数
    if(argc != 3)
    {
        ROS_INFO("usage: add_two_ints_client X Y");
        return 1;
    }
    
    //创建节点句柄
    ros::NodeHandle n;
    
    //创建一个client,请求add_two_int_service
    //service消息类型是learning_communication::AddTwoInts
    ros::ServiceClient client = n.serviceClient<learning_communication::AddTwoInts>("add_two_ints");
    
    //创建learning_communication::AddTwoInts类型的service消息
    learning_communication::AddTwoInts srv;
    srv.request.a = atoll(argv[1]);
    srv.request.b = atoll(argv[2]);
    
    //发布service请求,等待假发运算的应答结果
    if(client,call(srv))
    {
        ROS_INFO("SUm: %ld", (long int)srv.response.sum);
    }
    else
    {
        ROS_ERROR("Faild to call service add_two_ints");
        return 1;
    }
    
    return 0;
}


2.3 定义服务请求与应答

2.3.1 定义srv文件

代码: AddTwoInts.srv

int64 a
int64 b
---
int64 sum
2.3.2 在package.xml中添加功能包依赖
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
2.3.3 在CMakeLists.txt添加编译选项
find_package(...... message_generation)
catkin_package(CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs message_runtime)
add_service_files(FILES AddTwoInts.srv)

部分ROS版本需将exec_depend改成run_depend

2.4 编译代码

add_executable(talker src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_generate_messages_cpp)

add_executable(listener src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_generate_messages_cpp)

2.5 运行代码

rosrun learning_communication server

rosrun learning_communication client 3 5

三、动作编程

概念:

  • 一种问答通信机制;
  • 带有连续反馈;
  • 可以在任务过程中止运行;
  • 基于ROS的消息机制实现

参数:

参数概念
goal发布任务目标
cancel请求取消任务
status通知客户端当前的状态
feedback周期反馈任务运行的监控数据
result向客户端发送任务的执行结果,只发布一次

3.1 实现动作服务器

方法:

  1. 初始化ROS节点;
  2. 创建动作服务器实例;
  3. 启动服务器,等待动作请求;
  4. 在回调函数中完成动作服务功能的处理,并反馈进度信息;
  5. 动作完成,发送结束信息。

代码: Dodishes_server.cpp

#include <ros/ros.h>
#include <actionlib/server/simple_action_server.h>
#include "learning_communication/DoDishesAction.h"

typedef actionlib::SimpleActionServer<learning_communication::DoDishesAction> Server;

//收到action的goal后调用该回调函数
void execute(count learning_communication::DoDishesGoalConstPtr& goal, Ssrver* as)
{
    ros::Rate r(1);
    learning_communcation::DpDishesFeedback feedback;
    
    ROS_INFO("Dishwasher %d is working.", goal->dishwasher_id);
    
    //假设洗盘子的进度,并且按照1hz的频率发布进度feedback
    for(int i=1,;i<=10; i++)
    {
        feedback.percent_complete = i * 10;
        as->publishFeedback(feedback);
        r.sleep;
    }
    
    //当action完成后,向客户端返回结果
    ROS_INFO("Dishwasher %d finish working,", goal->dishwasher_id);
    as->setSucceeded();    
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "do_dishes_setver");
    ros::NodeHandle n;
    
    //当action完成后,向客户端返回结果
    ROS_INFO("Dishwasher %d dinish working.", goal->dishwasher_id);
    as->setSucceeded();   
}

int mian(int argc, char** argv)
{
    ros::init(argc, argv, "do_wishes_server");
    ros::NodeHandle n;
    
    //定义一个服务器
    Server server(n, "do_dishes", boost::bind(&execute, _l, &server), false);
    
    //服务器开始运行
    server.start();
    
    ros::spin();
    
    return 0;
}

3.2 实现动作客户端

方法:

  1. 初始化ROS节点;
  2. 创建动作客户端实例;
  3. 连接动作服务器;
  4. 发送动作目标;
  5. 根据不同类型的服务端反馈处理回调函数。

代码: DoDishes_client.cpp

#include <actionlib/client/simple_action_client.h>
#include "learning_communication/DoDishesAction.h"

typedef actionlib::SimpleActionClient<learning_communication::DoDIshesAction> Client;

//当action完成后会调用该回调函数一次
void doneCb(const actionlib::SimpleClientGoalState& state,
           const learning_communication::DoDishesResultConstPtr& result)
{
    ROS_INFO("Yay! The dishes are now clean");
    ros::shutdown();
}

//当action激活后会调用该回调函数一次
void activeCb()
{
    ROS_INFO("Goal just went active");    
}

//收到feedback后调用该回调函数
void feedbackCb(const learning_communication::DoDishesFeedbackConstPtr& feedback)
{
    ROS_INFO(" percent_complete : %f ", feedback->percent_complete);
}

int main(int argc, char** argv)
{
    ros::init(argc, argv, "do_dishes_client");
    
    //定义一个客户端
    Client client("do_dishes", true);
    
    //等待服务器端
    ROS_INFO("Waiting for action server to start.");
    client.waitForServer();
    ROS_INFO("Action server started, sending goal.");
    
    //创建一个action的goal
    learning_communication::DoDishesGoal goal;
    goal.dishwasher_id = 1;
    
    //发送action的goal给服务器端,并且设置回调函数
    client.sendGoal (goal, &doneCb, &activeCb, &feedbackCb);
    
    ros::spin();
    
    return 0;
}

3.3 自定义动作消息

3.3.1 定义action文件;

# 定义目标信息
uint32 dishwasher_id
# Specify which dishwasher we want to use
---
# 定义结果信息
uint32 total_dishes_cleaned
--
# 定义周期反馈的消息
float32 percent_complete

3.3.2 在package.xml中添加功能包依赖

<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>

3.3.3 在CMakeLists.txt添加编译选项

find_package(catkin REQUIRED actionlib_msgs actionlib)
add_action_files(DIRECTORY action FILES DoDIshes.action)
generate_messages(DEPENDENCIES actionlib_msgs)

部分ROS版本中的exec_depend需要改成run_depend

3.4 编译代码

  1. 设置需要编译的代码和生成的可执行文件;
  2. 设置链接库;
  3. 设置依赖。
add_executable(DoDishes_clients src/DoDishes_client.cpp)
target_link_libraries(DoDishes_client $(catkin_LIBRARIES))
add_dependencies(DoDishes_client ${${PROJECT_NAME}_EXPORTED_TARGETS})

add_executable(DoDishes_server src/DoDishes_server.cpp)
target_link_libraries(DoDishes_server $(catkin_LIBRARIES))
add_dependencies(DoDishes_server ${${PROJECT_NAME}_EXPORTED_TARGETS})

3.5 运行程序

rosrun learning_communication DoDishes_client

rosrun learning_communication DoDishes_server

四、分布式通信

  1. 设置IP,主机和从机之间相互联通
  2. 设置.bashrc文件
export ROS_MASTER_URI = http://master:11311

五、ROS中的关键组件

5.1 launch文件

5.1.1 首尾标记<launch>

<launch>
	<node pkg="turtlesim" name="sim1" type="turtlesim_node">
    <node pkg="turtlesim" name="sim2" type="turtlesim_node">
</launch>

5.1.2 启动节点

<node pkg="turtlesim" name="sim1" type="turtlesim_node">
pkg: 节点所在的功能包名称
type:节点的可执行文件名称
name:节点运行时的名称
output:
respawn:
required:
ns:
args:

5.1.3 参数设置

<!-- 设置ROS系统运行中的参数,存储在参数服务器中 -->
<param name="output_frame" value="odom"/>
name: 参数名
value:参数值

<!-- 加载参数文件中的多个参数 -->
<rosparam file="params.yaml" command="load" ns="params"/>

<!-- launch文件内部的局部变量,仅限于launch文件使用 -->
<arg name="arg-name" default="arg-calue">
name: 参数名
value:参数值
    
<!-- 调用 -->
<param name="foo" value="$(arg arg-name)"/>
<node name ="node" pkg="package" type="type" args="$(arg arg-name)"/>

5.1.4 重映射

<!-- 重映射ROS计算图资源的命名 -->
<remap from="/turtlebot/cmd_vel" to= "/cmd_vel">
from: 原命名
to  : 映射后的命名

5.1.5 嵌套

<!-- 包含其他launch文件,类似C语言中的头文件包含 -->
<include file="$(dirname)/other.launch"/>
file: 包含的其他launch文件路径
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值