*roslaunch的使用和编写: $ roslaunch package_name file.launch
roslaunch文件是使用XML语言来描述节点以及相关参数的设置和软件的启动的。
<launch>标签:是任何roslaunch文件的根元素。它的唯一目的是作为其他元素的容器。其包含的元素有:
<node> 启动节点 <param> 对参数服务器进行参数设置
<remap> 声明映射名 <machine> 声明要启动的机器
<rosparam> 使用rosparam文件启动设置ROS参数,此节点的~/local命名空间中
<include> 包含其他的roslaunch文件 <env> 为启动节点指定一个环境变量
<test> 启动一个测试节点 <arg> 声明一个提要
<group> 共享一个名称空间或重新映射组封闭元素,将几个nodes放时同一个namespace
<node>标签:指定一个ROS节点,启动和关闭节点。roslaunch不保证节点开始的顺序。因为没有办法从外部知道节点何时被完全初始化,所以所有被启动的节点必须是稳健的,以便以任何顺序启动。
<node pkg=”package” type=”executable” name=”node” output= "screen" respawn="true">
启动节点(name),使用(type)从可执行的包(pkg)与所述命令参数args=””.
pkg:节点包;
type:包内可执行程序名,由包内CMakeLists.txt控制生成,每个可执行程序的main里都会有定义节点的语句(ros::init(argc,argv,”节点名字”)),这里的节点只用于代码编译;
name:节点名称;就是ros::init()中的节点名字。
args:传递参数到节点;
respawn:如果节点退出,则自动重新启动节点;
required:如果节点死亡,杀死整个roslaunch;
output=“log | screen”,如果'screen',stdout / stderr从节点将被发送到屏幕。如果是“log”,stdout / stderr输出将被发送到$ ROS_HOME/ log中的日志文件,stderr将继续发送到屏幕。默认值为“log”;
launch-prefix =“prefix arguments”,用于预先添加到节点的启动参数的命令/参数,能够启用rviz,xterm,漂亮或其他方便的工具;
<node>标记中使用以下XML标签:<rosparam>将rosparam文件加载到此节点的~/local命名空间中,<param>在节点的〜/local命名空间中设置一个参数。
ns=”namespace”,前提是节点的代码在创建 ros::Pbulisher 和 ros::Subccriber 对象时使用了相对名称。同样启动文件中的节点名称是相对名称。
<remap>重映射是基于替换的思想,每个重映射包含一个原始名称和一个新名称。每当节点使用重映射中的原始名称时,ROS客户端库就会将它默默地替换成其对应的新名称。
<include file> 在启动文件中包含其他启动文件的内容( 包括所有的节点和参数)。
*参数服务器:参数服务器是个编程框架,用于方便分布式并行程序的编写,其中重点是对大规模参数的分布式存储和协同的支持。
优点:1.有效通信,由于是异步的通信,因此,不需要停下来等一些机器执行完一个iteration(除非有必要),这大大减少了延时。
2,宽松的一致性:进一步减少了同步的成本和延时,参数服务器允许算法设计者根据自身情况来做算法收敛速度和系统性能之间的衡量;
3,弹性可扩展:使用了一个分布式hash表使得新的server节点可以随时动态的插入到集合中;因此,新增一个节点不需要重新运行系统;
4,容错性和持久性:从非灾难性机器故障中恢复,只需要1秒,而且不需要中断计算。Vector clocks 保证了经历故障之后还是能运行良好;
5.易用性:全局共享的参数可以被表示成各种形式:vector,matrices 或者相应的sparse类型,这大大方便了机器学习算法的开发。并且提供的线性代数的数据类型都具有高性能的多线程库。
*rosparam命令在ROS的参数服务器上操作和存储数据,参数服务器可以存储整数,浮点数,布尔类型,字典,列表。我们使用rosparam list 命令可以查看参数服务器上的内容:rosparam list ; 使用rosparam set可以改变参数服务器上的参数,而rosparam get可以获取参数服务器上参数的值。parameter(特指在参数服务器parameter server中存储的参数)是运行中的ROS系统使用的数值,存储在参数服务器中,每个活跃的节点都可以通过ros::param::get来获取parameter的值,用户也可以通过rosparam来获得parameter的值而argument只在启动文件内才有意义他们的值是不能被节点直接获取的。
arg,给launch文件内部设置参数,仅在launch文件内有效。
rosparam set 设置参数 rosparam get 获取参数
rosparam load 从文件中加载参数 rosparam dump 转储参数到文件
rosparam delete 删除参数 rosparam list 列出参数名字
*使用C++获取参数:使用ROS参数的C++接口如下:
void ros::param::set(parameter_name, input_value);
bool ros::param::get(parameter_name, output_value);
其中参数名是字符串,set的输入值可以是字符\bool\int\double类型,get的输出值是上述某个类型的变量(通过引用传递)。
除了命令行、C++还可以在启动文件中设置参数,<param name="param-name" value="param-value" />
YAML文件:优势, YAML的可读性好、YAML和脚本语言的交互性好、- YAML使用实现语言的数据类型、YAML有一个一致的信息模型、YAML易于实现。
*使用ros::Publisher类创建句柄在话题上发布消息,首先创建一个publisher将要使用的消息,在setup函数中调用advertise.订阅话题的第一步是包括头文件,并且创建一个回调函数。静态分配一个ros::Subscriber对象,它规定了所用的消息类型,在setup函数中调用subscribe.
*发布者程序:
a.包含消息类型:每个ROS话题都与一个消息类型相关联,每个消息类型都有一个C++头文件,需在程序中为每个用到的消息类型包含这个头文件
b.创建发布者对象:发布消息的实际工作是由类名ros::Publisher的一个对象完成的。ros::Publisher pub = n.advertise<message_type>(topic_name, queue_size);
n是ros::NodeHandle类的一个对象,message_type模板参数,是要发布的消息数据类型,queue_size是发布队列大小,如果发消息太快,超过这个值,缓冲区把之前的消息扔掉
c.创建并填充消息对象:用上一步的消息类型定义一个消息对象msg,并给msg赋值
d.发布消息:pub.publish(msg)这个方法把所给的消息添加到发布者输出消息队列中,从这里它尽快被发送到相同话题的订阅者那里。
e.定义输出格式:(可选)ROS_INFO_STREAM(“sending msg”<<msg.para1<<msg.para2)
*订阅者程序:
a. 编写回调函数:发布和订阅一个重要区别是订阅者节点无法知道消息什么时候到达。为了应对这事实,必须把响应收到消息事件代码放到回调函数里,ros每接收到一个新消息调用一次这个函数。订阅者的回调函数类似:void function(const packagename::type_name &msg).其中packagename和type_name与发布消息时相同,指明想订阅话题的消息类,回调函数的主体有权限访问接收到消息的所有域,并以它认为合适的方式存储、使用或丢弃接收到的数据
b. 创建订阅者对象:为了订阅一个话题,我们需要创建一个ros::Subscriber对象,ros::Subscriber sub = n.subscribe(topic_name,queue_size, pointer_to_callback_function); queue_size接收消息队列大小,新消息太多,把之前的消息扔掉;最后一个参数是指向回调函数的指针,有消息到达时通这个指针找到回调函数,对函数名使用符号运算符(&,“取址”)来获得函数的指针,后面不要加上括号,&可选,rosPublisher没有提到消息类型 ,因为subscribe是模板化的,C++编译器会根据提供的函数指针中的数据类型判断正确的类型
c. 给ROS控制权:最后的复杂之处在于只有明确给ROS许可时,它才会执行回调函数。ros::spinOnce();要求ros执行所有挂起的回调函数,然后将控制权返给我们 ros::spin();要求ros等待并执行回调函数,直到这个节点关机,忽略 ros::spin 会导致程序在开始运行后不久就退出。忽略 ros::spinOnce 使程序表现的好像没有接收到任何消息。
*ros中的tf: tf是ROS中建立坐标系并使用各个坐标间转换关系的工具.广播tf(发布建立的坐标系)步骤如下:a,定义一个广播, tf::TransformBroadcaster br;b,定义存放转换信息(平动/转动)的变量 ,tf:: Transform transform; c,设置坐标原点,transform. setOrigin (tf::Vector3(msg->x, msg->y, msg->z));d,定义旋转,tf::Quaternion q;q.setRPY(0, 0,msg-> theta);transform.setRotation(q);e,将变换广播出去,br.sendTransform(tf::Stamped Trans form(transform, ros::Time::now(), "world", sub_name));
ros::Timer:定时器不能代替实时线程/内核,它们仅对没有硬实时要求的事物有用。
ros::Timer ros::NodeHandle::createTimer(ros::Duration period, <callback>, bool oneshot = false); period:定时器回调函数之间的时间间隔;<callback>定时器回调,函数、类方法或者函数子对象;oneshot:是否只定时一次,false,就是连续定时。
节点初始化:对于一个cpp节点(指基于roscpp程序包的节点),其初始化有两个阶段:
a. 通过调用ros::init()函数来初始化节点
b. 通过创建ros::NodeHandle实例来启动节点
ros::init():在node代码中在调用其它roscpp函数前,首先调用ros::init()函数: ros::init(argc, argv, "my_node_name");argc和argv:解析命令行重映射参数;node_name:节点名,在ros系统中,节点名必须唯一;如果重名节点被运行,那之前的节点就会自动关闭。
初始化节点只是简单地从命令行参数和环境中确定节点名、名字空间以及重映射。它不会与主节点master交互。
创建ROS程序包:
1,组成:一个catkin程序包必须包含catkin相容的package.xml来提供有关程序包的元信息,catkin版本的CMakeLists.txt文件,而catkin综合包中必须包含一个对CMakeLists.txt文件的引用。每个目录下只能有一个程序包。一个简单的工作空间看起来这样:
-workspace_folder/ //总的工作空间
- - src/ //源空间
- - - CMakeLists.txt //顶层CMake文件,由catkin提供
- - - package1
- - - - CMakeLists.txt //package1的CMakeLists.txt文件
- - - - package.xml //package1的Package manifest
- - - - source_code
- - - package2
- - - - CMakeLists.txt //package2的CMakeLists.txt文件
- - - - package.xml //package2的Package manifest
- - - - source_code
编译功能包:
法一:利用catkin编译,首先修改创建时生成的CMakeLists,
a.声明依赖包:find_package(catkin REQUIRED COMPONENTS
sensor_msgs cv_bridge image_transport)
find_package(OpenCV 2 REQUIRED)
find_package(PCL REQUIRED)
b.添加参与编译的节点程序
add_executable(node_name src/node_program) #添加可执行节点
target_link_libraries(node_name ${catkin_LIBRARIES})#链接库
add_dependencies(node_name package name_generator_messages_cpp)#为可执行文件添加对生成的消息文件的依赖
#因为catkin把所有的package并行的编译,如果要使用其他catkin工作空间中其他package的消息,同样也需要添加对他们各自生成的消息文件的依赖,就是一个先导入头文件的过程。
除了在创建功能包时添加系统提供的依赖,也可修改创建功能包时的package.xml
<build_depend>roscpp</build_depend> <run_depend>roscpp</run_depend>
法二:利用rosmake编译
用rosmake编译的是第二种利用roscreate创建的package,同样的在编译之前修改CMakeLists.txt文件和manifest.xml文件,但在修改CMakeLists.txt文件时需要加上如下语句来添加参与编译的节点程序:rosbuild_add_executable(node_name src/node_ name.cpp)
自定义的package.xml
--描述标签 <description>描述信息</description>
--维护者标签 <maintainer email="user@todo.todo">user</maintainer>
--许可标签 <license>TODO</license>应该选择一种许可协议并将它填写到这里。
--依赖项标签
<buildtool_depend>catkin</buildtool_depend> //编译工具包
<build_depend>roscpp</build_depend> //编译时依赖的程序包
<run_depend>python-yaml</run_depend> //运行时依赖的程序包
<test_depend>gtest</test_depend> //测试时依赖的程序包
--去掉不必要的注释和未使用的标签,完成package.xml
自定义的CMakeLists.txt
子目录下的CMakeLists.txt:
●cmake_minimum_required(VERSION 2.8.3)# 声明CMake的版本要求
●project(ros123)#指定工程名称
●set( var [value] [])#定义变量
●add_executable (bin_name source) #使用给定的源文件,为工程引入一个可执行文件
●add_library(libname [shared|static|]) #生成动/静态库
●include_directory([] dir1 dir2 ...) #向工程添加多个特定头文件搜索路径,路径间用空格
●include(file [])#载入CMakeLists.txt
●target_link_libraries(target lib1 lib2 ...)#为target添加需要链接的共享库
●find_package(<name> [])#用来调用预定义在Cmake_module_path下的Find<name>. cmake模块