工作空间是一个存放工程开发相关文件的文件夹,主要包含了源代码空间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进行编译即可完成自定义消息的创建。
整合前几步中,以一个实际的案例来描述设计一个自定义消息的话题发布订阅的实现过程。
cd ~/catkin_ws/src #进入目录
catkin_create_pkg my_tutorials roscpp rospy message_generation message_runtime #创建功能包
cd ~/catkin_ws #回到工作区根目录
catkin_make #编译工作区
rospack list #查看功能包列表中是否包含新创建的功能包
cd ~/catkin_ws/src/my_tutorials #进入功能包的文件夹目录
mkdir -p msg #创建msg目录
gedit my_msg1.msg #创建消息文件
以下是msg中的内容,保存后退出
uint8 data1
uint32 data2
float32 data3
string data4
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
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中
roscore #打开ros内核
#在终端2中
rosrun my_tutorials talker_node #运行话题发布节点
#在终端3中
rostopic list #查看是否存在
#如果可以看到my_topic1则说明节点正常运行。
#可以通过rostopic echo my_topic1 来订阅话题消息
#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