从一个hello ROS程序开始,包括分清ROS一个简单工作区的文件架构、编写程序、修改文件到执行等
创建“骨架”
1、创建工作区
可以把工作区类比成一个project类的文件夹,我们在一个任务中所创建的包都应该放入其中。
用标准的mkdir命令来创建工作区以及src子文件夹(workspace)(实际类似是一个文件夹)
mkdir -p test_ws/src
- 在之后的工作中,test_ws/都作为工作区目录。
catkin编译系统一次性编译同一工作区中的所有功能包。因此如果工作涉及几个相互独立的项目,则维护数个独立的工作区是必要的。
再执行编译:
catkin_make
2、创建功能包
打开src目录,键入下列代码创建功能包:
catkin_create_pkg 包名
catkin_create_pkg hello
完成这项操作之后,src下边多了hello功能包(src/hello/),且hello包里有如下两个文件:
- CMakeLists.txt: Cmake脚本文件,Cmake是一个跨平台编译系统,这个包含了一系列编译指令,包括应该生成哪种可执行文件,源文件,头文件和链接库的位置。
- package.xml:之前讨论过,略。
这两个文件也是我们之后对于每个功能包的成功实现要更改的文件。catkin_create_pkg所作的事情仅此而已。
这个三层的目录结构(工作区/src/包)对于简单项目看起来大材小用,但是catkin编译系统需要它。
包命名规范: 首字符必须小写字母,只允许小写字母、数字、下划线的出现。
编写ROS程序
与书上不同的是,我选择在功能包目录(hello)中创建src目录来存放C++源文件,尽管它不是严格必要的。
1、在功能包下创建src
在hello目录下打开shell
mkdir src
2、程序编写及解读
在src目录下创建hello.cpp文件,代码贴出
1 #include <ros/ros.h>
2 int main(int argc, char **argv)
{
3 ros::init(argc, argv, "hello_ros") ;
4 ros::NodeHandle nh ;
5 ROS_INFO_STREAM("hello, ros");
6 return 0;
}
代码解读:
- 1:初始化ROS客户端库。在程序起始处调用一次,第三个参数代表节点的默认节点名称
默认节点名可以通过之前提过的命令行__name:=方式或者之后介绍的launch文件中覆盖(重新修改)
- 2:实例化的对象是程序用于和ROS系统交互的主要机制。创建之后,该程序就会注册为The master的节点。
在内部,nodehandle类维护一个引用计数,仅仅在第一个nodehandle对象创建时才会在the master中注册新的节点,同样只有所有Nodehandle对象都销毁时,节点才会销毁。
这意味着,在一个程序中使用标准的roscpp接口来运行多个节点是不可能的。
- 6:宏,将生成一条消息,且这一消息被发送到不同位置,包括控制台窗口。第四章会有详细介绍。
编译运行
将ROS程序编译运行,交给catkin编译系统来处理,共如下几步:
1、声明依赖库
如果用catkin_create 后边跟全依赖的话不用修改关于依赖的这两个文件
意义:确保catkin能够向C++编译器提供标记来定位编译功能包所需的头文件和链接库。
a. 修改CMakelist文件
打开功能包(hello)下的CMakeLists.txt文件
默认:
find_package(catkin REQUIRED)
修改为
find_package(catkin REQUIRED COMPONENTS 依赖包名)
它提供了ROS的C++客户端库
代码如下:
find_package(catkin REQUIRED COMPONENTS roscpp)
- 注:这里其实可以不必这么麻烦,在我们创建功能包的时候完全可以包名后加所需依赖以空格分隔。实际上这两种操作得到的效果都是一样的。
其次,加入如下代码:
include_directories(
include
${catkin_INCLUDE_DIRS}
)
b.修改xml文件
打开功能包(hello)下的package.xml文件
通过buile_depend(编译依赖)和run_depend(运行依赖)两个关键字实现:
<build_depend>依赖库名</build_depend>
<run_depend>依赖库名</run_depend>
在我们的编译和运行都要用到roscpp库,因此
代码如下:
<build_depend>roscpp</build_depend>
<run_depend>roscpp</run_depend>
- 注:在清单文件中声明的依赖库并没有在编译过程中用到;
如果你在此处忽略它们,你可能不会看到任何错误消息,直到发布你的包给其他人,他们可能在没有安装所需包的情况下编译你发布的包而导致报错。 - 注:在indiago之后的版本中,都将exec_depend替换掉run_depend
2、声明可执行文件
修改CMakelist文件
声明我们需要创建的可执行文件,一般形式如下:
add_executable(executable-name source-files)
target_link_libraries(executable-name ${catkin_LIBRARIES})
sourcefiles:是以CMakelist文件目录为当前目录索引而得到。
- 第一行声明了我们想要的可执行文件名,以及生成此可执行文件所需的源文件列表。(如果你有多个源文件,列在此处,用空格隔开)
- 第二行告诉cmake当连接这个可执行文件时需要链接哪些库(在上边find_package定义)。
-注: 如果你的包中多个可执行文件,每一个可执行文件都需要如此的两行代码。
在我们的例程中,需要一个名为hello(实际可以随意指定)的可执行文件,它通过hello.cpp的源文件编译而来,所以我们修改的代码为:
add_executable(hello src/hello.cpp)
target_link_libraries(hello ${catkin_LIBRARIES})
至此已经完成全部的配置
3、编译工作区
回到工作区目录(test_ws)下:
执行如下命令编译:
catkin_make
执行完毕后,你的工作区会创建devel和build两个子目录,包含编译脚本、目标代码和可执行文件等。
注:完成编写、调试、测试一系列功能后代码基本定型,devel和build两个子目录可以放心删除
如果编译有错误,比如找不到ros头文件等。最大可能性是CMakeLists.txt没有正确声明对roscpp的依赖
4、最后步骤
执行名为setup.bash的脚本文件,它是在devel子目录下生成的。
运行如下代码:
source devel/setup.bash
这个脚本设置了若干环境变量,脚本文件位devel/setup.bash,从而使ros能找到你的功能包和新生成的可执行文件。
它类似于2章介绍的全局的setup.bash,但是只为当前工作区量身定做;
除非目录结构发生变化,否则只用在每个终端执行一次。及时修改了代码且用catkin_make执行了重编译也不用。
PS:source ~/工作空间/devel/setup.bash可以添加进.bashrc文件,使用上更方便
添加方式1: 直接使用 gedit 或 vi 编辑 .bashrc 文件,最后添加该内容
添加方式2:echo “source ~/工作空间/devel/setup.bash” >> ~/.bashrc
执行hello程序
打开roscore
输入:
rosrun 功能包名 可执行文件节点名(在Cmakelist中定义的hello)
rosrun hello hello
至此我们的第一个程序就完成了,控制台上会输出一句程序里写的话。
也可以使用VSCODE编辑器进行操作,链接: 点击这里.