上一篇文章分析了 Apollo 框架中 Monitor 模块如何监控硬件,这篇文章继续分析 Monitor 是如何监控软件的
受监控的内容
上图是之前的文章总结的,可以看到软件监控有 8 类对象:
- Channel Monitor 数据通信频道监控
- Functional Safety Monitor 功能安全监控
- Latency Monitor 时延监控
- Localization Monitor 定位监控
- Module Monitor 模块监控
- Process Monitor 监控
- Recorder Monitor 数据记录监控
- Summary Monitor 监控状态汇总
ChannelMonitor
Channel 是 CyberRT 中的通信渠道,它的监控是配合 LatencyMonitor 一起使用的
RunOnce() :
- 从hmi中获取要被监控的channel
- 从latency_monitor_查询channel的频率
- 通过 update_freq 和 freq 更新channel的时延状态
所以,关键是获取 update_freq 和 freq 两个参数的赋值过程。
如果 LatencyMonitor 在自身查询到了相应 channel 的频率,则赋值给 freq,然后返回 true,否则返回 false。
有了这两个参数后,后面的逻辑判断就交给了 UpdateStatus()。
- 通过 channel 的名字获取相应的 Reader 和最后一条 msg
- 判断是否有 Reader,如果没有提示这个channel 没有注册,并返回
- 通过 reader 判断时延,如果小于0或者大于则设置状态为 FATAL
- 通过判断 msg 相应的 field 内容是否有缺失,如果有则设置为状态为 ERROR
- 过判断 freq 的值是否在合理的最小与最大范围内,如果不是则设置状态为 WARN
- 一切正常则判断为 OK
因为 SummaryMonitor 的评估提升状态时,不同的状态是为权重的,并且可以向下覆盖,比如FATAL 可以覆盖小于它的其它状态,ERROR 不能覆盖 FATAL 但能覆盖 WARN 和 OK 、UNKOWN。
到这里 Channel 如何被监控大的逻辑就弄清楚了,但还有 2 个小细节需要弄明白。
- channel 配套的 Reader 和最后一条 msg
- LatencyMonitor 工作流程
GetReaderAndLatestMessage
通过MonitorManager去创建不同的Reader,然后开始观测,并获取这个channel最后一次消息,然后保存下来病返回。
主要是通过配置变量信息决定是否创建想要的MonitorManager。这些变量有:
{FLAGS_control_command_topic,
{FLAGS_localization_topic,
{FLAGS_perception_obstacle_topic,
{FLAGS_prediction_topic,
......
包含了感知数据、定位、预测、规划、控制 topic 相关。
LatencyMonitor
ChannelMonitor 内部要借助 LatencyMonitor 提供的数据做一些判断,那么LatencyMonitor是如何产生实验数据呢?
LatencyMonitor 也是周期性执行任务的模块,不过相比其它的 Monitor,它额外实现了 3 个方法:
public:
GetFrequency()
private:
PublishLatencyReport()
AggregateLatency()
可以看到,它内部会聚合各种时延,然后会定期发布时延报告。并且有一个服务类的API供其它类进行频率查询。
DEFINE_double(latency_monitor_interval, 1.5,
"Latency report interval in seconds.");
DEFINE_double(latency_report_interval, 15.0,
"Latency report interval in seconds.");
DEFINE_int32(latency_reader_capacity, 30,
"The max message numbers in latency reader queue.");
监控时延的周期是每 1.5s 一次,时延报告发布周期是每15s一次,时延相关的reader队列容量是30。
LatencyMonitor::RunOnce
可以看到,它创建了一个Reader用来监控 latency recording topic,其数据类型有相应的 proto 文件定义。
LatencyRecordMap包含:
- 消息头 header
- 模块名字
- LatencyRecord 数组
我们先看 LatencyRecord 它里面有 3 条信息:
- 开始时间 begin_time
- 结束时间 end_time
- 信息的 id
UpdateStat
LatencyMonitor::RunOnce中,会 UpdateStat(*it);
其实现如下
主要做了两件事:
- 保存每个msg的耗时信息到 track_map_
- 更新 freq_map 中模块的频率信息
我们重点关注第 2 个逻辑,也是前面内容分析到的 channelmonitor 会主动来查询 freqmap 中的值。
这个逻辑就是一个 LatencyRecordMap 中有一组 LatencyRecord,获取第一条记录中的开始时间,获取最后一条的结束时间。
如果结束时间大于开始时间,表明通信没有紊乱,频率值由下面公式计算:
freq = size/durantion
比如总共有 5 条记录,5 条记录总共耗时 10s。那么频率就是 5/10 = 0.5也就是1/2,代表什么呢?代表的是 1s 内只能传0.5 条信息。
定期发布时延报告
RunOnce 运行时会根据传入的 currenttime 和 flush_time_之差与定期报告的时长作比较,如果大于后者则需要对外发布时延信息。
PublishLatencyReport内容如下:
在发布时延报告前,先要内部聚合一下信息,然后通过创建的 LatencyReport writer 发送出去。
主要是聚合模块的时延和 E2E 的时延。
E2E 是什么呢?是从点云信息到各个模块输出的时间,也就是端到端时间。
这里有个重要的变量kE2EStartPoint 。
它是什么呢?其实是一条字符串,它被赋值为FLAGS_pointcloud_topic。也就是:
E2E Latency 的逻辑:
- 记录第一条点云数据的开始时间
- 依次记录那些不是点云数据的记录的开始时间,计算它们之间的差值,就成了这一个测试周期的 E2E 时延。
总结
- Channel监控时主要是监控注册过的channel,通过判断它的时延、内容、频率形成最终的状态报告
- Latency也需要读取相关的topic,通过根据不同的topic时间信息产生模块时延和端到端时延。
- Channel 和 Latency 的运行基于依靠于 CyberRT 的通信,所以,也必须保证 CyberRT 的 Channel 通信机制足够可靠,不然会产生误差。