一、创建ros工作空间ros_ws
$ mkdir -p ~/ros_ws/src
$ cd ~/ros_ws
$ catkin_make #将ros_ws文件夹初始化为ros的工作空间
此时的工程结构:
install:安装空间,通常与devel合为一个
src:代码空间,存放package源代码
build:编译空间,camke&catkin缓存和中间文件
devel:开发空间,存放生成的目标文件、头文件、动态链接库、静态链接库和可执行文件。
设置环境变量
$ source ~/ros_ws/devel/setup.bash
此方法设置环境变量仅在当前终端有效。
$ gedit ~/.bashrc
source ~/ros_ws/devel/setup.bash
$ source ~/.bashrc #使配置文件生效
此方法设置环境变量永久有效。
$ echo $ROS_PACKAGE_PATH #查看ros路径
二、创建功能包
$ cd ~/ros_ws/src
$ catkin_create_pkg topic_demo roscpp rospy std_msgs
其中,topic_demo为功能包名,添加roscpp(ROS提供的c++结构)、rospy(ROS提供的python结构)、std_msgs(ROS定义好的标准数据结构)依赖。
三、rostopic介绍
1、topic是ROS中的异步通信方式,即只发送,不管有没有对象接收,或者只接收,不管是谁发送的。Node之间通过publish-subscribe机制通信。
2、rostopic常用指令:
$ rostopic list #列出当前所有topic
$ rostopic info/topic_name #显示指定topic属性信息
$ rostopic echo/topic_name #显示指定topic内容
$ rostopic pub/topic_name #向指定topic发布内容
四、rostopic代码实现
$ cd topic_demo/src
$ gedit listener.cpp
$ gedit talker.cpp
创建listener.cpp文件
/**
* 该例程将订阅chatter话题,消息类型String
*/
#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节点
ros::init(argc, argv, "listener");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为chatter的topic,注册回调函数chatterCallback
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
//订阅第二个名为chatter1的topic,如果实现功能不同,可重写回调函数
ros::Subscriber sub1 = n.subscribe("chatter1", 1000, chatterCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
创建talker.cpp文件
/**
* 该例程将发布chatter话题,消息类型String
*/
#include
#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv)
{
// ROS节点初始化
ros::init(argc, argv, "talker");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Publisher,发布名为chatter的topic,消息类型为std_msgs::String
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// 设置循环的频率
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
// 初始化std_msgs::String类型的消息
std_msgs::String msg;
std::stringstream ss;
ss << "hello jingbaixue " << count;
msg.data = ss.str();
// 发布消息
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
// 循环等待回调函数
ros::spinOnce();
// 按照循环频率延时
loop_rate.sleep();
++count;
}
return 0;
}
关于ros::spin()和ros::spinOnce():
相同点:都是检测是否有数据进来,如果有数据进来则进行数据处理,因此ros::spinOnce()在publisher程序中没有任何意义。Subscriber中ros::spin()检测到数据进来则进行数据处理,调用chatterCallback()函数。
不同点:ros::spinOnce()只检测一次,如果有数据进来,则进行数据处理;若没有数据进来,则继续执行下一步;ros::spin()会循环检测,如果有数据进来,则进行数据处理,如果没有数据进来,则一直等待检测,所以在subscriber程序中,接收到publisher数据后会一直循环在chatterCallback()函数中,程序执行到ros::spin()会进入无限循环,直到进程结束,所以在ros::spin()语句后任何语句都没有意义,且ros::spin()语句一般不放入while循环中。
Subscriber程序中参数1000的含义:
1000表示队列长度,如果数据发送过快,系统会将来不及处理的数据存入队列中,当队列存满时,自动删除时间戳较早的数据,因此数据发送过快可能会出现丢包、断帧的情况。
CMakeList.txt文件中添加:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
actionlib_msgs
actionlib
) #指定依赖
add_executable(talker src/talker.cpp) #生成可执行文件
target_link_libraries(talker ${catkin_LIBRARIES})
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
可执行文件也可由多个cpp文件生成,如:
add_executable(talker src/talker1.cpp talker2.cpp talker3.cpp)
五、topic通信
$ cd ~/ros_ws
$ catkin_make
生成可执行文件listener和talker保存在“ros_ws/devel/lin/topic_demo/”路径下。
打开一个终端:
$ roscore #启动ROS
重新打开一个终端
$ rosrun topic_demo listener
再打开一个新的终端
$ rosrun topic_demo talker
六、转发topic信息,即订阅一个topic,同时将该topic信息发布出去
#include
#include "ros/ros.h"
#include "std_msgs/String.h"
class subscribeAndpublish()
{
public:
subscribeAndpublish()
{
sub=n.subscribe("chatter",1000,&subscribeAndpublish::callback,this);
senddata_pub=n.advertise<std_msgs::String>("send",1000);
}
void callback(const std_msgs::String::ConstPtr& msg)
{
std_msgs::String message;
message.data = msg->data.c_str();
ROS_INFO("I heard:[%s]", msg->data.c_str());
senddata_pub.publish(message);
}
private:
ros::NodeHandle n;
ros::Subscriber sub;
ros::Publisher senddata_pub;
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "landt");
subscribeAndpublish SAPObject;
ros::spin();
return 0;
}