执行命令,指定目录添加cpp文件
cd ~/catkin_ws/src/beginner_tutorials
如果没有src目录, 就自己创建一个目录叫src
cd src/
vim talker.cpp
复制代码粘贴:
#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<std_msgs::String>("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" 主题发布消息。以下是代码的详细说明:
#include "ros/ros.h"
和#include "std_msgs/String.h"
在代码的开头,我们包含了 ROS C++ 客户端库的头文件
ros/ros.h
,以及用于发布字符串消息的消息类型头文件std_msgs/String.h
。
int main(int argc, char **argv)
这是 C++ 程序的主函数。在 ROS 中,每个节点都必须包含一个
main
函数,用于初始化 ROS 节点并执行节点的主要逻辑。```argc
和
argv参数是传递给节点的命令行参数。在 ROS 中,
argc参数表示命令行参数的数量,
argv` 参数是一个字符串数组,包含所有命令行参数的值。
ros::init(argc, argv, "talker")
```ros::init
函数用于初始化 ROS 节点。在此示例中,我们将
argc和
argv参数传递给
ros::init` 函数,以便 ROS 节点可以解析命令行参数并进行必要的初始化。另外,我们将节点的名称设置为 "talker"。
ros::NodeHandle n
```ros::NodeHandle
类用于创建一个节点句柄,以便节点可以与 ROS 系统进行通信。在此示例中,我们创建了一个名为
n` 的节点句柄,用于向 ROS 发布和接收消息,以及订阅 ROS 主题。
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000)
```ros::Publisher
类用于创建一个发布者对象,用于向 ROS 主题发布消息。在此示例中,我们创建了一个名为
chatter_pub` 的发布者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了发布队列的大小为 1000 条消息。这意味着,如果发布者的发布速率太快,ROS 将会缓存多达 1000 条未发布的消息。
ros::Rate loop_rate(1)
```ros::Rate
类用于设置 ROS 节点的主循环的执行频率。在此示例中,我们创建了一个名为
loop_rate的
ros::Rate` 对象,将其设置为每秒执行 1 次。
while (ros::ok()) {...}
在主函数的主循环中,我们使用
while
循环来执行节点的主要逻辑。ros::ok()
函数用于检查节点是否已经被终止,如果节点已经被终止,则跳出循环。
std_msgs::String msg
``std_msgs::String
类型用于表示 ROS 中的字符串消息。在每次循环中,我们创建了一个名为
msg的
std_msgs::String` 对象,用于存储要发布的消息。
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 日志窗口中。
chatter_pub.publish(msg)
chatter_pub
发布者对象的publish
函数用于将消息发布到 "chatter" 主题上。在此示例中,我们将msg
变量中的字符串消息发布到 "chatter" 主题。
ros::spinOnce()
``ros::spinOnce()
函数用于让节点处理等待中的回调函数,例如订阅 ROS 主题时的回调函数。在此示例中,我们没有订阅任何主题,因此
ros::spinOnce()` 函数没有任何作用。但是,如果节点需要订阅主题或接收服务请求,则需要在主循环中调用此函数来处理回调函数。
loop_rate.sleep()
loop_rate
对象的sleep
函数用于使节点在主循环中按照指定的频率执行。在此示例中,我们将节点的执行频率设置为每秒执行 1 次,因此loop_rate.sleep()
函数将使节点在每个循环迭代之间休眠 1 秒,以确保节点的执行频率不会超过 1 秒。
count+=2
在每次循环中,我们将
count
变量的值增加 2,以便在每次循环中输出不同的字符串消息。这个变量的值将用于格式化要发布的消息。
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,以便在每次循环中输出不同的字符串消息。
vim listener.cpp
复制代码粘贴:
#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" 主题并输出接收到的消息。以下是代码的详细说明:
#include "ros/ros.h"
和#include "std_msgs/String.h"
在代码的开头,我们包含了 ROS C++ 客户端库的头文件
ros/ros.h
,以及用于发布字符串消息的消息类型头文件std_msgs/String.h
。
void chatterCallback(const std_msgs::String::ConstPtr& msg)
这是一个回调函数,用于处理接收到的 "chatter" 主题消息。在此示例中,我们创建了一个名为
chatterCallback
的回调函数,该函数接收一个std_msgs::String
消息类型的指针作为参数。当 "chatter" 主题发布新的消息时,ROS 将调用此回调函数,并将消息指针作为参数传递给函数。
int main(int argc, char **argv)
这是 C++ 程序的主函数。在 ROS 中,每个节点都必须包含一个
main
函数,用于初始化 ROS 节点并执行节点的主要逻辑。``argc
和
argv参数是传递给节点的命令行参数。在 ROS 中,
argc参数表示命令行参数的数量,
argv` 参数是一个字符串数组,包含所有命令行参数的值。
ros::init(argc, argv, "listener")
``ros::init
函数用于初始化 ROS 节点。在此示例中,我们将
argc和
argv参数传递给
ros::init` 函数,以便 ROS 节点可以解析命令行参数并进行必要的初始化。另外,我们将节点的名称设置为 "listener"。
ros::NodeHandle n
``ros::NodeHandle
类用于创建一个节点句柄,以便节点可以与 ROS 系统进行通信。在此示例中,我们创建了一个名为
n` 的节点句柄,用于向 ROS 发布和接收消息,以及订阅 ROS 主题。
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback)
``ros::Subscriber
类用于创建一个订阅者对象,用于订阅 ROS 主题并接收消息。在此示例中,我们创建了一个名为
sub的订阅者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了订阅队列的大小为 1000 条消息,以及要调用的回调函数
chatterCallback`。
ros::spin()
``ros::spin()` 函数将使节点进入一个无限循环,等待接收来自 ROS 系统的消息。此函数不会退出,直到节点被终止或出现致命错误。
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 日志窗口中。
然后编辑CMakeLists.txt 文件,看图命令
复制粘贴就好了,之前解释过了,指定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)
按esc 输入:wq 保存退出
执行命令:
cd ~/catkin_ws/ && catkin_make -j1
然后 测试消息发布器和订阅器(C++)
启动三个终端,分别运行如下代码
roscore
rosrun beginner_tutorials talker
rosrun beginner_tutorials listener
好了,我们的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
你们可以像办法把这个文件烤进来虚拟机这个位置,就是比较麻烦。
执行命令:
chmod +x talker.py
talker.py 文件代码解释:
import rospy
from std_msgs.msg import Stringdef 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" 主题的字符串消息。以下是代码的详细说明:
import rospy
和from std_msgs.msg import String
在代码的开头,我们导入了 ROS Python 客户端库
rospy
,以及用于发布字符串消息的消息类型std_msgs.msg.String
。
def talker():
这是一个函数,用于实现节点的主要逻辑。在此示例中,我们创建了一个名为
talker
的函数,用于发布 "chatter" 主题的字符串消息。
pub = rospy.Publisher('chatter', String, queue_size=10)
``rospy.Publisher
类用于创建一个发布者对象,用于发布 ROS 消息到指定的主题。在此示例中,我们创建了一个名为
pub` 的发布者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了发布队列的大小为 10 条消息。
rospy.init_node('talker', anonymous=True)
``rospy.init_node
函数用于初始化 ROS 节点。在此示例中,我们将节点的名称设置为 "talker",并将
anonymous参数设置为
True`,以便在多个节点启动时避免节点名称冲突。
rate = rospy.Rate(10) # 10hz
``rospy.Rate
类用于控制节点的循环频率。在此示例中,我们创建了一个名为
rate的
rospy.Rate` 对象,并将其设置为每秒循环 10 次。
while not rospy.is_shutdown():
这是一个循环结构,用于实现节点的主要逻辑。在此示例中,我们使用
rospy.is_shutdown()
函数来检查节点是否被终止。
hello_str = "hello world %s" % rospy.get_time()
在每次循环中,我们创建一个字符串变量
hello_str
,用于存储要发布的消息内容。在此示例中,我们将字符串设置为 "hello world",并使用rospy.get_time()
函数获取当前时间戳,将其添加到字符串末尾。
rospy.loginfo(hello_str)
``rospy.loginfo
函数用于将消息输出到 ROS 日志窗口中。在此示例中,我们将
hello_str变量作为参数传递给
rospy.loginfo` 函数,以便将消息内容输出到 ROS 日志窗口中。
pub.publish(hello_str)
``pub.publish
函数用于将消息发布到 ROS 主题中。在此示例中,我们将
hello_str变量作为参数传递给
pub.publish` 函数,以便将消息内容发布到 "chatter" 主题中。
rate.sleep()
``rate.sleep
函数用于控制节点的循环频率。在此示例中,我们使用
rate.sleep` 函数使节点按照我们在第 5 步中设置的频率循环。
if __name__ == '__main__':
这是 Python 中的一个特殊语句,用于检查当前脚本是否正在作为主程序运行。如果是,则执行以下代码;如果不是,则不执行以下代码。
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}
)
然后按esc 输入:wq 保存退出
和刚刚一样继续添加脚本
wget https://raw.githubusercontent.com/ros/ros_tutorials/melodic-devel/rospy_tutorials/001_talker_listener/listener.py
在scripts目录下执行
执行命令:
chmod +x listener.py
listener.py 文件代码解释:
import rospy
from std_msgs.msg import Stringdef 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" 主题并输出接收到的消息。以下是代码的详细说明:
import rospy
和from std_msgs.msg import String
在代码的开头,我们导入了 ROS Python 客户端库
rospy
,以及用于订阅字符串消息的消息类型std_msgs.msg.String
。
def callback(data):
这是一个回调函数,用于处理接收到的 "chatter" 主题消息。在此示例中,我们创建了一个名为
callback
的回调函数,该函数接收一个std_msgs.msg.String
消息类型的参数data
。当 "chatter" 主题发布新的消息时,ROS 将调用此回调函数,并将消息作为参数传递给函数。
def listener():
这是一个函数,用于实现节点的主要逻辑。在此示例中,我们创建了一个名为
listener
的函数,用于订阅 "chatter" 主题并输出接收到的消息。
rospy.init_node('listener', anonymous=True)
```rospy.init_node
函数用于初始化 ROS节点。在此示例中,我们将节点的名称设置为 "listener",并将
anonymous参数设置为
True`,以便在多个节点启动时避免节点名称冲突。
rospy.Subscriber('chatter', String, callback)
```rospy.Subscriber
类用于创建一个订阅者对象,用于接收指定主题的 ROS 消息。在此示例中,我们创建了一个名为
rospy.Subscriber的订阅者对象,并将其绑定到名为 "chatter" 的主题上。我们还指定了消息类型为
std_msgs.msg.String,回调函数为
callback`。
rospy.spin()
```rospy.spin
函数用于保持节点处于活动状态,以便能够接收 ROS 消息。在此示例中,我们使用
rospy.spin` 函数使节点保持活动状态,以便能够接收 "chatter" 主题的消息。
if __name__ == '__main__':
这是 Python 中的一个特殊语句,用于检查当前脚本是否正在作为主程序运行。如果是,则执行以下代码;如果不是,则不执行以下代码。
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}
)
然后按esc 然后输入:wq 保存退出
执行命令:
cd ~/catkin_ws/ && catkin_make -j1
然后分别新建三个终端,分别执行命令:
roscore
rosrun beginner_tutorials talker.py
rosrun beginner_tutorials listener.py
效果:
最后创建虚拟机快照,以后出问题,恢复到这个的快照。ok