ROS学习笔记

赵老师课件链接:http://www.autolabor.com.cn/book/ROSTutorials/

1.1 Hello world!实现

        781002b2119347ca8fd3f42135f7dc7c.png

mkdir -p 自定义空间名称/src
cd 自定义空间名称
catkin_make
cd src
catkin_create_pkg 自定义ROS包名 roscpp rospy std_msgs

先进入用户目录。

4b532ff6e8b3495887646c0b859a3464.png

按住Ctrl+t,创建一个工作空间。

1c86128d4c4a46df98775ccb0cc1c9ed.png

mkdir -p hello_world/src    //创建一个叫“hello_world"的工作目录,目录下有叫"src”的目录
cd hello_world              //进入目录“hello_world"
catkin_make                 //编译

17c9ce8d5c354791a43b5f0e07b93c6d.png

然后会发现除了src,还多了build和devel两个目录。

0a475c1b6b55472aa70962e1a1ea2fb4.png

cd src    //目录src
catkin_create_pkg hello_world roscpp rospy std_msgs
//创建功能包       功能包的名称      三个依赖包

src目录中会出现两个包体

a04a3c0d391843469c0b6e6f7791df9c.png

进入目录中的src目录(不要把两个src目录弄混)

218f31d3da2b4b69bef756970597746e.png

创建.cpp文件

#include "ros/ros.h"

int main(int argc,char *argv[])
{
	ros::init(argc,argv,"hello_node");//初始化ros节点
	ROS_INFO("hello world!");//ros中的输出函数
	return 0;
}

CMakLists.txt配置

打开CMakeLists.txt文件

13f674ba385c48eb8ce13c4ba456c097.png

找到136行

41d47e15f45a48ceaa26e15595d360d3.png


回到工作空间的目录

ea0ef61d4d3248d3bf79cfc0d7d7fb16.png

打开终端(窗口1)

catkin_make

编译

408d56baefb741829df87bb2f7345e83.png

编译成功

再新开一个窗口2(Ctrl+T)

roscore

启动ROS核心

744740f612d2462294b7b8354c45b963.png

回到原窗口

source ./devel/setup.bash//修改环境变量
rosrun helloworld haha
//运行  包名       映射名

输出如图

82d7f5cd1c644ebdab5bd9fc05b6e538.png

1.2 VScode实现Hello world

ac69697adf664a5db7743cecc840be36.png

在VScode里创建目录

经典创建文件

fe7fbc71661446379fe95b29c0cec169.png

mkdir -p test2/src //创建目录下有src目录的test2目录
cd test2           //打开test2
catkin_make        //编译
code .             //启动vscode

c376d8c1b0454f76b4653b4b4c5b6025.png


配置使Ctrl+Shift+B能编译文件

Ctrl+Shift+B呼出任务栏(快捷选项和选中的文件夹有关)

c84e3905ce2941be9f722eadccd8652b.png

打开后将内容替换为:

{
// 有关 tasks.json 格式的文档,请参见
    // https://go.microsoft.com/fwlink/?LinkId=733558
    "version": "2.0.0",
    "tasks": [
        {
            "label": "catkin_make:debug", //代表提示的描述性信息
            "type": "shell",  //可以选择shell或者process,如果是shell代码是在shell里面运行一个命令,如果是process代表作为一个进程来运行
            "command": "catkin_make",//这个是我们需要运行的命令
            "args": [],//如果需要在命令后面加一些后缀,可以写在这里,比如-DCATKIN_WHITELIST_PACKAGES=“pac1;pac2”
            "group": {"kind":"build","isDefault":true},
            "presentation": {
                "reveal": "always"//可选always或者silence,代表是否输出信息
            },
            "problemMatcher": "$msCompile"
        }
    ]
}

 使Ctrl+Shitf+B可以编译文件


右键src,下滑,选择"Create Catkin Package“

14f84217e98a42508cbeb0210392adba.png

输入一个name(随意)

2bc16981511947ee8cad2064acff195d.png

输入这三个依赖

4b4fc82fa6c04538b76d7f7d0442eca6.png

创建文件如下:

353fafdbe42e43669d33037a678cc9b8.png

在src右键新建文件

编码 

a1987fba24764e8d85eb52cee1462689.png

#include "ros/ros.h"

int main(int argc,char *argv[])
{
    ros::init(argc,argv,"hello"); //初始化
    ROS_INFO("hello world");      //输出
    return 0;
}

然后我们会发现莫名其妙的报错(其实运行起来没问题)

让VScode不会乱报错

打开文件,修改成酱紫0096a3f23f6e4f6988e1b62d1d2766a0.png

{
  "configurations": [
    {
      "browse": {
        "databaseFilename": "",  //这项
        "limitSymbolsToIncludedHeaders": true //这项
      },
      "includePath": [
        "/opt/ros/noetic/include/**",
        "/home/elysian-realm/textbook/src/helloworld/include/**",//这项
        "/usr/include/**"
      ],
      "name": "ROS",
      "intelliSenseMode": "gcc-x64",
      "compilerPath": "/usr/bin/gcc",
      "cStandard": "gnu17", //这项
      "cppStandard": "c++17" //这项
    }
  ],
  "version": 4
}

这样就不会莫名其妙报错了


打开文件

a2f30ebcee734dcfb14ed722be6b2fde.png

具体配置看CMakLists.txt配置

VScode中运行代码

编译后打开新终端

启动核心

08b4f7db8dcc437a80ca39871a583483.png

再新建终端

8fee51e428c04f4f899e7315aed3fd53.png

rosrun中第二个名称为映射名 

中文乱码问题

在main里面加入

setlocale(LC_ALL,"");

1.3 launch文件

e791808d93a34f608d5c3858191bd52c.png

新建launch文件夹,文件格式是.xml

2e747c065b0d45b895ea6000352b7e17.png

文件基本格式

436b08d9b33e4138a764611b45a82dcd.png

launch文件的运行代码 

5b03a8d0465c4b56ad105c3a31bffa91.png

source ./devel/setup.bash
roslaunch hello_vscode start_turtle.launch
//        包名          文件名

 然后继续填写launch文件

d23a92330b90468aadb4dd3677b9e464.png

<launch>
    <!-- 添加被执行的节点 -->
    <node pkg="turtlesim" type="turtlesim_node" name="turtle_GUI"/>
          //包名           节点                  命名
    <node pkg="hello_vscode" type="hello_vscode_c" name="" output="screen"/>
                                                         //输出到屏幕上
</launch>

 1.4 ROS文件系统

http://www.autolabor.com.cn/book/ROSTutorials/chapter1/15-ben-zhang-xiao-jie/151-roswen-jian-xi-tong.html

2.0 话题通信理论模型

0d507f3c24624f73ae2018281cbe57cd.png

ac65606ba4a94efabacfd1d39d45a7f2.jpeg

2.1.1 话题通信理论模型 

http://www.autolabor.com.cn/book/ROSTutorials/di-2-zhang-ros-jia-gou-she-ji/22hua-ti-tong-xin/211-li-lun-mo-xing.html

933554f187fa4d9ea9030b1d3520c60e.png

整个流程由以下步骤实现:

0.Talker注册

Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。

1.Listener注册

Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。

2.ROS Master实现信息匹配

ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。

3.Listener向Talker发送请求

Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。

4.Talker确认请求

Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。

5.Listener与Talker件里连接

Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。

6.Talker向Listener发送消息

连接建立后,Talker 开始向 Listener 发布消息。

注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议

注意2: Talker 与 Listener 的启动无先后顺序要求

注意3: Talker 与 Listener 都可以有多个

注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker 与 Listern 照常通信。

 1.发布方逻辑实现

http://www.autolabor.com.cn/book/ROSTutorials/di-2-zhang-ros-jia-gou-she-ji/22hua-ti-tong-xin/212-hua-ti-tong-xin-zhi-c-shi-xian.html

老流程,在src目录里"Create Catkin Package“,导入依赖包。3b94ee67cfb745e9b7aaab3cbf96d6e5.png

 新建.cpp文件,编码如下26b004adca6b4c65aa3f0595a89a7f0e.png

#include "ros/ros.h"
#include "std_msgs/String.h"
/*
     发布方实现:
        1.包含头文件:
         ros中文本类型 ---> std_msgs/String.h
        2.初始化ROS节点
        3.创建节点句柄
        4.创建发布者对象
        5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
    //2.初始化ROS节点
    ros::init(argc,argv,"ErGouZi");//第三个参数是节点名称
    //3.创建节点句柄B
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    //5.编写发布逻辑并发布数据
    //先创建被发布的信息
    std_msgs::String msg;
    //编写循环,循环中发布数据
    while(ros::ok())
    {
        msg.data = "hello";
        pub.publish(msg);
    }
    return 0;
}

 关于前几条数据丢失的补充(important)

发送数据之前要现在master那里注册,然而经常会出现注册完成之前发送就已经开始的情况,酱紫会导致前几条数据丢失,所以经常在循环前进行如下操作:

ros::Duration(3).sleep();

让程序休眠3秒,给注册留足时间 


老规矩,调CMakeLists包

0d5e9944b66746418f00075539a1ecf7.png

代码详解:

NodeHandle

ros::NodeHandle nh;

d3c8d2821b984ece88f5fd1ffc27e077.png

ros::NodeHandle nh;                     // 创建NodeHandle对象

    // 创建一个发布器
    ros::Publisher pub = nh.advertise<std_msgs::String>("example_topic", 10);

    // 创建一个订阅器
    ros::Subscriber sub = nh.subscribe("example_topic", 10, callback);

6de4409ef3514231bfbfb9f5926a0778.png


 5359feadaeea4455bb33c25e5910c99f.png



advertise 

 ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);

 b8f49bae63c54afa8db47d2411cef85e.png

ros::Publisher advertise<MsgType>(const std::string &topic, uint32_t queue_size, bool latch = false);

9b0c7f07dc7c4b77bc7fc00563b6ba4e.png

d4738fda3a224ef2b561d35516ff8f9d.png

 ros::ok()

while(ros::ok())

710a444d26e64e158fd5e3adcf84698f.png740f627827eb4edbb99474d8565a7ef4.png 

2.发布逻辑实现

将代码做出如下修改

dae196643d4e49ddb177e6af0692c58e.png

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/*
     发布方实现:
        1.包含头文件:
         ros中文本类型 ---> std_msgs/String.h
        2.初始化ROS节点
        3.创建节点句柄
        4.创建发布者对象
        5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化ROS节点
    ros::init(argc,argv,"ErGouZi");//第三个参数是节点名称
    //3.创建节点句柄B
    ros::NodeHandle nh;
    //4.创建发布者对象
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
    //5.编写发布逻辑并发布数据
    //先创建被发布的信息
    std_msgs::String msg;
    //设置发布频率
    ros::Rate rate(10);//10HZ
    //设置计数器
    int count=0;
    //编写循环,循环中发布数据
    while(ros::ok())
    {
        count++;
        //msg.data = "hello";
        //创建对象
        std::stringstream ss;
        //将数据插入ss的缓冲区
        ss<<"hello--->"<< count;
        //从ss中提取字符串(string)并传递给msg
        msg.data=ss.str();
        pub.publish(msg);
        //从ss中提取字符串,转化为c风格并传递给%s
        ROS_INFO("发布的数据是:%s",ss.str().c_str());
        //根据rate设置的频率延时(类似于delay)
        rate.sleep();
    }
    return 0;
}

代码详解: 

 std::stringstream ss;
ss << "hello--->" << count;
msg.data = ss.str();
pub.publish(msg);
ROS_INFO("发布的数据是:%s", ss.str().c_str());

 数据流分析

1b3514d757a64e0888d218ef9a0aa56d.png

 ss.str().c_str()分析

a2527a0b02d34a39a44180cccce26f40.png

 3.订阅逻辑实现

新建.cpp文件,编码如下

0c2141fbf1224ed4a610606ff8ad4d0d.png

#include "ros/ros.h"
#include "std_msgs/String.h"
/*
     发布方实现:
        1.包含头文件:
         ros中文本类型 ---> std_msgs/String.h
        2.初始化ROS节点
        3.创建节点句柄
        4.创建订阅者对象
        5.处理订阅到的数据
        6.spin()函数
*/

//5.处理订阅到的数据
void doMsg(const std_msgs::String::ConstPtr &msg)
{
    ROS_INFO("翠花订阅的数据:%s",msg->data.c_str());
}

int main(int argc, char *argv[])
{
    //2.初始化ROS节点
    ros::init(argc,argv,"CuiHua");//节点不能重名,要有唯一性
    //3.创建节点句柄
    ros::NodeHandle nh;;
    //4.创建订阅者对象
    ros::Subscriber sub = nh.subscribe("fang",10,doMsg);//泛型会自动识别
    ros::spin();
    return 0;
}

 记得配置CMakeLists

049a3b2971c242439c004d9e9bef22a5.png

 代码详解:

ConstPtr

void doMsg(const std_msgs::String::ConstPtr &msg)

{

ROS_INFO("翠花订阅的数据:%s",msg->data.c_str());

}

0f038966c74b4396a1f4cdb0c31e09d9.png

对msg补充

9711609156e44dcea24d369958b48ba1.png

 spin()

ros::spin(); 

据弹幕说和单片机的中断很像 

机制上确实很像

 13ac5aa950254a23bb6dc6781a05e5b8.png

 2.1.2话题通信自定义msg

在资源包下方创建文件夹

30ee0c52b2e040feaba42182fe32d5a7.png

在文件夹里创建文件(一般为.msg格式)

15cd516bbc65491a83b6cec5c8e495b0.png

在文件中定义需要录入的message(和结构体很像)

cc1d10181b1546ebb7db5711d4010439.png类型后面的数字表示数据的位数

然后找到package.xml

61c87176d08c4783b5b20f488f33417a.png

 找到CMakeLists.txt(步骤很多)

1.  10行找到find_package表示编译时需要依赖于msg功能包

1cc69a2bb04148098f4711198ab2ad91.png

 2.  51行找到add_messge_files(里面的两个message文件仅仅是示例)

b9f5187f977f4d198ce494f75c140fc0.png

 修改成酱紫

8b79c1555b9246098297416c59b1b3b1.png

 3.  71行,放开

表示编译Person.msg要依赖于std_msgs

因为Person.msg中每一条消息的实现都是标准消息,所以要构建这种依赖关系

d87ffa35daee4969a5ffb66686fde38b.png

 4.  108行,放开,添加message_runtime

catkin_package和find_package相对应,编译时依赖于find_package,find_package又依赖于catkin_package。

find_package出错会导致编译出错

catkin_package出错可能编译不会有问题,但是运行结果会出问题

42d194da61e643488d3e984cf608f7c1.png

编译

然后会生成许多文件

其中,Person.h是c++的头文件

_Person.py是python的

cf2b55723ed74ae0ac7b1d73d2326244.png

配置vscode(主要是防止异常报错,不配置也能正常运行)

右键,在集成终端中打开 

99e73e4601344a5590593576f1b8f854.png

 pwd,打印当前路径

bb27c2c3a77a4a1b81e5e1e7c780070f.png

  添加到c_cpp_properties.json的includePath里

其中的“**”表示该路径里的任意文件

18717fe26763475c8776f466804ab8f0.png

自定义msg的使用 

发布方实现

代码如下

dcc4630df0564c138854b0fd9f5478f5.png

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"
/*
     发布方实现:
        1.包含头文件:
            #include "plumbing_pub_sub/Person.h"
        2.初始化ROS节点
        3.创建节点句柄
        4.创建发布者对象
        5.编写发布逻辑并发布数据
*/
int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化ROS节点
    ros::init(argc,argv,"BanZhuRen");
    //3.创建节点句柄
    ros::NodeHandle cc;
    //4.创建发布者对象
    ros::Publisher pub = cc.advertise<plumbing_pub_sub::Person>("chat",10);
    //5.编写发布逻辑并发布数据
    plumbing_pub_sub::Person person;
    //5-1.传建被发布的数据
    person.name = "张三";
    person.age = 1;
    person.height = 1.73;
    //5-2.设置发布频率
    ros::Rate rate(1);
    //5-3.循环发布数据
    while(ros::ok())
    {
        //修改发布数据
        person.age++;
        //核心:数据发布
        pub.publish(person);
        //休眠
        rate.sleep();
        //建议
        ros::spinOnce();
    }
    return 0;
}

 配置CMakeLists.txt

39160741b05d4c0295e1c40f8b9d5443.png

调用头部包 

fa0155730e4141e487009604ee1c2733.png

代码详解:

spinOnce()

 ros::spinOnce();

 110798cc65fb4a24a5ec7e3112d5fe86.png

add_dependencies 

 add_dependencies(demo03_pub_person ${PROJECT_NAME}_generate_messages_cpp)

为了防止源文件被编译时头文件没有被被编译而导致出错,先编译头文件,再编译源文件。 

Add:关于回调机制

大致可以类比为单片机中的中断(interrupt)

035f28bea17b4e5b9a4fe83254e5f734.png

spin() 函数

 堵塞当前进程并保持活跃等待回调。

spinOnce()函数

不堵塞当前进程但是会在回调时相应。 

回调事件例如:

fcc4156ba3e84559ade4ff8c5d34d2f2.png

 回调函数则例如之前编写的doMsg函数:

void doMsg(const std_msgs::String::ConstPtr &msg)
{
    ROS_INFO("翠花订阅的数据:%s",msg->data.c_str());
}
ros::Subscriber sub = nh.subscribe("fang",10,doMsg);//泛型会自动识别

订阅方实现

其实和之前差不多,源代码需要对这些做改动

ae84d94b354a486395f1321808a3e42f.png

需要注意的是person和结构体不一样,不能用person.xxx

在CMakeLists.txt的配置上和发布方相同。 

2.2服务通信

5fbc9b4b8a454de0af5e8a5c1bc9053a.png

 0.Server在Master中注册自己的话题和RPC地址(远程通话地址)

1.Client在Master里注册个人信息(只有话题)

2.Master将ROS RPC地址传递给Client(不是原本的RPC地址,这个地址经过ROS的封装)

4.Client拿到地址后对Server进行请求

5.Server收到请求后产生相应给Client(TCP通信)

http://www.autolabor.com.cn/book/ROSTutorials/di-2-zhang-ros-jia-gou-she-ji/23-fu-wu-tong-xin/222-fu-wu-tong-xin-zi-ding-yi-srv.html

2.2.1服务端实现

先新建文件(在1.2里有)

在src的子目录里创建srv文件夹,在srv文件夹里创建.srv文件,格式如下

41fa9e02fe8e4f219834ed9111d586d4.png

# 客户端请求时发送的两个数字
int32 num1
int32 num2
---
# 服务器响应发送的数据
int32 sum

 配置package

祖宗之法,不可变也

425869bf3e17424d90692a796ab8c293.png

配置CMakeLists

配置方式和话题通信相同,除了这里

fc2574da52b74ffd943ff3ffbd1c9580.png

配置c_cpp_properties.json

1.打开devel下的include文件夹,右键,在集成终端中打开

2.pwd:复制当前文件的地址

3.打开c_cpp_properties.json

4.在"includePash"里加入路径如图

 新建.cpp,编码如下

#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"
/*
    服务端实现:解析客户端提交的数据,并运算再产生响应
        1.包含头文件
        
        
        
        
*/
bool doNums(plumbing_server_client::AddInts::Request &request,
            plumbing_server_client::AddInts::Response &response)
{
    //1.处理请求
    int num1=request.num1;
    int num2=request.num2;
    ROS_INFO("soyo的道歉次数:sum1=%d,sum2=%d",num1,num2);
    //2.组织响应
    int sum=num1+num2;
    response.sum=sum;
    ROS_INFO("你这个人,真的满脑子都是自己呢x%d",sum);
    return true;
}

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化ROS节点
    ros::init(argc,argv,"soyo");//节点名称唯一
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建一个服务对象
    ros::ServiceServer server=nh.advertiseService("addInts",doNums);//有回调函数后泛型会自动推导
    ROS_INFO("你能在五秒内说八次对不起吗?");
    //5.处理服务对象并产生响应
    //6.spin()
    ros::spin();
    return 0;
}

配置CMakeLists

148行第一个是映射名,第二个是固定格式,相当于“功能包+_gencpp"

意为先编译头文件,再编译源文件

运行

调用ros的service服务,call后面跟话题,空格,Tab按两次补齐

修改参数,回车得到结果

 2.2.2客户端实现

新建.cpp文件

编码如下

#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"

/*
    客户端实现:提交两个整数,并处理结果
        1.包含头文件
        2.初始化ROS节点
        3.创建节点句柄
        4.创建一个客户端对象
        5.提交请求并处理响应
*/

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //2.初始化ROS节点
    ros::init(argc,argv,"DaBao");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建一个客户端对象
    ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");
    //5.提交请求并处理响应
    plumbing_server_client::AddInts ai;
    //5-1.组织请求
    ai.request.num1 = 10;
    ai.request.num2 = 20;
    //5-2.处理响应
    bool flag = client.call(ai);
    if (flag)
    {
        ROS_INFO("响应成功!");
        //获取结果
        ROS_INFO("sum=%d",ai.response.sum);
    }
    else
    {
        ROS_INFO("处理失败!");
    }
    
    return 0;
}

配置CMakeLists

将这三处复制并改为客户端的就行。

代码详解:

ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");

 

 服务客户端和服务端必须使用相同的 .srv 文件定义并共享相同的服务名称

 bool flag = client.call(ai);

 client呼叫服务端并返回值(一般为运行结果)

返回值来自doNums的返回值

2.2.3客户端优化实现

修改代码

#include "ros/ros.h"
#include "plumbing_server_client/AddInts.h"

/*
    客户端实现:提交两个整数,并处理结果
        1.包含头文件
        2.初始化ROS节点
        3.创建节点句柄
        4.创建一个客户端对象
        5.提交请求并处理响应
    实现参数的动态提交:
        1.格式: rosrun xxxx xxxx num num
        2.节点执行时,需要获取命令中的参数,并组织进 request
*/

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    //实现优化,获取命令中的参数
    if(argc!=3)
    {
        ROS_INFO("输入错误,请重试!");
        return 0;
    }
    //2.初始化ROS节点
    ros::init(argc,argv,"DaBao");
    //3.创建节点句柄
    ros::NodeHandle nh;
    //4.创建一个客户端对象
    ros::ServiceClient client = nh.serviceClient<plumbing_server_client::AddInts>("addInts");
    //5.提交请求并处理响应
    plumbing_server_client::AddInts ai;
    //5-1.组织请求
    // ai.request.num1 = 10;
    // ai.request.num2 = 20;
    ai.request.num1 = atoi(argv[1]);
    ai.request.num2 = atoi(argv[2]);
    //5-2.处理响应
    bool flag = client.call(ai);
    if (flag)
    {
        ROS_INFO("响应成功!");
        //获取结果
        ROS_INFO("sum=%d",ai.response.sum);
    }
    else
    {
        ROS_INFO("处理失败!");
    }
    
    return 0;
}

实现效果

代码解析:

if(argc!=3)

{

ROS_INFO("输入错误,请重试!");

return 0;

}

//2.初始化ROS节点

ros::init(argc,argv,"DaBao");

关于这里为什么后进行初始化

可以看到init会自动解析数据并剔除无用内容,此处argv[0]为文件名

关于atoi

ai.request.num1 = atoi(argv[1]);

ai.request.num2 = atoi(argv[2]);

 2.2.4注意事项

 应位于组织数据之前,发送请求之后

实现效果

 

2.3参数服务器

2.3.1 参数服务器理论模型

2.3.2 对参数的增/改

新建功能包和源文件,编码如下

#include "ros/ros.h"
/*
    需要实现参数的新增与修改

*/
int main(int argc, char *argv[])
{
    ros::init(argc,argv,"set_param_set");
    ros::NodeHandle nh;
    //参数新增:
    //manner1:句柄.setParam("键",值)
    nh.setParam("type","daHuang");
    nh.setParam("radius",0.15);
    //manner2:ros::param::set("键",值)
    ros::param::set("type_1","xiaoBai");
    ros::param::set("radius_1",0.15);
    //参数修改:同上,本质是覆盖
    nh.serParam("type","xiaoHuang");
    return 0;
}

CMakeLists配置

实现结果

rosparam list:展示当前参数服务器的所有参数

rosparam get /键:查询键的值

2.3.3 参数服务器获取参数

API 1:

参数服务器操作之查询_C++实现:
    在 roscpp 中提供了两套 API 实现参数操作
    ros::NodeHandle

        param(键,默认值) 
            存在,返回对应结果,否则返回默认值

        getParam(键,存储结果的变量)
            存在,返回 true,且将值赋值给参数2
            若果键不存在,那么返回值为 false,且不为参数2赋值

        getParamCached(键,存储结果的变量)--提高变量获取效率
            存在,返回 true,且将值赋值给参数2
            若果键不存在,那么返回值为 false,且不为参数2赋值

        getParamNames(std::vector<std::string>)
            获取所有的键,并存储在参数 vector 中 

        hasParam(键)
            是否包含某个键,存在返回 true,否则返回 false

        searchParam(参数1,参数2)
            搜索键,参数1是被搜索的键,参数2存储搜索结果的变量

    ros::param ----- 与 NodeHandle 类似

解析:getParamCached:

查询缓存中是否已经有该键(之前是否被调用过),若有,则直接调用,没有,执行远程调用。

getParamNames:

    ros::NodeHandle nh;
    std::vector<std::string> names;
    nh.getParamNames(names);
    for(auto &&name: names)
    {
        ROS_INFO("遍历的元素:%s",name.c_str());
    }

std::vector<std::string> names;

 

 1.声明和初始化

std::vector<std::string> names; // 空的字符串向量
std::vector<std::string> names = {"Alice", "Bob", "Charlie"}; // 初始化列表

2.添加元素

names.push_back("David"); // 在末尾添加 "David"

3.访问元素

std::cout << names[0] << std::endl; // 访问第一个元素(不安全)
std::cout << names.at(0) << std::endl; // 安全访问第一个元素

4.删除元素

names.pop_back(); // 删除末尾元素
names.erase(names.begin() + 1); // 删除第二个元素

 5.插入元素

names.insert(names.begin(), "Eve"); // 在开头插入 "Eve"

 6.迭代元素

for (const auto& name : names) {
    std::cout << name << std::endl; // 打印每个名字
}

 7.获取大小和容量

std::cout << names.size() << std::endl; // 当前元素个数
std::cout << names.capacity() << std::endl; // 当前分配的容量

8.清空元素

names.clear(); // 清空向量

循环解析:

实现效果:

searchParam:

实现效果:

API 2:

使用的是param命名空间下的函数(ros::param::function)实际作用可根据名称与上面一一对应

 2.3.4参数服务器删除函数


    参数服务器操作之删除_C++实现:

    ros::NodeHandle
        deleteParam("键")
        根据键删除参数,删除成功,返回 true,否则(参数不存在),返回 false

    ros::param
        del("键")
        根据键删除参数,删除成功,返回 true,否则(参数不存在),返回 false

2.4常用命令

2.4.1 rosnode

rosnode ping    测试到节点的连接状态
rosnode list    列出活动节点
rosnode info    打印节点信息
rosnode machine    列出指定设备上节点
rosnode kill    杀死某个节点
rosnode cleanup    清除不可连接的节点

可以使用rosnode列出相关指令

rosnode xx -h可以详细查询

打开话题通信

rosnode list效果如下

其中rosout是roscore启动的节点

rosnode ping查看节点连接状况

rosnode info查看节点信息

 rosnode machine查看某设备上的节点

 rosnode kill效果如下

rosnode cleanup:

启动乌龟节点和控制节点,再关闭

节点依然被认为存活

可以用cleanup清除

 2.4.2 rostopic

rostopic bw     显示主题使用的带宽
rostopic delay  显示带有 header 的主题延迟
rostopic echo   打印消息到屏幕
rostopic find   根据类型查找主题
rostopic hz     显示主题的发布频率
rostopic info   显示主题相关信息
rostopic list   显示所有活动状态下的主题
rostopic pub    将数据发布到主题
rostopic type   打印主题类型

打开话题通信

rostopic list

后两个是和日志相关的


rostopic echo 获取相应话题传递的消息

 需要注意的是,如果使用的是自定义类型的消息,需要进入相关目录才能使用


 rostopic pub可以发布消息

 格式:rostopic pub  (-r num(消息发布的频率,可不谢)) 话题 消息类型 后面可以用Tab补齐


rostopic info

查看话题的相关信息


rostopic hz查看话题发布频率(每发布一次,打印一次)

2.4.3 rosservice

rosservice args 打印服务参数
rosservice call    使用提供的参数调用服务
rosservice find    按照服务类型查找服务
rosservice info    打印有关服务的信息
rosservice list    列出所有活动的服务
rosservice type    打印服务类型
rosservice uri    打印服务的 ROSRPC uri

启动服务端

rosservice  list

后四个与日志相关

rosservice call 服务(两次Tab补齐,数据修改后回车)

 rossevice info

rosservice type打印服务的数据类型

 2.4.4 rosmsg

rosmsg show    显示消息描述
rosmsg info    显示消息信息
rosmsg list    列出所有消息
rosmsg md5    显示 md5 加密后的消息
rosmsg package    显示某个功能包下的所有消息
rosmsg packages    列出包含消息的功能包

rosmsg list | grep -i msg名称,查询相关消息(全部列出很多,不方便使用)

rosmsg show显示消息描述(和info作用好像一样?)

 2.4.5 rossrv

rossrv show    显示服务消息详情
rossrv info    显示服务消息相关信息
rossrv list    列出所有服务信息
rossrv md5    显示 md5 加密后的服务消息
rossrv package    显示某个包下所有服务消息
rossrv packages    显示包含服务消息的所有包

 和msg一模一样,直接参考msg就行

2.4.6 rosparam

rosparam set    设置参数
rosparam get    获取参数
rosparam load    从外部文件加载参数
rosparam dump    将参数写出到外部文件
rosparam delete    删除参数
rosparam list    列出所有参数

rosparam list(这里全是roscore的)

rosparam set

rosparam get

rosparam delete

rosparam dump xx.yaml将参数保存到磁盘上(序列化)

重启roscore

rosparam load xx.yaml将磁盘上的数据载入到内存中

2.5 实操

2.5.1 控制乌龟运动

1.话题与消息获取

列出话题,打开计算图确定是哪一个

获取话题下的消息类型

 获取消息的格式

分别为线速度和角速度

线速度中的xyz对于三维坐标系中的坐标表示的含义

角速度中的xyz可以参考文档:2.5.1 实操01_话题发布 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

视频:098实操01_话题发布03_速度格式_Chapter2-ROS通信机制_哔哩哔哩_bilibili

2.验证速度消息

挂起

控制乌龟运动获得消息

 2.5 实操
2.5.1 控制乌龟运动
1.话题与消息获取

列出话题,打开计算图确定是哪一个

获取话题下的消息类型

 获取消息的格式

分别为线速度和角速度

线速度中的xyz对于三维坐标系中的坐标表示的含义

角速度中的xyz可以参考文档:2.5.1 实操01_话题发布 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

视频:098实操01_话题发布03_速度格式_Chapter2-ROS通信机制_哔哩哔哩_bilibili
2.验证速度消息

挂起

控制乌龟运动获得消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值