ROS学习记录

ROS学习记录

  • 第一步-创建工作空间
  • 第二步-创建功能包
  • 第三步-话题订阅和发布
  • 第四步-自定义消息类型
  • 第五步-自定义消息下的话题发布与订阅

第一步-创建工作空间

工作空间是一个存放工程开发相关文件的文件夹,主要包含了源代码空间src,编译空间build,开发空间devel,安装空间install
src目录中,包含了整个工程的编译列表文件(CMakeLists.txt),以及各个包package的编译列表文件和包的相关参数信息package.xml
创建工作空间的相关命令为

mkdir -p ~/catkin_ws/src          #创建在根目录下的工作空间的源代码空间路径 ~/catkin_ws/src,-p是指当已经存在时也不进行报错
cd ~/catkin_ws/src				  #进入源代码空间
catkin_init_workspace			  #初始化源代码空间的命令
cd ~/catkin_ws					  #进入工作空间
catkin_make 					  #编译整个工作空间

在成功编译完之后,工作空间中会自动生成build和devel两个目录及相关文件(如果你编译失败了,可以根据编译结果的提示修改CMakeLists.txt的编译内容)。在devel的目录中已经生成了几个setup.*sh的环境变量设置脚本。为了使这些环境变量可以生效,需要使用以下命令

source ~/catkin_ws/devel/setup.bash  

执行完命令之后可以用以下命令来检查是否生效,若路径中包含了刚才创建的工作空间路径即表明工作空间创建完成。

echo $ROS_PACKAGE_PATH

另外,source 命令只会在当前的终端生效,在下一次打开终端之后就需要重新执行source命令。可以将sourch命令添加到终端的配置文件中。我们可以在根目录中查看相关文件

cd ~/			#进入根目录
ls -a     	    #查看所有文件包括隐藏文件,可以看到其中有一个.bashrc的文件,该文件就是终端的配置文件
gedit .bashrc	#编辑这个文件

在文件下面添加一行命令

source ~/catkin_ws/devel/setup.bash

就可以不用在每次打开终端时重复输入source命令。

注:也可以直接文件重定向命令 echo “source ~/catkin_ws/devel/setup.bash”>> ~/.bashrc

第二步-创建功能包

ros中的功能包的组成格式为

package_name/
——CMakeLists.txt #功能包的编译信息文件
——package.xml #对于功能包属性的描述信息文件
——/include #头文件目录
——/launch #启动文件目录
——/msg #消息文件目录
——/src #源代码目录
——/srv #服务文件目录
注:功能包不可以嵌套其他的功能包,多个功能包之间必须平行的放置在代码空间中

创建功能的命令:

catkin_create_pkg  [depend1] [depend2] [depend3]

注: 创建功能包的命令要到工作区目录下的src文件夹中进行执行。
catkin_create_pkg:创建功能包的命令
package_name:功能包的名字
dependx:创建的功能所依赖的其他功能包,
在ROS操作系统中,存在两个性质的包路径,一个是系统工作区的包路径,一个是用户工作空间的包路径,可以用以下命令行来获取路径信息。

echo $ROS_PACKAGE_PATH

若想要删除自己创建的功能包时,只需要到工作区中删除该文件夹,然后再工作空间的根目录进行一次catkin_make即可。
若想要删除系统工作取的功能包时,则需要用以下命令来实现。

sudo apt-get purge    #删除功能包
sudo apt-get autoremove				#删除功能包的依赖包

第三步-话题订阅和发布

首先,在功能包路径中的src文件夹中创建c++源文件。
源文件主要包含以下内容:

#include "ros/ros.h"
//引用的头文件及其他需要的头文件
int main(int argc,char **argv)
{
	ros::init(argc,argv,"NODE_NAME");
	//创建节点,将argc和argv参数传入,并且节点名称不可重复
	ros::NodeHandle n;
	//创建节点句柄,用于对节点的相关操作
	
    ros::Publisher pub = n.advertise("TOPIC_NAME",1000);
    //创建一个话题,是对话题内容的定义,即话题包含的是字符串信息

    ros::Subscriber sub = n.subscribe("TOPIC_NAME",1000,${SUBCRIBE_CALLBACK_FUNCTION});
    //创建一个订阅话题的subscriber	
	
	ros::Rate loop_rate(10);
	//设置循环的频率,单位为hz
	
	while(ros::ok())
	{
		std_msgs::String msg;
		std::stringstream ss;
		ss<<"hello world";
		msg.data = ss.str();
		//将字符串流写入消息中
		pub.publish(msg);
		//发布消息
		ros::spinOnce();
		//对订阅消息的回调处理
		loop_rate.sleep();
		//周期循环
	}
	return 0;
}

编写完源文件后,需要设置编译选项文件,将功能包目录下的CMakeLists.txt打开,补充写入以下编译命令。

include_directories(include ${catkin_INCLUDE_DIRS})   				    #添加引用路径
add_executable(FILE_NAME src/*.cpp)										#添加编译对象信息,FILE_NAME是编译生成的可执行文件名称
target_link_libraries(FILE_NAME ${catkin_LIBRARIES})					#添加链接库信息
add_dependencies(FILE_NAME ${PROJECT_NAME}_generate_messages_cpp)	    #添加依赖库信息

保存后回到catkin_ws目录下,执行catkin_make命令。
编译成功后,执行该节点。

roscore #在一个终端中输入
rosrun PACKAGE_NAME NODE_NAME #在新的终端下打开

第四步-自定义消息类型

首先在功能包的目录下创建msg的文件夹,并在文件夹中创建消息文件 MSG_NAME.msg,并且定义消息中包含的信息,例如:

uint8 data1 
float32 data2
uint32 data3

然后在到功能包的CMakeLists.txt和package.xml中添加相关编译及依赖选项。
在package.xml中,需要补充的是

message_generation		#这里编译的依赖包
message_runtime	#这里是运行时的依赖包

在CMakeLists.txt中,需要补充的是

#寻找依赖的元功能包
find_package(catkin REQUIRED COMPONENTS
	……
	roscpp
	rospy
	std_msgs
	message_generation
	……
)
#添加好catkin编译需要的依赖
catkin_package(
	……
	CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
	……
)
#添加自定义的消息文件
add_message_files(
	FILES
	MSG_NAME.msg
)
#这里面是用到的消息的依赖
generate_messages(
	DEPENDENCIES
	std_msgs
)

设置完成后回到catkin_ws目录下进行catkin_make进行编译即可完成自定义消息的创建。

第五步-自定义消息下的话题发布与订阅

整合前几步中,以一个实际的案例来描述设计一个自定义消息的话题发布订阅的实现过程。

  1. 首先在工作区 ~/catkin_ws/src 中使用功能包创建命令 catkin_create_pkg 创建一个名为 my_tutorials 的功能包。
cd ~/catkin_ws/src															    #进入目录
catkin_create_pkg my_tutorials roscpp rospy message_generation message_runtime  #创建功能包
cd ~/catkin_ws																	#回到工作区根目录
catkin_make																		#编译工作区
rospack list																	#查看功能包列表中是否包含新创建的功能包
  1. 在功能包目录下创建msg文件夹,并且创建自定义消息文件my_msg1.msg,编写消息文件内容。
cd ~/catkin_ws/src/my_tutorials    #进入功能包的文件夹目录
mkdir -p msg					   #创建msg目录
gedit my_msg1.msg 			       #创建消息文件

以下是msg中的内容,保存后退出

uint8 data1
uint32 data2
float32 data3
string data4
  1. 回到功能包路径下,接下来需要修改的是功能包的CMakeLists.txtpackage.xml补充编译选项信息和包依赖信息。
cd ~/catkin_ws/src/my_tutorials   #打开功能包路径
gedit package.xml				  #编辑包信息文件

要保证package.xml文件下包含以下依赖包信息:

  roscpp
  rospy
  message_generation

  roscpp
  rospy
  message_generation

  message_runtime
  roscpp
  rospy
  message_generation

在创建时我们已经添加了这几个依赖包,因此就无需再次手动添加。
在CMakeList.txt文件中添加对消息文件my_msg1.msg的编译选项:

find_package(catkin REQUIRED COMPONENTS
  message_runtime
  roscpp
  rospy
  message_generation
)
#添加依赖包信息

add_message_files(
 FILES
 my_msg1.msg
)
#添加需要编译的消息文件

catkin_package(
  ……
  CATKIN_DEPENDS message_runtime roscpp rospy message_generation
  ……
)
#catkin编译时所依赖的包(似乎不加也可以编译通过)

generate_messages(
  DEPENDENCIES
  std_msgs  # Or other packages containing msgs
)
#由于我们的消息文件中的 uint8 uint32 等类型是ros系统工作区中 std_msgs包所包含的,因此消息生成也需要添加对于std_msgs包的依赖信息。

回到工作区目录下进行编译

cd ~/catkin_ws/
catkin_make
  1. 创建一个话题发布节点。
    进入功能包目录下的src目录创建节点源文件(节点代码,可以是cpp也可以是python)
cd ~/catkin_ws/src/my_tutorials/src			#进入到源代码目录
gedit talker.cpp							#创建话题发布节点

键入以下代码并保存

#include "ros/ros.h"
//调用ros的头文件
#include "my_tutorials/my_msg1.h"
//调用自定义消息的头文件
#include "std_msgs/String.h"
//调用std_msgs头文件
#include 

int main(int argc,char **argv)
{


    ros::init(argc,argv,"talker");
    //创建话题发布节点

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

    ros::Publisher pub = n.advertise("my_topic1",1000);
    //创建一个名为my_topic1的话题,其中 重定向了话题的消息类型
    //1000定义话题的队列长度
    
    ros::Rate loop_rate(10);
    //设置循环频率

    my_tutorials::my_msg1  msg;     
    //定义自定义消息变量

    int count = 0;

    while(ros::ok())
    {
		std::stringstream ss;	
        count++;        
		ss << "string count " << count;
		//重定向字符串流ss
        msg.data1+=1;
		msg.data2+=10;
		msg.data3+=0.1f;
		msg.data4 = ss.str();
        ROS_INFO("topic message subcribe %d",count);
	    //打印消息调试
        pub .publish(msg);
        //发布信息
        ros::spinOnce();
        //订阅信息回调函数
        loop_rate.sleep();
        //休眠
    }
    return 0;
} 

接下来要对编写CMakeList.txt中的编译选项,在该文件下,有一块区域是专门定义编译内容的:

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

add_executable(talker_node src/talker.cpp)
add_dependencies(talker_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(talker_node
  ${catkin_LIBRARIES}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/my_tutorials.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/my_tutorials_node.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )

其中,只需要根据文本中的提示信息,添加对talker.cpp的编译信息即可:

add_executable(talker_node src/talker.cpp)
add_dependencies(talker_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(talker_node
  ${catkin_LIBRARIES}
)
#其中talker_node是对talker.cpp编译的结果,并且需要将add_executable放置到第一行,否则将会出现找不到add_dependencies对象target的报错。

: 其中的talker_node可以是自定义的,也可以通过宏字符${PROJECT_NAME}来统一工程文件命名。

编写完成后回到工作区进行编译:

cd ~/catkin_ws
catkin_make
  1. 测试话题发布节点是否正常运行
    通过rostopic echo的功能来测试话题发布节点是否正常运行
#在终端1中
roscore #打开ros内核
#在终端2中
rosrun my_tutorials talker_node #运行话题发布节点
#在终端3中
rostopic list  #查看是否存在
#如果可以看到my_topic1则说明节点正常运行。
#可以通过rostopic echo my_topic1 来订阅话题消息

ROS学习记录_第1张图片
ROS学习记录_第2张图片
以上是对话题发布节点的测试图

  1. 编写话题订阅节点并运行
    同样的方式编写listener.cpp,同样在功能包的src目录下创建:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include "my_tutorials/my_msg1.h"

//注意callback的传参需要根据消息类型进行变换
void pub_callback(const my_tutorials::my_msg1::ConstPtr& msg)
{
	ROS_INFO("data1:%d",msg->data1);
	ROS_INFO("data2:%d",msg->data2);
	ROS_INFO("data3:%f",msg->data3);
	ROS_INFO("data4:%s",msg->data4.c_str());
}

int main(int argc,char **argv)
{
    ros::init(argc,argv,"listener");
    //创建订阅任务节点
    ros::NodeHandle n;
    //创建节点句柄
    ros::Subscriber sub = n.subscribe("my_topic1",1000,pub_callback);
    //创建一个订阅my_topic1话题的subscriber
    ros::spin();
    //订阅信息回调函数     
    return 0;
} 

以同样的方式编写CMakeList.txt:

add_executable(listener_node src/listener.cpp)
add_dependencies(listener_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(listener_node
  ${catkin_LIBRARIES}
)

运行订阅节点和发布节点:

roscore 
rosrun my_tutorials talker.cpp
rosrun my_tutorials listener.cpp

:三个命令需要在三个终端中打开。
ROS学习记录_第3张图片
至此,便完成了自定义消息的话题发布订阅过程。

你可能感兴趣的:(linux)