ROS1/2机器人操作系统与时间Time的不解之缘

news2024/11/17 7:42:43

时间对于机器人操作系统非常重要。

所有机器人类的编程中所涉及的变量如果需要在网络中传输都需要这个数据结构的时间戳。

宏观上,ROS1、ROS2各版本都有官方支持的时间节点。

ROS时钟--支持时间倒计时小工具

效果如下:

如果要部署机器人操作系统,ROS1最好选择noetic,ROS2最好选择humble

学习核心要点:

  • ros1:indigo、kinetic、melodic、noetic均可。

  • ros2:foxy、humble均可。

时钟和时间

官网介绍了支持编程的 ROS 原理和应用,这些编程既可以实时运行,也可以模拟时间运行,后者可能更快或更慢。

背景知识

许多机器人算法本质上依赖于定时同步。 为此,要求在ROS网络中运行的节点具有同步的系统时钟,以便它们可以准确地报告事件的时间戳。

与此同时,在很多实际案例中,能够控制系统的进度很重要。

实时计算需要严格的时间控制。还有后续处理如下:

在回放记录的数据时,支持对时间进度的加速、减慢或阶梯控制通常非常有价值。 此控件允许到达特定时间并暂停系统,以便可以对其进行深入调试。 可以使用传感器数据的日志来执行此操作,但是如果传感器数据与系统的其余部分不同步,则会破坏许多算法。

使用抽象时间源的另一个重要用例是,当针对模拟机器人而不是真实机器人运行记录的数据时。 根据仿真特性,模拟器可能能够比实时运行得快得多,或者可能需要运行得更慢。 比实时运行得更快的速度对于高级测试以及允许重复系统测试很有价值。 对于精度比速度更重要的复杂系统,比实时仿真慢是必要的。 通常,仿真是系统的限制因素,因此模拟器可以成为更快或更慢播放的时间源。 此外,如果模拟暂停,系统也可以使用相同的机制暂停。

为了提供简化的时间接口,将提供 ROS 时间和持续时间数据类型。 要查询最新时间,将提供 ROS 时钟接口。 时间源可以管理一个或多个时钟实例。

使用抽象时间的挑战

有许多同步算法,它们通常可以实现比网络上设备之间网络通信延迟更好的精度。 但是,这些算法利用了有关时间恒定和连续性质的假设。

使用抽象时间的一个重要方面是能够操纵时间。 在某些情况下,加速、减慢或完全暂停时间对于调试非常重要。

支持暂停时间的能力要求不假设时间值总是在增加。

当通信时间传播的变化时,通信网络中的延迟成为一个挑战。 时间抽象的任何更改都必须传达给图中的其他节点,但会受到正常的网络通信延迟的影响。 这种不准确性与通信延迟成正比,也与模拟时间与实时相比前进的速率增加成正比(“实时因素”)。 如果在使用时间抽象时需要非常准确的时间戳,可以通过减慢实时因素来实现,从而使通信延迟相对较小。

最后一个挑战是时间抽象必须能够向后跳转,此功能对于日志文件播放非常有用。 此行为类似于负日期更改后的系统时钟,并且要求开发人员使用时间抽象来确保其算法可以处理不连续性。 必须为开发人员 API 提供适当的 API,以启用向前和向后的时间跳转通知。

使用介绍

通常,ROS 客户端库将使用计算机的系统时钟作为时间源,也称为“clock”或“挂钟”(如实验室墙上的时钟)。但是,当运行模拟或回放记录的数据时,通常需要让系统使用模拟时钟,以便可以加速、减慢或逐步控制系统的感知时间。例如,如果要将传感器数据回放到系统中,则可能希望时间与传感器数据的时间戳相对应。

为了支持这一点,ROS 客户端库可以监听用于发布“模拟时间”的 /clock 主题。

为了使代码利用 ROS 模拟时间,所有代码都必须使用适当的 ROS 客户端库时间 API 来访问时间和睡眠,而不是使用语言原生例程。这将使您的系统无论使用挂钟还是模拟时钟,都能进行一致的时间测量。下面简要介绍了这些 API,但您应该熟悉所选的客户端库以获取更多详细信息。

在多台计算机上使用挂钟时间时,在它们之间同步时间非常重要。ROS 不提供此功能,因为已经有成熟的方法(例如 ntp,我们推荐的同步工具是 chrony)来执行此操作如果不同步多台机器的时钟,它们将不会就 tf 中使用的时间计算达成一致。

注意:这不是实时系统的 API!

使用 /clock 主题中的模拟时间

为了让ROS节点根据/clock主题使用模拟时间,在初始化节点之前,必须将/use_sim_time参数设置为true。这可以在启动文件中或从命令行完成。

如果设置了 /use_sim_time 参数,ROS 时间 API 将返回 time=0,直到收到来自 /clock 主题的值。然后,时间将仅在收到来自 /clock 主题的消息时更新,并且在更新之间保持不变。

对于使用模拟时间时持续时间的计算,客户端应始终等到收到第一个非零时间值后再开始,因为 /clock 主题中的第一个模拟时间值可能很高。

注意:在 ROS C Turtle 之前,节点会自动订阅 /clock 主题,如果有任何内容发布到 /clock 主题,节点将使用模拟时间。

运行时钟服务器

时钟服务器是发布到 /clock 主题的任何节点,单个 ROS 网络中不应运行多个节点。在大多数情况下,时钟服务器是模拟器或日志回放工具。

为了解决启动顺序的任何问题,在使用时钟服务器的任何启动文件中将 /use_sim_time 参数设置为 true 非常重要。如果您正在播放带有 rosbag 播放的包文件,则使用 --clock 选项将在播放包文件时运行时钟服务器。

最简单的案例演示:

ROS1

roscpp

//Get the time and store it in the time variable.
ros::Time time = ros::Time::now();
//Wait a duration of one second.
ros::Duration d = ros::Duration(1, 0);
d.sleep();

rospy

rospy.get_rostime() #get time as rospy.Time instance
rospy.get_time() #get time as float secs
rospy.sleep(duration)

具体cpp案例:

获取当前时间

ros::Time begin = ros::Time::now();

创建时间和持续时间实例

浮点数

ros::Time a_little_after_the_beginning(0.001);
ros::Duration five_seconds(5.0);

整型数

ros::Time a_little_after_the_beginning(0, 1000000);
ros::Duration five_seconds(5, 0);

转换时间和持续时间实例

double secs =ros::Time::now().toSec();

ros::Duration d(0.5);
secs = d.toSec();

时间和持续时间算术

ros::Duration two_hours = ros::Duration(60*60) + ros::Duration(60*60);
ros::Duration one_hour = ros::Duration(2*60*60) - ros::Duration(60*60);
ros::Time tomorrow = ros::Time::now() + ros::Duration(24*60*60);
ros::Duration negative_one_day = ros::Time::now() - tomorrow;

云端实践-蓝桥ROS云课

使用turtlesim看一下时间戳的重要性。

在三个终端分别输入:

roscore

开启主节点roscore

rosrun turtlesim turtlesim_node [13:46:56]

[ INFO] [1677476826.846406306]: Starting turtlesim with node name /turtlesim
[ INFO] [1677476826.858068449]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]

启动turtlesim仿真

rosrun turtlesim draw_square [13:46:58]

[ INFO] [1677476941.925480026]: New goal [7.544445 5.544445, 0.000000]
[ INFO] [1677476943.845465404]: Reached goal
[ INFO] [1677476943.845537205]: New goal [7.448444 5.544445, 1.570796]
[ INFO] [1677476947.797971492]: Reached goal
[ INFO] [1677476947.798056675]: New goal [7.454037 7.544437, 1.568000]
[ INFO] [1677476949.733859442]: Reached goal
[ INFO] [1677476949.733924604]: New goal [7.453769 7.448437, 3.138796]
[ INFO] [1677476953.669993390]: Reached goal
[ INFO] [1677476953.670053662]: New goal [5.453913 7.472422, 3.129600]
[ INFO] [1677476955.605801148]: Reached goal
[ INFO] [1677476955.605865104]: New goal [5.549906 7.471271, 4.700396]
[ INFO] [1677476959.541884586]: Reached goal
[ INFO] [1677476959.541945308]: New goal [5.507532 5.471720, 4.691200]
[ INFO] [1677476961.478066122]: Reached goal
[ INFO] [1677476961.478127597]: New goal [5.509565 5.567698, 6.261996]
[ INFO] [1677476965.413272345]: Reached goal
[ INFO] [1677476965.413332146]: New goal [7.508642 5.506937, 6.252800]
[ INFO] [1677476967.349857822]: Reached goal
[ INFO] [1677476967.349916915]: New goal [7.412686 5.509853, 1.540412]
[ INFO] [1677476971.285716384]: Reached goal

启动画正方形

这里面时间不是常见格式的,那么参考如下:

rqt→console

时间不同格式表达

其中关于时间:

ros::Timer timer = nh.createTimer(ros::Duration(0.016), boost::bind(timerCallback, _1, twist_pub));
Node: /draw_square
Time: 13:55:19.311055762 (2023-02-27)
Severity: Info
Published Topics: /rosout, /turtle1/cmd_vel

New goal [5.306689 5.861878, 5.998100]

Location:
/tmp/binarydeb/ros-kinetic-turtlesim-0.7.1/tutorials/draw_square.cpp:printGoal:41
#include <boost/bind.hpp>
#include <ros/ros.h>
#include <turtlesim/Pose.h>
#include <geometry_msgs/Twist.h>
#include <std_srvs/Empty.h>

turtlesim::PoseConstPtr g_pose;
turtlesim::Pose g_goal;

enum State
{
  FORWARD,
  STOP_FORWARD,
  TURN,
  STOP_TURN,
};

State g_state = FORWARD;
State g_last_state = FORWARD;
bool g_first_goal_set = false;

#define PI 3.141592

void poseCallback(const turtlesim::PoseConstPtr& pose)
{
  g_pose = pose;
}

bool hasReachedGoal()
{
  return fabsf(g_pose->x - g_goal.x) < 0.1 && fabsf(g_pose->y - g_goal.y) < 0.1 && fabsf(g_pose->theta - g_goal.theta) < 0.01;
}

bool hasStopped()
{
  return g_pose->angular_velocity < 0.0001 && g_pose->linear_velocity < 0.0001;
}

void printGoal()
{
  ROS_INFO("New goal [%f %f, %f]", g_goal.x, g_goal.y, g_goal.theta);
}

void commandTurtle(ros::Publisher twist_pub, float linear, float angular)
{
  geometry_msgs::Twist twist;
  twist.linear.x = linear;
  twist.angular.z = angular;
  twist_pub.publish(twist);
}

void stopForward(ros::Publisher twist_pub)
{
  if (hasStopped())
  {
    ROS_INFO("Reached goal");
    g_state = TURN;
    g_goal.x = g_pose->x;
    g_goal.y = g_pose->y;
    g_goal.theta = fmod(g_pose->theta + PI/2.0, 2*PI);
    printGoal();
  }
  else
  {
    commandTurtle(twist_pub, 0, 0);
  }
}

void stopTurn(ros::Publisher twist_pub)
{
  if (hasStopped())
  {
    ROS_INFO("Reached goal");
    g_state = FORWARD;
    g_goal.x = cos(g_pose->theta) * 2 + g_pose->x;
    g_goal.y = sin(g_pose->theta) * 2 + g_pose->y;
    g_goal.theta = g_pose->theta;
    printGoal();
  }
  else
  {
    commandTurtle(twist_pub, 0, 0);
  }
}


void forward(ros::Publisher twist_pub)
{
  if (hasReachedGoal())
  {
    g_state = STOP_FORWARD;
    commandTurtle(twist_pub, 0, 0);
  }
  else
  {
    commandTurtle(twist_pub, 1.0, 0.0);
  }
}

void turn(ros::Publisher twist_pub)
{
  if (hasReachedGoal())
  {
    g_state = STOP_TURN;
    commandTurtle(twist_pub, 0, 0);
  }
  else
  {
    commandTurtle(twist_pub, 0.0, 0.4);
  }
}

void timerCallback(const ros::TimerEvent&, ros::Publisher twist_pub)
{
  if (!g_pose)
  {
    return;
  }

  if (!g_first_goal_set)
  {
    g_first_goal_set = true;
    g_state = FORWARD;
    g_goal.x = cos(g_pose->theta) * 2 + g_pose->x;
    g_goal.y = sin(g_pose->theta) * 2 + g_pose->y;
    g_goal.theta = g_pose->theta;
    printGoal();
  }

  if (g_state == FORWARD)
  {
    forward(twist_pub);
  }
  else if (g_state == STOP_FORWARD)
  {
    stopForward(twist_pub);
  }
  else if (g_state == TURN)
  {
    turn(twist_pub);
  }
  else if (g_state == STOP_TURN)
  {
    stopTurn(twist_pub);
  }
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "draw_square");
  ros::NodeHandle nh;
  ros::Subscriber pose_sub = nh.subscribe("turtle1/pose", 1, poseCallback);
  ros::Publisher twist_pub = nh.advertise<geometry_msgs::Twist>("turtle1/cmd_vel", 1);
  ros::ServiceClient reset = nh.serviceClient<std_srvs::Empty>("reset");
  ros::Timer timer = nh.createTimer(ros::Duration(0.016), boost::bind(timerCallback, _1, twist_pub));

  std_srvs::Empty empty;
  reset.call(empty);

  ros::spin();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/375880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IP路由原理、静态路由及动态路由区分

1、什么是路由? 路由器 在互联网中进行路由选择所使用的设备&#xff0c;或者说&#xff0c;实现路由的设备&#xff0c;我们称之为路由器。 路由器关键功能&#xff1a;检查数据包的目的地确定信息源发现可能的路由选择最佳路由验证和维护路由信息 什么是路由 路由是指导I…

洗地机什么牌子最好?洗地机品牌排行榜前十名

洗地机是近年来火热的清洁电器&#xff0c;凭借强劲的顽渍污渍去除和高效的地面清洁性能&#xff0c;成为了保持洁净家居环境、为生活做减法的黑科技&#xff0c;颇受生活达人们的追捧和青睐。洗地机的品牌、种类众多&#xff0c;一时间令人眼花缭乱。今天&#xff0c;我想为大…

fastadmin:在新增页面,打开弹窗单选,参数回传

样式&#xff1a;核心代码&#xff1a;一、弹窗的控制器中&#xff1a;// 定义一个公共函数select()&#xff0c;如果这个请求是Ajax&#xff0c;则返回index()函数&#xff0c;否则返回view对象的fetch()函数。 public function select() {if ($this->request->isAjax(…

C 学习笔记 —— 函数指针

函数指针 上面的第二个char (* f) (int);写法就是函数指针的声明&#xff1b; 首先&#xff0c;什么是函数指针&#xff1f;假设有一个指向 int类型变量的指针&#xff0c;该指针储存着这个int类型变量储存在内存位置的地址。 同样&#xff0c;函数也有地址&#xff0c;因为函…

LC-3—MIO、MMIO、Caller Save、Callee Save

LC-3—MIO、MMIOMMIOMIOCaller Save、Callee Save举个例子MMIO MMIO&#xff08;Memory Mapped I/O&#xff09;是一种在系统内存中映射I/O端口的技术&#xff0c;它允许设备直接访问内存中的特定地址&#xff0c;从而实现I/O操作。MMIO技术可以提高I/O操作的效率&#xff0c;…

Mysql 事务版本链(RR 与 RC 的本质区别)

版本链其实就是CURD的历史记录&#xff0c;回滚的本质也是用版本链中的最近一条历史记录覆盖当前记录。版本链针对的是每个表中的记录&#xff0c;只要表中有任意一条记录被修改&#xff0c;版本链中就会新增一条历史记录。 目录 1、为什么需要版本链&#xff1f; 2、有关版本…

《爆肝整理》保姆级系列教程python接口自动化(二十四)--unittest断言——中(详解)

简介 上一篇通过简单的案例给小伙伴们介绍了一下unittest断言&#xff0c;这篇我们将通过结合和围绕实际的工作来进行unittest的断言。这里以获取城市天气预报的接口为例&#xff0c;设计了 2 个用例&#xff0c;一个是查询北京的天气&#xff0c;一个是查询 南京为例&#xf…

接口幂等性的通用解决方案golang版

文章目录简介幂等性如何实现前端应当处理后端基于 token redis 处理简介 接口的幂等性是指&#xff1a; 用户对同一个操作发起多次请求&#xff0c;系统的设计需要保证其多次请求后结果是一致的。常见的如支付场景&#xff0c;连续快速点击两次支付 10 元&#xff0c;不应该扣…

阶段八:服务框架高级(第三章:分布式缓存Redis)

阶段八&#xff1a;服务框架高级&#xff08;第三章&#xff1a;分布式缓存Redis&#xff09;Day-分布式缓存Redis0.学习目标1.Redis持久化1.1.RDB持久化 【重要】1.1.1.执行时机1.1.2.RDB原理1.1.3.小结1.2.AOF持久化【重要】1.2.1.AOF原理1.2.2.AOF配置1.2.3.AOF文件重写1.3.…

搜广推 WideDeep 与 DeepCrossNetwork (DCN) - 记忆+泛化共存

😄 这节来讲讲Wide&Deep与Deep&CrossNetwork (DCN)。从下图可看出WD非常重要,后面衍生出了一堆WD的变体。本节要讲的WD和DCN结构都非常简单,但其设计思想值得学习。 🚀 wide&deep:2016年,谷歌提出。 🚀 Deep&CrossNetwork (DCN):2017年,谷歌和斯坦…

刷题小抄1-2数之和

时间复杂度和空间复杂度 对于一个算法高效性的评估,分为时间复杂度与空间复杂度两种,在算法优化到极致的情况下,只能舍弃时间来换取空间,或者舍弃空间来换取时间,故而两者可以说是互斥关系 时间复杂度衡量的是算法运行的速度,而空间复杂度衡量的是算法运行所需要的额外内存空…

【Ap AutoSAR入门与实战开发03】-【Ap_s2s模块02】:到底什么是基于信号,什么是基于服务,两者的主要区别是什么?

总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 1 基于信号的通信2 基于服务的通信3 面向服务设计举例在【Ap_s2s模块01】中我们大概讲述了,为什么要有s2s模块,并且简单的阐述了基于信号的通信和基于SOME/IP的服务的通信,我们接下来详细分析一下这两个概念。…

Torch单独层赋值

20230227 - 引言 对于torch中的权值初始化方式&#xff0c;以往都是采用默认的方式&#xff0c;或者利用初始化库里面的函数。但是如果想尝试一些自己的想法&#xff0c;那就必须自己来填充这部分数据&#xff0c;例如看到的内容&#xff0c;利用PCA的公式来对权值进行填充。…

CSGO社区服务器搭建架设服务器配置以及环境准备

CSGO社区服务器搭建架设服务器配置以及环境准备 CSGO作为一款射击动作游戏还原场景真实性广受大批玩家的热爱&#xff0c;很多小伙伴也有想过自己搭建的话需要知道那些东西。 我是艾西&#xff0c;今天跟大家聊聊搭建架设前我们需要知道的事情&#xff1a; Windows&#xff1…

【谷歌grc】recaptcha browser-error 错误

grc 谷歌人机验证错误 https://www.google.com/recaptcha/api/siteverif 返回错误信息 browser-error [{"success": false,"error-codes": ["browser-error"] }]之前都是调通能用的&#xff0c;突然之间就不能用了&#xff0c;查了半天也没有找…

前端实现压缩图片的功能(vue-element)

前言&#xff1a; 随着现在手机像素&#xff0c;拍照功能越来越好&#xff0c;随之而来的是本地图片越来越大&#xff0c;那么如何更好的将本地图片上传到后端接口呢&#xff1f;这是后台管理系统常见的场景和头疼的问题&#xff0c;这里分享下个人的方法。 实现效果&#xff…

java多线程(八)线程等待与线程唤醒2

三、wait(long timeout)和notify() wait(long timeout)会让当前线程处于“等待(阻塞)状态”&#xff0c;“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法&#xff0c;或者超过指定的时间量”&#xff0c;当前线程被唤醒(进入“就绪状态”)。 下面的示例就是演示…

入职数据分析公认的好书|建议收藏

众所周知&#xff0c;数据分析经常出现在我们的日常生活中&#xff0c;各行各业都需要数据分析。可你知道什么是数据分析&#xff1f;它在企业里到底扮演什么角色&#xff1f;以及如果我们自己也想拥有数据分析的能力&#xff0c;以便更好的满足数据分析的需求&#xff0c;我们…

系列六、存储引擎

一、存储引擎介绍 大家可能没有听说过存储引擎&#xff0c;但是一定听过引擎这个词&#xff0c;引擎就是发动机&#xff0c;是一个机器的核心组件。 比如&#xff0c;对于舰载机、直升机、火箭来说&#xff0c;他们都有各自的引擎&#xff0c;是他们最为核心的组件。而我们在选…

vue脚手架多页自动化生成实践

前言 在前端开发过程中&#xff0c;常常面对多种业务场景。到目前为止&#xff0c;前端对于不同场景的处理通常会采用不同的渲染方案来组合处理&#xff0c;常见的渲染方案包括&#xff1a;CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generati…