ROS2获取当前系统时间的方法

C++ 标准库中的三种时钟

std::chrono::system_clock

  using namespace std::chrono_literals;
  const auto start = std::chrono::system_clock::now();


#include <iostream>
#include <iomanip>
#include <vector>
#include <numeric>
#include <chrono>
 
volatile int sink;
int main()
{
    std::cout << std::fixed << std::setprecision(9) << std::left;
    for (auto size = 1ull; size < 1000'000'000ull; size *= 100) {
        // record start time
        auto start = std::chrono::system_clock::now();
        // do some work
        std::vector<int> v(size, 42);
        sink = std::accumulate(v.begin(), v.end(), 0u); // make sure it's a side effect
        // record end time
        auto end = std::chrono::system_clock::now();
        std::chrono::duration<double> diff = end - start;
        std::cout << "Time to fill and iterate a vector of " << std::setw(9)
                  << size << " ints : " << diff.count() << " s\n";
    }
}

/* 输出
Time to fill and iterate a vector of 1         ints : 0.000006568 s
Time to fill and iterate a vector of 100       ints : 0.000002854 s
Time to fill and iterate a vector of 10000     ints : 0.000116290 s
Time to fill and iterate a vector of 1000000   ints : 0.011742752 s
Time to fill and iterate a vector of 100000000 ints : 0.505534949 s
*/

//https://en.cppreference.com/w/cpp/chrono/system_clock/now

system_clock是系统范围的时钟。它是可修改的。比如同步网络时间。所以系统的时间差可能不准。

std::chrono::steady_clock

#include <iostream>
#include <iomanip>
#include <ctime>
#include <chrono>
 
int main()
{
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    std::time_t now_c = std::chrono::system_clock::to_time_t(now - std::chrono::hours(24));
    std::cout << "24 hours ago, the time was "
              << std::put_time(std::localtime(&now_c), "%F %T") << '\n';
 
    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    std::cout << "Hello World\n";
    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::cout << "Printing took "
              << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
              << "us.\n";
}


/*输出
24 hours ago, the time was 2011-10-25 12:00:08
Hello World
Printing took 84us.
*/

https://www.apiref.com/cpp-zh/cpp/chrono/time_point.html

steady_clock是单调时钟。此时钟的时间点无法减少,像物理秒表一样。通常精度能达到纳秒级别,适合用来计算程序执行时间

std::chrono::high_resolution_clock

#include <iostream>
#include <ctime>
#include <ratio>
#include <chrono>

int main ()
{
  using namespace std::chrono;

  high_resolution_clock::time_point t1 = high_resolution_clock::now();

  std::cout << "printing out 1000 stars...\n";
  for (int i=0; i<1000; ++i) std::cout << "*";
  std::cout << std::endl;

  high_resolution_clock::time_point t2 = high_resolution_clock::now();

  duration<double> time_span = duration_cast<duration<double>>(t2 - t1);

  std::cout << "It took me " << time_span.count() << " seconds.";
  std::cout << std::endl;

  return 0;
}

/* 输出
printing out 1000 stars...
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
********************************************************************************
****************************************
It took me 0.091001 seconds.
*/
//参考:https://cplusplus.com/reference/chrono/high_resolution_clock/now/

high_resolution_clock在不同标准库之间有不同的实现。

通常它只是 std::chrono::steady_clockstd::chrono::system_clock 的别名,但实际是哪个取决于库或配置。例如对于 gcc libstdc++ 它是 system_clock ,对于 MSVC 它是 steady_clock ,而对于clang libc++ 它取决于配置。

所以推荐直接使用对应的时钟而不是high_resolution_clock

ROS2中的时间戳

ROS2中定义了三种时钟。默认是使用RCL_SYSTEM_TIME。它和C++中的std::chrono::system_clock是一样的,即系统时间。

typedef enum rcl_clock_type_t
{
  /// Clock uninitialized
  RCL_CLOCK_UNINITIALIZED = 0,
  /// Use ROS time
  RCL_ROS_TIME,
  /// Use system time
  RCL_SYSTEM_TIME,
  /// Use a steady clock time
  RCL_STEADY_TIME
} rcl_clock_type_t;

ROS2中存在两种时间戳。一种是实际的物理系统时间,另一种是仿真时间。仿真时间通常是Gazebo发出的/clock话题。

/clock话题

clock:
  sec: 65
  nanosec: 212000000
---
clock:
  sec: 65
  nanosec: 298000000
---
clock:
  sec: 65
  nanosec: 388000000

当需要在仿真环境中测试程序时,要将use_sim_time置为True。不然会出现下面的报错信息。表示时间不匹配。tf关系不能正常找到。

报错信息:

[controller_server]: Extrapolation Error: Lookup would require extrapolation into the future.  Requested time 1646190408.221065 but the latest data is at time 19.413000, when looking up transform from frame [map] to frame [odom]

置位use_sim_time可以写在launch文件中。如下所示:

def generate_launch_description():

    use_sim_time = LaunchConfiguration('use_sim_time', default='false')

    return LaunchDescription([
        DeclareLaunchArgument(
            'use_sim_time',
            default_value=use_sim_time,
            description='Use simulation (Gazebo) clock if true'),


        Node(
            package='time_api_test',
            executable='time_api_test',
            parameters=[{'use_sim_time': use_sim_time}],
            arguments=[],
            output='screen'),
    ])

ROS2中获取当前时间戳

下面的代码片段罗列了获取系统时间的几种方法:

    auto t = rclcpp::Clock().now();
    RCLCPP_INFO(this->get_logger(), "[rclcpp::Clock().now()] sec:%lf nano:%ld", t.seconds(), t.nanoseconds());

    auto t1 = std::chrono::system_clock::now(); 
    time_t tt = std::chrono::system_clock::to_time_t ( t1 );
    RCLCPP_INFO(this->get_logger(), "[std::chrono::system_clock::now()] sec:%ld", tt);

    std::chrono::steady_clock::time_point td = std::chrono::steady_clock::now(); 
    std::chrono::steady_clock::duration dtn = td.time_since_epoch();
    double secs = dtn.count() * std::chrono::steady_clock::period::num / std::chrono::steady_clock::period::den;
    RCLCPP_INFO(this->get_logger(), "[std::chrono::steady_clock::now()] sec:%lf", secs);
    
    auto t2 = this->get_clock()->now();
    RCLCPP_INFO(this->get_logger(), "[get_clock()->now()] sec:%lf nano:%ld", t2.seconds(), t2.nanoseconds());

    auto t3 = this->now();
    RCLCPP_INFO(this->get_logger(), "[this->now()] sec:%lf nano:%ld", t3.seconds(), t3.nanoseconds());

注意,完整的示例代码可在公众号《首飞》中回复“time” 获取到。

use_sim_timefalse时,运行上面测试代码的结果为:

[time_api_test-1] [INFO 1658498046.378262276] [time_api_test]: [rclcpp::Clock().now()] sec:1658498046.378260 nano:1658498046378260389 
[time_api_test-1] [INFO 1658498046.378315552] [time_api_test]: [std::chrono::system_clock::now()] sec:1658498046 
[time_api_test-1] [INFO 1658498046.378325479] [time_api_test]: [std::chrono::steady_clock::now()] sec:5906.000000 
[time_api_test-1] [INFO 1658498046.378333539] [time_api_test]: [get_clock()->now()] sec:1658498046.378333 nano:1658498046378333309 
[time_api_test-1] [INFO 1658498046.378341987] [time_api_test]: [this->now()] sec:1658498046.378342 nano:1658498046378341769 

可以发现rclcpp::Clock().now()get_clock()->now()this->now()获取到的时间与std::chrono::system_clock::now()是一致的。

这里需要注意的一点是rclcpp::Clock().now()get_clock()->now()this->now()获取到的时间戳均包含seconds()nanoseconds()方法。seconds()nanoseconds()方法都返回了当前的时间,是等价的,只是单位不一样。一个是以秒为单位,一个是纳秒为单位。

auto t = this->get_clock()->now()

builtin_interfaces::msg::Time tt = t;
    
RCLCPP_INFO(this->get_logger(), "sec: %lf nano: %lf  tt_sec: %ld tt_nano: %ld", t.seconds(), t.nanoseconds(), tt.sec, tt.nanosec);

但是ROS2中的时间类型builtin_interfaces::msg::Time是需要把秒和纳秒组合起来才能表示当前时间的。

double now_sec = msg->header.stamp.sec + msg->header.stamp.nanosec * 1e-9;

now_sec是以秒为单位的时间。

use_sim_timetrue时,运行上面测试代码的结果为:

注意,运行测试代码前。我运行了turtlebot3gazebo仿真环境。以便系统中有/clock话题来提供仿真时间。

[time_api_test-1] [INFO 1658498456.593959180] [time_api_test]: [rclcpp::Clock().now()] sec:1658498456.593951 nano:1658498456593951346 
[time_api_test-1] [INFO 1658498456.594114487] [time_api_test]: [std::chrono::system_clock::now()] sec:1658498456 
[time_api_test-1] [INFO 1658498456.594132577] [time_api_test]: [std::chrono::steady_clock::now()] sec:6316.000000 
[time_api_test-1] [INFO 1658498456.594142330] [time_api_test]: [get_clock()->now()] sec:300.636000 nano:300636000000 
[time_api_test-1] [INFO 1658498456.594149351] [time_api_test]: [this->now()] sec:300.636000 nano:300636000000 

可以看到rclcpp::Clock().now()还是与std::chrono::system_clock::now()保持一致。说明rclcpp::Clock().now()不能返回仿真时间。

get_clock()->now()this->now()是等效的。他们均返回了仿真时间。

经过测试对比,可得出结论rclcpp::Clock().now()无法正确获取仿真时间。所以代码中要获取时间戳时,可调用get_clock()->now()this->now()接口。这样可以保证在标志位use_sim_time变化时,代码各处使用的时间戳是一致的。

计算程序运行时间的方法

rclcpp::Clock steady_clock_{RCL_STEADY_TIME};

auto start_time = steady_clock_.now();
//do something ...
auto cycle_duration = steady_clock_.now() - start_time;

RCLCPP_INFO(get_logger(), "Cost %.4f s", cycle_duration.seconds());

使用RCL_STEADY_TIME时钟去计算程序运行时间是比较准确的。


觉得有用就点赞吧!

我是首飞,一个帮大家填坑的机器人开发攻城狮。

另外在公众号《首飞》内回复“机器人”获取精心推荐的C/C++,Python,Docker,Qt,ROS1/2等机器人行业常用技术资料。

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

首飞爱玩机器人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值