【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
前面我们安装了ROS环境,接着就可以开始进行ROS程序的开发了。在开发之前,我们有几个概念需要厘清一下。第一个是workspace,它相当于一个项目。毕竟,一个电脑上面可以有很多的workspace,也就相当于有很多的项目。第二个是package,它相当于功能块,功能块里面可以有很多的程序,package本身就是很多程序的一个集合。第三个就是node,直接把它看成独立的程序就可以了。
1、创建workspace
创建工作区是所有工作的第一步。可以通过创建workspace下面src目录的方法,直接进行创建,
mkdir -p ./catkin_ws/src
2、编译workspace
创建完成之后,就可以直接编译了。这个时候虽然什么也没有,但是catkin_make命令会帮我们自动创建build和dev这两个目录,
cd ./catkin_ws
catkin_make
3、创建package
有了工作区,就可以开始创建工作包了。工作包的创建方法也不复杂,主要使用catkin_create_pkg命令即可,前提是先进入src目录,
cd ./catkin_ws/src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
4、开始准备代码talker.cpp & listener.cpp
package里面也是有src目录的,虽然有点绕,这个我们可以稍微花点时间理解一下。现在假设有两个程序,一个是talker.cpp,一个是listener.cpp,他们都是放在package的src目录里面。所以第一步,先进入package里面的src目录,即,
./catkin_ws/src/beginner_tutorials/src
准备talker.cpp文件,
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line. For programmatic
* remappings you can use a different version of init() which takes remappings
* directly, but for most command-line programs, passing argc and argv is the easiest
* way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 0;
while (ros::ok())
{
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
接着准备listener.cpp文件,
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line. For programmatic
* remappings you can use a different version of init() which takes remappings
* directly, but for most command-line programs, passing argc and argv is the easiest
* way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "listener");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The subscribe() call is how you tell ROS that you want to receive messages
* on a given topic. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. Messages are passed to a callback function, here
* called chatterCallback. subscribe() returns a Subscriber object that you
* must hold on to until you want to unsubscribe. When all copies of the Subscriber
* object go out of scope, this callback will automatically be unsubscribed from
* this topic.
*
* The second parameter to the subscribe() function is the size of the message
* queue. If messages are arriving faster than they are being processed, this
* is the number of messages that will be buffered up before beginning to throw
* away the oldest ones.
*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
/**
* ros::spin() will enter a loop, pumping callbacks. With this version, all
* callbacks will be called from within this thread (the main one). ros::spin()
* will exit when Ctrl-C is pressed, or the node is shutdown by the master.
*/
ros::spin();
return 0;
}
5、修改CMakeLists.txt
这里需要注意的是,相关的CMakeLists.txt文件不是src目录下面文件,而是package下面的文件。这一点很容易改错的。
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
## Declare ROS messages and services
#add_message_files(FILES Num.msg)
#add_service_files(FILES AddTwoInts.srv)
## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)
## Declare a catkin package
catkin_package()
## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})
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)
6、开始编译
编译就非常简单了。需要回到workspace的目录,然后输入catkin_make就可以了,
cd ./catkin_ws
catkin_make
不出意外的话,我们在./catkin_ws/devel/lib/beginner_tutorials看到对应的可执行文件。
7、启动程序
启动程序需要三个步骤。第一个步骤,设置一下rosrun的环境,即,
source devel/setup.sh
这样后期rosrun的时候,就可以直接启动node程序了。第二个步骤,输入roscore,这个相当于整个底层信息交换的大脑。第三个步骤,就是启动talker和listener了,
rosrun beginner_tutorials talker
rosrun beginner_tutorials listener
所有操作都完成之后,我们就可以看到talker程序这边在不断发送消息,而listener这边在不断地接收消息。这是一个完整的publish和subscribe程序对。
整个过程看上去自己要做的内容很少。这是因为ros已经帮助我们完成了大部分的工作,用ldd命令看下生成的程序就知道了,
feixiaoxing@feixiaoxing-VirtualBox:~/Desktop/catkin_ws/devel/lib/beginner_tutorials$ ldd listener
linux-vdso.so.1 (0x00007ffeb4bfe000)
libroscpp.so => /opt/ros/noetic/lib/libroscpp.so (0x00007f2381bc9000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f2381b69000)
librosconsole.so => /opt/ros/noetic/lib/librosconsole.so (0x00007f2381b06000)
libroscpp_serialization.so => /opt/ros/noetic/lib/libroscpp_serialization.so (0x00007f2381b01000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f238191f000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f2381904000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2381710000)
libxmlrpcpp.so => /opt/ros/noetic/lib/libxmlrpcpp.so (0x00007f23816ec000)
librostime.so => /opt/ros/noetic/lib/librostime.so (0x00007f23816bf000)
libcpp_common.so => /opt/ros/noetic/lib/libcpp_common.so (0x00007f23816b1000)
libboost_thread.so.1.71.0 => /lib/x86_64-linux-gnu/libboost_thread.so.1.71.0 (0x00007f2381685000)
libboost_chrono.so.1.71.0 => /lib/x86_64-linux-gnu/libboost_chrono.so.1.71.0 (0x00007f2381677000)
libboost_filesystem.so.1.71.0 => /lib/x86_64-linux-gnu/libboost_filesystem.so.1.71.0 (0x00007f2381657000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2381508000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2381db2000)
librosconsole_log4cxx.so => /opt/ros/noetic/lib/librosconsole_log4cxx.so (0x00007f23814e8000)
librosconsole_backend_interface.so => /opt/ros/noetic/lib/librosconsole_backend_interface.so (0x00007f23814e3000)
liblog4cxx.so.10 => /lib/x86_64-linux-gnu/liblog4cxx.so.10 (0x00007f2381305000)
libboost_regex.so.1.71.0 => /lib/x86_64-linux-gnu/libboost_regex.so.1.71.0 (0x00007f2381205000)
libconsole_bridge.so.0.4 => /lib/x86_64-linux-gnu/libconsole_bridge.so.0.4 (0x00007f23811fd000)
libapr-1.so.0 => /lib/x86_64-linux-gnu/libapr-1.so.0 (0x00007f23811c4000)
libaprutil-1.so.0 => /lib/x86_64-linux-gnu/libaprutil-1.so.0 (0x00007f2381196000)
libicui18n.so.66 => /lib/x86_64-linux-gnu/libicui18n.so.66 (0x00007f2380e97000)
libicuuc.so.66 => /lib/x86_64-linux-gnu/libicuuc.so.66 (0x00007f2380cb1000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f2380ca6000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2380ca0000)
libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f2380c65000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f2380c37000)
libicudata.so.66 => /lib/x86_64-linux-gnu/libicudata.so.66 (0x00007f237f176000)