从GPS轨迹到热点与OD:Python出租车数据挖掘全流程解析
2026/9/15 14:07:05 网站建设 项目流程

简介:基于Python实现的武汉市出租车轨迹数据挖掘与分析项目,代码已经过测试可运行,适合计算机相关专业学生用于毕业设计、课程设计或项目实训。压缩包共36个文件,大小约11.58MB,以10个Python脚本为核心,覆盖轨迹读取排序、插值平滑与路网匹配、上下车点可视化与热区分析、轨迹聚类、OD热点空间交互网络构建、目的地预测及异常轨迹分析等步骤;另含武汉行政区划的shapefile空间数据和少量说明文档,方便在真实地理数据上进行空间统计。通过阅读代码能完整掌握出租车GPS轨迹从原始清洗到业务建模的流程,例如如何识别客流热点、构建OD网络和检测异常轨迹。用户下载后可按目录顺序演练,也可二次修改用于自己的实验或毕业设计。目前已有213人学习下载,整体体量适中,是快速上手轨迹数据挖掘与空间分析的良好参考。

1. 为什么要专门写一套出租车轨迹挖掘而不是调库跑通

如果只是把出租车GPS数据读进DataFrame再画几张散点图,两个小时就够了。但真实场景里,武汉这样千万级人口城市的出租车轨迹数据动辄上亿条,单日记录就可能超过2000万行,叠加信号漂移、停车等待、跨江大桥信号遮挡、司机交接班等因素后,原始数据基本不能用。所谓数据挖掘,不是跑一个聚类算法看个热闹,而是先把轨迹还原成可用信息:载客点在哪、空驶去了哪、哪条路几点最堵、司机交接班对载客点分布的影响有多大。这篇文章从数据结构出发,完整走一遍清洗、停留识别、轨迹压缩、特征工程、热点挖掘和OD分析,全部用Python实现,代码可以直接在本地Jupyter或VS Code里跑起来。适合刚接触时空数据挖掘的Python使用者,也适合有数据分析经验但没处理过轨迹数据的人——你真正缺的不是算法,而是轨迹数据特有的预处理和验证手段,这是普通DataFrame教程里不会讲的。

2. 轨迹数据的原始形态与三个跑通全流程的Python环境准备

2.1 出租车轨迹记录里到底有哪些字段可以用

武汉出租车轨迹数据最常见的来源是车辆GPS终端,按固定频率上报位置,典型字段如下。不同数据源字段名略有差异,但核心信息一致。

import pandas as pd # 武汉出租车轨迹典型字段,按真实数据常见顺序排列 sample_cols = [ "vehicle_id", # 车辆唯一标识 "time", # 记录时间,格式一般为 YYYY-MM-DD HH:MM:SS "lng", # WGS-84 或 GCJ-02 坐标系下的经度 "lat", # 纬度 "speed", # 瞬时速度,单位 km/h "direction", # 行驶方向角,0-360度 "passenger", # 载客状态:0空车 1载客 "mileage", # 累计里程,部分数据源有 ] df = pd.read_csv("wuhan_taxi.csv", names=sample_cols, dtype={ "vehicle_id": "str", "passenger": "int8", }, parse_dates=["time"]) print(df.shape) print(df.dtypes)

这里有个关键点:vehicle_id必须读成字符串,否则前导零会丢失;time直接用parse_dates转换,避免后面反复调用pd.to_datetime拖慢速度。passengerint8足够,因为取值只有0和1。读入后先别急着清洗,花30秒看一下数据范围。

print(df["time"].min(), df["time"].max()) print(df["lng"].describe()) print(df["lat"].describe())

从经度范围可以直接判断坐标是否合理。武汉市中心大约在东经114.2°到114.5°、北纬30.4°到30.7°之间,如果数据里出现经度小于73或大于135、纬度小于3或大于54的记录,说明混入了脏数据或火星坐标偏移未处理,需要在下游处理中排除。

2.2 处理亿级轨迹的Python环境要怎么组织

处理亿级数据,Pandas单机跑得动,但要用对方式。三个要点直接决定效率:第一,用Parquet或Feather格式落地中间结果,不要反复读写CSV;第二,清洗和特征工程尽量用向量化操作,避免逐行iterrows();第三,安装GeoPandas用于地理空间索引,安装scikit-learn用于聚类,安装OSMnx或Shapely用于路网匹配。下面是推荐的环境组织。

conda create -n trajectory python=3.10 -y conda activate trajectory conda install -c conda-forge geopandas scikit-learn shapely pyproj -y pip install pandas pyarrow osmnx matplotli

提示:轨迹数据涉及经纬度投影计算,pyproj用于坐标系转换,GeoPandas在空间过滤和栅格聚合时性能远超纯Pandas循环。如果只需要最简功能,可以暂时不装OSMnx,用GeoPandas自带的sjoin也够。

环境准备好后,先把原始数据按天切分落盘,这是一种常见做法,能显著降低后续多次全表扫描的代价。

df["date"] = df["time"].dt.date for d, group in df.groupby("date"): group.drop(columns=["date"]).to_parquet(f"data/{d}.parquet", index=False)

按天切分的好处有两个:一是单日数据量大约百万到千万行,内存可控;二是后面做OD分析、热点挖掘时,天然按天聚合更符合业务习惯。切分后删掉原始DataFrame释放内存:

import gc del df gc.collect()

2.3 坐标系不一致会让聚类结果偏离几百米,先统一再挖掘

轨迹数据最容易踩的坑是坐标系混用。国内GPS设备输出的原始坐标是WGS-84,但部分数据源为了合规会转成GCJ-02(火星坐标),有的地图厂商SDK直接输出BD-09(百度坐标)。三者偏差大约在几十米到几百米之间,这在城市道路级别的挖掘中足以让一个路口的热点被分到旁边街区。判断当前坐标系的简单方法是取一个已知地标对照,比如武汉长江大桥中心约在东经114.2848°,北纬30.5463°,如果数据在该点位附近偏移约0.0001到0.0005度,大概率是GCJ-02未转换。用pyproj统一转换。

from pyproj import Transformer # 假设原始数据是GCJ-02,统一转成WGS-84 trans = Transformer.from_crs("gcj02", "epsg:4326", always_xy=True) df["lng"], df["lat"] = trans.transform(df["lng"].values, df["lat"].values)

如果数据本身就是WGS-84,此步骤可跳过。但要注意:如果后续路网匹配使用OSMnx或公开路网数据,路网通常也是WGS-84,必须保证轨迹和路网在同一坐标系下。

3. 轨迹预处理的四个必要环节:清洗、停留识别、轨迹切分、压缩

3.1 清洗不是删空值那么简单,要看漂移点和速度突变

轨迹数据的异常分为三类:超出城市范围的离群点、由GPS漂移造成的瞬时跳变、由设备故障导致的重复或乱序时间戳。第一类直接用经纬度边界过滤,第三类按下述方式删除。

import numpy as np # 基于经纬度边界过滤武汉城区范围 mask = ( (df["lng"] > 113.8) & (df["lng"] < 114.8) & (df["lat"] > 30.2) & (df["lat"] < 30.9) ) df = df[mask] # 删除同一车辆同一时间戳重复记录 df = df.drop_duplicates(subset=["vehicle_id", "time"]) # 按车辆和时间排序,保证轨迹数据时间顺序一致 df = df.sort_values(["vehicle_id", "time"]).reset_index(drop=True)

速度突变需要记住一个关键点:不要单看绝对速度。城市出租车在桥梁上和隧道口速度差距极大,但GPS在隧道内可能产生几十秒的定位漂移,瞬时速度会跳到120km/h以上。过滤规则用「相邻点距离除以时间差得到的平均速度」更合理,这种速度叫载体速度,能反映两个点间的真实行驶快慢。

# 计算相邻点位移和方向角,排除因漂移产生的虚假高速 df["prev_lng"] = df.groupby("vehicle_id")["lng"].shift(1) df["prev_lat"] = df.groupby("vehicle_id")["lat"].shift(1) df["prev_time"] = df.groupby("vehicle_id")["time"].shift(1) # 用Haversine公式计算距离,此处用简化球面距离 R = 6371000.0 dlat = np.radians(df["lat"] - df["prev_lat"]) dlng = np.radians(df["lng"] - df["prev_lng"]) a = np.sin(dlat / 2) ** 2 + np.cos(np.radians(df["prev_lat"])) * np.cos(np.radians(df["lat"])) * np.sin(dlng / 2) ** 2 df["dist"] = 2 * R * np.arcsin(np.sqrt(a)) # 时间差转小时,速度单位 km/h df["dt_hour"] = (df["time"] - df["prev_time"]).dt.total_seconds() / 3600.0 df["moving_speed"] = df["dist"] / 1000.0 / df["dt_hour"].replace(0, np.nan) # 载体速度超过 200km/h 视为漂移,删除该段连接 df.loc[df["moving_speed"] > 200, ["prev_lng", "prev_lat", "prev_time"]] = np.nan

删除漂移点不是直接把速度异常的行删掉,因为前后两段轨迹可能都是正常的,错在当前点连接了不该相连的两点。处理方式是置空prev字段,这样后续按空值切分轨迹时,轨迹就会在这个位置断开,而不是把两段不相干路线强行连在一起。

3.2 载客状态切换是天然的轨迹切分点

出租车的载客状态(passenger字段)是轨迹切分的天然标注。一次行程的起点是passenger从0变1的记录,终点是1变0的记录。先找到所有状态切换点,再切出单次行程,这种做法的好处是行程定义和业务口径一致,后面做的起讫点(OD)分析、行程时长统计都不需要重新定义。

# 找到每个车辆的载客状态变化点 df["pkg_prev"] = df.groupby("vehicle_id")["passenger"].shift(1) df["trip_start"] = (df["pkg_prev"] == 0) & (df["passenger"] == 1) df["trip_end"] = (df["pkg_prev"] == 1) & (df["passenger"] == 0) # 对每条行程分配唯一ID df["trip_id"] = df["trip_start"].cumsum() # 行程结束时,下一段行程开始前的记录都属于当前行程 df.loc[df["trip_end"], "trip_id"] = df["trip_start"].cumsum() df.loc[df["passenger"] == 0, "trip_id"] = np.nan

注意一个容易出错的地方:如果原始数据没有passenger字段,比如只有空驶记录怎么办。这时退而求其次:用停留检测切分轨迹。停留意味着车辆长时间低速且位移很小,一般是等人或交接班,将停留点前后的轨迹视为两次行程。停留检测做法:

# 基于时间窗和距离阈值识别停留点 df["pt_delta"] = df["dist"].fillna(0) df["pt_time"] = (df["time"] - df["prev_time"]).dt.total_seconds().fillna(0) # 滑动窗口内累计位移小于50米且持续超过180秒,标记为停留 def mark_stay(g): stay = np.zeros(len(g), dtype=bool) i = 0 while i < len(g): j = i while j < len(g) and g["pt_time"].iloc[i:j+1].sum() < 180: j += 1 seg = g.iloc[i:j] if seg["pt_delta"].sum() < 50 and seg["pt_time"].sum() >= 180: stay[i:j] = True i = max(j, i+1) return stay df["is_stay"] = df.groupby("vehicle_id", group_keys=False).apply(mark_stay)

这段代码的核心逻辑是滑动累积时间,不是固定窗口。固定窗口会漏掉长时间低速缓行的情况,而这里只要窗口中累计位移很小且持续3分钟以上,就判为停留。停留段落在后续轨迹压缩中要保留原始点,因为它们包含载客点位置的语义信息。

3.3 DTW和道格拉斯-普克压缩,轨迹挖掘里的取舍

原始轨迹点太密,直接用原始点做聚类或路网匹配,计算量巨大且噪声点多。常见的轨迹压缩算法有道格拉斯-普克(Douglas-Peucker)和基于时间阈值的均匀抽稀。道格拉斯-普克保留形状特征,适合几何形态分析;时间抽稀则适合速度、拥堵类分析——因为你需要等间隔时间戳,而不是等距离点。以下用递归实现道格拉斯-普克,先算每点到首尾连线的垂直距离。

from typing import List, Tuple def douglas_peucker(points: List[Tuple[float, float]], epsilon: float) -> List[Tuple[float, float]]: if len(points) <= 2: return points # 首尾连线 x1, y1 = points[0] x2, y2 = points[-1] # 垂直距离向量化计算 dx = x2 - x1 dy = y2 - y1 if dx == 0 and dy == 0: dists = np.sqrt((np.array(points)[:, 0] - x1)**2 + (np.array(points)[:, 1] - y1)**2) else: dists = np.abs((dy * (np.array(points)[:, 0] - x1) - dx * (np.array(points)[:, 1] - y1)) / np.sqrt(dx*dx + dy*dy)) idx_max = np.argmax(dists) if dists[idx_max] > epsilon: left = douglas_peucker(points[:idx_max+1], epsilon) right = douglas_peucker(points[idx_max:], epsilon) return left[:-1] + right else: return [points[0], points[-1]]

实际使用时,不要对整条轨迹一次压缩,而是先按轨迹ID或行程ID分组,逐一压缩。

GROUP_EPS = 0.0005 # 约50米,车辆GPS精度在此量级可接受 def compress_group(group): pts = list(zip(group["lng"], group["lat"])) pts_c = douglas_peucker(pts, GROUP_EPS) return pts_c # 仅对载客轨迹压缩 df_trip = df[df["passenger"] == 1].copy() compressed = df_trip.groupby("trip_id").apply(compress_group)

压缩比一般在80%到95%之间,即原始1000个点最后只剩50到200个点,但路线形状和载客点位置保持不变。压缩后的轨迹才适合做全局聚类和OD矩阵,否则1亿点跑DBSCAN的内存消耗直接让机器卡死。

4. 轨迹特征工程与两个高价值挖掘任务:热点区域聚类和OD时空分析

4.1 轨迹点的高维特征:不只是经纬度和速度

原始轨迹只有坐标、时间、速度和方向,但真正能用于预测或分析的标签性特征需要手工构造。下表是轨迹挖掘任务里最常用的一批衍生特征:

特征名计算方式适用场景
时间槽按小时切分(0-23)时段热度分析
方向变化率相邻点方向角差值的绝对值识别绕路、急转弯
单位时间载客次数按车辆一小时内的载客状态切换次数司机运营效率
载客率载客时间/总工作时长司机收入分析
速度分位数单段轨迹速度的P50/P90拥堵判别、路线规划
乘客上车点经度纬度行程起点坐标热点区域、候车点选址
行程直线距离起点到终点Haversine距离OD分析、运力调度

方向变化率的计算要小心角度跨越360度的边界,比如从350度变到10度,差值应该是20度而不是340度。下面这段代码处理了这个问题。

# 当前方向角与下一方向角差值 df["dir_diff"] = np.abs(df["direction"].diff()) df["dir_diff"] = np.where(df["dir_diff"] > 180, 360 - df["dir_diff"], df["dir_diff"]) # 按行程ID聚合得到急转弯次数 turn_features = df_trip.groupby("trip_id")["dir_diff"].agg(["mean", "max", lambda x: (x > 30).sum()]) turn_features.columns = ["turn_mean", "turn_max", "turn_count"]

注意:方向角在静止停放时可能剧烈跳动,因此要先剔除速度接近0的点再算方向变化率,否则噪声直接淹没信号。

4.2 用DBSCAN找出热点区域,参数要按武汉的街道尺度设

热点区域挖掘是出租车轨迹数据挖掘最经典的任务之一,输出的是一批中心点经纬度和覆盖半径,可以直接用于调度、广告投放选址、候车区规划。主流算法是DBSCAN聚类,它不需要预先指定簇数量,能识别噪声点,这对轨迹数据非常友好。武汉热点区域聚类前,先选特征:只用上车点经纬度,然后用DBSCAN。

from sklearn.cluster import DBSCAN # 提取上车点 pickup_points = df_trip[df_trip["trip_start"] == True][["lng", "lat"]].values # 用 haversine 距离度量替代欧氏距离 from sklearn.metrics import pairwise_distances import math def haversine_dist(X, Y): R = 6371000 X = np.radians(X) Y = np.radians(Y) dlat = Y[:, 0][:, None] - X[:, 0] dlng = Y[:, 1][:, None] - X[:, 1] a = np.sin(dlat/2)**2 + np.cos(X[:, 0])[None, :] * np.cos(Y[:, 0])[:, None] * np.sin(dlng/2)**2 return 2 * R * np.arcsin(np.sqrt(a)) D = haversine_dist(pickup_points, pickup_points) db = DBSCAN(eps=150, min_samples=20, metric="precomputed") labels = db.fit_predict(D) # 聚类中心用簇内点均值 hotspots = [] for lab in set(labels): if lab == -1: continue pts = pickup_points[labels == lab] hotspots.append({ "cluster_id": lab, "lng": np.mean(pts[:, 0]), "lat": np.mean(pts[:, 1]), "count": len(pts) }) hotspot_df = pd.DataFrame(hotspots).sort_values("count", ascending=False)

这里两个参数必须说明。eps=150表示聚类半径150米,武汉市区出租车上车点密度高,150米约等于一个街区的尺度,能精确到路口级别,如果设成500米会把多个路口合并成一个热点,失去调度参考价值。min_samples=20是最小簇点数,全天数据下20个点可以过滤偶发上下客,但如果你分析的是晚高峰1小时的数据,20个点要求过高,热点会过少。一个常见做法是分时段跑DBSCAN,而不是全量跑一次:早高峰用min_samples=10,平峰用min_samples=20,晚高峰用min_samples=30。时段划分代码:

df_trip["hour"] = df_trip["time"].dt.hour for hour, grp in df_trip.groupby("hour"): pts = grp[grp["trip_start"] == True][["lng", "lat"]].values if len(pts) < 50: continue D = haversine_dist(pts, pts) db = DBSCAN(eps=150, min_samples=15 if hour in [7,8,9,17,18,19] else 20, metric="precomputed") labels = db.fit_predict(D) # 打印每个时段簇数量与点数 print(hour, len(set(labels)) - (1 if -1 in labels else 0), (labels != -1).sum())

聚类完成后需要验证结果。常见做法是回到地图上随机抽样50个簇,人工查看是否落在商圈、地铁站或大型社区门口。如果大量簇落在立交桥或江河中央,先回去检查坐标转换和漂移点清洗,而不是调聚类参数。

4.3 OD矩阵和跨江需求:武汉特有的空间约束分析

武汉被长江和汉江分割成三镇,跨江出行是极其重要的需求形态。OD矩阵分析需要把上车点和下车点映射到空间网格,再统计网格间的流量。网格大小采用0.01度×0.01度约1公里见方比较合适,过小则矩阵稀疏,过大则丢失街道级信息。用GeoPandas做网格映射:

import geopandas as gpd from shapely.geometry import Point, Polygon import numpy as np # 生成武汉城区0.01度网格 lng_min, lng_max = 113.8, 114.8 lat_min, lat_max = 30.2, 30.9 grids = [] for i in np.arange(lng_min, lng_max, 0.01): for j in np.arange(lat_min, lat_max, 0.01): grids.append((i, j, i+0.01, j+0.01)) grid_df = gpd.GeoDataFrame({ "grid_id": range(len(grids)), "geometry": [Polygon([(a,b),(c,b),(c,d),(a,d)]) for a,b,c,d in grids] }, crs="EPSG:4326") # 将行程起终点映射到网格 df_trip["geo_o"] = gpd.points_from_xy(df_trip["o_lng"], df_trip["o_lat"]) df_trip["geo_d"] = gpd.points_from_xy(df_trip["d_lng"], df_trip["d_lat"]) o_join = gpd.sjoin(df_trip[["trip_id", "geo_o"]], grid_df, how="left") d_join = gpd.sjoin(df_trip[["trip_id", "geo_d"]], grid_df, how="left") od_matrix = pd.crosstab(o_join["grid_id"], d_join["grid_id"])

pd.crosstab输出的是一个N×N稀疏矩阵,N是网格数。武汉城区大约0.1度×0.7度的范围,网格数在700个左右,OD矩阵就是700×700,完全可以直接操作。跨江需求分析不要只看OD矩阵绝对值,要做期望流量修正——因为网格OD矩阵天然和网格内人口密度、上车点总数相关。一种常见做法是计算每个网格对间的流量占比,即流量除以该网格总上车数,消除规模差异后,跨江OD的实际强度才可比。

提示:OD矩阵的结果不要直接读成热点结论。如果发现长江两岸网格间流量很高,先确认是否是轮渡和地铁上下客点在出租车轨迹附近,因为出租车上车点可能在地铁站出口周围,流量会叠加。

5. 从轨迹速度分布反推路况拥堵验证,用对照实验确认挖掘结果可信

5.1 把轨迹速度聚合到路段上的交叉验证

热点聚类和OD分析做完,如何确定结果可信?拿官方路况数据对不上的情况很常见,尤其是公开轨迹数据往往只有部分车辆,覆盖不全。一个不依赖外部数据的验证方法:用同一份轨迹数据,分别挖掘出拥堵路段,然后与你聚类结果里的载客热点做时间相关性对照。拥堵路段识别从轨迹速度做起。

# 每辆车每5分钟的平均速度,用于路况时段分析 df_trip["time_bin"] = df_trip["time"].dt.floor("5min") speed_bin = df_trip.groupby(["time_bin", "vehicle_id"])["speed"].mean().reset_index() # 按5分钟窗口聚合区域平均速度 area_speed = speed_bin.groupby("time_bin")["speed"].agg(["mean", "median", "count"]) area_speed["time"] = area_speed.index area_speed = area_speed.set_index("time") # 计算相对拥堵指数:当前速度/全天平均速度 area_speed["congest_idx"] = area_speed["median"] / area_speed["mean"].mean()

拥堵指数小于0.8可以认为该时段出现拥堵状态。将这个拥堵指数和对应时段的载客热点簇数量做相关性分析。逻辑是:晚高峰交通拥堵时,路上车速慢、乘客等待时间更长,但热点簇的总数不应急剧下降,因为需求仍然存在。如果某一时段拥堵指数上升伴随热点簇数量显著下降,说明这个聚类受噪声影响,需要检查DBSCAN参数。

from scipy.stats import pearsonr # 按小时聚合拥堵指数和热点簇数量 hourly_congest = area_speed.resample("1h").median() hotspot_count_per_hour = df_trip[df_trip["trip_start"] == True].groupby(df_trip["time"].dt.hour).apply( lambda g: len(set(DBSCAN(eps=150, min_samples=20, metric="precomputed").fit_predict( pairwise_distances(g[["lng", "lat"]].values, metric=haversine_dist) ))) - 1 ) print(pearsonr(hourly_congest["congest_idx"], hotspot_count_per_hour))

相关系数绝对值大于0.5时,说明拥堵时段和热点分布有同步变化,聚类结果在时间维度上自洽。如果系数接近0,优先检查轨迹时间戳是否有时区偏移,以及载客状态是否在真实交接班时段产生了错误标记。

5.2 用剪枝后的特质数据和Baseline对比验证

另一种验证方法更直接:不调整算法,而是更换数据样本构造对照实验。把轨迹数据按车辆类型分为两类——武汉的出租车中,有传统的双班制车辆和少量新能源单班车,它们的载客状态切换频率不同。分别跑热点聚类,对比热点中心点经纬度的偏移距离。

# 假设已识别双班车与单班车,分别聚类 for label in ["double_shift", "single_shift"]: grp = df_trip[df_trip["vehicle_type"] == label] pts = grp[grp["trip_start"] == True][["lng", "lat"]].values D = pairwise_distances(pts, metric=haversine_dist) labels = DBSCAN(eps=150, min_samples=15, metric="precomputed").fit_predict(D) centers = [] for lab in set(labels): if lab == -1: continue centers.append(np.mean(pts[labels == lab], axis=0)) print(label, "clusters:", len(centers))

如果两类的热点中心点坐标差超过200米,或者簇数量差异超过30%,说明你的聚类结果受车辆类型分布影响过大,热点不代表真实需求,而更可能反映了抽样偏差。此时不要急着发表结论,需要按车辆类型分层抽样,再做一次聚类,才能得到稳健结果。

5.3 轨迹压缩验证:恢复原轨迹的误差控制

道格拉斯-普克压缩本身带有信息损失,验证压缩对下游影响的一种做法是:将压缩后的轨迹点还原成折线,然后计算与原始轨迹的平均Hausdorff距离。这个指标若小于30米,说明压缩没有改变道路属性。

from scipy.spatial.distance import directed_hausdorff def hausdorff_dist(orig_pts, comp_pts): orig_arr = np.array(orig_pts) comp_arr = np.array(comp_pts) h1 = directed_hausdorff(orig_arr, comp_arr)[0] h2 = directed_hausdorff(comp_arr, orig_arr)[0] return max(h1, h2) # 取一条行程对比 sample_trip = df_trip[df_trip["trip_id"] == 1] orig = list(zip(sample_trip["lng"], sample_trip["lat"])) comp = compressed[1] # 该行程压缩后的点序列 print("Hausdorff distance:", hausdorff_dist(orig, comp), "meters")

如果Hausdorff距离过大,把epsilon从0.0005调整到0.0002,即约20米精度。注意不要调得太小,否则压缩退化失去意义;也不要只验证一条轨迹,抽样100条取平均和P90,只有P90小于30米才算全量可接受。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询