【一学就会的ROS基础入门教程 】03-1 ROS基础编程:ROS工作空间的创建、话题topic的发布与接收、以及话题消息的自定义使用

文前白话

本篇系列博客是依据哔站视频 【古月居】ROS 21讲 入门教程,学习整理,用于记录实践过程,加强学习,在系列整理的章节引导篇博客中,附带课程的课件和代码,需要的可以自行翻阅下载,共同学习。水平有限,若有整理错误之处,还请给予留言反馈,不胜感激。

系列整理的章节引导篇博客链接: 一学就会的ROS基础入门教程系列.

  • 本篇从ROS的相关应用编程继续学习: ROS工作空间的创建、话题topic的发布与接收、以及话题消息的自定义使用。

1、创建工作空间与功能包

关于工作空间的介绍

在这里插入图片描述
在这里插入图片描述

  • 具体的文件夹理解:
  • src : 存放代码的空间(功能包的代码、配置文件、lunach文件)
  • build: 编译空间(存放编译过程中产生的中间文件)
  • devel: 开发空间(编译生成的可执行文件、库、脚本)
  • Install: 安装空间 (install 指令生成的结果存放空间)
创建开发工作空间主要的终端操作命令步骤

在这里插入图片描述

  • 具体的步骤解析:
  • 1、从创建工作空间&初始化(创建文件夹src)
    在这里插入图片描述

在这里插入图片描述
2、在工作空间下进行编译:

在这里插入图片描述

  • 编译结果:

在这里插入图片描述

  • 再执行:
  • catkin_make install

在这里插入图片描述

创建功能包
  • 主要的命令流程:

在这里插入图片描述

  • 实际操作:在创建的 src 文件夹下:
  • 执行创建命令:

在这里插入图片描述

  • 生成功能包的两个标配文件:

在这里插入图片描述

  • CMakeLists.txt 和 package.xml 的作用:

功能包必须有的文件,package.xml 是介绍功能包的信息:名称、版本、功能、开发者信息、开源许可证、依赖信息等等。
CMakeLists.txt :描述功能包编译的规则,编译时候需要依赖的库

在这里插入图片描述

  • 其中生成的文件夹:src 放置功能包的代码文件(如cpp文件)、Include 放置 头文件(如c++的.h头文件)

  • 为了使功能包生效,进行编译:

在这里插入图片描述

  • 编译完成后,要运行功能包的某一个程序文件,先要设置一下工作空间的环境变量:
    执行:
source devel/setup.bash

在这里插入图片描述

  • 设置以后,才能让系统找得到工作空间,并且找到工作空间对应的功能包

在这里插入图片描述

  • echo $ROS_PACKAGE_PATH 是 ros 自身的环境变量,它会通过环境变量查找所有ros功能包的路径, 只有经过 设置功能包的 source devel/setup.bash 后, ROS_PACKAGE_PATH (ros 自身的环境变量)才会包含创建的工作空间的路径,才能找到对应的功能包,运行程序文件。

  • 为了避免每次创建功能包,忘记执行环境变量的配置,可以将 source devel/setup.bash 命令复制添加到 .bashrc 的配置文件里面:
    ** .bashrc在根目录下: (CTRL+h 可以显示隐藏文件)**
    在这里插入图片描述
    在这里插入图片描述

  • 这样,就不用每次都在终端进行这个工作空间的环境变量的配置。(重启终端生效)

2、简单的话题发布与接收

发布者Publisher的编程实现(发布 Topic)

原理图示:

在这里插入图片描述

  • 具体的操作:

1、创建功能包:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
2、创建话题发布者的代码

  • 发布话题的逻辑步骤:
    在这里插入图片描述

  • 话题发布代码


/**
 * 该例程将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist
 */
 
#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

int main(int argc, char **argv)
{
	//1、 (初始化)ROS节点初始化
	ros::init(argc, argv, "velocity_publisher");  // "velocity_publisher"节点名称; argc, argv  初始化的参数输入

	//2、(注册) 创建节点句柄,管理节点的资源
	ros::NodeHandle n;    

	// 3、(创建消息)创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
	// 队列理解:发布的频率较高,不能及时接收,就先存到队列中去
	//(如果队列满了,就会把时间较久的数据抛出掉,留下10个最新的数据)。
	ros::Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);

	// 4、(发布消息)设置循环的频率
	ros::Rate loop_rate(10);

	int count = 0;
	while (ros::ok())  
	{  //while 循环主要就是封装数据、并发布数据、延时满足频率进入到下次循环
	    // 初始化geometry_msgs::Twist类型的消息
		geometry_msgs::Twist vel_msg;   // 类
		vel_msg.linear.x = 0.5;   // 运动的线速度  m/s
		vel_msg.angular.z = 0.2;   // 角速度  弧度/m

	    // 发布消息
		turtle_vel_pub.publish(vel_msg);  
		//调用方法发布数据,数据内容会通过ROS底层系列的通讯机制,将数据压入队列,队列通过底层的以太网
		//的ros—topic 的 tcp 通讯机制不断的发出去,这里通过下面的ROS_INFO,输出信息,告知客户端,信息发送的状态
		ROS_INFO("Publsh turtle velocity command[%0.2f m/s, %0.2f rad/s]", 
				vel_msg.linear.x, vel_msg.angular.z);

	    // 按照循环频率延时
	    loop_rate.sleep();
	}

	return 0;
}


3、 配置话题发布者代码的编译规则:

  • 其实就是,规定编译可执行文件生成的文件名、链接相关的依赖库;
    在这里插入图片描述

  • 将上述两句指令,拷贝到 CMakeLists.txt 文件中

  • 对于指令的解释:

  • add_executable(velocity_publisher src/velocity_publisher. cpp)

  • 作用:描述将哪一个程序文件(velocity_publisher. cpp),编译成哪一个可执行文件(velocity_publisher src)

  • target_link_libraries(velocity_publisher S(catkin_LIBRARIES])

  • 作用: 帮助将生成的可执行文件与ROS 的一些库建立连接(c++的库)、

  • 在这里插入图片描述
    在这里插入图片描述

4、进行功能包的编译

在这里插入图片描述

在这里插入图片描述

5、设置环境变量:

  • 在工作空间的根目录:
    执行: source devel/setup.bash

  • 注:如果已经将 source devel/setup.bash 命令复制添加到 .bashrc 的配置文件里面,可以 忽略此步骤。

6、终端下运行功能包:


在这里插入图片描述
在这里插入图片描述

订阅者Suberscriber的编程实现(接收 Topic)

逻辑原理图:

在这里插入图片描述

在这里插入图片描述

具体操作:

1、在功能包的 src 中创建 话题订阅者的代码:


/**
 * 该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
 */
 
#include <ros/ros.h>
#include "turtlesim/Pose.h"

// 接收到订阅的消息后,会进入消息回调函数
void poseCallback(const turtlesim::Pose::ConstPtr& msg)  // turtlesim::Pose是消息的 结构,与turtle1/pose是一致的, 通过常指针调用
{
    // 将接收到的消息打印出来
    ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y);
}

int main(int argc, char **argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "pose_subscriber");

    // 创建节点句柄
    ros::NodeHandle n;

    // 创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback
    ros::Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);
//10是队列长度,存储从发布者传输的数据(缓冲区)
// 因为不知道什么时候会有消息传输进来,一旦发现,就

    // 循环等待回调函数(死循环,一旦发现 队列中 有数据进来就 立刻调用personInfoCallback来处理)
    ros::spin();

    return 0;
}

//注意:回调函数非常重要,要完成消息的处理,要高效(不嵌套) ;不能卡住,影响下一个数据的进入



2、配置话题订阅者代码编译规则:

在这里插入图片描述

  • 即是:

将:

在这里插入图片描述
添加到:

在这里插入图片描述

3、 进行编译

  • 在工作空间下执行:
catkin_make

4、配置环境变量:

  • 在工作空间的根目录:
    执行: source devel/setup.bash

  • 注:如果已经将 source devel/setup.bash 命令复制添加到 .bashrc 的配置文件里面,可以 忽略此步骤。

5、终端下运行订阅话题:

  • 终端下分别执行:
    在这里插入图片描述
    在这里插入图片描述

  • 当你不知道某个文件夹的具体某一个文件的名称时候,可以进入该文件夹下 用 tab 键 补全 显示来查看:

  • 如果TAB键不能补全,说明可能环境变量不对、或者程序没有编译成功。

  • 再打开海龟的键盘操纵节点,移动小海龟 :

在这里插入图片描述

  • 海龟对应的位置信息就会实时显示出来。

注:实现语言C++ 和 Python

  • 功能包的实现既可以是c++也可以是 python 语言,区别在于,python脚本语言不用进行编译。

在这里插入图片描述

  • 创建单独的文件夹来存放python代码文件:仅仅是为了区分c++,方便管理
    在这里插入图片描述

  • 同时,由于语言的不同,python 不需要编译,可以直接执行
    在这里插入图片描述

3、话题消息的自定义与使用

  • 现有的 ros 话题消息无法满足需求,可以自己来定义/发布话题

逻辑原理:

在这里插入图片描述

具体操作步骤:

1、自定义话题消息

在这里插入图片描述

  • 其中,注意理解宏定义的用法:

在这里插入图片描述

  • 在功能包的文件夹下创建自定义消息存放的文件夹:msg(方便查找管理)

在这里插入图片描述

  • 然后,创建自定义消息内容的文件:

在这里插入图片描述

  • 内容可以自行发布:

在这里插入图片描述

  • .msg 文件格式既不是c++也不是python文件,它是 ros 在编译过程中,结合具体的定义来变成对应的 c++ 或者 python 程序。

2、 设置编译规则:

  • 即是:添加功能包依赖(编译依赖+执行依赖)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 在添加编译选项

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、在工作空间下进行编译:

在这里插入图片描述
在这里插入图片描述

4、通过程序调用生成的头文件:

  • 创建发布者与订阅者的个自代码:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



/**
 * 该例程将发布/person_info话题,自定义消息类型learning_topic::Person
 */
 
#include <ros/ros.h>
#include "learning_topic/Person.h"

int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "person_publisher");

    // 创建节点句柄
    ros::NodeHandle n;

    // 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
    ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);

    // 设置循环的频率
    ros::Rate loop_rate(1);

    int count = 0;
    while (ros::ok())
    {
        // 初始化learning_topic::Person类型的消息
    	learning_topic::Person person_msg;
		person_msg.name = "Tom";
		person_msg.age  = 18;
		person_msg.sex  = learning_topic::Person::male;   // 通过宏定义调用显示 性别 信息的值male =1 

        // 发布消息
		person_info_pub.publish(person_msg);

       	ROS_INFO("Publish Person Info: name:%s  age:%d  sex:%d", 
				  person_msg.name.c_str(), person_msg.age, person_msg.sex);

        // 按照循环频率延时
        loop_rate.sleep();
    }

    return 0;
}


/**
 * 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
 */
 
#include <ros/ros.h>
#include "learning_topic/Person.h"

// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", 
			 msg->name.c_str(), msg->age, msg->sex);
}

int main(int argc, char **argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "person_subscriber");

    // 创建节点句柄
    ros::NodeHandle n;

    // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);

    // 循环等待回调函数
    ros::spin();

    return 0;
}

5、配置topic发布/接收代码编译规则:

在这里插入图片描述

  • 即是:在功能包文件夹下的 CMakeList.txt 中,添加:

在这里插入图片描述
在这里插入图片描述

  • 其中,add_dependencies…
    (有一些代码是动态生成的,需要把生成的可执行文件与动态生成的程序产生依赖关系;用来动态的跟我们生成的头文件产生连接(与自定义的消息产生连接))

6、编译并在终端运行发布者和订阅者:

在这里插入图片描述

  • 编译:

在这里插入图片描述

  • 运行 roscore, 打开节点管理器:

在这里插入图片描述

在这里插入图片描述

  • 实现通讯。

  • 此时,如果关闭 roscore (ROS Master) 发布者 与订阅者仍然可以继续通讯,但是不能 与其他的节点进行 通讯。

  • (这里已经提前设置了环境变量)

  • python版本:

在这里插入图片描述

  • 将代码放在对应的python文件夹中:
    在这里插入图片描述
  • 除去不用编译,其他步骤同c++版本步骤一致。
  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值