ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

机会网络仿真器ONE中ExternalMovement移动模型配置与实战解析

机会网络仿真器ONE中ExternalMovement移动模型配置与实战解析 说实话做机会网络方向的研究最绕不开的就是机会网络仿真器ONE。前两年我刚接触它的时候一直用系统自带的RandomWaypoint和MapBasedMovement结果仿真结果和真实场景总对不上。后来我认真研究了ExternalMovement移动模型才发现ONE是支持直接把外部移动数据导进来的。这篇文章我就把这段时间积累的经验完整梳理一遍重点聊聊ExternalMovement的工作原理、文件格式、配置步骤和踩坑记录给做延迟容忍网络、机会网络仿真、移动模型研究的同学一条可以直接照做的路线。1. ExternalMovement移动模型在ONE里的定位1.1 先理解ONE仿真器为什么需要移动模型机会网络仿真器ONE全称是Opportunistic Network Environment simulator它的核心工作就是把节点移动、节点接触、消息转发这三个环节绑定在一起仿真。节点移动是第一步因为节点之间的相遇机会完全由移动轨迹决定。如果移动模型不贴近真实后面的路由协议、缓存管理、消息投递率全都会失真。ONE自带了好几种移动模型比如RandomWaypoint、RandomDirection、MapBasedMovement、ShortestPathMapBasedMovement、BusMovement等。这些模型的共同点是移动轨迹由算法实时生成节点随机选目标点、沿地图道路走、或者模拟公交线路。它们适合做理论研究因为参数可调、重复性好但缺点是太“干净”了。真实场景下的人、车、动物不会按随机方式移动而是有社会习惯、道路限制、时间周期性RandomWaypoint根本模拟不出来。ExternalMovement移动模型就是为了解决这个问题存在的。它不自己生成轨迹而是从一个外部文件里读取每个节点在每个时间点的坐标。你可以把GPS轨迹、Wi-Fi探针数据、出租车订单数据、校园卡打卡数据转成指定格式然后让ONE里的节点严格按这些坐标移动。这样仿真里的节点接触关系就和真实采集到的移动模式高度一致实验结果的说服力会强很多。1.2 内置移动模型和ExternalMovement的本质区别内置移动模型是“移动行为模型”ExternalMovement是“移动数据回放模型”。前者描述的是节点怎么走后者描述的是节点已经走过的路径。两者的差别很大。用生活化一点的说法内置模型像演员按剧本自由发挥导演只给大方向比如“在城市里随机逛”ExternalMovement则像一个提词器每一步都提前写好了演员只需要照着念。因此ExternalMovement不需要关心道路网、障碍物、速度限制它直接告诉仿真器“这个节点此刻就在这个坐标”。这里面有一个很容易被忽视的点使用ExternalMovement时节点坐标不一定落在ONE的地图区域内。如果外部移动数据里的经纬度没有经过投影转换很可能节点直接跑到地图外导致后续接触判断失效。后面第4部分我会详细说这个问题。1.3 什么场景下必须用外部移动数据我总结了几类特别适合用ExternalMovement的场景如果你正在做这些方向可以考虑尽早换掉RandomWaypoint基于真实轨迹的协议评估。你手里有志愿者携带GPS设备采集的移动轨迹想评估这组真实接触模式下Epidemic、SprayAndWait、PROPHET等路由协议的性能差异。车载移动网络仿真。你从SUMO或者真实出租车GPS数据里提取了车辆轨迹希望通过ONE模拟车与车之间的机会通信。动物携带传感器网络研究。像追踪大象、海鸟的项目移动数据都是生物学家采集的没法用随机模型近似。灾难救援应急通信。救援人员的移动路线是根据地形和任务规划的不是随机游走。在这些场景里ExternalMovement几乎是最省事的方案而且ONE本身就是学术界使用率很高的DTN仿真器用它回放真实数据审稿人很容易理解你的实验设计。2. ExternalMovement读取外部轨迹文件的核心原理2.1 它是怎么从文件里拿到坐标的ExternalMovement本质上是一个MovementModel子类它不依赖MapBasedMovement那样的路径计算而是把所有坐标预先放在一个纯文本文件里。当ONE初始化节点时ExternalMovement会读取文件里的记录为每个节点建立位置映射仿真时钟每前进一个步长它会从文件里读取对应时间点的坐标更新节点位置。这里的关键点是外部文件里的时间戳必须和仿真时间对应起来。ONE的仿真时间是从第0秒开始的所以文件里记录的每一行时间也自然从0开始。如果文件里的记录从实际采集时间比如08:30:00开始那就需要先做归一化处理把第一个时间点变成0秒。否则仿真器在0秒时找不到对应记录节点就不会动。另外还有一个很多人没注意的机制ExternalMovement并不是随机访问文件而是顺序读取。所以外部文件里的记录必须严格按时间排序。如果同一时间戳的节点记录是乱序的可以但不同时间戳之间必须递增。我遇到过写成倒序的轨迹文件结果节点只在最开始动了一下之后全停在原地。2.2 轨迹文件的标准行格式不同版本的ONEExternalMovement读取的文件格式可能略有差异但最常见的一种标准格式是时间 节点ID X坐标 Y坐标也就是用空格或者Tab分隔的四列数据。举个例子0 0 100.0 200.0 0 1 150.0 180.0 0 2 200.0 220.0 1 0 105.0 198.0 1 1 151.0 181.0 1 2 202.0 219.0这里的含义很直接第0秒时节点0在坐标(100.0, 200.0)节点1在(150.0, 180.0)节点2在(200.0, 220.0)第1秒时更新到下一组坐标。需要注意节点ID是从0开始还是从1开始这取决于你在default_settings.txt里给节点编的编号。比如Group1.nrofHosts 3节点ID通常就是0、1、2。轨迹文件里的节点ID要和这个对应起来如果文件里写了ID5但仿真里只有3个节点那这条记录不会被任何人使用甚至可能引发异常。2.3 从真实GPS轨迹生成外部移动文件的完整思路我估计不少读者手里已经有GPS轨迹数据了比如手机导出的kml、GPX、CSV。但把原始轨迹变成ONE能识别的格式中间要经过几步处理这里我列一个比较通用的处理流程坐标转换。GPS原始数据一般是经纬度WGS84但ONE的世界坐标是平面米制坐标。需要把经纬度投影到平面坐标系常见做法是用UTM投影或者用Python的pyproj库做转换。如果你的实验地图就是通过OpenStreetMap导出的那最好保证轨迹坐标和地图坐标在同一个投影坐标系下。时间归一化。原始时间一般是“2024-01-05 08:30:01”这种格式需要转换成从0开始的秒数。最简单的方法是用Python的datetime库先找到第一条记录的时间戳然后让每条记录的时间减去起始时间。采样间隔统一。GPS采样的间隔可能不均匀第1秒有记录、第2秒缺失、第3秒又要等5秒。ONE在读取外部轨迹时希望时间步长尽量和仿真步长匹配。如果间隔不均匀可以用线性插值补齐。最简单的做法是设定一个目标时间间隔比如1秒然后对每条轨迹做插值重采样。ID映射。原始数据里的设备ID可能是Mac地址或者一串编号需要映射成0、1、2这样的整数节点ID。按时间排序。最终输出前用第一列时间做升序排序。有一个我必须强调的点不要直接拿原始经纬度填进轨迹文件。有一次我用了一个校园GPS数据集抓了几行原始数据发现经度116.3、纬度39.9这种值直接导进ONE后节点全部出现在世界地图外接触率低得离谱。事后查了半天才意识到是坐标投影的问题。3. 在ONE中导入外部移动数据的完整实操3.1 准备文件与目录结构拿到ONE仿真器源码或者release包之后整个目录下会有movement、reports、bin、lib等文件夹。ExternalMovement需要的外部移动数据文件一般建议放在movement目录下因为ONE的项目结构里本来就有这个目录方便统一管理。比如ONE/ movement/ real_trace.txt scenario_settings.txt bin/ one.sh lib/ ...把轨迹文件弄成real_trace.txt之后打开default_settings.txt这里面是仿真场景的全局配置。通常你会看到类似这样的内容Scenario.name externalMovementDemo Scenario.simulateConnections true Scenario.updateInterval 0.1 Scenario.endTime 36003.2 编辑配置文件切换移动模型接下来是核心配置。找到default_settings.txt把移动模型的配置改成ExternalMovement。我用的ONE版本配置方式如下Group.movementModel ExternalMovement ExternalMovement.file movement/real_trace.txt有些版本里ExternalMovement还需要设置ExternalMovement.prefix。这个参数的作用是告诉解析器轨迹文件里的节点ID是否带前缀。比如某些GnomeMapping工具导出的数据里节点ID是p1、p2这样的形式如果设置了前缀ONE会做字符串匹配如果没设置它会尝试直接转成整数。我一般不建议开前缀因为纯数字ID处理起来最稳。还需要确认Group的节点数量和轨迹文件里的ID一致。比如我设置了Group1.nrofHosts 3 Group1.bufferSize 5M Group1.movementModel ExternalMovement那么轨迹文件里的节点ID就应该是0、1、2不能多也不能少。如果配置文件里写了nrofHosts 10但轨迹文件只给了3个节点的数据另外7个节点一开始会报错或者一直停在坐标(0,0)。3.3 消息生成、仿真时长等配套参数调整外部移动数据回放只是移动层的事要让整个机会网络仿真真正跑起来消息层参数也得同步调整。比如我想模拟节点之间产生消息然后转发的情况就需要在配置里加入消息生成器Events1.class MessageEventGenerator Events1.interval 10, 20 Events1.size 500, 1500 Events1.hosts 0, 2 Events1.prefix M这段配置表示每隔10到20秒随机生成一条消息消息大小在500到1500字节之间在节点0到2之间随机选择收发双方。仿真时长的设置要参考轨迹文件的时间范围。如果你的轨迹文件覆盖了3600秒那Scenario.endTime至少要设成3600。如果轨迹文件只覆盖了1800秒而endTime设成了3600仿真并不会自动把轨迹延伸到后面而是节点停在最后的位置不动后半段接触情况其实是错误的。所以我的习惯是先用脚本统计轨迹文件的最大时间再反推仿真时长。还要注意Scenario.updateInterval这个参数。它决定了仿真器每隔多少秒刷新一次节点的位置和连接关系。如果轨迹文件里的采样间隔是1秒而updateInterval设成了0.1秒ONE会在相邻两个轨迹点之间做线性插值吗实际要看版本但很多版本不会做复杂的插值它会去寻找当前时刻对应的轨迹行。如果找不到恰好匹配的行可能直接沿用上一行的位置。为了保证精度我建议让轨迹文件的采样间隔和updateInterval保持倍数关系。比如updateInterval 0.5轨迹文件每0.5秒一行。4. 配置ExternalMovement时的常见坑与排查记录4.1 轨迹文件不生效节点一动不动的排查这个是我见过最多的问题明明配置了ExternalMovement跑完仿真一看报告节点位置全都没变。排查步骤可以按以下顺序确认配置文件里没有拼错文件名。ExternalMovement.file movement/real_trace.txt这个名字如果和实际文件不一致加载时一般会报FileNotFound但偶尔会因为路径拼接问题静默失败。确认节点组确实用了ExternalMovement。有些人同时配置了多个Group其中一个Group还是RandomWaypoint导致报告的移动轨迹看起来一半节点乱跑、一半节点不动。确认轨迹文件是按时间递增排列的。打开文件看前几行如果时间列类似“1800 3 100 100”开头说明文件是从中间截取的ONE从0秒开始找不到0时刻记录节点就永远停在那里。确认坐标范围正常。有些轨迹文件有非法坐标比如-nan或者infONE解析的时候会出错但报错信息不一定直接指向坐标问题。我在实际调试中会写一个极简的Python脚本来检查轨迹文件遍历每一行确认总数、节点ID集合、时间最大值和最小值、坐标边界。这个脚本只花两分钟但能省下半天找bug的时间。4.2 时间戳、坐标范围和地图的匹配问题坐标范围问题值得单独拿出来说。ONE里的世界坐标系默认是从(0,0)到MovementModel.worldSize默认值通常是4500x3400单位是米。如果你导入的轨迹坐标是某个城市中心点附近的UTM坐标坐标值可能是几十万米那节点会全部落在世界边界外面。解决办法有两个第一个是把轨迹坐标缩放到ONE世界范围。比如原轨迹的X范围是300000到300100那就整体减去300000再加一个合适的偏移让它落在0到4500之间。这个办法简单但会牺牲真实空间比例。第二个是直接修改MovementModel.worldSize让世界范围匹配你的投影坐标范围。比如你的轨迹X范围是250000到255000Y范围是4000000到4005000那就把worldSize设成5000, 5000并把所有坐标减去起始点偏移。这样仿真的空间关系会更真实因为相对距离没变。地图的问题就更微妙了。ONE的地图文件通常是roads.wktMapBasedMovement会参考它。但ExternalMovement完全无视地图文件里给什么坐标就用什么坐标。所以即使轨迹点落在建筑里或者在湖泊中心ONE也不会做任何修正。这是外部移动数据的特性不是bug但你要在实验设计里意识到这一点。4.3 读取大文件时的性能和内存问题如果你导入的轨迹文件是真实采集的大数据比如每0.1秒一行、几百个节点跑24小时文件很容易到几GB。ONE读取这种大文件会有两个问题一是启动阶段需要把整个文件读进内存做初始化内存不够直接OutOfMemoryError。解决办法是适当调整JVM堆内存比如运行one.sh的时候加上-Xmx2g参数。二是仿真阶段逐行读取的大文件如果IO频繁仿真的墙钟时间会拉得非常长。我个人的经验是对于真实轨迹没必要把采样频率做得太高。机会网络的节点接触研究时间分辨率到1秒通常足够了不需要0.1秒的精度。可以用重采样把文件量降一个数量级比如把10Hz轨迹抽稀成1Hz文件大小会小很多仿真效率也明显提升。另外一个思路是按天切分轨迹把24小时数据切成长度为1到2小时的小文件单独跑每个时段最后再汇总结果。5. 一些实操体会和扩展思路5.1 我建议你注意的细节使用ExternalMovement这段时间我最大的体会是移动轨迹文件本身就是一个“数据产品”它的质量直接决定仿真结果的可靠程度。很多人把精力放在路由协议调参上反而忽略了移动轨迹的清洗和验证。我在实际项目中吃过亏拿到一份出租车GPS轨迹时直接转成ONE格式结果跑出来的结果异常好后来一查是因为轨迹数据里有大量静止点节点长时间不动导致接触次数偏多。这个教训让我后来不管拿到什么数据都先做一份基础的统计分析平均移动速度、静止点占比、节点在中心区域停留时间确认数据符合真实规律再进仿真。另外ExternalMovement不支持动态添加或删除节点。如果你希望模拟节点中途加入网络或者退出网络单纯靠ExternalMovement实现不了。一种折中方案是把节点位置写成某个特殊非法值比如(-1,-1)让节点离开仿真场景。但更干净的方法是结合ONE的MessageEventGenerator和外部脚本在仿真时间轴里动态控制节点组不过这就要动到仿真器源码了。5.2 从轨迹回放到更深层次的方向扩展如果你已经熟练掌握了ExternalMovement下一步可以考虑把外部移动数据和更丰富的场景参数结合起来结合“社会关系”做移动感知路由。同一个文件里的轨迹可以提取节点共现矩阵计算接触频率和接触时长再把这些指标导入路由算法作为决策权重。把外部轨迹和“消息产生位置”做关联。比如只在某个区域内产生求救消息这样能模拟灾害救援里的局部通信负载。用真实轨迹验证“缓存替换策略”。当移动数据真实以后消息投递率和缓存占用变化的曲线会有更多非平稳特征这些比随机移动模型里的结果更值得分析。我身边已经有同学把ExternalMovement和深度学习结合用真实轨迹训练移动预测模型然后让节点提前缓存消息效果比传统缓存策略提升了不少。这条路虽然工作量大但确实是机会网络仿真里比较前沿的方向。最后再说一个调试技巧在跑完整仿真前先写一个统计脚本检查轨迹文件的节点ID集合确认和配置文件里的nrofHosts完全一致。我在实际项目中一开始没做这个检查结果有2个节点始终一动不动排查了半天才发现是轨迹文件里漏了这两个ID的记录。这种问题靠肉眼看日志很难发现用脚本一查就清清楚楚。做外部移动数据回放数据检查做得越细后面仿真结果就越可信。
返回列表