第一次用博客记录菜鸟学习ROS过程,如果发现文章出现的错误,还请指出,十分感谢。
ROS中功能包的编译规则
在ROS中,编写好C++节点代码后,我们通常要在运行之前需要将代码编译成可执行文件。我们可以通过对CMakeLists. txt修改来实现:
如当我们编写好订阅者listener节点
1.打开功能包中的CMakeLists.txt文件,找到以下配置项,去掉注释并稍作修改
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
CMakeLists. txt用于定义如何编译和构建C++代码及依赖项
CMakeLists. txt是CMake的配置文件。而CMake是一个工具,它通过读取CMakeLists. txt来生成特定于平台的构建系统的文件,如Makefile,然后可以使用make命令来编译源代码,生成可执行文件或库。
2.CMakeLists.txt修改完成后,在工作空间的根路径下开始编译:
$ cd ~/catkin_ws
$ catkin_make
而当我们使用Python编写节点程序时,则不需要进行编译。
Python作为一种脚本语言,是直接由Python解释器执行的,其代码不用编译成二进制可执行文件,则可以直接编译。
ROS中的环境设置
在运行节点之前,我们需要在终端中设置环境变量,否则无法找到功能包最终编译的可执行文件:
$ cd ~/catkin_ws
$ source ./devel/setup.bash
此命令只在当前终端中生效,如果希望环境变量在所有终端中有效,则可以将环境变量添加到终端的配置环境中:
在终端中输入:
gedit ~/.bashrc
在.bashrc中添加:
source ~/catkin_ws/devel/setup.bash
这里我们再介绍另一条相似的指令
$ source /opt/ros/kinetic (melodic)/setup.bash
这通常在我们下载好ROS后,设置环境变量的时候执行。其会加载kinetic (melodic)的相关环境变量和路径设置,使我们可以使用其提供的工具和功能,如使用rosrun,roslaunch等以ros开头的命令。
ROS中的记录数据
为了方便调试测试,ROS提供了数据记录与回放的功能包——
rosbag,可以帮助开发者收集ROS系统运行时的消息数据,然后在离线状态下回放。
我们使用rosbag抓取运行话题的消息,并且打包成一个文件放置
到指定文件夹中:
$ mkdir ~/bagfiles
$ cd ~/bagfiles
$ rosbag record -a
进入刚才创建的文件夹~/bagfiles中,有一个以时间命名并且
以.bag为后缀的文件,这就是成功生成的数据记录文件
LaserScan
LaserScan是ROS中用于表示激光扫描数据的一种消息类型
此为激光雷达消息包的格式定义:
LaserScan消息类型用于发布激光扫描数据,这些数据通常由激光雷达传感器获取。
LaserScan消息中包含了一个名为Header的结构体,它是ROS中许多消息类型的标准组成部分,其中Header主要存储的是时间戳timestamp(表示本次扫描中接受到第一束测距激光反射信号时间)和坐标系ID frame_id(指明了激光雷达的基准坐标系)
节点代码解析
ros::NodeHandle nh;
创建了节点句柄对象nh,这个对象是节点和ROS通讯的关键。
如何理解NodeHandle?
可以把他理解成为一位管家,ROS就是由这位管家打理的一所豪宅,我们有什么需要就直接吩咐这个管家就行了,他会帮忙处理好一切。在一个节点中能有多个句柄对象,如同一个豪宅中有多位管家。
在Python中,rospy实现了类似NodeHandle的功能:
rospy.init_node()
向ROS管家rospy申请初始化节点,参数为节点的节点名称。
ros::spinOnce();
ros::spinOnce()是ROS中用来一次处理消息的非阻塞函数,所谓非阻塞函数,意味着它不会等待新的消息的到来,而是有消息立即处理,没有消息继续执行后续的代码。
更直观的意思是:
假设我们节点正在忙着自己的事情,当调用ros::spinOnce()函数时,会转过身去瞅一眼,有没有新的消息包接收到。如果没有调用这个函数,则节点就会在while()循环里忙着自己的事情,会导致节点错过消息包的接收,回调函数无法响应。
与 ros::spinOnce()相比,ros::spin() 会持续阻塞在消息循环上,直到节点关闭或者没有更多消息需要处理,之后才会继续执行后续代码。
ros::Rate loop_rate(10);
ros::Rate()主要用来设置循环执行的频率,单位Hz。通常在循环外部创建一个ros::Rate对象,并在循环内部调用其sleep()控制循环的频率。
如:
// 设置循环的频率
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
// 初始化std_msgs::String类型的消息
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;
}
ros::Rate loop_rate(10)中loop_rate为频率对象名字,10为要控制循环每秒执行的次数,这里表示一秒钟执行10次。loop_rate.sleep()此函数会在while()循环内做一个短时间的阻塞,使循环以每秒10次的频率固定运行。
Python中控制订阅者的回调函数
#导库
import rospy
from std_msgs.msg import String
#构建回调函数
def chao_callbacke(msg):
rospy.loginfo(msg.data) #显示消息包内容
if __name__=="__main":
rospy.init_node("ma_node")#申请初始化节点
sub = rospy.Subscriber("订阅的主题名称",String,chao_callbacke,queue_size=10)
rospy.spin()#让节点保持运行状态,等待消息包的到来
参考:
ROS机器人开发实践_胡春旭
bilibili机器人工匠阿杰