首页 > 其他分享 >如何实现两个机器时间同步-计算rtt+offset

如何实现两个机器时间同步-计算rtt+offset

时间:2024-09-13 14:51:04浏览次数:9  
标签:同步 dros Res utils rtt offset TimestampRTT ts1

背景:

之前的项目大致可以分为两层,逻辑层和设备层,运行在同一个主机上。
最近在着手搭建一个仿真平台,在另外一台主机上部署机器人机器相关硬件设备,比如陀螺仪,轮机,雷达等。

由于两台主机的时间戳不同步问题,导致定位系统有问题,为此需要实现两个主机的时间同步。

具体分两步:
1)测量两主机的网络延迟(round trip time,RTT)。
2)根据RTT计算时间偏移offset。

一、计算RTT

参考:https://blog.csdn.net/ingnight/article/details/100518409#:~:text=%E4%B8%80%E3%80%81roun
用于测量两台机器之间的网络延迟,即“往返时间(round trip time,RTT)”,原理如下:

大致逻辑图:

 相关proto:

/*
定时运行,比如每5分钟执行一次。
1. A->B. 发3个TimestampRTT_Req
2. B会收到3个TimestampRTT_Req. B-A:再回复3个TimestampRTT_Res
3. A收到TimestampRTT_Res后,记录下收到的时间戳A_ts2. RTT1 = ((A_ts2 - A_ts1) - (B_ts2 - B_ts1)) / 2;
4. 再求平均 RTT_Avg
5. 求 offsetTs = A_ts1 - (B_ts1 - RTT_Avg) = A_ts1 + RTT_Avg - B_ts1

如何使用 offsetTs:
B_ts转换成A的时间戳 Ats = offsetTs + B_ts
*/
message TimestampRTT_Req{
  int64 A_ts1 = 1; // GetNow_Steady
}

message TimestampRTT_Res{
  int64 A_ts1 = 1; // 接收端赋值。就是收到的 A_ts1
  int64 A_ts2 = 2; // 接收端不用赋值。收到回复后,再记录一下时间戳。
  int64 B_ts1 = 3; // 接收端赋值。GetNow_Steady recv time
  int64 B_ts2 = 4; // 接收端赋值。GetNow_Steady res time
}

伪代码:

主机A:

    
// 发送端:实际调用时,需要连续发5-10个
    void sendSync()
    {
        printf("[%s] EcalPubManager::sendSync() 发送 TimestampRTT_Req +++++++++++++++  \n", dros::utils::GetCurTimeStamp_MilSec().c_str());
        m_startSyncTs = dros::utils::GetNow_Steady();
        static eCAL::protobuf::CPublisher<dros::pb::timestampTest::TimestampRTT_Req> pub(m_DROS_DOMAIN + "TimestampRTT_Req");
        dros::pb::timestampTest::TimestampRTT_Req data;
        data.set_a_ts1(dros::utils::GetNow_Steady());
        pub.Send(data);
    }


// 接收
    void ProtoCallbackRecvTimestampRTT_Res(const dros::pb::timestampTest::TimestampRTT_Res& message) 
    {
        printf("[%s] EcalPubManager::ProtoCallbackRecvTimestampRTT_Res() 收到 TimestampRTT_Res:[%s]  \n", 
            dros::utils::GetCurTimeStamp_MilSec().c_str(), message.DebugString().c_str());

        dros::pb::timestampTest::TimestampRTT_Res message1;
        message1.CopyFrom(message);
        message1.set_a_ts2(dros::utils::GetNow_Steady());
        m_vecTimestampRTT_Res.push_back(message1);  // 放到一个数组里,主要为了取平均值,提高精度。
    }


  // 计算offset
    long long GetTsOffset()
    {    
        if(!m_bComputeOffset)
        {
            auto diff = dros::utils::GetNow_Steady() - m_startSyncTs;
            if(diff > 5000)
            {
                printf("[%s] EcalPubManager::GetTsOffset() 同步指令已经接收完成, 正在计算offset ...  \n", 
                    dros::utils::GetCurTimeStamp_MilSec().c_str());
                m_bComputeOffset = true;
                if(m_vecTimestampRTT_Res.size() > 2)
                {
                    long long rttCount = 0;
                    int mCount = 0;
                    for(auto iii: m_vecTimestampRTT_Res)
                    {
                        mCount++;
                        auto rtt = ((iii.a_ts1() - iii.a_ts2()) - (iii.b_ts1() - iii.b_ts2())) / 2;
                        rttCount += rtt;
                        printf("[%s] EcalPubManager::GetTsOffset() 正在计算offset iii:[%s] rtt:%lld $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  \n", 
                            dros::utils::GetCurTimeStamp_MilSec().c_str(), iii.DebugString().c_str(), rtt);
                    }
                    long long avgRTT = rttCount / mCount;   // 求平均值,提高精度
                    long long offset = m_vecTimestampRTT_Res[0].a_ts1() + avgRTT - m_vecTimestampRTT_Res[0].b_ts1();
                    m_TsOffset = offset;
                    printf("[%s] EcalPubManager::GetTsOffset() 新 offset:[%lld] avgRTT:[%lld] $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  \n", 
                        dros::utils::GetCurTimeStamp_MilSec().c_str(), offset, avgRTT);
                }
                else 
                {
                    printf("[%s] EcalPubManager::GetTsOffset() m_vecTimestampRTT_Res 为空。将重新开始发送Req $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  \n", 
                        dros::utils::GetCurTimeStamp_MilSec().c_str());
                    startSyncTime();
                }
            }
            else
            {
                printf("[%s] EcalPubManager::GetTsOffset() m_bComputeOffset is false, 正在接收 RTT_Res $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  \n", 
                    dros::utils::GetCurTimeStamp_MilSec().c_str());
            }
        }
        else
        {
            printf("[%s] EcalPubManager::GetTsOffset() m_bComputeOffset is true, 无需计算offset $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$  \n", 
                dros::utils::GetCurTimeStamp_MilSec().c_str());
        }
        return m_TsOffset;
    }

 

主机B:

// 收到req,立马回复

void ProtoCallbackRecvSimTimestampRTT_Req(const dros::pb::timestampTest::TimestampRTT_Req& message) 
{
  printf("[%s] ProtoCallbackRecvSimTimestampRTT_Req() +++++++++++++++ message:[%s] \n",
    dros::utils::GetCurTimeStamp_MilSec().c_str(), message.DebugString().c_str());
  static eCAL::protobuf::CPublisher<dros::pb::timestampTest::TimestampRTT_Res> pub(m_DROS_DOMAIN + "TimestampRTT_Res");
    dros::pb::timestampTest::TimestampRTT_Res data;
    data.set_a_ts1(message.a_ts1());
  data.set_a_ts2(0);
  data.set_b_ts1(dros::utils::GetNow_Steady() + offsetTest); // 接收到数据时的时间戳
  std::this_thread::sleep_for(std::chrono::milliseconds(10));  // 这个可以取消,主要为了模拟处理消耗。
  data.set_b_ts2(dros::utils::GetNow_Steady() + offsetTest); // 处理完成后的时间戳
    pub.Send(data);
}

 

计算offset:

offset = ats1 + RTT - bts1;

二、如何使用offset:

主机A接收到B发来的时间戳加上offset就是转换后的时间戳
TsA = offset + TsB

三、总结及说明:

1.上述数据结构是用protobuf定义的。

2.数据传输通过ecal发送proto的方式。

 

标签:同步,dros,Res,utils,rtt,offset,TimestampRTT,ts1
From: https://www.cnblogs.com/xcywt/p/18411849

相关文章

  • 最新知识付费系统3.0整站+自动采集同步插件
    源码简介:最新更新:1.修复更新到最新版本2.自动采集插件重写3.关闭采集授权域名直接对接4.更新插件主动请求同步资源原始功能:支持分类替换将主站同步过来的文章分类进行替换支持自定义文章作者(选择多个作者则同步到的文章作者将会随机分配)支持添加黑名单分类添加后......
  • Debezium数据同步基础概论
    一、概述在当今的分布式系统和现代企业架构中,数据的生成和存储已经变得高度分散。不同的系统、服务和应用程序可能都在各自的数据库中记录数据。这种环境下,保持数据的一致性和实时同步变得尤为复杂。特别是在需要对多个系统中的数据进行整合时,数据捕获和同步的挑战就更加突......
  • 证券公司上千台服务器数据同步时,如何进行文件传输管控?
    证券公司的数据中心是一个至关重要的基础设施,它承担着数据处理、存储、分析和传输等重要任务,对于保障证券公司的业务连续性、提高运营效率、降低风险等方面具有不可替代的作用。数据中心是企业数据集中的载体和支持平台,是实现数据集中的必要手段。在证券公司中,数据中心不仅是一个......
  • MySQL学习笔记(二)InnoDB内存模型与磁盘同步机制
    InnoDB存储引擎ACID是我们在数据库设计的时候,尽可能的去满足的设计原则。A原子性、C一致性I隔离性D持久性,其中InnoDB存储引擎就是满足了我们ACID设计原则的。内存缓存结构(BufferPool)如果每次获取数据都去磁盘获取,这样效率明显比较慢。所以innoDB为了性......
  • Java 入门指南:Java 并发编程 —— 同步工具类 CyclicBarrier(循环屏障)
    文章目录同步工具类CyclicBarrier构造函数常用方法工作机制使用步骤适用场景CyclicBarrier与CountDownLatch的区别示例代码同步工具类JUC(Java.util.concurrent)是Java提供的用于并发编程的工具类库,其中包含了一些通信工具类,用于在多个线程之间进行协调和通信,特别......
  • ①MODBUS TCP 通信单元(MODBUS TCP 转 RS485)Modbus TCP转Modbus RTU/ASCII网关同步采集
    ModbusTCP转ModbusRTU/ASCII网关同步采集无需编程高速轻松组网MS-A1-50X1系列作为MODBUSTCP通信的服务器进行动作。可通过MODBUSTCP通信,将MS-A1-50X1系列产品通过RS485采集的仪器仪表之类的值作为通信数据输出到PLC,上位机等。系统配置概述使用MS-A1-50X1系......
  • [ZJOI2007] 时态同步
    [ZJOI2007]时态同步题目描述小Q在电子工艺实习课上学习焊接电路板。一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字\(1,2,3\cdots\)进行标号。电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点,都存在且仅存在一条通路(通路指连接两个元件的......
  • 思源笔记-S3-七牛云-多设备同步
    文档参考:思源笔记配置S3同步、思源笔记使用七牛云编写日期:2024.9.9一、思源笔记安装思源笔记官方下载地址选择对应系统版本进行下载双击【SiYuanInstaller.exe】进行安装二、注册账号注册账号是为了购买订阅,订阅后才提供S3/WEBDAV同步功能打开SiYuan......
  • LTE PSS主同步信号PSS搜索阶段频偏估计
    频偏的影响:本期要讲到PSS搜索阶段,整数倍频偏和小数倍频偏的估计方法,整数倍频偏指的是子载波间隔的整数倍比如15k、30k等,小数倍频偏指的是一个子载波间隔以内的。在OFDM通信系统中,频偏是一个比较敏感的词,正常如果频偏估不准会带来一系列的问题,比如OFDM信号的正交性遭到破坏,带来......
  • 解决微软OneNote不能同步的问题
    https://blog.csdn.net/zhuwei/article/details/140926025前言最近发现开启本地http代理服务器后onenoteforwindows10无法同步,但是我的代理配置是无误的.经过查询发现,这个是由于微软的UWP应用的新特性引起的.在win10中的所有UWP应用均运行在被称为appcontainer的虚拟沙箱......