ROS基础理论学习(含代码)

ROS基础理论学习

一些疑惑:

1.talker,nh,chatter的关系?

talker是节点名称,nh是节点的实例化,chatter是发布的话题名称。 talker是对外(其他节点)的名字,nh是对内(本节点)控制talker这个节点的具体操作的句柄。 所以任何节点都可以的句柄都可以叫nh,同时,节点名talker,看起来是发布者,但其实内里可以使用句柄nh做订阅操作,可以说talker告诉别人你叫什么,nh告诉别人你能做什么,两者可以不统一。一般nh做的事体现在chatter(话题名称)上和talker(节点名称)上都有体现。

2. “setlocale(LC_ALL,“”);”这行代码啥意思?

chat:
在 ROS 节点中,尤其是在处理多语言输出时,调用 setlocale(LC_ALL, “”); 可以确保程序能够正确处理和显示不同语言的字符(例如中文、法文等),避免出现乱码问题。
总之,这行代码的作用是让程序适应当前环境的语言和文化设置,以确保输出的文本能够正确显示。

3.std_msgs::String和std::string有啥区别,为什么这么像?

chat:

来源:
    std::string:是 C++ 标准库的一部分,提供了一种灵活的方式来处理字符串。
    std_msgs::String:是 ROS(Robot Operating System)中的一个消息类型,专门用于在 ROS 系统中传递字符串信息。

基础:
    std_msgs::String 是基于 std::string 实现的,它内部封装了一个 std::string 对象,以便在 ROS 消息传递框架中进行使用。这种设计使得 std_msgs::String 可以方便地在不同的 ROS 节点之间传输字符串数据。

功能:
    std::string 允许进行各种字符串操作,如拼接、搜索、修改等。
    std_msgs::String 则主要用于消息的传递和序列化/反序列化,以便在网络上发送或接收字符串数据。

总结

可以说,std_msgs::String 是 ROS 中对 std::string 的扩展和应用,前者的作用主要是封装、传输数据,后者主要作用是处理字符串。

4. “ std::stringstream ss; ss << msg_front << count; ” 这里的std::stringstream又是啥?ss << msg_front << count;又是什么意思?

chat:
std::stringstream 是 C++ 标准库中的一个类,属于 头文件,用于处理字符串流。它允许你将多个数据类型(如字符串、整数等)格式化为字符串,这种方式类似于输出到控制台,但实际上是构造一个字符串。
也就是说std::stringstream是一个融合器,可以把不同种类的数据融合成一个字符串。

ss << msg_front << count; 这行代码使用了 << 操作符将 msg_front 和 count 的值添加到 ss 中。
在执行 ss << msg_front << count; 后,你可以调用 ss.str() 来获取最终生成的字符串。例如,如果 msg_front 是 "Count: " 以及 count 是 5,那么 ss.str() 将返回 “Count: 5”。
妙~~~啊~~~

5. “ros::Rate r(1);” 这啥意思?

具体来说,这行代码的意思是创建一个 ros::Rate 对象 r,并将其设置为每秒1次的频率(即1 Hz)。
控制执行频率:通过设置频率,可以确保在主循环中执行任务(如发布消息或处理数据)的间隔时间。
实现定时循环:在循环中使用 r.sleep() 可以使程序暂停,直到达到设定的频率,从而维持稳定的执行速率。

6. “ros::spinOnce()”;和 "ros::spin(); " 有什么区别?

ros::spin():
功能:ros::spin() 会进入一个循环,不断地处理回调队列中的消息。这意味着只要节点处于活动状态,ros::spin() 将持续运行,直到节点关闭。
适用场景:当你希望节点在后台持续接收和处理消息时使用,例如在订阅者节点中。

ros::spinOnce()
功能:ros::spinOnce() 处理一次回调队列中的所有消息,但不会进入循环。调用该函数后,控制权会返回到主程序中,可以继续执行其他代码。
适用场景:当你需要在处理回调的同时执行其他任务时使用,例如在一个循环中进行定时操作或其他逻辑处理。

区别是ros::spin(),是一个循环,程序会一直运行ros::spin()来处理回调队列中的消息,直到节点关闭;而ros::spinOnce()只处理一次回调队列中的消息,如果要重复处理,则需把ros::spinOnce()放入循环中

7.这里的msg.data = ss.str();msg为什么能.data,ss为什么不是.c_str()?

msg是std_msgs::String 类型的数据,std_msgs::String 类型的数据是ROS系统特有的,专门用来发布数据用的,msg.data相当于是c语言中std::string字符串类型的,包含了std_msgs::String 类型数据的字符串的具体内容。

ss 是一个 std::stringstream 类型的对象,std::stringstream 是一个可以在内存中读写字符串的流类。它具有一个成员函数 str(),用来获取当前流中的内容并返回一个 std::string 对象。

.c_str() 是一种将 std::string 转换为 C 风格字符串(const char*)的方式,通常用于与需要 C 风格字符串的函数或库进行交互。

#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;
}

1.client.cpp里“if(argc!=3) {ROS_ERROR( “请提交两个整数”); } ” 这里的argc值是啥,哪来的?

argc(argument count)表示传递给程序的命令行参数的数量,包括程序本身的名称。
argv(argument vector)是一个字符串数组,其中包含了每个命令行参数的值。

通俗来讲,argc就是你这个主函数的参数的数量,默认情况下只包含文件名这一个参数,当文件需要输入参数时,argc = 输入参数数量+1 ,因为这个程序要调用求和服务,所以要输入两个参数,加上文件名一共三个。

2. 写出一个服务和一个客户的思路是?

第一步:定义.srv文件以“ — ”隔开接受数据和回复数据类型。
第二步:编写服务.cpp文件,
1.里面要有服务的头文件
2.定义子函数来完成服务功能:
bool 函数名(包名::.srv文件名::Request& req,包名::.srv文件名::Response& resp)
{
函数里面能调用.srv定义的数据类型,
req.(.srv定义的接受数据名称) 能调用客户传来的数据
resp.(.srv定义的回复数据名称) 能给回复值赋值
还可以打印出接收的数据:
ROS_INFO(“服务器收到的数据为:%d,%d”,num1,num2);
return true;
}
3.主函数里要初始化节点,实例化句柄,创建服务:
ros::ServiceServer server=nh.advertiseService(“服务名”,子函数名)

创建客户:
ros::ServiceClient client = nh.serviceClient<软件包名::.srv文件名>(“服务名”);
ros::service::waitForService(“num”);

3.client.cpp里 srv_test::num ai; ai.request.num1 = atoi(argv[1]); ai.request.num2 = atoi(argv[2]); 这里atoi什么意思?

先说明一下,srv_test::num ai;定义了一个服务的数据,/srv_test/srv/num.srv文件里定义了输入参数:num1和num2,输出参数:sum。所以这里的ai包括这三个数据,ai.request.num1、ai.request.num2、ai.response.sum。

atoi 是一个 C++ 标准库函数,用于将字符串转换为整数。它的全称是 “ASCII to integer”。如果 argv[1] 或 argv[2] 不能被转换为有效的整数,atoi 将返回 0。

赋值给请求属性:
ai.request.num1 和 ai.request.num2 是请求对象中的成员变量,分别用于存储两个整数参数。这些值将在后续调用服务时发送给服务端。这里argv[0]是文件名,argv[1]以及后面的所有参数都是可以自己输入的,输入几个参数看服务需要几个。

#include "ros/ros.h"
#include "srv_test/num.h"

int main(int argc,char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"num_client");
    ros::NodeHandle nh;
    
    if(argc!=3)
    {
        ROS_ERROR("请提交两个整数");

    }
    ros::ServiceClient client = nh.serviceClient<srv_test::num>("num");
    ros::service::waitForService("num");
    srv_test::num ai;
    ai.request.num1 = atoi(argv[1]);
    ai.request.num2 = atoi(argv[2]);

    bool flag=client.call(ai);

    if(flag)
    {
        ROS_INFO("请求正常处理,响应结果为:%d",ai.response.sum);

    }
    else
    {
    ROS_ERROR("请求处理出错");
    return 1;
}


return 0;

}

今天中午写了一个服务软件包:客户端输入参数1,2,3,分别回应三个不同的字符串,记录一些问题

1. .srv里定义字符串: string 参数名

在 ROS 的 srv 文件中,常见的数据类型包括以下几种:

基本数据类型:
    int8, int16, int32, int64
    uint8, uint16, uint32, uint64
    float32, float64
    bool
    string

数组:
    你可以在 srv 文件中定义数组类型,例如 float64[],表示一个浮点数数组。

2. 回应字符串的回应打印有问题,打印string、std::string类型要加.c_str()

srv_test::music music;                        #定义服务参数以作为回应的接收载体,也方便调用

music.request.ques1 = atoi(argv[1]);         	#将命令行参数 argv[1] 转换为整数并赋值给 music.request.ques1
 
bool flag = client.call(music);							#向服务发送请求,输入参数为music

if(flag)
    {
        ROS_INFO("请求发送成功,回复为“%s”",music.response.ans1.c_str());   #在.srv里定义的ans1是string类型,打印的时候要加**.c_str()**
    }
    else
    {
        ROS_ERROR("请求出错");
    }

3.自己的几点收获:客户端的输入参数都是作为字符串的一部分存储在 char *argv[] 里,int argc 是参数个数,所以请求服务时的输入参数要用 atoi(argv[1]) 把字符串转为int类型。把发送请求作为bool值可以判断请求是否成功。

4.关于服务和客户的基本框架:

服务:music_server.cpp

#include "ros/ros.h"
#include "srv_test/music.h"

bool music_ques_ans(srv_test::music::Request &req,srv_test::music::Response &res)
{
    setlocale(LC_ALL,"");
    int ques1=req.ques1;

    ROS_INFO("接收到的数据为“%d”",ques1);
    if (ques1==1)
    {   
        res.ans1="chong";
 
    }
       
    else if (ques1==2)
    {   
        res.ans1="tian";

    }
    else if (ques1==3)
    {   
        res.ans1="chong";
   
    }
    else 
    { 
        ROS_ERROR("请输入 1  或 2 或 3");
        return 1;
    }
    return true;

}


int main(int argc,char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"music_server");

    ros::NodeHandle nh;
    ros::ServiceServer server=nh.advertiseService("music",music_ques_ans); 
    ROS_INFO("服务已启动");
    ros::spin();
    return 0;
}

客户:music_client.cpp

#include "ros/ros.h"
#include "srv_test/music.h"

int main(int argc,char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"music_client");
    ros::NodeHandle nh;
    


    ros::ServiceClient client = nh.serviceClient<srv_test::music>("music");
    ros::service::waitForService("music");
    srv_test::music music;

    music.request.ques1 = atoi(argv[1]);
  

    bool flag = client.call(music);
    if(flag)
    {
        ROS_INFO("请求发送成功,回复为“%s”",music.response.ans1.c_str());
    }
    else
    {
        ROS_ERROR("请求出错");
    }
    return 0;
}

基本框架:

  1. List item

先写.srv文件,定义服务的输入、输出参数,例如,

int32   ques1
---
string   ans1

然后配置文件,配置好后直接编译,在.devel/include里生成.h头文件。

  1. List item
    写.cpp的服务和客户文件
    先说server.cpp
    包含两个函数:主函数和服务函数
    主函数:

初始化节点,实例化节点,
定义服务,

ros::ServiceServer server=nh.advertiseService("服务名",服务函数名);

重复运行服务,

ros::spin();

服务函数:
定义,
函数类型 函数名(包名::.srv文件名::Request &req,包名::.srv文件名::Response &res)
接收输入参数,自定操作。

  1. List item
    client.cpp
    初始化节点,实例化节点,定义客户,
 ros::ServiceClient client = nh.serviceClient<包名::.srv文件名>("服务名");

等待服务响应,

ros::service::waitForService("服务名");

服务参数实例化,

包名::.srv文件名  实例化名称

把作为字符的输入参数转为.srv里定义的对应的类型

实例化名称.request.服务自定义类型名  = atoi(argv[直接找到对应的位置]);

发送请求同时将其作为bool值

bool flag = client.call(实例化名称);

打印得到的响应

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值