Files

173 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""绘制横向误差和航向误差随时间变化图。"""
from __future__ import annotations
import argparse
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from plot_trajectory_comparison import (
build_reference,
configure_matplotlib,
discover_csv_files,
load_and_resample,
output_path,
shade_localization_jump_windows,
)
def plot_errors(
csv_path: Path,
frequency_hz: float,
filter_window_seconds: float,
output_directory: str | None,
show: bool,
) -> Path:
"""生成单份CSV的横向/航向误差图。"""
frame, metadata = load_and_resample(
csv_path,
frequency_hz,
filter_window_seconds,
)
reference = build_reference(frame, metadata)
time = frame["TimeSeconds"].to_numpy()
lateral = np.asarray(reference["lateral_error_mm"])
heading = np.asarray(reference["heading_error_degrees"])
invalid = frame[
"InvalidNearLocalizationJump"
].to_numpy(dtype=bool)
lateral_for_statistics = lateral.copy()
heading_for_statistics = heading.copy()
lateral_for_statistics[invalid] = np.nan
heading_for_statistics[invalid] = np.nan
lateral_rmse = float(
np.sqrt(np.nanmean(lateral_for_statistics**2))
)
heading_rmse = float(
np.sqrt(np.nanmean(heading_for_statistics**2))
)
lateral_max = float(
np.nanmax(np.abs(lateral_for_statistics))
)
heading_max = float(
np.nanmax(np.abs(heading_for_statistics))
)
is_in_place_rotation = (
reference["kind"] == "in_place_rotation"
)
fig, axes = plt.subplots(
2,
1,
figsize=(10.0, 7.0),
sharex=True,
)
axes[0].plot(time, lateral, linewidth=1.5)
axes[0].axhline(0.0, color="black", linewidth=0.8)
if is_in_place_rotation:
axes[0].set_ylabel("旋转中心位置漂移 / mm")
axes[0].set_title(
f"原地自转位置漂移:RMS={lateral_rmse:.2f} mm"
f"最大值={lateral_max:.2f} mm"
)
else:
axes[0].set_ylabel("横向误差 / mm")
axes[0].set_title(
f"横向误差:RMSE={lateral_rmse:.2f} mm"
f"最大绝对值={lateral_max:.2f} mm"
)
shade_localization_jump_windows(axes[0], metadata)
axes[0].grid(True, alpha=0.3)
axes[1].plot(
time,
heading,
color="tab:orange",
linewidth=1.5,
)
axes[1].axhline(0.0, color="black", linewidth=0.8)
axes[1].set_xlabel("时间 / s")
axes[1].set_ylabel(
"目标角度剩余误差 / °"
if is_in_place_rotation
else "航向误差 / °"
)
axes[1].set_title(
(
f"目标角度剩余误差:RMSE={heading_rmse:.2f}°,"
f"最大绝对值={heading_max:.2f}°"
)
if is_in_place_rotation
else (
f"航向误差:RMSE={heading_rmse:.2f}°,"
f"最大绝对值={heading_max:.2f}°"
)
)
shade_localization_jump_windows(axes[1], metadata)
axes[1].grid(True, alpha=0.3)
if metadata["localization_jump_events"]:
axes[1].legend(loc="best")
fig.suptitle(
f"横向/航向误差随时间变化\n"
f"{metadata['controller_name']} - "
f"{metadata['trajectory_name']}"
)
fig.tight_layout()
destination = output_path(
csv_path,
output_directory,
"tracking_errors",
)
fig.savefig(destination, dpi=300, bbox_inches="tight")
if show:
plt.show()
plt.close(fig)
if is_in_place_rotation:
print(
f"{csv_path.name}: position drift RMS="
f"{lateral_rmse:.3f} mm, "
f"target-angle error RMS={heading_rmse:.3f} deg"
)
else:
print(
f"{csv_path.name}: lateral RMSE="
f"{lateral_rmse:.3f} mm, "
f"heading RMSE={heading_rmse:.3f} deg"
)
return destination
def main() -> None:
configure_matplotlib()
parser = argparse.ArgumentParser(
description="绘制横向误差和航向误差随时间变化图。"
)
parser.add_argument("files", nargs="*", help="一个或多个CSV文件")
parser.add_argument("--frequency", type=float, default=20.0)
parser.add_argument("--window", type=float, default=0.55)
parser.add_argument("--output-dir")
parser.add_argument("--show", action="store_true")
args = parser.parse_args()
for csv_path in discover_csv_files(args.files):
destination = plot_errors(
csv_path,
args.frequency,
args.window,
args.output_dir,
args.show,
)
print(f"已生成:{destination}")
if __name__ == "__main__":
main()