汽车正变得越来越智能,越来越连通,然而你对帮助我们的车辆运行的技术有多少研究?我很想知道我如何能连接我的车辆的通信中心,以及可以在Wolfram笔记本中创建什么样的界面来报告所收集的数据。
我通过将一个微控制器连接到我的汽车控制器区域网络(CAN)总线,开始了我的数据科学副业项目,我通过简单地记录通过CAN总线发送的信息,收集了大量的信息。这些大量的数据给了我一个大型的数据集(超过25GB),这些数据来自数千次的汽车旅行:包括发动机温度、转向角、燃油流量等的时间序列。使用Wolfram语言,很容易建立一个用户界面,用于交互式地分析和处理这些CAN数据,以及一个EntityStore,用EntityValue语法对更长的时间尺度的数据进行程序化分析。这些工具使我能够分析发动机预热时间和我的燃料使用趋势,并在对GPS定位数据进行一些调整后,发现我在哪些路口等待的时间最长。
让我们来探讨一下你们中的任何一个人如何能够自己做到这一点,以及我所考虑的进一步发展的方向。
从Web表单开始
我在2017年写了一篇关于如何在Wolfram Cloud中创建一个应用来跟踪和分析汽油里程的文章(https://blog.wolfram.com/2017/06/22/create-a-tracker-to-analyze-gas-mileage-using-wolfram-tech/)。这是一个直接的选择,通过将数字(如燃料量、燃料成本、里程数)打入FormFunction,将其存储在Databin中,并设置自动报告(通过模板笔记本)来收集一些基本数据,完成2D和3D图表以及GeoHistogram可视化。
这种方法给出了一些有趣的数据相关性,将汽油里程的恶化与寒冷的天气联系起来,并发现了燃料价格的下降,例如我在2019年初观察到的下降:
像所有的手工数据收集一样,这个过程很乏味,而我真的想更进一步。通过观察我的汽车上显示瞬时汽油里程和其他平均数的显示屏,我知道,为了填充显示屏,正在产生比我手工收集的更多的数据。就在那时,我开始思考如何才能获得这些数据,以至于我受到启发去研究车载诊断标准(OBD-II)和CAN总线探测。
CAN总线嗅探
大多数现代汽车在仪表板下有一个小端口,可以用来与汽车的所有电子系统进行通信,如发动机、车身、收音机等。这个端口由OBD-II标准定义,通常用于收集排放历史和排除发动机问题。但这是所有汽车系统有组织地相互通信的地方:一组连接汽车中许多设备的两根导线,称为CAN总线。CAN总线是一种经常用于汽车的标准总线系统,与Mathematica本身一样成熟。我必须学习很多东西,不仅要连接到这个通信中心,而且要把通过它的数据归结出来。所有通过总线发送的信息,如发动机温度、方向盘角度和单个车轮速度,都有代表其用途的ID。
消息结构
首先要理解的是单个CAN消息的结构。真正重要的信息大约有12个字节:消息的ID大约有4个字节,外加多达8个字节的消息数据。较低的ID优先于较高的ID,当正确接收消息时,网络确认(ACK)上的设备优先于较高的ID。以下是CAN消息(来自CSS Electronics的)部分的可视化:
物理组装
如果你也想跟着做这个,你需要一些硬件:一个微控制器,一个与CAN总线本身连接和一个存储收集数据。下面是我使用的硬件。
l 微控制器:Arduino UNO Rev3
l CAN接口:Seeed CAN-BUS Shield V1.2
l RTC和存储:Adafruit数据记录器护盾
当然,你还需要一条电缆将你的车辆中的OBD-II端口与你的微控制器连接起来,另外还需要一条电缆来提供电源。
在进入软件之前,还有一些其他硬件方面的考虑要记住。由于速度的原因,我从RTC的方波输出连接了一根跳线到一个中断引脚,这样我就可以跟踪时间了(后面会有更多的内容!),我还去掉了一些可选的终止电阻。CAN通常在两线总线的两端都有终端电阻(以避免驻波)。汽车的CAN总线已经有了终端电阻,所以如果我用另一个终端电阻连接我的Arduino,整体电阻可能会发生变化,我不想冒这个风险而产生副作用。
数据格式
起初,我为数据的格式和如何存储数据而烦恼。我最初的天真想法是将现在的时、分、秒以及总线信息(CAN消息ID加上消息的数据)存储在一个纯文本字符串中,用逗号作为分隔符。但是,这被证明是非常慢和大的——每条CAN消息产生大约40个字节(你可能记得它只有大约12个 "有趣 "的字节)。
通过一点工作,我能够优化我的代码,只存储最低限度的信息,以跟上在总线上发送的大量流量。使用连接到RTC的中断,我可以快速有效地计算汽车启动后的秒数,将CAN ID和消息内容存储在一个数据结构中,并将这些数据写成二进制。这节省了空间,总体上更有效率,每条信息只产生14个字节:
数据样本和可视化
一旦原始数据被存储在文件中,我就可以将其读入Wolfram语言进行分析。你可以自己尝试一下——只要从Wolfram Cloud上下载并提取代码和部分数据:
然后浏览到提取的目录:
为了读取文件,我需要告诉Wolfram Language应该期待什么样的格式(基于之前显示的结构):
然后,我可以使用BinaryReadList和该规范从单个文件中导入数据。每个文件名包括一个标准化的开始日期和时间。例如,这导入的是2019年3月18日的特定汽车旅行的数据:
正如你所看到的,它由50多万个点组成——对于10分钟的汽车旅行来说是相当紧张的。下面是一个存储的样本,其中第一列是已经过去的秒数,第二列是十进制的CAN消息ID,其余各列给出了实际的消息数据:
当我开始工作时,这基本上是我所拥有的一切,没有反向工程数据的手册。经过大量的实验,我开始拼凑出哪些ID属于哪个系统,我开始从这些看起来很神秘的数据中提取有用的信息。首先,让我们做一个函数,将其中的一些数据转化为时间序列。
现在我们来看看来自不同ID的数据片断:
这里是发动机冷却液温度数据的可视化(ID:0x360,字节#4),使用DateListPlot:
CANGUI
手动处理这样的文件是很乏味的,尤其是在探索许多行程的时候。创建一个GUI将使其更快、更容易探索和绘制数据。我已经建立了一个交互式的Wolfram语言GUI,称为CANGUI,用于处理和可视化以我的数据格式记录的CAN总线消息。这个点选界面和消息空间视图是一个快速和肮脏的尝试,使用DynamicModule来浏览数据范围,探索这些范围内的数据的ID,并绘制这些TimeSeries。到目前为止,它对我来说是非常有效的!
你可以评估以下函数来调出完整的界面:
在左边,你可以选择一天,然后根据其开始和结束时间选择可用的行程。在中间的灰色背景区域,你会看到一些消息的部分,要么完全确定,要么只是怀疑。不幸的是,对于哪些信息包含哪些数据和哪些单位,并没有免费提供完整的文件,所以有些是猜测。由于这很难追踪,我把已知的ID放到一个消息空间的网格中,你可以在这里看一下:
回到GUI:在右边,你会看到一个在所选行程中捕获的所有ID的表格,以及从灰色背景区域选择的单个ID的图。在这里,你可以使用复制按钮,将时间序列放在你的剪贴板上,以便进一步探索:
你还可以使用中间一栏底部的细节下拉菜单,以任何你想要的方式加强可视化,甚至添加新的条目:
此外,通过在最左边选择多个行程,你可以在一个图中比较所有行程的时间序列数据,比如这个里程表的图:
通过这个界面,你可以打入ID,给出名称,改变用于生成时间序列的Wolfram语言代码,以及添加其他选项,以帮助理解和提取有用的数据。
个人行程分析
尽管GUI在最初探索数据和理解信息内容时很有帮助,但对于提取超过几个行程的时间尺度的信息来说,它很快就变得太麻烦了。此外,我还对其他数据感兴趣,例如我现有的基于表格的汽油里程信息,甚至是来自我的智能手机的GPS数据。在对处理这些问题的好方法进行了一些思考之后,我开始建立一个EntityStore。
使用这段代码,我可以创建和注册EntityStore,并将其与我的数据库中的GPS数据对齐——同样,这只是全部数据中的一小部分。首先,我导入实体存储代码并创建一个EntityStore。
然后我注册了实体店:
这个系统真的很好,有几个原因。我可以用隐含的EntityClass语法来搜索和筛选行程。另外,每个行程实体只根据文件名加载核心元数据,然后保留对文件的引用,其余的信息可以根据需要提取(一次加载所有数据的效率真的很低)。下面是对每个行程实体收集的那种简单元数据的快速浏览:
当然,提取我从GUI中收集的信息可以通过创建属性来完成,以同样的方式以编程方式产生时间序列。例如,现在我可以很容易地提取给定行程中的刹车和油门踏板的时间序列。
下面是行程中两个踏板的时间序列图:
深入研究燃油经济性
在解释我的车的燃油经济性时,这个系统的易用性使我能够以极其有用的方式比较数据。例如,我在GUI的帮助下找到了燃油流速。
只要稍加努力,就可以整合这个时间序列,计算出整个行程的总燃料消耗量:
为了检查其准确性,我试着将我的完整数据集中一周的行程总数与我在网络表格中单独输入的数字进行比较。从我的CAN数据中计算出的燃料用量约为12加仑。
这看起来很体面,因为我的车的油箱大约有14加仑的燃料。而如果我们与我手动记录的 "FuelUsed "数据相比较,结果是相当接近的。
而这只是一个数据点。如果我把我用网络表格收集的所有数据与我用新的CAN嗅探系统测量的数据进行比较,我可以用散点图看到结果的匹配程度。
请注意,大多数点位于虚线y=x附近。为了验证这一结果,我可以计算出数据的拟合。
下面是之前显示的同样的图,但包括了那条(蓝色实线)趋势线:
请注意,结果有很好的相关性,所以这些方法在大多数情况下是一致的,尽管有一些异常值(例如,我在一些行程中没有使用CAN嗅探系统)。
当然,这意味着我可以准确地计算出我在每一次旅行中的燃料使用量!然后我就可以做更多有趣的事情了。然后我可以做更多有趣的事情,比如按月比较我的燃油经济性分布。为了做到这一点,我首先汇总了所有行程的数据(数据量太大,无法分享),并计算出中位数值。
然后我按月对数据进行分组,并对其进行适当排序。
从这里我可以用BoxWhiskerChart绘制我的月度燃油经济性分布图(注意,你使用缩小的数据集的结果会与这里显示的略有不同)。
纳入GPS数据
在纳入手动跟踪的MPG数据后,我想把GPS信息也纳入其中。因此,我开始研究使用我的智能手机来自动收集这些数据。
与其自己从头开始建立一个安卓应用程序来做这件事,我发现使用一个自动化应用程序要容易得多。我使用了MacroDroid,但几乎所有的自动化应用都可以胜任这项工作。下面是我使用的配置的一个快速截图,如果你想跟着做的话。
有了这个,我的智能手机的GPS数据每10秒就会被检索并上传到Databin,但只有在我与汽车的蓝牙连接的情况下(以节省手机电池和数据)。可用的数据包括GPS位置、速度、精确度、时间以及我是否开始、结束或在途中。
作为一个例子,请看这张地图,我从Wolfram总部到伊利诺伊州香槟市的一个比较繁忙的地区进行了一次短途旅行(蓝色圆圈是简单的位置,红点表示精确度)。
正如你在这个图中所看到的,红色的准确度指标比道路的车道宽得多,这意味着这个数据对于车道级别的准确度来说可能不够好(例如检测右边与左边的车道)。而要知道我是朝哪个方向的,我必须用上下文与之前记录的点相比。
该图包括工具提示,给出了关于位置、速度、时间和精确度的所有细节。
行程日期/时间分布
截止到2020年3月,我已经收集了大约一年的旅行的GPS数据,我的Databin中已经积累了超过7万个点。这里有一个快速的Manipulate,可以看到这些旅行在不同时间尺度上的DateHistogram(忽略了由于技术上的困难而造成的数据记录上的一些空白)。
一个明显的趋势是我倾向于在周末多开车。
单一城市的速度分析
让我们看看香槟市所有记录点的GeoHistogram(当然是匿名的!)。颜色与每个仓中的点的数量有关。
正如你所看到的,我在特定的地方花了很多时间。我们可以只看每小时15英里以下的速度,看看我在这些行程中花了多少时间坐着不动(十字路口、建筑等)。注意到在我常去的某些地方的高度集中,比如我上下班的路上。
停车位置分析
再放大一点,我想找到我在香槟市Wolfram Research停车场的停车位置会很有趣。每个点代表我停放的汽车的GPS读数。
现在我改变了网格的大小,所以我们可以看到我在哪里停车以及我选择在那里停车的频率。请注意,地理定位数据可能有些偏差,因为我相当肯定我没有在其他地方、草地上或多个车位上停车。也就是说,很明显,在停车方面,我几乎是一个习惯性的生物--我在一个相当狭窄的区域内停车。
未来的方向
这篇文章中的所有内容最初都是在流行病之前完成的;毫不奇怪,今年我在车里的时间少了很多。因此,虽然我没有收集很多驾驶数据,但我想了很多关于如何进一步发展的问题。
例如,我希望能够检测到谁在开车(让另一个司机下载自动化应用程序,并添加一个新的Databin字段用于用户识别)。有了更多的硬件,我可以连接一个Wi-Fi芯片,以便立即将数据上传到Wolfram云,尽管是一个较小的基本数据集。另一个想法是将CAN数据直播到一个屏幕上,以便实时审查和分析。最后,我认为研究我往返各地的不同路线会很有趣,甚至可以用里程表或航位推算来计算GPS位置之间的路径。如果你已经探索过类似的东西,或者有更多关于如何扩展这个概念的想法,请在评论中告诉我。