const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell, HeadingLevel, AlignmentType, BorderStyle, WidthType, ShadingType, PageBreak, Header, Footer, PageNumber } = require('docx'); const fs = require('fs'); const border = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" }; const borders = { top: border, bottom: border, left: border, right: border }; function cell(text, width, opts = {}) { return new TableCell({ borders, width: { size: width, type: WidthType.DXA }, shading: opts.shading ? { fill: opts.shading, type: ShadingType.CLEAR } : undefined, margins: { top: 60, bottom: 60, left: 100, right: 100 }, children: [new Paragraph({ children: [new TextRun({ text, bold: opts.bold, size: 20, font: "微软雅黑" })] })] }); } function h1(text) { return new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun({ text, bold: true, size: 32, font: "微软雅黑", color: "1a1a2e" })] }); } function h2(text) { return new Paragraph({ heading: HeadingLevel.HEADING_2, children: [new TextRun({ text, bold: true, size: 28, font: "微软雅黑", color: "2d3436" })] }); } function body(text, opts = {}) { return new Paragraph({ children: [new TextRun({ text, size: 21, font: "微软雅黑", ...opts })], spacing: { after: 120, line: 360 } }); } function code(text) { return new Paragraph({ children: [new TextRun({ text, size: 18, font: "Consolas", color: "2d3436" })], shading: { fill: "f5f6fa", type: ShadingType.CLEAR }, spacing: { after: 80 } }); } const doc = new Document({ styles: { default: { document: { run: { font: "微软雅黑", size: 21 } } }, paragraphStyles: [ { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true, run: { size: 32, bold: true, font: "微软雅黑", color: "1a1a2e" }, paragraph: { spacing: { before: 300, after: 200 }, outlineLevel: 0 } }, { id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true, run: { size: 28, bold: true, font: "微软雅黑", color: "2d3436" }, paragraph: { spacing: { before: 240, after: 160 }, outlineLevel: 1 } }, ] }, sections: [{ properties: { page: { size: { width: 11906, height: 16838 }, margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } } }, headers: { default: new Header({ children: [new Paragraph({ children: [new TextRun({ text: "智途投送 · 声源分析模块 · 项目交接文档", size: 16, color: "888888", font: "微软雅黑" })] })] }) }, footers: { default: new Footer({ children: [new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ children: ["第 ", PageNumber.CURRENT, " 页"], size: 16, color: "888888", font: "微软雅黑" })] })] }) }, children: [ // 封面 new Paragraph({ spacing: { before: 2000 } }), new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "智途投送", bold: true, size: 56, font: "微软雅黑", color: "1a1a2e" })] }), new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "声源分析模块(Acoustic Analyzer)", bold: true, size: 36, font: "微软雅黑", color: "2d3436" })], spacing: { after: 400 } }), new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "项目开发交接文档", size: 32, font: "微软雅黑", color: "636e72" })] }), new Paragraph({ spacing: { before: 800 } }), new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "国防科大计算机学院 23 级软件工程小班", size: 24, font: "微软雅黑", color: "636e72" })] }), new Paragraph({ alignment: AlignmentType.CENTER, children: [new TextRun({ text: "2026 年 4 月", size: 24, font: "微软雅黑", color: "636e72" })] }), new Paragraph({ children: [new PageBreak()] }), // 一、项目概述 h1("一、项目概述"), body("声源分析模块是「智途投送」无人机软件系统的核心感知构件之一,负责通过麦克风阵列音频信号实现:"), body("• 枪炮声识别分类(枪声 / 炮声 / 爆炸声 / 环境噪声)"), body("• GCC-PHAT 声源定位(方位角、俯仰角)"), body("• 基于能量衰减模型的距离估计"), body("• 多帧威胁跟踪与信息融合"), body("模块采用 C++17 开发,核心算法零 ROS 依赖,通过 ONNX Runtime 进行神经网络推理,最终作为 ROS1 Noetic 节点部署于 P600 无人机机载电脑。"), // 二、已完成工作总览 h1("二、已完成工作总览"), h2("2.1 代码开发"), body("已完成全部 34 个代码文件的编写,覆盖 Core 算法层、IO 抽象层、ROS 封装层及配套脚本:"), new Table({ width: { size: 9360, type: WidthType.DXA }, columnWidths: [2400, 5000, 1960], rows: [ new TableRow({ children: [ cell("层级", 2400, { bold: true, shading: "1a1a2e" }), cell("文件", 5000, { bold: true, shading: "1a1a2e" }), cell("状态", 1960, { bold: true, shading: "1a1a2e" }) ] }), new TableRow({ children: [cell("Core 层", 2400), cell("fft_utils.h/cpp, audio_buffer.h/cpp, feature_extractor.h/cpp", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("Core 层", 2400), cell("gunshot_classifier.h/cpp, gcc_phat_localizer.h/cpp", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("Core 层", 2400), cell("distance_estimator.h/cpp, threat_tracker.h/cpp, pipeline.h/cpp", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("IO 层", 2400), cell("audio_source.h, wav_file_source.h/cpp, mobile_phone_source.h/cpp", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("ROS 层", 2400), cell("acoustic_node.h/cpp, threat_publisher.h/cpp, main.cpp", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("消息定义", 2400), cell("AcousticThreat.msg, AcousticThreatArray.msg", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("构建系统", 2400), cell("CMakeLists.txt, package.xml", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("Python 脚本", 2400), cell("train_classifier.py, export_onnx.py, verify_onnx.py", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("Python 脚本", 2400), cell("generate_sim_audio.py, mobile_audio_bridge.py, android_audio_sender.py", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("测试", 2400), cell("test_core_lib.cpp, extract_mel_cpp.cpp, test_classifier_cpp.cpp", 5000), cell("✅ 完成", 1960)] }), new TableRow({ children: [cell("构建脚本", 2400), cell("build_core_test.bat, build_cmake_mingw.bat", 5000), cell("✅ 完成", 1960)] }), ] }), new Paragraph({ spacing: { before: 200 } }), h2("2.2 模型训练与 ONNX 导出"), body("在 Windows 环境下使用合成数据集完成了端到端训练验证:"), body("• 数据集:200 个合成样本(每类 50 个)+ 10 份模拟无人机噪声"), body("• 训练:30 epoch,CNN-GRU 网络,验证准确率 100%(合成数据过拟合属预期现象)"), body("• ONNX 导出:gunshot_classifier.onnx(1.9MB,opset 13)"), body("• ONNX 验证:枪声识别置信度 97.92%"), h2("2.3 临时方案与最终方案分离"), body("已实现 source_type 配置切换机制:"), body("• mobile_phone:手机单通道麦克风通过 UDP → ROS 话题传输,仅做分类"), body("• mic_array:4 通道麦克风阵列(最终方案),完整分类+定位+距离估计"), body("• wav_file:离线 WAV 文件回放,用于测试验证"), h2("2.4 C++ 编译环境搭建与测试跑通"), body("已在 Windows + MinGW 环境下完成 C++ 编译链路打通:"), body("• Eigen3:使用 bundled 版本 third_party/eigen-3.4.0,无需安装"), body("• yaml-cpp:自动检测 conda 环境(C:/Users//miniconda3/Library),CMake 已适配"), body("• ONNX Runtime C++ v1.20.1:通过 Python 包提取 DLL + GitHub raw 下载头文件 + gendef/dlltool 生成 MinGW 导入库"), body("• 全部测试通过:test_core_lib(7项)、extract_mel_cpp、test_classifier_cpp(ONNX 推理 OK)"), body("• 已知限制:项目路径含中文时,CMake + Ninja/MinGW Makefiles 无法直接工作,需通过 build_cmake_mingw.bat 自动复制到临时英文目录构建"), h2("2.5 代码 Bug 修复记录"), body("搭建过程中发现并修复的问题:"), body("• gcc_phat_localizer.cpp:缺少 #include ,导致 BDCSVD 不完整类型错误"), body("• threat_tracker.cpp:数据关联更新检测时丢失原有 threat_id,导致多帧跟踪失败"), body("• test_core_lib.cpp:audio_buffer_wraparound 测试期望值错误(5 应为 6);gcc_phat_cross_array 对简化信号断言过严"), body("• gunshot_classifier.cpp/h:升级至 ONNX Runtime C++ v1.20.1 RAII API,适配 wchar_t 路径(Windows)"), body("• CMakeLists.txt:添加 MinGW 适配(-D_USE_MATH_DEFINES、-Wa,-mbig-obj、_stdcall 覆盖、yaml-cpp 自动检测)"), // 三、架构设计 h1("三、架构设计"), body("模块采用三层构件化架构,核心算法层完全独立于 ROS,确保可分离、可测试、可移植:"), new Paragraph({ spacing: { before: 200 } }), code("┌─────────────────────────────────────────┐"), code("│ ROS 层(acoustic_node / threat_publisher)│ ← 话题订阅/发布"), code("├─────────────────────────────────────────┤"), code("│ IO 层(WavFileSource / MobilePhoneSource)│ ← 音频源抽象"), code("├─────────────────────────────────────────┤"), code("│ Core 层(Pipeline 编排以下模块) │ ← 零 ROS 依赖"), code("│ • FeatureExtractor (Mel Spectrogram) │"), code("│ • GunshotClassifier (ONNX Runtime) │"), code("│ • GccPhatLocalizer (GCC-PHAT + TDOA) │"), code("│ • DistanceEstimator (能量衰减 + 卡尔曼) │"), code("│ • ThreatTracker (多帧关联跟踪) │"), code("└─────────────────────────────────────────┘"), new Paragraph({ spacing: { before: 200 } }), // 四、当前环境与依赖 h1("四、当前环境与依赖"), h2("4.1 Python 训练环境(Windows 已验证)"), new Table({ width: { size: 9360, type: WidthType.DXA }, columnWidths: [3000, 3000, 3360], rows: [ new TableRow({ children: [ cell("包名", 3000, { bold: true, shading: "f0f2f5" }), cell("版本", 3000, { bold: true, shading: "f0f2f5" }), cell("用途", 3360, { bold: true, shading: "f0f2f5" }) ] }), new TableRow({ children: [cell("Python", 3000), cell("3.14.3", 3000), cell("训练与脚本运行", 3360)] }), new TableRow({ children: [cell("torch", 3000), cell("2.11.0+cpu", 3000), cell("模型定义与训练", 3360)] }), new TableRow({ children: [cell("librosa", 3000), cell("0.11.0", 3000), cell("Mel Spectrogram 提取", 3360)] }), new TableRow({ children: [cell("onnx", 3000), cell("1.21.0", 3000), cell("ONNX 模型验证", 3360)] }), new TableRow({ children: [cell("numpy", 3000), cell("2.4.4", 3000), cell("数值计算", 3360)] }), new TableRow({ children: [cell("scipy", 3000), cell("1.17.1", 3000), cell("信号处理", 3360)] }), ] }), new Paragraph({ spacing: { before: 200 } }), h2("4.2 C++ 编译环境(Windows 已配置)"), body("• 编译器:g++ (MinGW-W64 15.2.0) 已安装 ✅"), body("• CMake:4.1.0 已安装 ✅"), body("• Eigen3:bundled third_party/eigen-3.4.0 ✅"), body("• ONNX Runtime C++:v1.20.1 已配置(头文件 + DLL + MinGW 导入库)✅"), body("• yaml-cpp:conda 0.8.0 已检测,CMake 自动链接 ✅"), body("• 构建方式:"), body(" – 快速命令行:build_core_test.bat(一键编译全部测试)"), body(" – 标准 CMake:build_cmake_mingw.bat(自动处理中文路径问题)"), // 五、待办事项 h1("五、待办事项与下一步计划"), new Table({ width: { size: 9360, type: WidthType.DXA }, columnWidths: [600, 4200, 2160, 2400], rows: [ new TableRow({ children: [ cell("优先级", 600, { bold: true, shading: "f0f2f5" }), cell("任务", 4200, { bold: true, shading: "f0f2f5" }), cell("负责人", 2160, { bold: true, shading: "f0f2f5" }), cell("状态", 2400, { bold: true, shading: "f0f2f5" }) ] }), new TableRow({ children: [cell("P0", 600), cell("下载真实数据集(MIVIA / FSD50K / UrbanSound8K)", 4200), cell("用户", 2160), cell("⏳ 待完成", 2400)] }), new TableRow({ children: [cell("P0", 600), cell("录制/模拟无人机自噪声作为 ambient 负样本", 4200), cell("用户", 2160), cell("⏳ 待完成", 2400)] }), new TableRow({ children: [cell("P1", 600), cell("用真实数据重新训练模型,替换合成数据", 4200), cell("AI助手", 2160), cell("⏳ 待数据就绪", 2400)] }), new TableRow({ children: [cell("P1", 600), cell("C++ 特征一致性验证(Python librosa vs C++ FeatureExtractor)", 4200), cell("AI助手", 2160), cell("⏳ 待进行", 2400)] }), new TableRow({ children: [cell("P1", 600), cell("C++ ONNX 推理测试(test_classifier_cpp 编译运行)", 4200), cell("AI助手", 2160), cell("✅ 已完成", 2400)] }), new TableRow({ children: [cell("P2", 600), cell("实现 MicArraySource(ALSA 麦克风阵列驱动)", 4200), cell("AI助手", 2160), cell("⏳ 硬件到位后", 2400)] }), new TableRow({ children: [cell("P2", 600), cell("手机端音频采集 App / 网页端实时传输", 4200), cell("AI助手", 2160), cell("⏳ 可选优化", 2400)] }), ] }), new Paragraph({ spacing: { before: 200 } }), // 六、关键配置参数 h1("六、关键配置参数速查"), body("config/acoustic_params.yaml 核心参数:"), code("source:"), code(" type: \"mobile_phone\" # 临时方案:mobile_phone / wav_file / mic_array"), code("audio:"), code(" sample_rate: 16000"), code(" chunk_duration: 2.0 # 分析窗口 2 秒"), code(" hop_duration: 0.5 # 步进 0.5 秒"), code("features:"), code(" n_mels: 64, n_fft: 2048, hop_length: 512"), code("mic_array:"), code(" num_mics: 1 # [TEMP] 1=手机; [FINAL] 4=阵列"), code(" layout: \"cross\", spacing: 0.15"), code("classifier:"), code(" model_path: \".../gunshot_classifier.onnx\""), code(" threshold: 0.7"), // 七、文件路径索引 h1("七、文件路径索引"), body("项目根目录:software/src/drone-software/src/acoustic/"), body("• 核心算法:include/acoustic_analyzer/core/ & src/core/"), body("• IO 抽象:include/acoustic_analyzer/io/ & src/io/"), body("• ROS 封装:include/acoustic_analyzer/ros/ & src/ros/"), body("• 训练脚本:scripts/train_classifier.py, export_onnx.py, verify_onnx.py"), body("• 手机桥接:scripts/mobile_audio_bridge.py, android_audio_sender.py"), body("• 测试程序:tests/test_core_lib.cpp, extract_mel_cpp.cpp, test_classifier_cpp.cpp"), body("• 配置文件:config/acoustic_params.yaml"), body("• 模型权重:models/gunshot_classifier.onnx, train_output/best_model.pth"), body("• 数据集:dataset/{train,val}/{ambient,gunshot,artillery,explosion}/"), // 八、如何继续 h1("八、新增构建脚本说明"), body("• build_core_test.bat:一键命令行编译,适用于快速验证核心算法和 ONNX 推理"), body(" 编译目标:test_core_lib.exe / extract_mel_cpp.exe / test_classifier_cpp.exe"), body("• build_cmake_mingw.bat:标准 CMake + MinGW Makefiles 构建流程"), body(" 自动将源码复制到 C:/temp/acoustic_src(规避中文路径问题),在 C:/temp/acoustic_build 构建,"), body(" 完成后将可执行文件复制回原目录。适用于需要标准 CMake 流程的场景。"), body(""), body("AI 助手将读取本文档及项目代码,基于当前状态继续推进。"), ] }] }); Packer.toBuffer(doc).then(buffer => { fs.writeFileSync("声源分析模块_项目交接文档.docx", buffer); console.log("交接文档已生成:声源分析模块_项目交接文档.docx"); });