"""一次运行四个轨迹实验绘图脚本。""" from __future__ import annotations import argparse import os import subprocess import sys from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path SCRIPT_NAMES = ( "plot_trajectory_comparison.py", "plot_tracking_errors.py", "plot_speed_response.py", "plot_angular_command.py", ) def build_command( script_path: Path, files: list[str], frequency_hz: float, filter_window_seconds: float, output_directory: str | None, show: bool, ) -> list[str]: """为一个绘图脚本构造与统一入口一致的命令行参数。""" command = [ sys.executable, str(script_path), *files, "--frequency", str(frequency_hz), "--window", str(filter_window_seconds), ] if output_directory: command.extend(["--output-dir", output_directory]) if show: command.append("--show") return command def run_script( script_path: Path, files: list[str], frequency_hz: float, filter_window_seconds: float, output_directory: str | None, show: bool, ) -> tuple[str, int, str, str]: """运行一个绘图脚本并返回名称、退出码及标准输出和错误。""" result = subprocess.run( build_command( script_path, files, frequency_hz, filter_window_seconds, output_directory, show, ), cwd=script_path.parent, capture_output=True, text=True, encoding="utf-8", errors="replace", env={ **os.environ, "PYTHONIOENCODING": "utf-8", }, check=False, ) return ( script_path.name, result.returncode, result.stdout.strip(), result.stderr.strip(), ) def main() -> None: """并行执行四类实验图的生成任务。""" parser = argparse.ArgumentParser( description="一次生成轨迹、误差、速度响应和角速度指令四类图。" ) parser.add_argument( "files", nargs="*", help="一个或多个CSV文件;省略时处理data_process目录下全部CSV。", ) parser.add_argument( "--frequency", type=float, default=20.0, help="固定重采样频率,默认20 Hz。", ) parser.add_argument( "--window", type=float, default=0.55, help="Savitzky-Golay滤波窗口,默认0.55 s。", ) parser.add_argument( "--output-dir", help="图片输出目录;省略时由各绘图脚本使用默认目录。", ) parser.add_argument( "--show", action="store_true", help="生成后请求显示图片。", ) args = parser.parse_args() if args.frequency <= 0.0: parser.error("--frequency必须大于0。") if args.window <= 0.0: parser.error("--window必须大于0。") script_directory = Path(__file__).resolve().parent script_paths = [ script_directory / name for name in SCRIPT_NAMES ] missing_scripts = [ str(path) for path in script_paths if not path.is_file() ] if missing_scripts: parser.error( "缺少绘图脚本:" + ",".join(missing_scripts) ) print("开始并行生成四类实验图……") failures: list[str] = [] with ThreadPoolExecutor( max_workers=len(script_paths) ) as executor: futures = [ executor.submit( run_script, script_path, args.files, args.frequency, args.window, args.output_dir, args.show, ) for script_path in script_paths ] for future in as_completed(futures): script_name, return_code, stdout, stderr = ( future.result() ) print(f"\n[{script_name}]") if stdout: print(stdout) if stderr: print(stderr, file=sys.stderr) if return_code == 0: print("执行成功。") else: failures.append(script_name) print( f"执行失败,退出码={return_code}。", file=sys.stderr, ) if failures: print( "\n以下脚本执行失败:" + ",".join(failures), file=sys.stderr, ) raise SystemExit(1) print("\n四类实验图均已生成。") if __name__ == "__main__": main()