目的:学会如何用C++编写一个ROS节点,并创建launch文件
最终效果:创建出一个hello_world的基础程序包,以及一个发送节点,一个接收节点,用于传递“Hello World”字符串消息,并创建启动文件
1、新建一个名为catkin_ws的工作空间
//创建一个名称为catkin_ws的文件夹
mkdir -p ~/catkin_ws/src
//切换到src文件夹
cd catkin_ws/src
//初始化工作空间
catkin_init_workspace
//回到工作空间根目录
cd ..
//编译工作空间
catkin_make
2、配置环境变量
//home文件夹下打开.bashrc文件
gedit ~/.bashrc
//添加如下两行命令并保存
source ~/catkin_ws/devel/setup.bash
export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:~/catkin_ws/
3、创建ROS程序包
//切换到src文件夹下
cd catkin/src
//创建名为hello_world的ROS程序包,以后创建其他的包也是此方法,只需修改包的名称即可
catkin_create_pkg hello_world roscpp rospy std_msgs
4、创建发布消息的节点
这是一个发布“Hello World”字符串消息的节点,将其保存在src/talker.cpp文件中,以此为例,告诉你以后自己写其他发布消息的节点哪些要修改,哪些只需照抄。
#include "ros/ros.h"//创建ROS节点必须包含的头文件,固定格式
#include "std_msgs/String.h"//如果要在节点中使用某种特定的消息类型,一定要包含的消息头文件,此处根据实际情况决定
#include <sstream>//C++中用来处理面向流的输入和输出的标准库,此处根据实际情况决定
int main(int argc,char **argv)//初始化节点必须要的玩意,固定格式
{
ros::init(argc,argv,"talker");//初始化一个名为talker的节点,以后写其他的节点只要修改talker就行
ros::NodeHandle n;//创建名为n的节点句柄,自己写程序只要改n这个地方就行
ros::Pulisher chatter_pub=n.advertise<std_msgs::String>("chatter",1000);//ROS节点中发布话题的方式,chatter_pub是发布者实例,发布消息的类型是std_msgs/String的话题,话题名称为chatter,队列大小为1000,n为访问实例,要修改的话就改提到的这几个地方
ros::Rate_loop_rate(10);//制定循环的频率,此处为10HZ,固定格式
int count=0;//进入节点的主循环,在节点未发生异常的情况下将一直在循环,固定格式
while(ros::ok())//当Ctrl+C按下时,ros::ok()将会返回false,固定格式
{
//初始化std_msgs::String类型的消息
std_msgs::String msg;
std::stringstream ss;
ss<<"hello world"<<count;
msg.data=ss.str();//用data来存储字符串
//发布消息
ROS_INFO("%s",msg.data.c_str())//ROS_INFO类似于cout和printf
chatter_pub.publish(msg);//发布封装完毕的消息msg
//循环等待回调函数
ros::SpinOnce();//用来处理节点订阅话题的所有回调函数
//按照循环频率延时,固定格式
loop_rate.sleep();
++count;
}
return 0;
}
5、创建接收消息的节点
#include "ros/ros.h"//固定格式
#include "std_msgs/String.h"//如果要在节点中使用某种特定的消息类型,一定要包含的消息头文件,此处根据实际情况决定
//接收到订阅的消息,进入回调函数
void chatterCallback(const std_msgs::String::ConstPtr & msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());//将接收的消息打印出来
}
int main(int argc,char **argv)//固定格式
{
ros::init(argc,argv,"listener");//初始化一个名为listener的节点
ros::NodeHandle n;//创建名为n的节点句柄
ros::Subscriber sub=n.subscribe("chatter",1000,chatterCallback);//创建一个Subscriber的话题,注册回调函数chatterCallback
//循环等待回调函数
ros::Spin();
return 0;
6、编辑CMakeLists.txt文件
在hello_world/src文件夹中,修改CMakeLists.txt文件,加入如下两段
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
7、编译C++节点
cd ~/catkin_ws
catkin_make
8、执行C++节点
打开三个终端,分别运行如下三条命令
roscore
rosrun hello_world talker
rosrun hello_world listener
9、创建启动文件
ROS启动文件的优势在于可以在一条指令中运行任意数量的节点,launch保存在包目录中的launch文件夹下面,此处命名为talker_listener
<launch>
<node name="listener_node" pkg="hello_world" type="listener" output="screen"/>
<node name="talker_node" pkg="hello_world" type="talkerer" output="screen"/>
</launch>
node name: 该节点的名字,相当于代码中 ros::init() 中设置的信息,有了它代码中的名称会被覆盖;
pkg: 表示该节点的package,相当于 rosrun 命令后面的第一个参数;
type: 可执行文件的名字,rosrun 命令的第二个参数;是否可以理解为要执行的节点;
output=“screen”: 将标准输出显示在屏幕上而不是记录在日志中
类似的还有:
respawn=“true” :请求复位,roslaunch 会在该节点崩溃时重新启动该节点;
required=“true” :必要节点,roslaunch 会在该节点终止时终止其他活跃节点;
ns=“namespace”: 命名空间,为节点内的相对名称添加命名空间前缀
arg name=“arguments”:节点需要的输入参数
10、更改文件可执行权限
cd catkin_ws/src/hello_world/launch
sudo chmod +x talker_listener.launch
11、执行launch文件
launch hello_world talker_listener.launch
12、查看计算图
rqt_graph
13、查看话题列表
rostopic list