ROS入门(C++ 及 思路)

使用Autolabor官方入门教程,笔记内容为注意事项

首页 - Autolabor开源ROS机器人底盘 - 官方网站

_______________________________________________________

一. ROS通信机制的解释

例如在生活中,我们在家里有一个放零食的箱子,而操作服务器就是这个箱子,无论是使用者还是开发者都可以共同使用他。

1.话题通信 是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息

                                                 雷达------->导航(计算)-------->底盘

在传递消息和传递运动控制指令时,就使用了话题通信,那么

                                       雷达就是发布,  导航是订阅发布,底盘是订阅。

简单的三要素,发布 - 订阅 - 中间要素

                                                           角色----> 流程 ----> 注意

(1)角色: 

           1. master -----> 管理者(媒婆)

           2. talker ------>发布者 (男方)

           3. listner ------->订阅者 (女方)

(2)流程 :

master 可以根据话题  建立发布者和订阅者之间的连接

0. 男方提交信息       (房,手机号) ——————发布者比订阅者信息多 

1. 女方提交信息       (房)

2. 媒婆把男方电话发给女方

3. 女方打电话给男方(房 连接)

4. 男方响应(加微信/给微信)

5. 女方加男方微信

(3)注意:

1.使用的协议由RPC和TCP;

2.步骤1和步骤0没有顺序关系

3.talker 和 listener可以存在多个

4.建立连接后,master就可以关闭了

5.上述流程已经封装,直接调用就好

—————————————————————————

我们要设置         发布   和    订阅方(分别进行)- (我们的实操部分只进行C++实现案例)

接下来 将从头开始一一介绍(VScode的配置请参照上面的链接进行修改,否则无法正常调试)

一 : 发布框架+发布逻辑

-----------------

(1)建立工作空间

建立 demo02_ws 工作空间 

键入 code. 可以直接用vs打开工作空间

--------------------

(2)添加包 (头文件)---第一次为文件名,第二次为我们添加的包(后面服务可作修改)

 

-----------------------------

(3)新建一个发布者文件(写代码的位置,在src目录下新建一个cpp文件)

 内容如下:

/*
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)

         PS: 二者需要设置相同的话题


    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号

    实现流程:
        1.包含头文件 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 发布者 对象
        5.组织被发布的数据,并编写逻辑发布数据

*/
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>

int main(int argc, char  *argv[])
{   
    //设置编码
    setlocale(LC_ALL,"");

    //2.初始化 ROS 节点:命名(唯一)
    // 参数1和参数2 后期为节点传值会使用
    // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
    ros::init(argc,argv,"talker");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能

    //4.实例化 发布者 对象
    //泛型: 发布的消息类型
    //参数1: 要发布到的话题
    //参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
    ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);

    //5.组织被发布的数据,并编写逻辑发布数据
    //数据(动态组织)
    std_msgs::String msg;
    // msg.data = "你好啊!!!";
    std::string msg_front = "Hello 你好!"; //消息前缀
    int count = 0; //消息计数器

    //逻辑(一秒10次)
    ros::Rate r(1);

    //节点不死
    while (ros::ok())
    {
        //使用 stringstream 拼接字符串与编号
        std::stringstream ss;
        ss << msg_front << count;
        msg.data = ss.str();
        //发布消息
        pub.publish(msg);
        //加入调试,打印发送的消息
        ROS_INFO("发送的消息:%s",msg.data.c_str());

        //根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
        r.sleep();
        count++;//循环结束前,让 count 自增
        //暂无应用
        ros::spinOnce();
    }


    return 0;
}

------------------------

(4)在  CmakeLists   里面修改参数 (映射名字和定义名字最好一样)

修改下面两处

(1)   

(2)   

然后shift+ctrl+b调试看看有没出错

-----------------------------------------

(5)启动ros核心 roscore rosrun rostopic(下面还会有具体说明)

         进入工作空间的方法:

 

————————————————————————————————————

二:订阅方

(1)新建订阅方文件(在src文件里)

具体内容:

/*
    需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)


    消息订阅方:
        订阅话题并打印接收到的消息

    实现流程:
        1.包含头文件 
        2.初始化 ROS 节点:命名(唯一)
        3.实例化 ROS 句柄
        4.实例化 订阅者 对象
        5.处理订阅的消息(回调函数)
        6.设置循环调用回调函数

*/
// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
    ROS_INFO("我听见:%s",msg_p->data.c_str());
    // ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;

    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)

    //     6.设置循环调用回调函数
    ros::spin();//循环读取接收的数据,并调用回调函数处理

    return 0;
}

-------------------------------

(2)修改 CmakeLists 

与发布者相同,对比着上面修改一下名称即可

-------------------------------

(3)启动命令行

与上面相同,同时启动3个命令行

但是这次最后一个也是rosrun启动

 ---------------------------------

注意事项在所给的课件里面有提到

如果要使用计算图查看,就再建立一个命令行

就可以查看了

-----------------------------------------------

自定义话题通信: (前置步骤)

(1)在上面的基础上继续完成,建立一个新的文件夹和一个个人文件

(msg  与   person.msg)

内容如下:

(2)接着,进行配置 (两处)

package.xml

 CmakeList

我们要配置依赖关系

 接着编译一下,可以生成我们过程中需要的文件

——————————————————————————

发布方:

1.前置步骤 配置vscode

在终端中打开

(2)新建一个自定义发布文件(仍然在之前建立的plu包下的src文件)

具体内容如下:

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"

int main(int argc,char *aegv[])
{
setlocale(LC_ALL,"");
//1 初始化节点
ros::init(argc,argv,"banZhuRen");
//2 创建句柄
ros::NodeHandle nh;
//3 创立发布对象
ros::Pulisher pub = nh.advertise<plumbing_pub_sub::Person>("liaoTian",10);

// 4-1 创建发布数据
plumbing_pub_sub::Person person;
person.name = "张三";
person.age = 1;
person.height = 1.73;
// 4-2 创建发布频率
ros::Rate rate(1);
// 4-3 循环发布
while(ros::ok())
{
//修改被发布数据
person.age+=1;
//数据发布
pub.publish(person);
ROS_INFO("发布消为:%s,%d,%.2f",person.name.c_str(),person,age.c_str(),person.height.c_str());
//休眠
rate.sleep();
//  最好写
ros::spinOnce();

}


return 0;
}

接下来的内容与上面一样,但是cmakelists里面多配置一个

 添加完成后再编译一下

-----------------------

(3)运行

roscore

 

------------------------------------------- 

对于订阅方也相同

 新建一个文件,然后写内容

void doPerson(const plumbing_pub_sub::Person::ConstPtr& person)
{
ROS_INFO("订阅消息为:%s,%d,%.2f",person.name.c_str(),person,age.c_str(),person.height.c_str());
}
int main(int argc.char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"jiaZhang");
ros::NodeHandle nh;
ros::Subscriber sub = nh/subscribe("liaoTian",10,doPerson);

ros::spin();
}

Cmakelist的三个配置也一样

 自定义与官方的区别在于,多了一个配置,其它的东西都是大同小异

—————————————————————————————————————

二- 服务通信

服务通信也是ROS中一种极其常用的通信模式,服务通信是基于请求响应模式的,是一种应答机制。也即: 一个节点A向另一个节点B发送请求,B接收处理请求并产生响应结果返回给A。

机器人巡逻过程中,控制系统分析传感器数据发现可疑物体或人... 此时需要拍摄照片并留存

定义srv 文件内的可用数据类型与 msg 文件一致,且定义 srv 实现流程与自定义 msg 实现流程类似:

步骤如下:

  1. 按照固定格式创建srv文件

  2. 编辑配置文件

  3. 编译生成中间文件

调用svc 文件,流程如下:

  1. 编写服务端实现
  2. 编写客户端实现
  3. 编辑配置文件
  4. 编译并执行 

----------------------------------------------

接下来为详细操作:

(1)创建新的功能包

 

 新建服务文件夹和文件(srv 与 Addints.srv

 package 和 Cmakelist下更改配置 

 

 

--------------------------------------

src 目录下建立deno01_server.cpp

具体内容如下:

/*
    需求: 
        编写两个节点实现服务通信,客户端节点需要提交两个整数到服务器
        服务器需要解析客户端提交的数据,相加后,将结果响应回客户端,
        客户端再解析

    服务器实现:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建 ROS 句柄
        4.创建 服务 对象
        5.回调函数处理请求并产生响应
        6.由于请求有多个,需要调用 ros::spin()

*/
#include "ros/ros.h"
#include "demo03_server_client/AddInts.h"

// bool 返回值由于标志是否处理成功
bool doReq(demo03_server_client::AddInts::Request& req,
          demo03_server_client::AddInts::Response& resp){
    int num1 = req.num1;
    int num2 = req.num2;

    ROS_INFO("服务器接收到的请求数据为:num1 = %d, num2 = %d",num1, num2);

    //逻辑处理
    if (num1 < 0 || num2 < 0)
    {
        ROS_ERROR("提交的数据异常:数据不可以为负数");
        return false;
    }

    //如果没有异常,那么相加并将结果赋值给 resp
    resp.sum = num1 + num2;
    return true;


}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"AddInts_Server");
    // 3.创建 ROS 句柄
    ros::NodeHandle nh;
    // 4.创建 服务 对象
    ros::ServiceServer server = nh.advertiseService("AddInts",doReq);
    ROS_INFO("服务已经启动....");
    //     5.回调函数处理请求并产生响应
    //     6.由于请求有多个,需要调用 ros::spin()
    ros::spin();
    return 0;
}

接下来再第一个CMakeList里面继续修改(也就是大包的Cmake)

(P137)----修改内容如下

P137
add_excutable(demo01_server src/demo01_server.cpp)

P147
add_dependencies(demo01_server ${PROJECT_NAME}_gencpp)

p150 开始 下面注释去掉
target_link_libraries(demo01_server
 ${catkin_LIBRARIES})

配置完成后,打开三个命令行

roscore

cd demo03_ws/
source ./devel/setup.bash
rosrun plumbing_server_client demo01_server

就可以使用了

---------------------------------

三 - 参数服务器

参数服务器实现是最为简单的,该模型如下图所示,该模型中涉及到三个角色:

  • ROS Master (管理者)
  • Talker           (参数设置者)
  • Listener        (参数调用者)

ROS Master 作为一个公共容器保存参数,Talker 可以向容器中设置参数,Listener 可以获取参数。

(直接再虚拟机上操作)

---------------------------------------------------------------------------

实操控制乌龟运动

 

 

 

 

具体内容如下:

/*
    编写 ROS 节点,控制小乌龟画圆

    准备工作:
        1.获取topic(已知: /turtle1/cmd_vel)
        2.获取消息类型(已知: geometry_msgs/Twist)
        3.运行前,注意先启动 turtlesim_node 节点

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建发布者对象
        4.循环发布运动控制消息
*/

#include "ros/ros.h"
#include "geometry_msgs/Twist.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"control");
    ros::NodeHandle nh;
    // 3.创建发布者对象
    ros::Publisher pub = nh.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel",1000);
    // 4.循环发布运动控制消息
    //4-1.组织消息
    geometry_msgs::Twist msg;
    msg.linear.x = 1.0;
    msg.linear.y = 0.0;
    msg.linear.z = 0.0;

    msg.angular.x = 0.0;
    msg.angular.y = 0.0;
    msg.angular.z = 2.0;

    //4-2.设置发送频率
    ros::Rate r(10);
    //4-3.循环发送
    while (ros::ok())
    {
        pub.publish(msg);

        ros::spinOnce();
    }


    return 0;
}

修改CMakeList配置:

 启动命令行运行:

 

 

-----------------------------------------------

订阅乌龟位姿(话题订阅)

-------准备工作-------

1. 新建launch文件(节点)

 2. 启动launch节点

 3. 通过命令方式获得位姿

 -----------------------------

以程序方式来打印位姿

package配置

 CmakeList配置

添加功能包

 -----------

 2.新建文件:test02_sub_pose

内容如下:

/*  
    订阅小乌龟的位姿: 时时获取小乌龟在窗体中的坐标并打印
    准备工作:
        1.获取话题名称 /turtle1/pose
        2.获取消息类型 turtlesim/Pose
        3.运行前启动 turtlesim_node 与 turtle_teleop_key 节点

    实现流程:
        1.包含头文件
        2.初始化 ROS 节点
        3.创建 ROS 句柄
        4.创建订阅者对象
        5.回调函数处理订阅的数据
        6.spin
*/

#include "ros/ros.h"
#include "turtlesim/Pose.h"

void doPose(const turtlesim::Pose::ConstPtr& p){
    ROS_INFO("乌龟位姿信息:x=%.2f,y=%.2f,theta=%.2f,lv=%.2f,av=%.2f",
        p->x,p->y,p->theta,p->linear_velocity,p->angular_velocity
    );
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    // 2.初始化 ROS 节点
    ros::init(argc,argv,"sub_pose");
    // 3.创建 ROS 句柄
    ros::NodeHandle nh;
    // 4.创建订阅者对象
    ros::Subscriber sub = nh.subscribe<turtlesim::Pose>("/turtle1/pose",1000,doPose);
    // 5.回调函数处理订阅的数据
    // 6.spin
    ros::spin();
    return 0;
}

再次配置CMakeList

 ----------------------------------------

启动乌龟(启动launch文件)后,再新建命令行

cd demo02_ws/
source ./devel/setup.bash
rosrun plumbing_test test02_sub_pose

猜你喜欢

转载自blog.csdn.net/ArtoriaLili/article/details/125162278