车联网大数据中心架构设计

1.数据采集

1. 车辆传感器的数据采集

车辆上安装了各种类型的传感器,用于监测车辆运行状态、环境数据、和驾驶行为。这些传感器的主要作用包括:
发动机状态监测:如转速、温度、燃油消耗等。
驾驶员行为监测:如座椅压力传感器、方向盘力矩传感器。
安全功能监测:如胎压监测(TPMS)、刹车系统状态。

2. TBOX(车载终端)作为数据的汇聚与中转

TBOX(Telematics Box)是车载终端设备,主要功能是采集、处理和传输车辆的传感器数据。其作用包括:
数据汇聚:将车辆内各类传感器的数据统一接入,通过多种通信协议与接口(如CAN总线、以太网等)接收数据。
数据处理:对采集的数据进行预处理(如压缩、筛选),以减少冗余数据量和提升传输效率。
通信桥梁:负责车辆与外界网络(云平台)的通信,支持蜂窝网络(4G/5G)、Wi-Fi等。

3. 数据传输到云端:使用MQTT协议

为了将数据从车辆安全传输到云端,使用了MQTT协议,该协议适合物联网设备。其特点包括:
轻量级:适合低带宽、高延迟的网络环境(如远程地区或弱信号区域)。
发布/订阅模型:车辆的TBOX作为数据发布者,云端服务器作为订阅者,支持大规模设备的并发通信。
QoS机制:通过不同的服务质量等级(QoS 0/1/2),保证数据传输的可靠性。

4. 使用Kafka协议接入终端设备

云端服务器接收到来自多个车辆的数据后,利用Kafka协议将这些数据接入其终端设备。

2.数据聚集

数据中心搭建好Kafka框架,接入各家开放的端口。具体分为如下几步。

1.Kafka 配置

  • 针对各车企开放统一的接入端口:
    • 开放 Kafka 的监听端口(默认 9092)。
    • 设置 advertised.listeners,定义外部设备(如车企端)访问 Kafka 的外部地址。
    • 提供可供连接的 Kafka Broker 地址和认证方式。
    • 定义特定的 Topic,用于不同车企上传数据。
    • 约定传输协议(如 MQTT/Kafka Producer API)。

2.Kafka Producer 接入

MQTT 传输的数据一般都是 JSON

  • 各车企开发 Kafka Producer 客户端:
    • 配置 Kafka Broker 地址和端口。
    • 定义数据主题(Topic):
      • 不同车企可使用独立主题。
      • 也可根据车辆类型、传感器类型分组。

3.数据ETL

几乎所有数据都是结构化的,已经和各家提前协调好。

基于Flink Sql做ETL

  • 数据源数据结构
{
    "vehicle_id": "12345",
    "timestamp": 1672531200000,
    "engine_status": {
        "rpm": 3000,
        "temperature": 85.5
    },
    "location": {
        "latitude": 37.7749,
        "longitude": -122.4194
    }
}
  • 使用 Flink SQL 定义 Kafka 源表,解析 JSON 数据:
CREATE TABLE vehicle_data (
    vehicle_id STRING,               -- JSON 中的 "vehicle_id"
    timestamp BIGINT,                -- JSON 中的 "timestamp"
    engine_status ROW<               -- 嵌套字段 "engine_status"
        rpm INT,                     -- "engine_status.rpm"
        temperature DOUBLE           -- "engine_status.temperature"
    >,
    location ROW<                    -- 嵌套字段 "location"
        latitude DOUBLE,             -- "location.latitude"
        longitude DOUBLE             -- "location.longitude"
    >
) WITH (
    'connector' = 'kafka',           -- 使用 Kafka 作为数据源
    'topic' = 'vehicle-topic',       -- Kafka 主题名
    'properties.bootstrap.servers' = 'localhost:9092',  -- Kafka 地址
    'format' = 'json',               -- 指定数据格式为 JSON
    'json.fail-on-missing-field' = 'false',  -- 忽略缺失字段
    'json.ignore-parse-errors' = 'true'      -- 忽略解析错误
);
);
  • 解析和查询 JSON 数据
-- 过滤并清洗数据
SELECT 
    vehicle_id,                                -- 从 JSON 数据中提取车辆唯一标识符
    engine_status.rpm AS engine_rpm,          -- 提取嵌套字段 "engine_status.rpm",重命名为 "engine_rpm"
    location.latitude AS lat,                 -- 提取嵌套字段 "location.latitude",并重命名为 "lat"
    location.longitude AS lon,                -- 提取嵌套字段 "location.longitude",并重命名为 "lon"
    CAST(FROM_UNIXTIME(timestamp / 1000)      -- 将 UNIX 时间戳(以毫秒为单位)转换为标准时间格式
        AS TIMESTAMP(3)) AS event_time,       -- 定义为 TIMESTAMP 类型,精确到毫秒
    CASE                                       -- 添加派生字段 "rpm_category"
        WHEN engine_status.rpm > 4000 THEN 'High'
        WHEN engine_status.rpm BETWEEN 1000 AND 4000 THEN 'Medium'
        ELSE 'Low'
    END AS rpm_category,                      -- 根据 rpm 的值分类
    location.latitude + 0.0001 AS adj_lat,    -- 计算派生字段 "adj_lat",假设需要对纬度进行微调
    location.longitude + 0.0001 AS adj_lon    -- 计算派生字段 "adj_lon",假设需要对经度进行微调
FROM vehicle_data
WHERE 
    engine_status.rpm > 0                     -- 过滤条件:仅保留发动机转速 (rpm) 大于 0 的数据
    AND location.latitude IS NOT NULL         -- 确保位置数据的纬度不为空
    AND location.longitude IS NOT NULL;       -- 确保位置数据的经度不为空
  • 写到TDengine里,TDengine是为时序数据专门设计的数据库,使用 Flink SQL 定义 TDengine 的目标表。TDengine 的 Flink Connector 支持表的直接映射。
CREATE TABLE processed_data (
    engine_rpm INT,                      -- 发动机转速
    latitude DOUBLE,                     -- 纬度
    longitude DOUBLE,                    -- 经度
    event_time TIMESTAMP(3),             -- 事件时间
    rpm_category STRING,                 -- RPM 分类
    vehicle_id STRING                    -- 标签字段:车辆 ID
) WITH (
    'connector' = 'jdbc',                -- 使用 JDBC Connector
    'url' = 'jdbc:TAOS-RS://localhost:6030/demo', -- TDengine JDBC URL
    'table-name' = 'vehicle_data',       -- 超级表名称
    'driver' = 'com.taosdata.jdbc.TSDBDriver', -- TDengine JDBC 驱动
    'username' = 'root',                 -- TDengine 用户名
    'password' = 'taosdata',             -- TDengine 密码
    'sink.buffer-flush.max-rows' = '500', -- 每次批量写入最多500行
    'sink.buffer-flush.interval' = '2s'   -- 每2秒强制写入
);
  • TDengine的表结构:
CREATE DATABASE IF NOT EXISTS demo; -- 创建数据库
USE demo;

-- 创建超级表,标签字段替换为 "vehicle_id"
CREATE STABLE vehicle_data (
    ts TIMESTAMP,                     -- 时间戳
    engine_rpm INT,                   -- 发动机转速
    latitude DOUBLE,                  -- 纬度
    longitude DOUBLE,                 -- 经度
    rpm_category NCHAR(16)            -- RPM 分类
) TAGS (
    vehicle_id NCHAR(32)              -- 标签:车辆 ID
);
  • 写入目标表
INSERT INTO processed_data
SELECT 
    engine_status.rpm AS engine_rpm,          -- 发动机转速
    location.latitude AS latitude,            -- 纬度
    location.longitude AS longitude,          -- 经度
    CAST(FROM_UNIXTIME(timestamp / 1000)      -- 时间戳转换为标准时间格式
        AS TIMESTAMP(3)) AS event_time,
    CASE                                       -- 根据 RPM 值分类
        WHEN engine_status.rpm > 4000 THEN 'High'
        WHEN engine_status.rpm BETWEEN 1000 AND 4000 THEN 'Medium'
        ELSE 'Low'
    END AS rpm_category,
    vehicle_id                                 -- 标签字段
FROM vehicle_data
WHERE engine_status.rpm > 0                   -- 过滤条件
  AND location.latitude IS NOT NULL
  AND location.longitude IS NOT NULL;
  • 提交运行
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;

public class FlinkSqlToTDengine {
    public static void main(String[] args) throws Exception {
        // 初始化 Flink Table 环境
        EnvironmentSettings settings = EnvironmentSettings.newInstance()
                .inStreamingMode()
                .build();
        TableEnvironment tableEnv = TableEnvironment.create(settings);

        // 创建 Kafka 源表
        tableEnv.executeSql(
                "CREATE TABLE vehicle_data ( " +
                "    vehicle_id STRING, " +
                "    timestamp BIGINT, " +
                "    engine_status ROW<rpm INT, temperature DOUBLE>, " +
                "    location ROW<latitude DOUBLE, longitude DOUBLE> " +
                ") WITH ( " +
                "    'connector' = 'kafka', " +
                "    'topic' = 'vehicle-topic', " +
                "    'properties.bootstrap.servers' = 'localhost:9092', " +
                "    'format' = 'json', " +
                "    'json.fail-on-missing-field' = 'false', " +
                "    'json.ignore-parse-errors' = 'true' " +
                ")"
        );

        // 创建 TDengine Sink 表
        tableEnv.executeSql(
                "CREATE TABLE processed_data ( " +
                "    engine_rpm INT, " +
                "    latitude DOUBLE, " +
                "    longitude DOUBLE, " +
                "    event_time TIMESTAMP(3), " +
                "    rpm_category STRING, " +
                "    vehicle_id STRING " + // 标签字段
                ") WITH ( " +
                "    'connector' = 'jdbc', " +
                "    'url' = 'jdbc:TAOS-RS://localhost:6030/demo', " +
                "    'table-name' = 'vehicle_data', " +
                "    'driver' = 'com.taosdata.jdbc.TSDBDriver', " +
                "    'username' = 'root', " +
                "    'password' = 'taosdata', " +
                "    'sink.buffer-flush.max-rows' = '500', " +
                "    'sink.buffer-flush.interval' = '2s' " +
                ")"
        );

        // 数据处理并写入 TDengine
        tableEnv.executeSql(
                "INSERT INTO processed_data " +
                "SELECT " +
                "    engine_status.rpm AS engine_rpm, " +
                "    location.latitude AS latitude, " +
                "    location.longitude AS longitude, " +
                "    CAST(FROM_UNIXTIME(timestamp / 1000) AS TIMESTAMP(3)) AS event_time, " +
                "    CASE " +
                "        WHEN engine_status.rpm > 4000 THEN 'High' " +
                "        WHEN engine_status.rpm BETWEEN 1000 AND 4000 THEN 'Medium' " +
                "        ELSE 'Low' " +
                "    END AS rpm_category, " +
                "    vehicle_id " + // 标签字段
                "FROM vehicle_data " +
                "WHERE engine_status.rpm > 0 " +
                "  AND location.latitude IS NOT NULL " +
                "  AND location.longitude IS NOT NULL"
        );

        System.out.println("Flink SQL ETL job with Super Table and vehicle_id as tags started.");
    }
}

4.基于TDengine的数据做一些分析统计

  • 获取某个区域最新的车辆状态:
SELECT * 
FROM vehicle_data 
WHERE region = 'beijing' 
ORDER BY ts DESC 
LIMIT 10;
  • 获取各个区域过去五分钟在线车辆的数量(可以用来制作热力图)
select 
region,count(*)
from (
SELECT  
  visit_type,vehicle_id
from vehicle_data
where ds= now() - 5m
group by region, vehicle_id
) group by region;
  • 计算车辆的累计行驶距离
SELECT vehicle_id, 
       SUM(ST_DISTANCE(latitude, longitude, LAG(latitude), LAG(longitude))) AS total_distance
FROM vehicle_data
GROUP BY vehicle_id;
  • 车辆轨迹回放
SELECT 
    TAGS.vehicle_id AS vehicle_id,
    ts,
    latitude,
    longitude
FROM vehicle_data
WHERE vehicle_id = '12345'
ORDER BY ts ASC;

至此整个数仓就建立完成了,上面的etl 脚本和kafka 维持正常运行即可,失败时自动重启。定时监看 flink 的 web ui 可以获知项目集群的工作状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值