机器人是一个带有传感器,作动器(电动机)和一个计算单元的机器。它可以更具用户的控制或者基于传感器的输入做出决定。
进程间的消息传递接口(进程间的通信):ROS提供的消息传递接口可以用于两个程序或进程之间的通信。如相机通过图像处理检测到人脸,并将坐标发送到跟踪器进程,而跟踪器进程就可以使用电动机来跟踪人脸。
与操作系统类似的特性:ROS不是真正的操作系统,但是可以提供一些操作系统的功能。如多线程,底层设备控制,包管理和硬件抽象。硬件抽象可以使程序员在不知道设备的完整细节的情况下编写程序,以适应来自不同供应商的产品。
支持高级编程语言和工具链:支持C++,Python,Lisp等语言,此外还可以对Java,C#等语言有实验性的支持。ROS为这些语言提供客户端库,所以可以通过这些语言使用ROS中的功能函数。
第三方库的可用性:如OpenCV,PCL等被集成在ROS的框架内
通用算法:ROS已经实现了一些通用的机器人算法,如PID,SLAM(即使定位与地图构建),路径规划算法,自适应蒙特卡洛定位算法等(AMCL)。
编程实现的简便性:除了通用算法之外,ROS包可以很容易的被其他机器人重用。
.....
由上述的命令常见了catkin工作空间,该控件是存放工程相关的文件的文件夹,并且可以主要分为下面四个文件夹
src:(代码空间,source space) 用来存放所有的功能包,所有功能包的代码,配置文件等都是放在这个里面;ROS的程序包只有在src文件夹下是才可以被编译执行。当执行编译命令【catkin_make】时,该命令就会检查src文件夹中的每个程序包并且一一编译它们。
bulid:(编译空间,build space) 存放编译过程中产生的文件;编译过程中会在该文件夹中创建一些中间编译文件和中间缓存文件,这些中间缓存文件可以避免在执行catkin_make命令时重新编译所有的文件。所以如果删了这个文件夹,那么所有的程序包都会被重新编译。比如已经编译了5个程序包了,现在又往src文件夹中添加了一个程序包,那么在执行下一次的catkin_make命令的时候就只有该新的程序包会被编译。
devel:(开发空间,development space) 存放编译生成的可执行文件,还有一些脚本文件,这些脚本文件可以将当前的工作空间添加到ROS的系统工作空间目录中。在.bashrc文件中添加以下命令,就可以在所有的终端会话总访问工作空间的程序包了。
source home/lim/<workspace_name>/devel/setup.bash
install:(安装空间,install space) ,该文件夹可以通过以下命令创建
catkin_make install
在上述步骤中生成了可执行文件之后,该文件就是存放用install命令安装的结果文件,当运行程序时,install文件夹中的相应的可执行文件就会被执行。
通过以上有了工作空间之后,就要创建程序包了。程序包通过以下命令实现:
catkin_create_pkg ros_package_name package_dependencies
上述命令中的【catkin_create_pkg】是创建程序包的命令,【ros_package_name】是要创建的程序包的名字,【package_dependencies】是程序包的依赖包。
在每个程序包下会看到如图4个文件:
CMakeLists.txt:包含了要编译程序包中的ROS源码和要创建可执行文件的所有的命令。
package.xml:该XML文件记录了程序包的依赖包和一些与自身相关信息等。
src文件夹:ROS程序包的源码保存在该文件夹下,通常,C++ 文件保存在该文件夹中,python脚本另创建文件夹”scripts"保存。
include:包含了源程序的头文件,该文件夹可以自动生成, 第三方的函数库文件也可以放在该文件夹中;
原则上可以用任意的编程语言编写ROS节点,但是ROS提供了几种编程语言的库,所以此时用这些库就会简单许多。若没有相应的库,那么就需要自己实现ROS库了。主要的客户端库有:
此外还有一些实验性的客户端库如rosjava,roslua等。
但在上面中常用的还是roscpp和rospy
头文件和模块
编写程序时,在C++中第一部分要包含头文件,在python中要导入python模块
初始化ROS节点
【在开始编写任何节点之前,都要初始化ROS节点】。
C++中:用以下几行代码初始化节点
int main(int argc,char *argv)
{
ros::init(argc,argv,"name_of_node")
...
}
在main函数中必须要包含ros::init()函数,该函数用来初始化节点,另外还可以通过(argc,argv等)向初始化函数传递命令行参数和节点名称。
Python中:第一个参数是节点名,第二个参数=True意味着可以在系统中同时运行该节点的多个实例。
rospy.init_node(‘name_of_node‘,anonymous=True);
在ROS节点中打印日志信息
ROS提供了记录日志信息的应用程序接口,这些信息是可读字符串,【表示节点的运行状态】
C++中:ROS_INFO(string_msg,args):记录节点输出的基本信息。
? ROS_WARN(string_msg,args):记录节点输出的警告信息。
? ROS_DEBUG(string_msg,args):记录节点输出的调试信息。等等
Python中:rospy.logingo(msg,*args)
? rospy.logwarn(msg,*args)
? rospy.logdebug(msg,*args)
创建节点句柄
初始化节点之后需要创建一个节点句柄实例,用于启动ROS节点或发布,订阅等操作。
C++中:如下,节点中的其他操作可以通过使用nh实例来访问。
ros::NOdeHandle nh;
python中:不需要,rospy模块内部自动解决这个问题
创建ROS消息定义
发布话题之前,要先创建一个ROS消息定义
C++中:创建std_msgs/String实例方式如下
std_msgs::String msg;
//创建之后添加数据
msg.data="Hello"
python中:添加相同的字符串数据如下
msg=String()
msg.data="Hello"
在ROS节点中发布话题
C++:发布话题语法如下,chatter_pub是发布者实例,chatter_pub会发布消息类型为std_msgs/String的话题,话题的名称为chatter,队列大小为1000
ros::Publisher publisher_object=node_handle.advertise<ROS message type>("topic_name",1000)
//创建了待发布的话题之后,通过publish发出
publisher_object.publish(message)
例如
ros::Publisher chatter_pub=nh.advertise<std_msgs::String>("Chatter",1000)
chatter_pub.publish(msg)
python:语法如下
publisher_instance=rospy.Publisher(‘topic_name‘,message_type,queue_size)
例如:
pub=rospy.Publisher(‘chatter‘,String,queue_size=1000)
pub.publish(hello_str)
在ROS节点中订阅话题
C++:订阅话题时不需要话题的类型,但是要给定【话题名和对应的回调函数】。回调函数是用户自定义的函数,一旦从话题中接收到ROS消息,那么回调函数就会被执行。在回调函数中,我们可以自定义要操作的内容,如可以打印消息或者根据消息做出决定。回调函数的编写看下一小节。
ros::Subscriber subscriber_obj=nodehandle.subscribe("topic_name",1000,callback function)
Python:在python中要指定消息的类型,同样也可以设置回调函数
rospy.Subscriber("topic_name",message_type,callback funtion name)
在ROS节点中编写回调函数
当订阅的话题有消息到达时,回调函数就会被触发。
C++:例子中是在回调函数中处理ROS字符串消息并显示数据。
语法:
void callback_name(const ros_message_const_pointer&pointer)
{
pointer->data//获取数据
}
实例:
void chatterCallback(const std_msgs::String::ConstPtr&msg)
{
ROS_INFO("I heard:[%s]",msg->data.c_str());
}
python:
def callback(data):
rospy.loginfo(rospy.get_caller_id()+"I heard %s",data.data)
ROS节点中的ROS spin()函数
在订阅或者发布函数之后,还需要调用一个函数来处理来自其他节点的订阅请求或发布请求。
ROS节点中的循环设置和ROS sleep()函数
为节点的循环设置设定一个恒定的速率,那么就可以用ros::Rate创建实例,并调用sleep()函数使之生效。
C++:
ros::Rate r(10); //10Hz的速率
r.sleep();
python:
rate=rospy.Rate(10)
rate.sleep()
设置和获取ROS参数
C++:获取参数如下。之前先要声明一个变量,然后用节点句柄node_handle中的【getParam()函数】获取所需要的参数
std::string global_name;
if(nh.getParam("/global_name",global_name))
{
....
}
设置参数如下:
nh.setParam("/global_param",5)
python:
global_name=rospy.get_param("/global_name")rospy.set_param(‘~private_int‘,‘2‘)
在该实例中创建名为hello_world的程序包,和一个发送节点和一个接受节点,用于传递字符串“Hello World"消息。
要在工作空间的src文件夹中创建程序包,创建之后产生的文件如下图所示。
catkin_create_pkg hello_world roscpp rospy std_msgs
其中的package.xml文件中包含了程序包及其依赖包的相关信息。
CMakeLists.txt是cmake文件,工程的名字在该文件的顶部,其内find_package命令用于寻找这个程序包所必须的系统依赖包。其内catkin_package命令是catkin提供的一个CMake宏,用于向编译系统指定工程所需要依赖的其他ROS程序包。
创建完程序包之后就可以创建节点,C++代码放在src文件夹中。如下的talker.cpp中,
talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <stream>
int main()
{
ros::init(argc,argv,"talker");
ros::NodeHandle n;
msgs::String>("chatter",1000);
ros::Rate loop_rate(10);
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;
}
return 0;
}
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 **argc)
{
ros::init(argc,argv,"listener");
ros::NodeHandle n;
ros::Subscriber sub=n.subscribe("chatter",1000,chatterCallback);
ros::spin();
return 0;
}
创建节点之后,需要编辑CMakeLists.txt文件,然后通过编译节点来创建可执行文件。如下所示,其中的add_executable表示从源代码中创建可执行文件,然后该文件通过target_link_libraries命令和指定的库函数相链接。
add_executable(talker src/talker.cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
target_link_libraries(listener ${catkin_LIBRARIES})
编辑完CMakeLists文件后就可以编译了,切换到工作空间的文件夹下,然后用编译命令catkin_make。
编译完成之后的可执行文件存放在catkin_ws/devel/lib/{程序包名}/文件夹下,则执行该节点可通过如下的命令:
roscore
rosrun 程序包名 节点名
在执行rosrun命令之前先要设置一下环境变量 ,要在工作空间下进行,即source devel/setup.bash
或者在.bashrc中加入如下的命令
source /home/用户名/工作空间名/devel/setup.bash
首先在程序包中创建名为【scripts】文件夹,然后python的脚本文件都保存在这个文件夹中。
talker.py
#!/usr/bin/env python
# license removed for brevity
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
listener.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String
def callback(data):
rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)
def listener():
# In ROS, nodes are uniquely named. If two nodes with the same
# name are launched, the previous one is kicked off. The
# anonymous=True flag means that rospy will choose a unique
# name for our ‘listener‘ node so that multiple listeners can
# run simultaneously.
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()
创建好脚本文件后,还要在CMakeLists.txt文件中编辑。
catkin_install_python(PROGRAMS scripts/talker.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
catkin_install_python(PROGRAMS scripts/talker.py scripts/listener.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)
用catkin_make命令在工作空间中编译一下。
rosrun 程序包名 脚本名
ROS启动文件可以在一条指令中运行任意数量的节点。
C++: 该启动文件可以以此运行talker和listener两个节点,程序包在"pkg="域中,可执行文件在"type="域中,【其中”name=‘‘为节点的名字,可以任意命名】,此时若执行rosnode list命令,则节点显示的名字就是这里面的名字。注意,这里面的pkg,name,type 的顺序可以任意,另外, 组后的output是将结果显示在屏幕上,若没有这个,则节点执行了,一些结果就不显示了。将这个文件保存为【xxxx.launch】,则运行时就要执行【roslaunch 程序包名 xxxx.launch】
<launch>
<node pkg="hello_world" name="listener_node" type="listener" output="screen"/>
<node pkg="hello_world" name="talker_node" type="talker" output="screen"/>
</launch>
python:python中除了将type中的可执行文件的名字改一改,其他不变,运行时也同上。
<launch>
<node pkg="hello_world" name="listener_node" type="listener.py" output="screen"/>
<node pkg="hello_world" name="talker_node" type="talker.py" output="screen"/>
</launch>
如小乌龟移动的节点,要向turtle1/cmd_vel话题发送指令,则要查看该话题的发送的数据类型:
rostopic type /turtle1/cmd_vel
geometry_msgs/Twist
则得到话题的消息类型是geometry_msgs/Twist,则发送命令时要按照该类型发送,现在查看该类型的定义:
rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linear
float64 x
float64 y
float64 z
geometry_msgs/Vector3 angular
float64 x
float64 y
float64 z
结果如上所示,该消息的定义有如上两部分。
ROS中的服务是另外的一种通讯形式,采用一问一答的方式进行节点之间的通信。
rosservice list
调用:rosservice call 参数
查看服务参数:rosservice type /服务名
接上一行,具体参数:rossrv show
ROS参数用法:
rosparam set set parameter
rosparam get get parameter
rosparam load load parameters from file
rosparam dump dump parameters to file
rosparam delete delete parameter
rosparam list list parameter names
原文:https://www.cnblogs.com/LENMOD/p/14655408.html