编写 ROS 消息发布订阅器(五)

执行命令,指定目录添加cpp文件

cd ~/catkin_ws/src/beginner_tutorials

 如果没有src目录, 就自己创建一个目录叫src

cd src/
vim talker.cpp

编写 ROS 消息发布订阅器(五)_第1张图片

 复制代码粘贴:

#include "ros/ros.h"
#include "std_msgs/String.h"
int main(int argc, char **argv){
        ros::init(argc, argv, "talker");
        ros::NodeHandle n;
        ros::Publisher chatter_pub = n.advertise("chatter",
1000);
        ros::Rate loop_rate(1);
        int count = 0;
        while (ros::ok()) {
                std_msgs::String msg;
                std::stringstream ss;
                ss << "hello world " << count;
                msg.data = ss.str();
                ROS_INFO("%s", msg.data.c_str());
                chatter_pub.publish(msg);
                ros::spinOnce();
                loop_rate.sleep();
                count+=2;
        }
        return 0;
}

代码解释(可跳过,如果执行跑起来的话):

这是一个 ROS 节点的 C++ 代码,用于向 "chatter" 主题发布消息。以下是代码的详细说明:

  1. #include "ros/ros.h" 和 #include "std_msgs/String.h"

    在代码的开头,我们包含了 ROS C++ 客户端库的头文件 ros/ros.h,以及用于发布字符串消息的消息类型头文件 std_msgs/String.h

  2. int main(int argc, char **argv)

    这是 C++ 程序的主函数。在 ROS 中,每个节点都必须包含一个 main 函数,用于初始化 ROS 节点并执行节点的主要逻辑。

    ```argcargv 参数是传递给节点的命令行参数。在 ROS 中,argc 参数表示命令行参数的数量,argv` 参数是一个字符串数组,包含所有命令行参数的值。

  3. ros::init(argc, argv, "talker")

    ```ros::init函数用于初始化 ROS 节点。在此示例中,我们将argcargv参数传递给ros::init` 函数,以便 ROS 节点可以解析命令行参数并进行必要的初始化。另外,我们将节点的名称设置为 "talker"。

  4. ros::NodeHandle n

    ```ros::NodeHandle类用于创建一个节点句柄,以便节点可以与 ROS 系统进行通信。在此示例中,我们创建了一个名为n` 的节点句柄,用于向 ROS 发布和接收消息,以及订阅 ROS 主题。

  5. ros::Publisher chatter_pub = n.advertise("chatter", 1000)

    ```ros::Publisher类用于创建一个发布者对象,用于向 ROS 主题发布消息。在此示例中,我们创建了一个名为chatter_pub` 的发布者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了发布队列的大小为 1000 条消息。这意味着,如果发布者的发布速率太快,ROS 将会缓存多达 1000 条未发布的消息。

  6. ros::Rate loop_rate(1)

    ```ros::Rate类用于设置 ROS 节点的主循环的执行频率。在此示例中,我们创建了一个名为loop_rateros::Rate` 对象,将其设置为每秒执行 1 次。

  7. while (ros::ok()) {...}

    在主函数的主循环中,我们使用 while 循环来执行节点的主要逻辑。ros::ok() 函数用于检查节点是否已经被终止,如果节点已经被终止,则跳出循环。

  8. std_msgs::String msg

    ``std_msgs::String类型用于表示 ROS 中的字符串消息。在每次循环中,我们创建了一个名为msgstd_msgs::String` 对象,用于存储要发布的消息。

  9. std::stringstream ss; ss << "hello world " << count; msg.data = ss.str()

    在此示例中,我们使用 std::stringstream 类来格式化要发布的消息。我们首先创建一个名为 ss 的 std::stringstream 对象,然后使用 << 运算符将 "hello world " 和 count 变量的值添加到字符串流中。最后,我们将字符串流中的内容存储到 msg.data 变量中,以便将其发布到 ROS 主题10. ROS_INFO("%s", msg.data.c_str())

    ``ROS_INFO函数用于在 ROS 中输出日志消息。在此示例中,我们使用%s占位符和msg.data.c_str()函数将msg.data` 变量中的字符串消息输出到 ROS 日志窗口中。

  10. chatter_pub.publish(msg)

    chatter_pub 发布者对象的 publish 函数用于将消息发布到 "chatter" 主题上。在此示例中,我们将 msg 变量中的字符串消息发布到 "chatter" 主题。

  11. ros::spinOnce()

    ``ros::spinOnce()函数用于让节点处理等待中的回调函数,例如订阅 ROS 主题时的回调函数。在此示例中,我们没有订阅任何主题,因此ros::spinOnce()` 函数没有任何作用。但是,如果节点需要订阅主题或接收服务请求,则需要在主循环中调用此函数来处理回调函数。

  12. loop_rate.sleep()

    loop_rate 对象的 sleep 函数用于使节点在主循环中按照指定的频率执行。在此示例中,我们将节点的执行频率设置为每秒执行 1 次,因此 loop_rate.sleep() 函数将使节点在每个循环迭代之间休眠 1 秒,以确保节点的执行频率不会超过 1 秒。

  13. count+=2

    在每次循环中,我们将 count 变量的值增加 2,以便在每次循环中输出不同的字符串消息。这个变量的值将用于格式化要发布的消息。

  14. return 0

    主函数的最后一行用于返回程序的退出状态。在此示例中,我们将退出状态设为 0,表示程序正常退出。

综上所述,这个 ROS 节点的主要逻辑是向 "chatter" 主题发布带有计数器的字符串消息。在节点的 main 函数中,我们首先使用 ros::init 函数初始化 ROS 节点,并将节点的名称设置为 "talker"。然后,我们创建了一个名为 chatter_pub 的发布者对象,用于将字符串消息发布到 "chatter" 主题上。接下来,我们使用 ros::Rate 类设置节点的主循环执行频率,并使用 while 循环执行节点的主要逻辑。在每次循环中,我们使用 std::stringstream 类格式化要发布的消息,并将其存储在 std_msgs::String 类型的变量 msg 中。然后,我们使用 ROS_INFO 函数在 ROS 日志窗口中输出字符串消息,并使用 chatter_pub.publish 函数将其发布到 "chatter" 主题上。最后,我们使用 ros::spinOnce 函数处理等待中的回调函数,并使用 loop_rate.sleep 函数使节点在每个循环迭代之间休眠,以确保节点的执行频率不会超过设置的频率。在每个循环迭代中,我们还将 count 变量的值增加 2,以便在每次循环中输出不同的字符串消息。

编写 ROS 消息发布订阅器(五)_第2张图片

vim listener.cpp

编写 ROS 消息发布订阅器(五)_第3张图片

 复制代码粘贴:

#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");
        ros::NodeHandle n;
        ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
        ros::spin();
        return 0;
}

代码解释(可跳过):

这是一个 ROS 节点的 C++ 代码,用于订阅 "chatter" 主题并输出接收到的消息。以下是代码的详细说明:

  1. #include "ros/ros.h" 和 #include "std_msgs/String.h"

    在代码的开头,我们包含了 ROS C++ 客户端库的头文件 ros/ros.h,以及用于发布字符串消息的消息类型头文件 std_msgs/String.h

  2. void chatterCallback(const std_msgs::String::ConstPtr& msg)

    这是一个回调函数,用于处理接收到的 "chatter" 主题消息。在此示例中,我们创建了一个名为 chatterCallback 的回调函数,该函数接收一个 std_msgs::String 消息类型的指针作为参数。当 "chatter" 主题发布新的消息时,ROS 将调用此回调函数,并将消息指针作为参数传递给函数。

  3. int main(int argc, char **argv)

    这是 C++ 程序的主函数。在 ROS 中,每个节点都必须包含一个 main 函数,用于初始化 ROS 节点并执行节点的主要逻辑。

``argcargv 参数是传递给节点的命令行参数。在 ROS 中,argc 参数表示命令行参数的数量,argv` 参数是一个字符串数组,包含所有命令行参数的值。

  1. ros::init(argc, argv, "listener")

    ``ros::init函数用于初始化 ROS 节点。在此示例中,我们将argcargv参数传递给ros::init` 函数,以便 ROS 节点可以解析命令行参数并进行必要的初始化。另外,我们将节点的名称设置为 "listener"。

  2. ros::NodeHandle n

    ``ros::NodeHandle类用于创建一个节点句柄,以便节点可以与 ROS 系统进行通信。在此示例中,我们创建了一个名为n` 的节点句柄,用于向 ROS 发布和接收消息,以及订阅 ROS 主题。

  3. ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback)

    ``ros::Subscriber类用于创建一个订阅者对象,用于订阅 ROS 主题并接收消息。在此示例中,我们创建了一个名为sub的订阅者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了订阅队列的大小为 1000 条消息,以及要调用的回调函数chatterCallback`。

  4. ros::spin()

    ``ros::spin()` 函数将使节点进入一个无限循环,等待接收来自 ROS 系统的消息。此函数不会退出,直到节点被终止或出现致命错误。

  5. return 0

    主函数的最后一行用于返回程序的退出状态。在此示例中,我们将退出状态设为 0,表示程序正常退出。

综上所述,这个 ROS 节点的主要逻辑是订阅 "chatter" 主题并输出接收到的消息。在节点的 main 函数中,我们首先使用 ros::init 函数初始化 ROS 节点,并将节点的名称设置为 "listener"。然后,我们创建了一个名为 sub 的订阅者对象,用于订阅 "chatter" 主题并接收消息。我们还指定了订阅队列的大小为 1000 条消息,并将回调函数 chatterCallback 与订阅者对象绑定,以便在收到新消息时自动调用。

在回调函数 chatterCallback 中,我们使用 ROS_INFO 函数将接收到的消息内容输出到 ROS 日志窗口中。当节点收到来自 "chatter" 主题的新消息时,ROS 将自动调用回调函数 chatterCallback 并将消息指针作为参数传递给该函数。在回调函数中,我们使用 msg->data.c_str() 函数获取消息内容,并使用 ROS_INFO 函数在 ROS 日志窗口中输出消息内容。

最后,我们使用 ros::spin() 函数进入一个无限循环,等待接收来自 ROS 系统的消息。此函数将不会退出,直到节点被终止或出现致命错误。在每次循环中,ROS 将检查订阅队列中是否有新的消息,如果有,则自动调用回调函数 chatterCallback 并传递消息指针作为参数。在回调函数中,我们将消息内容输出到 ROS 日志窗口中。

编写 ROS 消息发布订阅器(五)_第4张图片

然后编辑CMakeLists.txt 文件,看图命令

编写 ROS 消息发布订阅器(五)_第5张图片

 复制粘贴就好了,之前解释过了,指定talker 为节点,位置是src/talker.cpp 。下面的添加链接库的代码

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
#add_dependencies(talker ${PROJECT_NAME}_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
#add_dependencies(listener ${PROJECT_NAME}_generate_messages_cpp)

编写 ROS 消息发布订阅器(五)_第6张图片

 按esc 输入:wq 保存退出

执行命令:

cd ~/catkin_ws/ && catkin_make -j1

编写 ROS 消息发布订阅器(五)_第7张图片

然后 测试消息发布器和订阅器(C++)

启动三个终端,分别运行如下代码

roscore
rosrun beginner_tutorials talker
rosrun beginner_tutorials listener

编写 ROS 消息发布订阅器(五)_第8张图片

 好了,我们的c++的消息发布器和订阅器代码就写好了

--------------------------------------------分割线----------------------------------------------------------------------------

下面我们开始python 版本的了,上面的c++ 版本,下面是python版本

执行命令,创建在begineer什么的 功能包 里面创建一个scripts文件夹

mkdir ~/catkin_ws/src/beginner_tutorials/scripts

此链接可能会被dns污染,请用其他方法下载这个脚本 

wget https://raw.githubusercontent.com/ros/ros_tutorials/melodic-devel/rospy_tutorials/001_talker_listener/talker.py

编写 ROS 消息发布订阅器(五)_第9张图片

 编写 ROS 消息发布订阅器(五)_第10张图片

 你们可以像办法把这个文件烤进来虚拟机这个位置,就是比较麻烦。

执行命令:

chmod +x talker.py

 talker.py 文件代码解释:


import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    rate = rospy.Rate(10) # 10hz
    while not rospy.is_shutdown():
        hello_str = "hello world %s" % rospy.get_time()
        rospy.loginfo(hello_str)
        pub.publish(hello_str)
        rate.sleep()

if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException:
        pass

这是一个 ROS 节点的 Python 代码,用于发布 "chatter" 主题的字符串消息。以下是代码的详细说明:

  1. import rospy 和 from std_msgs.msg import String

    在代码的开头,我们导入了 ROS Python 客户端库 rospy,以及用于发布字符串消息的消息类型 std_msgs.msg.String

  2. def talker():

    这是一个函数,用于实现节点的主要逻辑。在此示例中,我们创建了一个名为 talker 的函数,用于发布 "chatter" 主题的字符串消息。

  3. pub = rospy.Publisher('chatter', String, queue_size=10)

    ``rospy.Publisher类用于创建一个发布者对象,用于发布 ROS 消息到指定的主题。在此示例中,我们创建了一个名为pub` 的发布者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了发布队列的大小为 10 条消息。

  4. rospy.init_node('talker', anonymous=True)

    ``rospy.init_node函数用于初始化 ROS 节点。在此示例中,我们将节点的名称设置为 "talker",并将anonymous参数设置为True`,以便在多个节点启动时避免节点名称冲突。

  5. rate = rospy.Rate(10) # 10hz

    ``rospy.Rate类用于控制节点的循环频率。在此示例中,我们创建了一个名为raterospy.Rate` 对象,并将其设置为每秒循环 10 次。

  6. while not rospy.is_shutdown():

    这是一个循环结构,用于实现节点的主要逻辑。在此示例中,我们使用 rospy.is_shutdown() 函数来检查节点是否被终止。

  7. hello_str = "hello world %s" % rospy.get_time()

    在每次循环中,我们创建一个字符串变量 hello_str,用于存储要发布的消息内容。在此示例中,我们将字符串设置为 "hello world",并使用 rospy.get_time() 函数获取当前时间戳,将其添加到字符串末尾。

  8. rospy.loginfo(hello_str)

    ``rospy.loginfo函数用于将消息输出到 ROS 日志窗口中。在此示例中,我们将hello_str变量作为参数传递给rospy.loginfo` 函数,以便将消息内容输出到 ROS 日志窗口中。

  9. pub.publish(hello_str)

    ``pub.publish函数用于将消息发布到 ROS 主题中。在此示例中,我们将hello_str变量作为参数传递给pub.publish` 函数,以便将消息内容发布到 "chatter" 主题中。

  10. rate.sleep()

``rate.sleep函数用于控制节点的循环频率。在此示例中,我们使用rate.sleep` 函数使节点按照我们在第 5 步中设置的频率循环。

  1. if __name__ == '__main__':

    这是 Python 中的一个特殊语句,用于检查当前脚本是否正在作为主程序运行。如果是,则执行以下代码;如果不是,则不执行以下代码。

  2. try: talker() except rospy.ROSInterruptException: pass

    在 if __name__ == '__main__': 语句的后面,我们使用 try 和 except 语句块来捕获可能抛出的 rospy.ROSInterruptException 异常。在此示例中,我们使用 try 语句块调用 talker() 函数,如果出现 rospy.ROSInterruptException 异常,则使用 except 语句块中的 pass 语句来忽略该异常。

综上所述,这个 ROS 节点的主要逻辑是发布 "chatter" 主题的字符串消息。在节点的 talker 函数中,我们首先创建了一个 rospy.Publisher 对象,用于将消息发布到 "chatter" 主题中。然后,我们使用 rospy.init_node 函数初始化 ROS 节点,并将节点的名称设置为 "talker"。接下来,我们使用 rospy.Rate 类控制节点的循环频率,并在循环中使用 rospy.is_shutdown() 函数检查节点是否被终止。在每次循环中,我们创建一个字符串变量 hello_str,用于存储要发布的消息内容,并使用 rospy.loginfo 函数将消息内容输出到 ROS 日志窗口中。然后,我们使用 pub.publish 函数将消息发布到 "chatter" 主题中,并使用 rate.sleep 函数控制节点的循环频率。

在 if __name__ == '__main__': 语句块中,我们使用 try 和 except 语句块来捕获可能抛出的 rospy.ROSInterruptException 异常。在 try 语句块中,我们调用 talker() 函数,以便启动节点的主要逻辑。如果出现 rospy.ROSInterruptException 异常,则使用 except 语句块中的 pass 语句来忽略该异常。

综上所述,这个 ROS 节点将按照我们在第 5 步中设置的频率循环,并在每次循环中发布带有当前时间戳的 "hello world" 字符串消息到 "chatter" 主题,并将消息内容输出到 ROS 日志窗口中。节点将一直运行,直到被终止或出现致命错误。

然后编辑CMakeLists.txt 文件,按图执行命令

复制代码粘贴 

catkin_install_python(PROGRAMS scripts/talker.py
 DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

编写 ROS 消息发布订阅器(五)_第11张图片

 然后按esc 输入:wq 保存退出

和刚刚一样继续添加脚本

wget https://raw.githubusercontent.com/ros/ros_tutorials/melodic-devel/rospy_tutorials/001_talker_listener/listener.py

在scripts目录下执行  

编写 ROS 消息发布订阅器(五)_第12张图片

 编写 ROS 消息发布订阅器(五)_第13张图片

 执行命令:

chmod +x listener.py 

编写 ROS 消息发布订阅器(五)_第14张图片

listener.py 文件代码解释:


import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + 'I heard %s', data.data)

def listener():
    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber('chatter', String, callback)

    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()

if __name__ == '__main__':
    listener()
 

这是一个 ROS 节点的 Python 代码,用于订阅 "chatter" 主题并输出接收到的消息。以下是代码的详细说明:

  1. import rospy 和 from std_msgs.msg import String

    在代码的开头,我们导入了 ROS Python 客户端库 rospy,以及用于订阅字符串消息的消息类型 std_msgs.msg.String

  2. def callback(data):

    这是一个回调函数,用于处理接收到的 "chatter" 主题消息。在此示例中,我们创建了一个名为 callback 的回调函数,该函数接收一个 std_msgs.msg.String 消息类型的参数 data。当 "chatter" 主题发布新的消息时,ROS 将调用此回调函数,并将消息作为参数传递给函数。

  3. def listener():

    这是一个函数,用于实现节点的主要逻辑。在此示例中,我们创建了一个名为 listener 的函数,用于订阅 "chatter" 主题并输出接收到的消息。

  4. rospy.init_node('listener', anonymous=True)

    ```rospy.init_node函数用于初始化 ROS节点。在此示例中,我们将节点的名称设置为 "listener",并将anonymous参数设置为True`,以便在多个节点启动时避免节点名称冲突。

  5. rospy.Subscriber('chatter', String, callback)

    ```rospy.Subscriber类用于创建一个订阅者对象,用于接收指定主题的 ROS 消息。在此示例中,我们创建了一个名为rospy.Subscriber的订阅者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了消息类型为std_msgs.msg.String,回调函数为 callback`。

  6. rospy.spin()

    ```rospy.spin函数用于保持节点处于活动状态,以便能够接收 ROS 消息。在此示例中,我们使用rospy.spin` 函数使节点保持活动状态,以便能够接收 "chatter" 主题的消息。

  7. if __name__ == '__main__':

    这是 Python 中的一个特殊语句,用于检查当前脚本是否正在作为主程序运行。如果是,则执行以下代码;如果不是,则不执行以下代码。

  8. listener()

    在 if __name__ == '__main__': 语句块中,我们调用 listener() 函数,以便启动节点的主要逻辑。

综上所述,这个 ROS 节点的主要逻辑是订阅 "chatter" 主题并输出接收到的消息。在节点的 listener 函数中,我们首先使用 rospy.init_node 函数初始化 ROS 节点,并将节点的名称设置为 "listener"。接下来,我们使用 rospy.Subscriber 类创建了一个名为 rospy.Subscriber 的订阅者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了消息类型为 std_msgs.msg.String,回调函数为 callback。在回调函数 callback 中,我们使用 rospy.loginfo 函数输出接收到的消息内容到 ROS 日志窗口中。最后,我们使用 rospy.spin 函数使节点保持活动状态,以便能够接收 "chatter" 主题的消息。

在 if __name__ == '__main__': 语句块中,我们使用 listener() 函数调用启动节点的主要逻辑。当节点开始运行时,它将自动订阅 "chatter" 主题,并等待接收该主题发布的消息。当 "chatter" 主题发布新的消息时,节点将调用 callback 回调函数,并将消息作为参数传递给函数,函数将消息内容输出到 ROS 日志窗口中。节点将一直运行,直到被终止或出现致命错误。

 编辑CMakeLists.txt 文件添加内容

catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py
 DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

编写 ROS 消息发布订阅器(五)_第15张图片

 编写 ROS 消息发布订阅器(五)_第16张图片

 然后按esc 然后输入:wq 保存退出

执行命令:

cd ~/catkin_ws/ && catkin_make -j1 

编写 ROS 消息发布订阅器(五)_第17张图片

 然后分别新建三个终端,分别执行命令:

roscore
rosrun beginner_tutorials talker.py
rosrun beginner_tutorials listener.py 

编写 ROS 消息发布订阅器(五)_第18张图片

效果:

编写 ROS 消息发布订阅器(五)_第19张图片

最后创建虚拟机快照,以后出问题,恢复到这个的快照。ok

你可能感兴趣的:(linux,机器人)