ROS入门讲义学习笔记1
ROS入门讲义笔记1 - topic_demo
最近开始在学习ROS,几经辗转找到了这个教程。这个教程比较简单,能快速上手,而且视频节奏比较好,不是很墨迹。缺点就是比较面向初学者,不够深入,但是默认你有一些Linux系统操作的基本常识。前面经历的安装环境的坑后面会补上,今天首先记录一下对自己比较有用的第一个package,以便日后复习,同时分享给同样需要的同学们。
资源下载:
Topic简介
topic是常用的一种ROS中的通信方式。对于实时性、周期性的消息,使用topic来传输是最佳的选择。topic是一种点对点的单向通信方式,这里的“点”指的是node,也就是说node之间可以通过topic方式来传递信息。
topic是一种异步通信方式,publisher不断发送topic,subscriber可以在需要的时候进行订阅并处理。
topic是N对N的,一个topic可以由多个publisher发布。同时多个subscriber可以订阅同一个topic。
topic使用message进行通信,message通常是自定义的数据结构。
ROS中使用的激光雷达、IMU、相机、里程计等不断发布消息并接收处理的单元,通常都是使用topic来进行通信的。
topic要经历下面几步的初始化过程:首先,publisher节点和subscriber节点都要到节点管理器进行注册,然后publisher会发布topic,subscriber在master的指挥下会订阅该topic,从而建立起sub-pub之间的通信。注意整个过程是单向的。其结构示意图如下:
topic_demo Package功能描述
两个node,通过message进行通信
- talker 节点发布模拟GPS消息(假想出来的,可能是传感器串口/USB读出数据,经过ROS封装发布message)
- listener 节点接受并处理该消息
- msg 格式自定义,包括坐标和工作状态
步骤
- 创建工作空间并初始化
- 创建package
- 定义msg
- 编写cpp文件
- 修改CMakeLists.txt & package.xml文件
- catkin_make & source setup.bash文件
创建工作空间并初始化
//创建工作空间
$ mkdir -p ~/test_ws/src
$ cd ~/test_ws
//初始化工作空间
$ catkin_make
工作空间名字可以任意取,但是里面必须包含/src。
执行catkin_make后可以看到多出来了build 和 devel 文件夹,build文件夹放的是CMake和catkin的缓存信息和中间文件,devel中存放目标文件,日常只与src打交道。
创建package
//创建package
$ cd ~/test_ws/src
$ catkin_create_pkg topic_demo roscpp std_msgs
使用 [catkin_create_pkg 包名 依赖项] 命令,创建package。
定义msg
//创建msg文件
$ cd ~/test_ws/src/topic_demo
//$ roscd topic_demo //也可以使用这条命令
$ mkdir msg
$ cd msg
$vi gps.msg//自定义gps.msg文件
gps.msg中需要定义工作状态和坐标
//定义msg
string state
float32 x
float32 y
gps.msg文件编译后会在~/test_ws/devel/include/topic_demo中生成一个gps.h文件,供cpp文件#include使用。
编写cpp
代码放在包的src文件当中。
//创建msg文件
$ cd ~/test_ws/src/topic_demo
$ cd src
$vi talker.cpp//自定义gps.msg文件
- talker.cpp
//ROS头文件
#include <ros/ros.h>
//自定义msg产生的头文件
#include <topic_demo/gps.h>
int main(int argc, char **argv)
{
//用于解析ROS参数,第三个参数为本节点名
ros::init(argc, argv, "talker");
//实例化句柄,初始化node
ros::NodeHandle nh;
//自定义gps msg
topic_demo::gps msg;
msg.x = 1.0;
msg.y = 1.0;
msg.state = "working";
//创建publisher。gps_info是topic名称,1是buffer的长度,因为是时发时收的
ros::Publisher pub = nh.advertise<topic_demo::gps>("gps_info", 1);
//定义发布的频率 1Hz
ros::Rate loop_rate(1.0);
//循环发布msg
while (ros::ok())
{
//以指数增长,每隔1秒更新一次
msg.x = 1.03 * msg.x ;
msg.y = 1.01 * msg.y;
//相当于printf,打印到控制台
ROS_INFO("Talker: GPS: x = %f, y = %f ", msg.x ,msg.y);
//以1Hz的频率发布msg
pub.publish(msg);
//根据前面定义的频率, sleep 1s
loop_rate.sleep();//根据前面的定义的loop_rate,设置1s的暂停
}
return 0;
}
- listener.cpp
//ROS头文件
#include <ros/ros.h>
//包含自定义msg产生的头文件
#include <topic_demo/gps.h>
//ROS标准msg头文件
#include <std_msgs/Float32.h>
void gpsCallback(const topic_demo::gps::ConstPtr &msg)//常指针作为参数,是从gps.h文件中来
{
//计算离原点(0,0)的距离
std_msgs::Float32 distance;//ROS原生Float32结构,只有一个成员,data。
distance.data = sqrt(pow(msg->x,2)+pow(msg->y,2));
//float distance = sqrt(pow(msg->x,2)+pow(msg->y,2));
ROS_INFO("Listener: Distance to origin = %f, state: %s",distance.data,msg->state.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("gps_info", 1, gpsCallback);
//ros::spin()用于调用所有可触发的回调函数。将进入循环,不会返回,用于阻塞,类似于在循环里反复调用ros::spinOnce()。
ros::spin();
return 0;
}
修改CMakeLists.txt & package.xml文件
CMakeList.txt 和 package.xml文件都是已经生成的模板,需要在几个地方做一下修改
1.CMakeLists.txt
最前面find_package处,指定依赖项
find_package(catkin REQUIRED COMPONENTS
message_generation
roscpp
std_msgs
)
添加自定义的msg,以及依赖项
## Generate messages in the 'msg' folder
add_message_files(
FILES
gps.msg
)
//...
## Generate added messages and services with any dependencies listed here
generate_messages(
DEPENDENCIES
std_msgs
)
catkin配置
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES topic_demo
CATKIN_DEPENDS roscpp std_msgs message_runtime
# DEPENDS system_lib
)
Build中引用头文件,指定头文件路径
include_directories(
include
${catkin_INCLUDE_DIRS}
)
Build最后,Install前,要加上执行文件以及其所需依赖项
add_executable(talker src/talker.cpp )
add_dependencies(talker test1_generate_messages_cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_executable(listener src/listener.cpp )
add_dependencies(listener test1_generate_messages_cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
- package.xml
需要将原本的< package format=“2” > 中的 format="2"去掉,教程中给出的都是< run depend >, fomat="2"的话给出的都是 < exec_depend >,这个在后文中也需要修改。
在原有代码的基础上添加
<build_depend>message_generation</build_depend>
<run_depend>message_runtime</run_depend>
修改之后的文件如下
<?xml version="1.0"?>
<package>
<name>topic_demo</name>
<version>0.0.0</version>
<description>The topic_demo package</description>
<! ....... -->
<buildtool_depend>catkin</buildtool_depend>
<build_depend>message_generation</build_depend>
<build_depend>roscpp</build_depend>
<build_depend>rospy</build_depend>
<build_depend>std_msgs</build_depend>
<run_depend>roscpp</run_depend>
<run_depend>rospy</run_depend>
<run_depend>std_msgs</run_depend>
<run_depend>message_runtime</run_depend>
<!-- The export tag contains other, unspecified, tags -->
<export>
<!-- Other tools can request additional information be placed here -->
</export>
</package>
catkin_make & source setup.bash文件
这一步就是进行编译。
首先回到工作空间,catkin_make需要在工作空间内执行
$ cd ~/test_ws //回到工作空间
$ catkin_make
注意!!!编译之后需要刷新环境,指定包在哪里,要不然生成的可执行文件可能无法运行。
$ source ~/test_ws/devel/setup.bash
source指令的生命周期只在terminal打开期间,如果常用该工作空间的话,可以将setup.bash文件写入~/.bashrc,这样就不用每次开一个terminal的时候都执行一遍source命令
$ echo "source ~/test_ws/devel/setup.bash" >> ~/.bashrc
以上就是编写的全部工作,下面来进行一下测试
测试
- 开启master节点
打开一个终端,输入
$ roscore
运行master节点,因为所有的publisher和subscriber都需要在master上进行注册才能通信。
2. 运行talker
开启另一个终端,输入
$ rosrun topic_demo talker
可以看到之前编写的模拟GPS程序发布情况
3. 运行listener
再开启一个终端,输入
$ rosrun topic_demo listener
即可看到模拟接收的结果
以上就是对topic这种通信方式的学习和总结,希望对你也有帮助。