Skip to content

基于AutoTraj库的轨迹数据生成

轨迹数据对于分析车辆历史或实时的行驶状况至关重要,其核心字段包括经纬度坐标、时间戳、速度、航向角等,AutoTraj库的【轨迹获取模块】能够生成字段齐全的轨迹数据。

项目地址:https://github.com/qyanswerai/AutoTraj

Image title

核心功能说明

  • 获取经纬度:给定起点、终点坐标,调用高德、百度、OpenRouteService(简称amapbaiduors)的相关接口或方法获取起终点之间的路线(路线上各个点的经纬度坐标)
  • 计算航向角:根据相邻点的坐标采用公式计算direction,正北为0,顺时针递增(0~360)
  • 生成速度及时间戳:进一步通过行驶状态模拟生成速度、时间戳字段,得到字段齐全的轨迹点,可用于轨迹分析(例如识别停留、低速段),轨迹重合率计算等

输入及输出

输入及示例

参数名 类型 是否必填 含义 说明 默认值
origin str 起点坐标 经度在前,纬度在后,经度和纬度以","分隔
坐标精确到小数点后6位即可
destination str 终点坐标 经度在前,纬度在后,经度和纬度用","分割
way_points str 途径点坐标 每个途径点经纬度以","分割,多个途径点坐标按顺序以";"分隔
最多支持16个途经点(method_type为baidu时,最多18个途径点;method_type为ors时,无明确限制)
""
method_type str 方法 获取轨迹的方式:高德amap、百度baidu、开源库ors amap
coord_type str 坐标系 起点、终点、途径点坐标系:国际坐标wgs84、高德gcj02、百度bd09ll
-_- 说明:bd09ll后面两个是小写L
gcj02
other_params dict 其他参数 其他参数:高德API、百度API、ors库所支持的其他参数,详见链接
高德:https://lbs.amap.com/api/webservice/guide/api/newroute#t4
百度:https://lbsyun.baidu.com/faq/api?title=webapi/webservice-direction/dirve
ors库:https://openrouteservice.org/dev/#/api-docs/v2/directions
None
interpolate_flag bool 是否插点 获取路线规划的结果后,可以在线上等距插点(增加点的密度) False
simulate_flag bool 是否新增字段 默认通过驾驶状态模拟确定时间戳、速度、航向角字段 True
result_coord_type str 结果坐标系 指定所获取的轨迹点的坐标系 wgs84
save_path str 保存路径 默认保存在data/result_data文件夹下 ""
save_name str 保存名称 可以指定文件名,需要符合命名规范,若不指定则以文件保存时刻的Unix时间戳作为文件名 ""
result_type str 保存类型 文件类型:表格型csv、字典型json csv
{
    "origin": "116.481028,39.989643",
    "destination": "116.434446,39.90816",
    "way_points": "116.461028,39.959643;116.441028,39.929643",
    "method_type": "amap",
    "coord_type": "gcj02",
    "other_params": {
        "show_fields": "polyline",    // 高德API参数:不设置时只返回基础信息,若需返回多个字段则以“,”分隔
        "cartype": 0,    // 百度API参数:0表示普通汽车,1表示纯电动汽车
        "profile": "driving-car",    // ors库参数:driving-car表示汽车,driving-car表示货车
        "format": "geojson"    // ors库参数:不设置时需解码才能提取坐标,设置为geojson则返回geojson格式的结果(方便提取坐标)
    },
    "interpolate_flag": false,
    "simulate_flag": false,
    "result_coord_type": "wgs84",
    "save_path": "data/result_data",
    "save_name": "test_01",
    "result_type": "csv"
}

【入参说明】

  • 若未给定save_path,则不保存轨迹文件
  • 建议method_typecoord_type相互匹配:amapgcj02baidubd09llorswgs84
  • 若不匹配则需转换坐标系(自动完成):例如method_typeamapcoord_typewgs84,则坐标系先转换为gcj02再调用API获取轨迹
  • 若起点origin或终点destination不在国内,则method_type只能为ors(入参检查时会自动转换)

输出及示例

参数名 类型 是否必填 含义 说明 默认值
origin str 起点坐标 与输入相同
destination str 终点坐标 与输入相同
way_points str 途径点坐标 与输入相同
final_method_type str 方法类型 与method_type相同
result_coord_type str 坐标系类型 wgs84
traj_points list 轨迹数据 获取的轨迹点,字段齐全 []
lng float 经度 坐标系为final_method_type
lat float 纬度 坐标系为final_method_type
timestamp int64 时间戳 Unix格式时间戳,精确到毫秒(以整数形式存储时需注意大小,13位)
direction float 航向角 范围0.0~360.0,正北为0,顺时针递增
speed float 速度 轨迹点瞬时速度,单位为km/h
{
    "origin": "116.481028,39.989643",
    "destination": "116.434446,39.90816",
    "way_points": "116.461028,39.959643;116.441028,39.929643",
    "final_method_type": "amap",
    "result_coord_type": "wgs84",
    "traj_points": [
        {"lng": 104.21018, "lat":30.525391, "timestamp": 1728762988000, "speed": 15.3, "direction": 135},
        {"lng": 104.210473, "lat":30.525124, "timestamp": 1728763000000, "speed": 2.0, "direction": 141},
        {"lng": 104.210488, "lat":30.525114, "timestamp": 1728763012000, "speed":0.0, "direction": 120},
        ...
        ]
}

流程详解

轨迹获取

【准备工作】:获取密钥,修改config.ini

【给定输入】:参照输入字段说明及示例

【轨迹获取主流程】main.py接收输入,调用traj_acquisition模块的traj_acquisition.py

  • 根据指定的method_type调用API获取轨迹,若失败则尝试其他备选方法
  • interpolate_flag == True,则通过等距插值补充轨迹点
  • simulate_flag == True,则调用traj_acquisition模块的traj_info_perfection.py
    • 通过车辆行驶状态模拟获取速度、时间戳、航向角字段(speedtimestamp
  • 否则,轨迹点只包含经纬度坐标字段(lnglat),可以根据坐标计算航向角(direction

【获取输出】:参照输出字段说明及示例

字段补全

字段补全思路

通过调用路径规划接口能获取轨迹点的坐标(lnglat),根据坐标字段可以计算航向角(direction),但是还缺少时间戳、速度字段(timestampspeed),本算法通过车辆行驶状态模拟得到

  • 计算direction:根据相邻点的坐标采用公式计算direction,正北为0,顺时针递增(0~360.0)
  • 生成speed:根据车辆行驶状态模拟得到每个轨迹点的速度(详见下文说明)

    • 给定初始速度、初始加速度状态
    • 计算下一时刻的速度,根据对应的状态转移概率得到下一时刻的加速度状态
    • 重复执行,确定所有轨迹点的速度
  • 生成timestamp:根据轨迹点的坐标、速度计算

    • 给定初始时间戳
    • 根据相邻点坐标采用公式计算距离,以两个点的速度均值作为路段的速度(若速度为0,则时间戳直接增加10秒),根据距离、速度计算路段的行程时间,进而得到时间戳
    • 重复执行,确定所有轨迹点的时间戳

速度生成

为了精细化模拟车辆行驶状态将车辆状态细分为速度状态、加速度状态

  • 速度状态:低速(0-50)、中速(50-70)、高速(70-90)、超高速(90-100)
  • 加速度状态:加速(速度增大)、减速(速度减小)、巡航(速度上下波动)
  • 最大速度默认为100km/h(以上数值适用于货车,可根据需要修改,例如小汽车最大速度可设置为120)
  • 速度调整:生成给定区间内的随机值(与速度状态有关),与当前速度做差或求和

状态转移说明(可结合代码辅助理解):已知t时刻的车辆状态(例如【低速 + 加速】)

  • 则可以根据【低速 + 加速】确定t+1时刻的速度(当前速度加上一个给定区间内的随机值)
  • 同样根据【低速 + 加速】对应的状态转移概率确定t+1时刻的状态
    {
        "low_speed": {
                         "accelerate": [0.8, 0.1, 0.1], 
                         "decelerate": [0.1, 0.8, 0.1],  
                         "cruise": [0.3, 0.3, 0.4]
                     },
        "mid_speed": {
                         "accelerate": [0.6, 0.1, 0.3],
                         "decelerate": [0.2, 0.5, 0.3],
                         "cruise": [0.3, 0.2, 0.5]
                     },
        "high_speed": {
                          "accelerate": [0.4, 0.2, 0.4],
                          "decelerate": [0.1, 0.5, 0.4],
                          "cruise": [0.1, 0.1, 0.8]
                      },
        "super_high_speed": {
                                "accelerate": [0.3, 0.4, 0.3],
                                "decelerate": [0.1, 0.6, 0.3],
                                "cruise": [0.2, 0.4, 0.4]
                            }
    }
    
Image title
桑基图

以300个轨迹点为例,生成的速度值如下图所示:

Image title
速度变化图

轨迹可视化

读取轨迹文件,使用folium库绘制轨迹

参数名 类型 是否必填 含义 说明 默认值
path str 轨迹路径 需进行可视化的轨迹数据路径
save_path str 保存路径 轨迹可视化结果保存路径
file_name str 文件名称 需进行可视化的轨迹数据文件名
data_type str 文件类型 文件类型:表格型csv、字典型json csv
coord_type str 坐标系类型 轨迹数据坐标系类型:wgs84、gcj02、bd09ll gcj02
  • 注意事项
    • 轨迹数据的坐标系为wgs84时(使用OpenStreetMap作为底图),可能加载失败,建议转换为其他坐标系再尝试绘图
    • 可视化结果为与file_name相同的html文件,可使用浏览器打开
Image title

补充说明

时间戳

Note

对于轨迹数据,时间戳是非常重要的字段,仅次于坐标字段 为了避免时间戳转换的风险(需考虑时区),建议处理及存储时始终使用Unix格式(13位,精确到毫秒),而不是转换为'%Y-%m%-d %H:%M:%S'等形式

Unix转换为Timestamp

  • 单个变量转换
import pandas as pd

# 给定的 Unix 时间戳(毫秒)
unix_timestamp_ms = 1732232345000

# 转换为 pandas Timestamp 对象,可以用tz字段指定时区(若不指定时区则为UTC时间)
timestamp = pd.Timestamp(unix_timestamp_ms, unit='ms', tz='Asia/Shanghai')
  • DataFrame列转换
df['timestamp'] = pd.to_datetime(df['timestamp'],unit='ms',utc=True)
df['timestamp'] = df['timestamp'].dt.tz_convert('Asia/Shanghai')

# 可以去除时区信息(如果将df保存为csv文件再读取,则timestamp保持不变,只是没有时区信息)
# df['timestamp'] = df['timestamp'].apply(lambda a: a.to_pydatetime().replace(tzinfo=None))

Timestamp转换为Unix

  • 单个变量转换
from datetime import datetime
import pandas as pd
import pytz


# 创建时间对象
# 创建不带有时区信息的时间对象
# 方式1
time1 = datetime.strptime('2024-11-21 23:39:05', '%Y-%m-%d %H:%M:%S')    # time为本地时间
int(time1.timestamp() * 1000)    # 得到1732203545000,正确的时间戳 
# 方式2
time2 = pd.Timestamp('2024-11-21 23:39:05')    # time为UTC时间
int(time2.timestamp() * 1000)    # 得到1732232345000,本地时间2024-11-22 07:39:05的时间戳 

# 【说明】:timestamp()获取的是本地的时间戳(精确到秒),对于time2.timestamp(),编译器根据系统时区得到本地时间2024-11-22 07:39:05,然后返回时间戳

# 创建带有时区信息的时间对象
# 方式1
time3 = datetime.strptime('2024-11-21 23:39:05', '%Y-%m-%d %H:%M:%S')
timezone = pytz.timezone('Asia/Shanghai')  # 东八区
int(time3.timestamp() * 1000)    # 得到1732203545000
# 方式2
time4=pd.Timestamp('2024-11-21 23:39:05',tz='Asia/Shanghai')
int(time4.timestamp() * 1000)    # 得到1732203545000
  • DataFrame列转换
# 将 'timestamp' 列转换为 datetime 类型
data['timestamp'] = pd.to_datetime(data['timestamp'])

# 将 'timestamp' 列加上东八区时区信息
data['timestamp'] = data['timestamp'].dt.tz_localize('Asia/Shanghai')

# 使用 .timestamp() 方法将 datetime 转换为 Unix 时间戳(秒级),然后乘以 1000 转换为毫秒级
data['locatetime'] = data['timestamp'].apply(lambda x: int(x.timestamp() * 1000))

Geojson

参考:Geojson相关使用链接

一个典型的Geojson文件包含一个要素集合对象,用于表示一组相关的地理要素

  • 可以在meta(非必要)中设置要素集合对象的相关属性
  • 要素集合对象必须包含type(值为 FeatureCollection)和features(要素对象数组)
    • features中可以记录点Point、线LineString等信息
    • type(值为Feature)和geometry(地理属性)是必要的,其他信息可以放在properties

一个可用于轨迹可视化的Geojson示例文件(包含起点、终点、轨迹线): 示例文件

Folium

使用Folium进行轨迹的可视化:绘制轨迹、起终点、增加测距功能等(简单的交互)

  • 要求坐标系为WGS84
  • 纬度在前,经度在后

相关教程:python交互式地图数据可视化神器folium

示例代码:code

桑基图

桑基图(Sankey diagram)是一种特定类型的流程图,能直观展示数据从一个或多个源节点流向一个或多个目标节点的过程,其中边的宽度与“流量”大小成正比。

  • 节点(Nodes):代表不同的分类或类别,是数据的起始点、中间点或终点。例如,某一时刻车辆的行驶状态可以作为一个节点
  • 边(Links):连接各个节点的线条,显示了数据从一个节点到另一个节点的流动。例如,车辆由当前时刻的状态转变为下一时刻的状态
import numpy as np
from pyecharts import options as opts
from pyecharts.charts import Sankey


def generate_sankey_data(matrix):
    """将转移矩阵转换为桑基图数据格式"""
    nodes = []
    links = []

    # 创建双层节点(左侧t时刻,右侧t+1时刻)
    for i, state in enumerate(states):
        nodes.append({"name": f"{state}_t"})
        nodes.append({"name": f"{state}_t+1"})

    # 生成链接关系
    for src_idx in range(3):
        src_name = f"{states[src_idx]}_t"
        for dst_idx in range(3):
            # 计算概率值占比
            prob = matrix[src_idx, dst_idx]

            links.append({
                "source": src_name,
                "target": f"{states[dst_idx]}_t+1",
                "value": prob * 100  # 放大显示比例
            })

    return nodes, links

def process():
    # 验证概率矩阵合法性
    if np.any(transition_matrix < 0):
        raise Exception("转移概率不应为负")
    if not np.allclose(transition_matrix.sum(axis=1), np.ones(3)):
        raise Exception("转移概率行和必须为1")

    # 生成可视化数据
    nodes, links = generate_sankey_data(transition_matrix)

    # 创建桑基图
    sankey = (
        Sankey(init_opts=opts.InitOpts(width="1000px", height="600px"))
        .add(
            series_name="状态转移",
            nodes=nodes,
            links=links,
            linestyle_opt=opts.LineStyleOpts(opacity=0.3, curve=0.5, color="source"),
            label_opts=opts.LabelOpts(position="right"),
            node_gap=30,
            node_align="justify",
            itemstyle_opts=opts.ItemStyleOpts(
                border_width=1,
                border_color="rgba(255,255,255,0.7)"
            )
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="车辆状态转移概率桑基图"),
            tooltip_opts=opts.TooltipOpts(
                trigger="item",
                formatter="""
                    {b}{c}%<br/>
                    """
            )
        )
    )

    # 自定义节点颜色
    for idx, node in enumerate(nodes):
        state_idx = idx // 2  # 每个状态占2个节点
        sankey.options["series"][0]["data"][idx]["itemStyle"] = {
            "color": state_colors[state_idx % 3]
        }

    # 渲染图表
    sankey.render("state_transition_sankey.html")


if __name__ == '__main__':
    # 状态定义
    states = ["加速", "匀速", "减速"]
    state_colors = ["#FF6B6B", "#4ECDC4", "#45B7D1"]

    # 示例状态转移概率矩阵(3x3)
    transition_matrix = np.array([
        [0.2, 0.6, 0.2],  # 加速 -> 各状态概率
        [0.3, 0.4, 0.3],  # 匀速 -> 各状态概率
        [0.1, 0.7, 0.2]  # 减速 -> 各状态概率
    ])

    process()