曲线图表头加齿轮,绘图设置对话框里可以直接调滤波 - What: ui/icons.py 新增 settings(齿轮)图标(八个齿 + 圆环,颜色取自主题 token,与另外三个图标同一套画法);force_plots.py 表头新增 btn_settings 与 plot_settings_requested 信号(放在缩放按钮之后、单位芯片之前),换主题时一并重绘图标,retranslate 里补 tooltip/可访问名;plot_settings_dialog.py 增加"滤波"分区:预设下拉(off/low_latency/balanced/low_noise + 自定义,显示的就是写进 sensor.json 的那个名字)、光流 EMA α(0.05–1.00,1.00 = 关)、中值窗口(1–31,1 = 关)、低通截止(0–60 Hz,0 = 关)、低通阶数(1–6)、采样率("自动跟随帧率"复选框 + 固定值)、"同时显示未滤波曲线";选预设即写入该预设的数值,手改任一数值自动切到"自定义",选中的预设名会随 filter_values() 一起带出(preset 字段),"_reset_defaults" 回到"什么都不滤";对话框底部按当前数值给提示:光流 EMA 打开时提示"这是测量设置、两次录制不可比"、全关时提示"当前不滤波"、录制中显示"录制中不能更改滤波"并禁用整个分区;截止频率达到或超过奈奎斯特(帧率一半)时用 dialog.filter_nyquist 明确说出"设备不会滤",其中 cutoff/nyquist/rate 三个数字由 FilterSettings.nyquist_issue() 提供(problems() 仍返回英文串供日志与测试用,判断条件只有一处)。filters.py 新增 FALLBACK_PRESETS 与 available_presets()(SDK 能导入就用 SDK 的 FILTER_PRESETS,导入不了才用这份文档副本,测试保证两者逐值一致)。app_window.py:把 plot_settings_requested 接到 _on_plot_settings,打开对话框时传入当前 _filter_settings()、filters_locked(正在跑或正在录)、以及 _device_sample_rate()(实测帧率优先,否则 30 fps,用于奈奎斯特检查);接受后把 filter_values().as_dict() 逐键写成 plot_* 偏好(与 _filter_settings() 读回的是同一组键),未在采集时立刻 set_filter_settings() 交给两个 worker,并在状态栏回报"已保存:{describe}(下次采集生效)"。两个 locale 文件补齐 21+3 个新键,中英各 343 键一一对应。tests/test_ui_smoke.py 新增 4 项:齿轮确实触发设置信号、预设/自定义切换与数值一致、录制中分区被禁用且给出理由、对话框写的键与 _filter_settings 读回的键是同一组。 - Why: 用户要求"建议曲线图增加一个齿轮的配置按键,可以调节曲线图的上限和滤波等"。此前滤波只能靠手改 preferences.json 的 plot_* 键(上一轮留下的口子),而 app 默认已改成"什么都不滤"——没有这个入口,等于把默认变成了唯一选择。 - Verification: studio 全量 89 passed(原 85 + 新增 4);shared 全量 158 passed;ui_check 554 项检查 0 失败;.repro/locale_check.py 扫描 64 个源文件里 287 个 t("…") 字面键,两个 locale 全部命中且键集合完全相同(343/343);离屏实测对话框:预设 balanced→flowEMA(0.5)+median(3)、改中值→自定义、选 low_noise→带低通、30 Hz 采样率下填 30 Hz 截止→弹出奈奎斯特警告、恢复默认→off/不滤波。 - Not in this change: CSV 的 17 列与未滤波第二 buffer(SessionStore/记录格式版本)、回放界面显示滤波前曲线、标定面板显示 filters 块、发布说明里"默认不再滤波"的措辞。

This commit is contained in:
ykcheungab
2026-09-14 11:08:25 +08:00
parent 148573934f
commit 92e0b95ca2
9 changed files with 515 additions and 16 deletions
+25
View File
@@ -56,6 +56,28 @@
"plot_shear_raw_max": "Shear raw max",
"plot_shear_raw_max_tip": "Shear-force axis ±max for FX/FY in raw (uncalibrated) mode. 0 = auto range",
"reset_defaults": "Reset to defaults",
"plot_filters": "Filters (applied while acquiring; screen and record agree)",
"filter_preset": "Preset",
"filter_preset_tip": "A ready-made combination; editing any value below switches to Custom",
"filter_preset_custom": "Custom",
"filter_flow_ema": "Flow EMA α",
"filter_flow_ema_tip": "Temporal smoothing of the optical flow. 1.00 = off. It changes what is *measured* (contact statistics and forces along with it), so two recordings with different α are not comparable — only use it when the flow jitter really has to go",
"filter_median": "Median window",
"filter_median_tip": "Sliding-median window over the force readings (samples). 1 = off. Removes single-sample spikes at the cost of about half a window of delay",
"filter_lowpass": "Low-pass cutoff",
"filter_lowpass_tip": "Low-pass cutoff for the force readings (Hz). 0 = off. Must stay below Nyquist (half the frame rate) or the device will not filter at all",
"filter_order": "Low-pass order",
"filter_order_tip": "Low-pass filter order (1–6). Steeper, with more phase distortion",
"filter_rate": "Sample rate",
"filter_rate_tip": "The rate the low-pass coefficients are designed for (Hz)",
"filter_rate_auto": "Follow the measured frame rate",
"filter_rate_auto_tip": "Design the low-pass for the device's measured rate; clear it to pin a number",
"filter_show_unfiltered": "Also show the unfiltered curves",
"filter_show_unfiltered_tip": "Draw the pre-filter readings beside the filtered ones, so what the filter did can be judged. Display only — the record is unaffected",
"filter_measurement_note": "The flow EMA is a *measurement* setting: changing it makes this recording incomparable with earlier ones.",
"filter_locked": "Filters cannot change while recording; this applies to the next session.",
"filter_nyquist": "Note: a {cutoff} Hz cutoff is at or above the Nyquist frequency of {nyquist} Hz (sample rate {rate} Hz) — the device would not filter. Lower the cutoff or pin a higher sample rate.",
"filter_off": "Not filtering (raw readings)",
"ok": "OK",
"cancel": "Cancel"
},
@@ -161,6 +183,8 @@
"overview_tip": "Fit the entire session; drag the overview strip to move the window",
"box_zoom": "Box zoom",
"box_zoom_tip": "Switch to rubber-band zoom (drag a region on the chart)",
"settings": "Chart settings",
"settings_tip": "Time window, axis ranges, and the acquisition filters (screen and record use the same data)",
"selection": "Selection {duration}s / {samples} samples — {detail}",
"selection_summary": "Selection {duration}s / {samples} samples",
"selection_detail": "mean {mean} · σ {std}",
@@ -333,6 +357,7 @@
"sample_invalid": "Invalid sample (#{count}): {reason} — marked invalid and excluded from the curves and from calibration",
"samples_dropped": "Record buffer is full; the oldest {count} samples were released (the full session is no longer complete)",
"run_failed": "Run failed: {error}",
"filters_saved": "Filter settings saved: {describe} (applies to the next session)",
"video_ended": "Video ended",
"camera_disconnected": "Camera disconnected",
"reset_ok": "Tracker reset",
+25
View File
@@ -56,6 +56,28 @@
"plot_shear_raw_max": "原始剪切上限",
"plot_shear_raw_max_tip": "未标定(原始)模式下的剪切力 FX/FY 纵轴 ±上限。0 = 自动量程",
"reset_defaults": "恢复默认",
"plot_filters": "滤波(作用于采集,界面与记录同步)",
"filter_preset": "预设",
"filter_preset_tip": "选择一档现成的滤波组合;改下面任一数值会变成“自定义”",
"filter_preset_custom": "自定义",
"filter_flow_ema": "光流 EMA α",
"filter_flow_ema_tip": "光流的时间平滑。1.00 = 关。它改变的是“测量本身”(接触统计与力值都会变),两个 α 不同的录制不可比 —— 只有确实需要压光流抖动时才调",
"filter_median": "中值窗口",
"filter_median_tip": "力读数的滑动中值窗口(采样数)。1 = 关。去掉单点尖峰,代价是约半个窗口的延迟",
"filter_lowpass": "低通截止",
"filter_lowpass_tip": "力读数低通截止频率(Hz)。0 = 关。必须在奈奎斯特频率(帧率的一半)以下,否则设备不会滤",
"filter_order": "低通阶数",
"filter_order_tip": "低通滤波器阶数(1–6)。越高越陡,相位失真也越大",
"filter_rate": "采样率",
"filter_rate_tip": "低通系数按哪个采样率设计(Hz)",
"filter_rate_auto": "自动跟随帧率",
"filter_rate_auto_tip": "按设备实测帧率设计低通系数;取消可固定一个数值",
"filter_show_unfiltered": "同时显示未滤波曲线",
"filter_show_unfiltered_tip": "在力值卡片上把滤波前的读数一并显示,便于判断滤波到底做了什么。只影响显示,不影响记录",
"filter_measurement_note": "光流 EMA 属于“测量”设置:改了它,本次录制与之前的不可比。",
"filter_locked": "录制中不能更改滤波;下一次采集生效。",
"filter_nyquist": "注意:低通 {cutoff} Hz 已达到或超过奈奎斯特频率 {nyquist} Hz(采样率 {rate} Hz)——设备不会滤。请调低截止频率或固定更高的采样率。",
"filter_off": "当前不滤波(原始读数)",
"ok": "确定",
"cancel": "取消"
},
@@ -161,6 +183,8 @@
"overview_tip": "把整个会话缩放到一屏;拖动下方总览条可移动视窗",
"box_zoom": "框选",
"box_zoom_tip": "切换为框选放大(拖动图表框选区域)",
"settings": "曲线图设置",
"settings_tip": "时间窗口、纵轴量程,以及采集端滤波(界面与记录用同一份数据)",
"selection": "选区 {duration}s / {samples} 点 — {detail}",
"selection_summary": "选区 {duration}s / {samples} 点",
"selection_detail": "均 {mean} · σ {std}",
@@ -333,6 +357,7 @@
"sample_invalid": "采样无效(第 {count} 次):{reason} — 该样本已标记无效,未计入曲线与标定",
"samples_dropped": "记录缓冲已达上限,最早的 {count} 个样本已被释放(全程记录不再完整)",
"run_failed": "启动失败: {error}",
"filters_saved": "滤波设置已保存:{describe}(下次采集生效)",
"video_ended": "视频已结束",
"camera_disconnected": "摄像机已断开",
"reset_ok": "追踪器已重置",