generate_samples.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. """Generate per-cycle pretraining samples from the wave database.
  2. Reads 预训练之文件头.csv, groups the files by (point_name, measurement_type),
  3. and for every wave file runs the project's cycle detector. Each detected cycle
  4. is saved as one .npy matrix with columns
  5. [signal_value, second_value, 1, 1]
  6. in time order. The file name is
  7. {wave_file_id}_{cycle_number}_{start_sample_index}_{end_sample_index_exclusive}.npy
  8. """
  9. import csv
  10. import sys
  11. import traceback
  12. from collections import defaultdict
  13. from pathlib import Path
  14. import numpy as np
  15. from tqdm import tqdm
  16. ROOT = Path(__file__).resolve().parents[1]
  17. sys.path.insert(0, str(ROOT))
  18. from app.algorithms.cycles import detect_cycles # noqa: E402
  19. from app.db import get_connection # noqa: E402
  20. HERE = Path(__file__).resolve().parent
  21. CSV_PATH = ROOT / "预训练之文件头.csv"
  22. OUTPUT_DIR = HERE / "inputdatas"
  23. MANIFEST_PATH = HERE / "manifest.csv"
  24. def load_file_rows() -> list[dict[str, str]]:
  25. with CSV_PATH.open(encoding="utf-8-sig") as handle:
  26. return list(csv.DictReader(handle))
  27. def read_wave_samples(connection, file_id: int) -> np.ndarray:
  28. with connection.cursor() as cursor:
  29. cursor.execute(
  30. """
  31. SELECT sample_index, signal_value, second_value
  32. FROM wave_sample
  33. WHERE wave_file_id = %s
  34. ORDER BY wave_file_id, sample_index
  35. """,
  36. (file_id,),
  37. )
  38. rows = cursor.fetchall()
  39. return np.asarray(
  40. [
  41. (
  42. float(row["sample_index"]),
  43. float(row["signal_value"]),
  44. float(row["second_value"]) if row["second_value"] is not None else np.nan,
  45. )
  46. for row in rows
  47. ],
  48. dtype=float,
  49. )
  50. def build_cycle_matrix(samples: np.ndarray, start: int, end: int) -> np.ndarray:
  51. signal = samples[start:end, 1]
  52. second = samples[start:end, 2]
  53. count = end - start
  54. return np.column_stack([signal, second, np.ones(count), np.ones(count)])
  55. def cycle_sample_bounds(samples: np.ndarray, start_offset: int, end_offset: int) -> tuple[int, int]:
  56. start_sample_index = int(samples[start_offset, 0])
  57. if end_offset < len(samples):
  58. end_exclusive = int(samples[end_offset, 0])
  59. else:
  60. end_exclusive = int(samples[-1, 0]) + 1
  61. return start_sample_index, end_exclusive
  62. def main() -> int:
  63. rows = load_file_rows()
  64. if not rows:
  65. print("CSV 为空,退出")
  66. return 1
  67. groups: dict[tuple[str, str], list[dict[str, str]]] = defaultdict(list)
  68. for row in rows:
  69. groups[(row["point_name"], row["measurement_type"])].append(row)
  70. manifest: list[dict[str, object]] = []
  71. total_cycles = 0
  72. connection = get_connection()
  73. try:
  74. group_progress = tqdm(sorted(groups.items()), desc="分组处理", unit="组")
  75. for group_index, (group_key, group_rows) in enumerate(group_progress, start=1):
  76. point_name, measurement_type = group_key
  77. folder = OUTPUT_DIR / f"{point_name}_{measurement_type}"
  78. folder.mkdir(parents=True, exist_ok=True)
  79. group_progress.set_postfix(group=f"{point_name}_{measurement_type}")
  80. for row in tqdm(
  81. sorted(group_rows, key=lambda item: int(item["id"])),
  82. desc=f" {point_name}_{measurement_type}",
  83. unit="文件",
  84. leave=False,
  85. ):
  86. file_id = int(row["id"])
  87. manifest_row: dict[str, object] = {
  88. "group": f"{point_name}_{measurement_type}",
  89. "point_name": point_name,
  90. "measurement_type": measurement_type,
  91. "wave_file_id": file_id,
  92. "rpm": row["rpm"],
  93. "sample_time": row["sample_time"],
  94. "cycle_count": 0,
  95. "total_samples": 0,
  96. "start_sample_index": "",
  97. "end_sample_index": "",
  98. "error": "",
  99. }
  100. try:
  101. samples = read_wave_samples(connection, file_id)
  102. if len(samples) == 0:
  103. raise ValueError("没有采样数据")
  104. cycles, diagnostics = detect_cycles(samples)
  105. manifest_row["cycle_count"] = len(cycles)
  106. saved = 0
  107. total_cycle_rows = 0
  108. first_start = ""
  109. last_end = ""
  110. for cycle in cycles:
  111. start_sample_index, end_exclusive = cycle_sample_bounds(
  112. samples, cycle.start_offset, cycle.end_offset
  113. )
  114. if not first_start:
  115. first_start = str(start_sample_index)
  116. last_end = str(end_exclusive)
  117. matrix = build_cycle_matrix(
  118. samples, cycle.start_offset, cycle.end_offset
  119. )
  120. if not np.all(np.isfinite(matrix[:, :2])):
  121. raise ValueError("周期内存在非有限值")
  122. path = folder / f"{file_id}_{cycle.number}_{start_sample_index}_{end_exclusive}.npy"
  123. np.save(path, matrix)
  124. saved += 1
  125. total_cycle_rows += len(matrix)
  126. total_cycles += saved
  127. manifest_row["cycle_count"] = saved
  128. manifest_row["total_samples"] = total_cycle_rows
  129. manifest_row["start_sample_index"] = first_start
  130. manifest_row["end_sample_index"] = last_end
  131. if saved == 0:
  132. manifest_row["error"] = (
  133. f"未检出完整周期 (触发脉冲 {diagnostics.get('triggerRunCount', '?')} 个, "
  134. f"零标记 {diagnostics.get('zeroMarkerCount', '?')} 个)"
  135. )
  136. except Exception as error:
  137. manifest_row["error"] = f"{error}: {traceback.format_exc(limit=1).strip()}"
  138. manifest.append(manifest_row)
  139. if manifest_row["error"]:
  140. print(f" id={file_id} 失败: {manifest_row['error'][:160]}")
  141. finally:
  142. connection.close()
  143. with MANIFEST_PATH.open("w", encoding="utf-8-sig", newline="") as handle:
  144. writer = csv.DictWriter(
  145. handle,
  146. fieldnames=[
  147. "group",
  148. "point_name",
  149. "measurement_type",
  150. "wave_file_id",
  151. "rpm",
  152. "sample_time",
  153. "cycle_count",
  154. "total_samples",
  155. "start_sample_index",
  156. "end_sample_index",
  157. "error",
  158. ],
  159. )
  160. writer.writeheader()
  161. writer.writerows(manifest)
  162. ok = sum(1 for item in manifest if not item["error"])
  163. failed = sum(1 for item in manifest if item["error"])
  164. print(f"\n完成: {ok} 个文件成功, {failed} 个失败, 共生成 {total_cycles} 个周期样本")
  165. return 0
  166. if __name__ == "__main__":
  167. sys.exit(main())