2021.3.16ROS常⽤API及头⽂件和源⽂件参考官⽅
主要有下⾯⼏个API
ROS节点的初始化相关API;
NodeHandle 的基本使⽤相关API;
话题的发布⽅,订阅⽅对象相关API;
中国十大王朝服务的服务端,客户端对象相关API;
时间相关API;
⽇志输出相关API。
1.初始化对象。
ros::init(int &argc,char **argv,const std::string& name,uint32_t options=0);
argc 参数个数
argv 参数列表
name 节点名称,需保证唯⼀性,不允许包含命名空间
options 节点启动选项,
2.NodeHandle 创建发布对象。
在ROS master注册并返回⼀个发布者对象,该对象可以发布消息。使⽤⽰例如下
ros::NodeHandle handle;
ros::Publisher pub = handle.adverti<std_msgs::Empty>("my_topic",1);
其中,Publisher adverti(const std::string& topic, uint32_t queue_size, bool latch = fal)
topic 发布信息使⽤的话题
queue_size 等待发送给订阅者的最⼤消息数量
latch(optional) 如果为true,该话题发布的最后⼀条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者return 调⽤成功时,会返回⼀个发布对象
3.订阅对象
/**
* \brief ⽣成某个话题的订阅对象
*
* 该函数将根据给定的话题在ROS master 注册,并⾃动连接相同主题的发布⽅,每接收到⼀条消息,都会调⽤回调
* 函数,并且传⼊该消息的共享指针,该消息不能被修改,因为可能其他订阅对象也会使⽤该消息。
*
* 使⽤⽰例如下:
void callback(const std_msgs::Empty::ConstPtr& message)
{
}
ros::Subscriber sub = handle.subscribe("my_topic", 1, callback);
*
* \param M [template] M 是指消息类型
* \param topic 订阅的话题
* \param queue_size 消息队列长度,超出长度时,头部的消息将被弃⽤
* \param fp 当订阅到⼀条消息时,需要执⾏的回调函数
* \return 调⽤成功时,返回⼀个订阅者对象,失败时,返回空对象
*
void callback(const std_msgs::Empty::ConstPtr& message){...}
ros::NodeHandle nodeHandle;
ros::Subscriber sub = nodeHandle.subscribe("my_topic", 1, callback);
if (sub) // Enter if subscriber is valid
{
...
}
*/
template<class M>
Subscriber subscribe(const std::string& topic, uint32_t queue_size, void(*fp)(const boost::shared_ptr<M const>&), const TransportHints& transport_hints = TransportHints())
4.服务端对象
/**
* \brief ⽣成服务端对象
*
火腿三明治* 该函数可以连接到 ROS master,并提供⼀个具有给定名称的服务对象。
*
* 使⽤⽰例如下:
\verbatim
bool callback(std_srvs::Empty& request, std_srvs::Empty& respon)
{
return true;
}
ros::ServiceServer rvice = handle.advertiService("my_rvice", callback);
\endverbatim
*
* \param rvice 服务的主题名称
* \param srv_func 接收到请求时,需要处理请求的回调函数
* \return 请求成功时返回服务对象,否则返回空对象:
\verbatim
bool Foo::callback(std_srvs::Empty& request, std_srvs::Empty& respon)
{
return true;
}
ros::NodeHandle nodeHandle;
Foo foo_object;
ros::ServiceServer rvice = nodeHandle.advertiService("my_rvice", callback);
if (rvice) // Enter if advertid rvice is valid
{
...
}
\endverbatim
*/
template<class MReq, class MRes>
ServiceServer advertiService(const std::string& rvice, bool(*srv_func)(MReq&, MRes&))
5.客户端对象
对象获取:
/**
* @brief 创建⼀个服务客户端对象
*
* 当清除最后⼀个连接的引⽤句柄时,连接将被关闭。
*
* @param rvice_name 服务主题名称
*/
template<class Service>
ServiceClient rviceClient(const std::string& rvice_name, bool persistent = fal,
const M_string& header_values = M_string())
请求发送函数:
/**
* @brief 发送请求
* 返回值为 bool 类型,true,请求处理成功,fal,处理失败。
*/
template<class Service>
动漫设计bool call(Service& rvice)
等待服务函数1:
/**
* ros::rvice::waitForService("addInts");
* \brief 等待服务可⽤,否则⼀致处于阻塞状态
* \param rvice_name 被"等待"的服务的话题名称
* \param timeout 等待最⼤时常,默认为 -1,可以永久等待直⾄节点关闭
* \return 成功返回 true,否则返回 fal。
*/
ROSCPP_DECL bool waitForService(const std::string& rvice_name, ros::Duration timeout = ros::Duration(-1));
等待服务函数2:
/**
* client.waitForExistence();
* \brief 等待服务可⽤,否则⼀致处于阻塞状态
* \param timeout 等待最⼤时常,默认为 -1,可以永久等待直⾄节点关闭
* \return 成功返回 true,否则返回 fal。
*/
bool waitForExistence(ros::Duration timeout = ros::Duration(-1));
经典英语歌
回旋函数
/**
* \brief 处理⼀轮回调
*
* ⼀般应⽤场景:
* 在循环体内,处理所有可⽤的回调函数
*
*/
ROSCPP_DECL void spinOnce();
/**
* \brief 进⼊循环处理回调
*/
ROSCPP_DECL void spin();
相同点:⼆者都⽤于处理回调函数;
不同点:ros::spin() 是进⼊了循环执⾏回调函数,⽽ ros::spinOnce() 只会执⾏⼀次回调函数(没有循环),在 ros::spin() 后的语句不会执⾏到,⽽ros::spinOnce() 后的语句可以执⾏。
时间
ROS中时间相关的API是极其常⽤,⽐如:获取当前时刻、持续时间的设置、执⾏频率、休眠、定时器...都与时间相关。
1.时刻
获取时刻,或者设置指定时刻。
ros::init(argc,argv,"hello_time");
ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调⽤失败
ros::Time right_now = ros::Time::now();//将当前时刻封装成对象
ROS_INFO("当前时刻:%.2f",Sec());//获取距离 1970年01⽉01⽇ 00:00:00 的秒数
ROS_INFO("当前时刻:%d",right_now.c);//获取距离 1970年01⽉01⽇ 00:00:00 的秒数
ros::Time someTime(100,100000000);// 参数1:秒数参数2:纳秒
ROS_INFO("时刻:%.2f",Sec()); //100.10
ros::Time someTime2(100.3);//直接传⼊ double 类型的秒数
ROS_INFO("时刻:%.2f",Sec()); //100.30
补充:可以引⽤time.h,将时间戳(即上述的Sec()和right_now.c)转换成年-⽉-⽇时-分-秒的格式。
2.持续时间
ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());
ros::Duration du(10);//持续10秒钟,参数是double类型的,以秒为单位
du.sleep();//按照指定的持续时间休眠
ROS_INFO("持续时间:%.2f",du.toSec());//将持续时间换算成秒
ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());陈庆州
3.持续时间与时刻运算
ROS_INFO("时间运算");
ros::Time now = ros::Time::now();
ros::Duration du1(10);
ros::Duration du2(20);
ROS_INFO("当前时刻:%.2f",Sec());
//1.time 与 duration 运算
ros::Time after_now = now + du1;
ros::Time before_now = now - du1;
ROS_INFO("当前时刻之后:%.2f",Sec());
ROS_INFO("当前时刻之前:%.2f",Sec());
//2.duration 之间相互运算
ros::Duration du3 = du1 + du2;
ros::Duration du4 = du1 - du2;
ROS_INFO("du3 = %.2f",Sec());
ROS_INFO("du4 = %.2f",Sec());
//PS: time 与 time 不可以运算
// ros::Time nn = now + before_now;//异常
4.定时器
ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调⽤失败
// ROS 定时器
/**
* \brief 创建⼀个定时器,按照指定频率调⽤回调函数。
*
重定位* \param period 时间间隔
* \param callback 回调函数
* \param oneshot 如果设置为 true,只执⾏⼀次回调函数,设置为 fal,就循环执⾏。
* \param autostart 如果为true,返回已经启动的定时器,设置为 fal,需要⼿动启动。
*/
//定时器的回调函数
void doSomeThing(const ros::TimerEvent &event){
ROS_INFO("-------------");
ROS_INFO("event:%s",std::to_string(event.Sec()).c_str());
}
//Timer createTimer(Duration period, const TimerCallback& callback, bool oneshot = fal,
// bool autostart = true) const;
// ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing);
ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,true);//只执⾏⼀次
// ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,fal,fal);//需要⼿动启动
// timer.start();
ros::spin(); //必须 spin
5.其他
bool ros::ok() 检查节点是否已经退出
void ros::shutdown(); 关闭节点
ROS_DEBUG("hello,DEBUG"); //不会输出
ROS_INFO("hello,INFO"); //默认⽩⾊字体
ROS_WARN("Hello,WARN"); //默认黄⾊字体
ROS_ERROR("hello,ERROR");//默认红⾊字体
ROS_FATAL("hello,FATAL");//默认红⾊字体
⼀、ROS中的⾃定义头⽂件调⽤
1.创建头⽂件
在功能包下的 include/功能包名⽬录下新建头⽂件: hello.h,⽰例内容如下:
#ifndef _HELLO_H
#define _HELLO_H
namespace hello_ns{
class HelloPub {
public:
void run();
};
}
#endif
注意:在 VScode 中,为了后续包含头⽂件时不抛出异常,请配置 .vscode 下 c_cpp_properties.json 的 includepath属性"/home/⽤户/⼯作空间/src/功能包/include/**"
2.可执⾏⽂件
在 src ⽬录下新建⽂件:hello.cpp,⽰例内容如下:
#include "ros/ros.h"
#include "test_head/hello.h"
namespace hello_ns {
void HelloPub::run(){
描写爱国的诗句ROS_INFO("⾃定义头⽂件的使⽤....");
}
}
int main(int argc, char *argv[])
{
tlocale(LC_ALL,"");
ros::init(argc,argv,"test_head_node");
hello_ns::HelloPub helloPub;
helloPub.run();
return 0;
}
3.配置⽂件
include_directories(
include
${catkin_INCLUDE_DIRS}
)
add_executable(hello src/hello.cpp)
add_dependencies(hello ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(hello
${catkin_LIBRARIES}
)
⼆、ROS中的⾃定义源⽂件调⽤
1.头⽂件
类似于上⾯,在功能包下的 include/功能包名⽬录下新建头⽂件: haha.h,⽰例内容如下:
#ifndef _HAHA_H
#define _HAHA_H
namespace hello_ns {
class My {
public:
void run();
};
}
#endif
在 VScode 中,为了后续包含头⽂件时不抛出异常,请配置 .vscode 下 c_cpp_properties.json 的 includepath属性"/home/⽤户/⼯作空间/src/功能包/include/**"
2.源⽂件
在 src ⽬录下新建⽂件:haha.cpp,⽰例内容如下:
#include "test_head_src/haha.h"
#include "ros/ros.h"
namespace hello_ns{
void My::run(){
ROS_INFO("hello,head and src ...");
}
}
3.可执⾏⽂件
在 src ⽬录下新建⽂件: u_head.cpp,⽰例内容如下:
#include "ros/ros.h"
#include "test_head_src/haha.h"
int main(int argc, char *argv[])
{
ros::init(argc,argv,"hahah");
hello_ns::My my;
my.run();
劳务班组
return 0;
}
4.配置⽂件