學習10:訊息釋出器和訂閱器(C++)
阿新 • • 發佈:2019-01-10
因為我不會python,所以只能用C++了 - -#
1 節點
ROS中把可執行檔案叫做【節點】。
去剛才的beginner_tutorial(眼花了,單詞打沒打錯?……)目錄下,新建src資料夾,裡面寫一個
talker.cpp
裡面程式碼如下
再新建一個listener.cpp檔案,程式碼如下/* * Copyright (C) 2008, Morgan Quigley and Willow Garage, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ // %Tag(FULLTEXT)% // %Tag(ROS_HEADER)% #include "ros/ros.h" // %EndTag(ROS_HEADER)% // %Tag(MSG_HEADER)% #include "std_msgs/String.h" // %EndTag(MSG_HEADER)% #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. */ // %Tag(INIT)% ros::init(argc, argv, "talker"); // %EndTag(INIT)% /** * 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. */ // %Tag(NODEHANDLE)% ros::NodeHandle n; // %EndTag(NODEHANDLE)% /** * 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. */ // %Tag(PUBLISHER)% ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); // %EndTag(PUBLISHER)% // %Tag(LOOP_RATE)% ros::Rate loop_rate(10); // %EndTag(LOOP_RATE)% /** * A count of how many messages we have sent. This is used to create * a unique string for each message. */ // %Tag(ROS_OK)% int count = 0; while (ros::ok()) { // %EndTag(ROS_OK)% /** * This is a message object. You stuff it with data, and then publish it. */ // %Tag(FILL_MESSAGE)% std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); // %EndTag(FILL_MESSAGE)% // %Tag(ROSCONSOLE)% ROS_INFO("%s", msg.data.c_str()); // %EndTag(ROSCONSOLE)% /** * 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. */ // %Tag(PUBLISH)% chatter_pub.publish(msg); // %EndTag(PUBLISH)% // %Tag(SPINONCE)% ros::spinOnce(); // %EndTag(SPINONCE)% // %Tag(RATE_SLEEP)% loop_rate.sleep(); // %EndTag(RATE_SLEEP)% ++count; } return 0; } // %EndTag(FULLTEXT)%
/* * Copyright (C) 2008, Morgan Quigley and Willow Garage, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ // %Tag(FULLTEXT)% #include "ros/ros.h" #include "std_msgs/String.h" /** * This tutorial demonstrates simple receipt of messages over the ROS system. */ // %Tag(CALLBACK)% void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); } // %EndTag(CALLBACK)% 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. */ // %Tag(SUBSCRIBER)% ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); // %EndTag(SUBSCRIBER)% /** * 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. */ // %Tag(SPIN)% ros::spin(); // %EndTag(SPIN)% return 0; } // %EndTag(FULLTEXT)%
2 編譯節點
寫好了程式碼自然要編譯把- -#
再CMakeList.txt加入
include_directories(include ${catkin_INCLUDE_DIRS}) add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES})
這會生成2個可執行檔案talker和listener。並且儲存cakin_ws/devel/lib/<package name>裡。
還需要一行
add_dependencies(talker beginner_tutorials_generate_messages_cpp)好了好了!!!先試試能不能編譯通過,然後再研究程式碼的意思!
回到最高的工作空間,執行catkin_make。
生成了一堆東西,編譯成功!
3. 執行節點
3.1 啟動roscore
這個顯然的。。先啟動了先。
直接執行roscore,這個前文說過多次了
3.2 啟動訂閱器(listener)和傳送器(talker)
首先確認source過那個我們剛在玩的工作空間下的setup,bash檔案啊!!
直接
$ rosrun beginner_tutorials listener (C++)
$ rosrun beginner_tutorials talker字面就可以猜出來2個程式碼的意思~ (別忘了每個終端都需要source一次啊!!!) 4 解讀程式碼 4.1 解讀釋出器程式碼
具體細節見程式碼,中文註釋
#include "ros/ros.h"
//貌似是核心的ros標頭檔案,先引用了先。
#include "std_msgs/String.h"
//黑人問號.jpg
#include <sstream>
int main(int argc, char **argv)
{
/**
* ros::init()函式需要argc和argv,這樣可以實現我們之前也做過的,重定向節點名稱。
* 重定向節點名稱,我們再前面小烏龜模仿移動的章節玩過了。
* 第三個引數,比如下main的"talker"貌似是預設的節點名,且不能包含奇怪字元比如/
*/
// %Tag(INIT)%
ros::init(argc, argv, "talker");
// %EndTag(INIT)%
/**
* NodeHandle 是一個重要的節點型別,我們後續操作要用到這個東西。
* 貌似定義的時候,有很好的初始化函式。解構函式也寫的很棒?
*/
// %Tag(NODEHANDLE)%
ros::NodeHandle n;
// %EndTag(NODEHANDLE)%
/**
* advertise返回一個Publisher物件,第一個引數,"chatter"是話題名,1000是緩衝佇列大小。
* (最多儲存)的訊息的數量。同時這個advertise還有2個功能,訊息型別不對他會拒絕釋出,
* 他有一個publish成員,可以用來發布訊息。(後面會出現介紹)。至於。。std_msg::String
* 是幹嘛的?難道指的是,釋出的訊息型別是std_msg::String型別的嘛?(看了原始碼,貌似是這個意思)。
*/
// %Tag(PUBLISHER)%
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
// %EndTag(PUBLISHER)%
// %Tag(LOOP_RATE)%
ros::Rate loop_rate(10); //程式下面有一個ros::ok(),這個大致就是10Hz的頻率執行ros::ok()
// %EndTag(LOOP_RATE)%
// %Tag(ROS_OK)%
/**
* ros::ok()為false的條件:
* 1:鍵盤按了ctrl+c
* 2:同樣名字的節點被運行了
* 3:ros::shutdown() 被呼叫
* 4:節點中所有的 NodeHandle被銷燬。
*/
int count = 0;
while (ros::ok())
{
// %EndTag(ROS_OK)%
/**
* This is a message object. You stuff it with data, and then publish it.
*/
// %Tag(FILL_MESSAGE)%
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
// %EndTag(FILL_MESSAGE)%
// %Tag(ROSCONSOLE)%
ROS_INFO("%s", msg.data.c_str()); //目測,是向終端輸出字串。
// %EndTag(ROSCONSOLE)%
// %Tag(PUBLISH)%
chatter_pub.publish(msg);//釋出msg這個訊息,msg型別正好也是之前再advertise裡定義的std_msg::String型別,型別統一
// %EndTag(PUBLISH)%
// %Tag(SPINONCE)%
ros::spinOnce();//貌似不寫這句話,回撥函式就沒用了
// %EndTag(SPINONCE)%
// %Tag(RATE_SLEEP)%
loop_rate.sleep();//上面定義了10Hz,這裡就會休眠0.1s的樣子。
// %EndTag(RATE_SLEEP)%
++count;
}
return 0;
}
// %EndTag(FULLTEXT)%
4.2 解讀程式碼接收器
一樣的,直接看程式碼,和程式碼中文註釋
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* 下面是一個回撥函式。再定義接收器的時候會用到。
* 看起來接收器實現的程式碼內部,會自動讓這個函式繫結一個
* 訊息的指標。比如這裡,接受的訊息型別是std_msgs::String型別,和前面定義
* 的釋出器釋出的訊息型別一樣。這裡抓到訊息,就用ROS_INFO命令,輸出到
* 終端,讓我們看到。
*/
// %Tag(CALLBACK)%
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
// %EndTag(CALLBACK)%
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
/**
* 這個訂閱器的程式碼和之前的釋出其很類似,引數分別為節點名、快取大小。
* 第三個引數和前面的回撥函式應該說過了。
* 很好的解構函式保證了,物件被銷燬的時候,就不會再繼續訂閱了。
*/
// %Tag(SUBSCRIBER)%
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
// %EndTag(SUBSCRIBER)%
/**
* spin()函式會進入一個迴圈,軟體實現中斷程式~?中斷級別之類的他怎麼實現呢- -#
* 當然了,還是會接受ctrl+c之類的退出。
*/
// %Tag(SPIN)%
ros::spin();
// %EndTag(SPIN)%
return 0;
}
// %EndTag(FULLTEXT)%