大疆御3e的SRT文件里藏着啥?用Python提取经纬度、姿态角,生成JSON地图数据

张开发
2026/4/9 11:11:45 15 分钟阅读

分享文章

大疆御3e的SRT文件里藏着啥?用Python提取经纬度、姿态角,生成JSON地图数据
大疆御3e SRT文件解析实战从飞行日志到地图可视化的完整指南当你完成一次激动人心的航拍任务后大疆御3e无人机除了带回精美的影像素材还默默记录了一份珍贵的飞行日志——SRT文件。这份看似普通的字幕文件里其实藏着无人机在空中的每一个心跳它的精确位置、飞行姿态甚至是拍摄参数。本文将带你深入探索这些数据的奥秘并手把手教你用Python将其转化为可交互的地图数据。1. 认识SRT文件无人机飞行的黑匣子SRT文件通常被认为是视频字幕的载体但在大疆无人机生态中它扮演着更为重要的角色。每次飞行结束后御3e会在存储卡中生成一个与视频同名的.srt文件这个文件实际上是一个结构化的飞行数据记录器。打开一个典型的御3e SRT文件你会看到类似这样的内容1 00:00:00,000 -- 00:00:00,033 iso:100, shutter:1/100, fnum:2.8 2 00:00:00,033 -- 00:00:00,066 [latitude: 39.9042] [longitude: 116.4074] [rel_alt: 120.5 abs_alt: 125.3] [gb_yaw: 45.2 gb_pitch: -2.1 gb_roll: 1.8]文件中每三行为一组包含序号数据记录的编号时间戳该组数据对应的时间区间数据行包含拍摄参数和飞行状态信息对我们最有价值的是第三行数据其中包含的关键参数有参数类别字段名描述单位定位数据latitude纬度度longitude经度度rel_alt相对起飞点高度米abs_alt绝对海拔高度米姿态数据gb_yaw偏航角(机头方向)度gb_pitch俯仰角(前后倾斜)度gb_roll横滚角(左右倾斜)度拍摄参数iso感光度-shutter快门速度秒fnum光圈值-理解这些参数的含义是后续数据处理的基础。特别是姿态角数据对于三维场景重建和飞行轨迹分析至关重要。2. Python解析SRT从原始文本到结构化数据现在我们来解决核心问题如何用Python从SRT文件中提取这些宝贵的数据与简单的文本处理不同我们需要考虑SRT的特殊格式和数据的完整性。2.1 文件读取与初步清洗首先创建一个新的Python脚本导入必要的库import re import json from typing import List, Dict定义一个函数来读取和预处理SRT文件def read_srt_file(file_path: str) - List[str]: 读取SRT文件并返回有效数据行 with open(file_path, r, encodingutf-8) as f: lines [line.strip() for line in f if line.strip()] # SRT文件每3行一组我们只需要数据行(每组第3行) data_lines [lines[i] for i in range(2, len(lines), 3)] return data_lines这个函数做了三件事读取文件并移除空行保留非空行的文本只提取每组第三行的数据内容2.2 数据提取的正则表达式方法相比简单的字符串分割使用正则表达式能更健壮地处理数据格式变化。我们为每种数据类型定义匹配模式def parse_srt_data(data_lines: List[str]) - List[Dict]: 从SRT数据行中解析出飞行参数 pattern re.compile( r\[latitude:\s*(-?\d\.\d)\] r\s*\[longitude:\s*(-?\d\.\d)\] r\s*\[rel_alt:\s*(-?\d\.\d)\s*abs_alt:\s*(-?\d\.\d)\] r\s*\[gb_yaw:\s*(-?\d\.\d)\s*gb_pitch:\s*(-?\d\.\d)\s*gb_roll:\s*(-?\d\.\d)\] ) flight_data [] for line in data_lines: match pattern.search(line) if match: lat, lon, rel_alt, abs_alt, yaw, pitch, roll map(float, match.groups()) flight_data.append({ latitude: lat, longitude: lon, relative_altitude: rel_alt, absolute_altitude: abs_alt, yaw: yaw, pitch: pitch, roll: roll, timestamp: len(flight_data) # 简单的序列编号作为时间参考 }) return flight_data提示正则表达式虽然看起来复杂但它能精准匹配数据格式避免因字符串分割位置变化导致的解析错误。建议将模式字符串单独定义方便后续调整。2.3 数据验证与清洗在实际应用中原始数据可能存在缺失或异常我们需要增加校验逻辑def validate_flight_data(flight_data: List[Dict]) - List[Dict]: 验证并清洗飞行数据 valid_data [] for point in flight_data: # 检查坐标是否在合理范围内 if not (-90 point[latitude] 90 and -180 point[longitude] 180): continue # 检查高度是否为合理正值 if point[absolute_altitude] -100 or point[absolute_altitude] 10000: continue # 检查姿态角范围 if not (-180 point[yaw] 180 and -90 point[pitch] 90 and -90 point[roll] 90): continue valid_data.append(point) return valid_data2.4 导出为JSON格式最后将处理好的数据导出为JSON文件def export_to_json(flight_data: List[Dict], output_path: str): 将飞行数据导出为JSON文件 with open(output_path, w, encodingutf-8) as f: json.dump(flight_data, f, indent2, ensure_asciiFalse)整合以上步骤的完整流程def process_srt_file(input_path: str, output_path: str): SRT文件处理完整流程 data_lines read_srt_file(input_path) flight_data parse_srt_data(data_lines) valid_data validate_flight_data(flight_data) export_to_json(valid_data, output_path) print(f成功处理{len(valid_data)}条飞行数据已保存至{output_path})3. 数据增强与高级处理基础的位置和姿态数据已经能提供很多信息但我们可以进一步丰富数据集使其更适合高级分析。3.1 计算飞行速度与方向利用连续数据点之间的变化我们可以估算无人机的运动状态def calculate_velocity(flight_data: List[Dict]) - List[Dict]: 计算点与点之间的速度和方向 from math import sqrt, atan2, degrees EARTH_RADIUS 6371000 # 地球半径(米) for i in range(1, len(flight_data)): prev flight_data[i-1] curr flight_data[i] # 计算水平距离(简化版Haversine公式) dlat radians(curr[latitude] - prev[latitude]) dlon radians(curr[longitude] - prev[longitude]) a sin(dlat/2)**2 cos(radians(prev[latitude])) * cos(radians(curr[latitude])) * sin(dlon/2)**2 c 2 * atan2(sqrt(a), sqrt(1-a)) distance EARTH_RADIUS * c # 计算高度变化 dalt curr[absolute_altitude] - prev[absolute_altitude] # 计算总位移(3D) displacement sqrt(distance**2 dalt**2) # 假设每秒一个数据点(实际应根据时间戳计算) velocity displacement / 1.0 # m/s # 计算航向角 heading degrees(atan2(dlon, dlat)) # 更新当前点数据 curr.update({ speed: velocity, heading: heading, distance_from_previous: displacement }) return flight_data3.2 数据平滑处理原始数据可能存在传感器噪声我们可以应用简单的滑动平均滤波def smooth_flight_data(flight_data: List[Dict], window_size: int 3) - List[Dict]: 对飞行数据进行平滑处理 if window_size 1 or len(flight_data) window_size: return flight_data smoothed_data [] for i in range(len(flight_data)): start max(0, i - window_size) end min(len(flight_data), i window_size 1) window flight_data[start:end] smoothed_point flight_data[i].copy() for key in [latitude, longitude, absolute_altitude, yaw, pitch, roll]: if key in flight_data[0]: values [p[key] for p in window] smoothed_point[key] sum(values) / len(values) smoothed_data.append(smoothed_point) return smoothed_data3.3 生成GeoJSON格式为了更好的地图兼容性我们可以将数据转换为GeoJSON格式def convert_to_geojson(flight_data: List[Dict]) - Dict: 将飞行数据转换为GeoJSON格式 features [] for i, point in enumerate(flight_data): feature { type: Feature, geometry: { type: Point, coordinates: [point[longitude], point[latitude], point[absolute_altitude]] }, properties: { timestamp: point.get(timestamp, i), yaw: point[yaw], pitch: point[pitch], roll: point[roll], speed: point.get(speed, 0), heading: point.get(heading, 0) } } features.append(feature) return { type: FeatureCollection, features: features }4. 数据可视化与应用有了结构化的飞行数据我们可以将其应用到各种场景中。以下是几种典型的应用方式。4.1 使用Python进行简单可视化Matplotlib可以快速展示飞行轨迹def plot_flight_path(flight_data: List[Dict]): 绘制飞行轨迹2D图 import matplotlib.pyplot as plt lons [p[longitude] for p in flight_data] lats [p[latitude] for p in flight_data] alts [p[absolute_altitude] for p in flight_data] plt.figure(figsize(12, 6)) # 2D轨迹 plt.subplot(1, 2, 1) plt.plot(lons, lats, b-, linewidth1) plt.scatter(lons[0], lats[0], cg, markero, labelStart) plt.scatter(lons[-1], lats[-1], cr, markerx, labelEnd) plt.xlabel(Longitude) plt.ylabel(Latitude) plt.title(Flight Path (2D)) plt.legend() plt.grid(True) # 高度变化 plt.subplot(1, 2, 2) plt.plot(range(len(alts)), alts, r-, linewidth1) plt.xlabel(Time Step) plt.ylabel(Altitude (m)) plt.title(Altitude Profile) plt.grid(True) plt.tight_layout() plt.show()4.2 导入Google Earth Pro将数据转换为KML格式后可以在Google Earth中查看3D轨迹def export_to_kml(flight_data: List[Dict], output_path: str): 将飞行数据导出为KML文件 kml_template ?xml version1.0 encodingUTF-8? kml xmlnshttp://www.opengis.net/kml/2.2 Document nameFlight Path/name Style idyellowLine LineStyle color7f00ffff/color width4/width /LineStyle /Style Placemark nameFlight Path/name styleUrl#yellowLine/styleUrl LineString extrude1/extrude tessellate1/tessellate altitudeModeabsolute/altitudeMode coordinates {coordinates} /coordinates /LineString /Placemark /Document /kml coordinates \n.join( f{p[longitude]},{p[latitude]},{p[absolute_altitude]} for p in flight_data ) with open(output_path, w, encodingutf-8) as f: f.write(kml_template.format(coordinatescoordinates))4.3 在Web地图中展示使用Folium库创建交互式HTML地图def create_interactive_map(flight_data: List[Dict], output_path: str): 创建交互式飞行轨迹地图 import folium # 以第一个点为中心创建地图 center [flight_data[0][latitude], flight_data[0][longitude]] m folium.Map(locationcenter, zoom_start16, tilesStamen Terrain) # 添加轨迹线 path [[p[latitude], p[longitude]] for p in flight_data] folium.PolyLine( path, colorred, weight3, opacity0.8, tooltipFlight Path ).add_to(m) # 添加起点和终点标记 folium.Marker( locationpath[0], iconfolium.Icon(colorgreen, iconplay, prefixfa), tooltipStart Point ).add_to(m) folium.Marker( locationpath[-1], iconfolium.Icon(colorred, iconstop, prefixfa), tooltipEnd Point ).add_to(m) # 保存为HTML文件 m.save(output_path)4.4 三维姿态可视化使用Plotly展示无人机的三维姿态变化def plot_3d_orientation(flight_data: List[Dict]): 绘制三维姿态变化图 import plotly.graph_objects as go fig go.Figure() # 时间序列 time_steps range(len(flight_data)) # 创建三个姿态角的子图 fig make_subplots(rows3, cols1, shared_xaxesTrue, subplot_titles(Yaw, Pitch, Roll)) fig.add_trace( go.Scatter(xtime_steps, y[p[yaw] for p in flight_data], nameYaw), row1, col1 ) fig.add_trace( go.Scatter(xtime_steps, y[p[pitch] for p in flight_data], namePitch), row2, col1 ) fig.add_trace( go.Scatter(xtime_steps, y[p[roll] for p in flight_data], nameRoll), row3, col1 ) fig.update_layout(height600, width800, title_textDrone Orientation Over Time) fig.update_yaxes(title_textDegrees, row1, col1) fig.update_yaxes(title_textDegrees, row2, col1) fig.update_yaxes(title_textDegrees, row3, col1) fig.update_xaxes(title_textTime Step, row3, col1) fig.show()5. 实际应用案例与技巧在实际项目中处理御3e的SRT文件时会遇到各种具体问题和需求。以下是几个真实场景下的解决方案。5.1 处理大文件与性能优化当飞行时间较长时SRT文件可能变得很大。这时需要优化内存使用def process_large_srt(input_path: str, output_path: str, batch_size: int 1000): 分批处理大型SRT文件 import json def write_batch(batch, file): if not batch: return json.dump(batch, file) file.write(\n) # 换行分隔每个批次 with open(input_path, r, encodingutf-8) as infile, \ open(output_path, w, encodingutf-8) as outfile: batch [] current_group [] for line in infile: line line.strip() if not line: continue current_group.append(line) if len(current_group) 3: # 完整的SRT记录组 data_line current_group[2] parsed parse_srt_line(data_line) # 假设有parse_srt_line函数 if parsed: batch.append(parsed) current_group [] if len(batch) batch_size: write_batch(batch, outfile) batch [] # 写入最后一批数据 write_batch(batch, outfile)5.2 与视频时间轴同步将飞行数据与视频帧精确匹配def align_with_video_timeline(srt_path: str, video_fps: float) - List[Dict]: 根据视频FPS为数据点添加精确时间戳 data_lines read_srt_file(srt_path) flight_data [] for i, line in enumerate(data_lines): # 解析原始时间戳 (00:00:00,000格式) time_part line.split(])[0].split([)[-1] hh_mm_ss time_part.split(,)[0] h, m, s hh_mm_ss.split(:) seconds int(h)*3600 int(m)*60 int(s) # 计算精确时间(秒) exact_time seconds (i % video_fps) / video_fps parsed parse_srt_line(line) if parsed: parsed[video_time] exact_time flight_data.append(parsed) return flight_data5.3 异常检测与数据修复自动识别并处理数据中的异常点def detect_and_fix_anomalies(flight_data: List[Dict]) - List[Dict]: 检测并修复数据异常 if len(flight_data) 3: return flight_data fixed_data flight_data.copy() # 检测位置突变(可能GPS信号丢失) for i in range(1, len(flight_data)-1): prev flight_data[i-1] curr flight_data[i] next_ flight_data[i1] # 计算两点间距离 def distance(p1, p2): return ((p2[longitude]-p1[longitude])**2 (p2[latitude]-p1[latitude])**2)**0.5 d_prev_curr distance(prev, curr) d_curr_next distance(curr, next_) d_prev_next distance(prev, next_) # 如果当前点明显偏离前后点可能是异常 if d_prev_curr 0.0002 and d_curr_next 0.0002 and d_prev_next 0.0001: # 用前后点的平均值修复当前点 fixed_data[i][longitude] (prev[longitude] next_[longitude]) / 2 fixed_data[i][latitude] (prev[latitude] next_[latitude]) / 2 fixed_data[i][absolute_altitude] (prev[absolute_altitude] next_[absolute_altitude]) / 2 return fixed_data5.4 与DJI Fly App数据交叉验证将SRT数据与DJI Fly App生成的飞行记录CSV进行对比def compare_with_dji_fly_csv(srt_data: List[Dict], csv_path: str): 将SRT数据与DJI Fly App导出的CSV进行对比 import pandas as pd # 读取CSV文件 df pd.read_csv(csv_path) # 提取关键列 csv_points [] for _, row in df.iterrows(): csv_points.append({ longitude: row[longitude], latitude: row[latitude], altitude: row[altitude(m)], timestamp: row[time(s)] }) # 简单对比数据点数量 print(fSRT数据点: {len(srt_data)}个) print(fCSV数据点: {len(csv_points)}个) # 对比起始点 print(\n起始点对比:) print(fSRT起点: {srt_data[0][latitude]}, {srt_data[0][longitude]}) print(fCSV起点: {csv_points[0][latitude]}, {csv_points[0][longitude]}) # 对比结束点 print(\n结束点对比:) print(fSRT终点: {srt_data[-1][latitude]}, {srt_data[-1][longitude]}) print(fCSV终点: {csv_points[-1][latitude]}, {csv_points[-1][longitude]})

更多文章