HongzeFu commited on
Commit
4b82e7f
·
1 Parent(s): 964fd91

obs fps control

Browse files
gradio-web/config.py CHANGED
@@ -4,6 +4,8 @@
4
  # --- Configuration ---
5
  VIDEO_PLAYBACK_FPS = 30.0 # Frame rate for demonstration video playback
6
  USE_SEGMENTED_VIEW = False # Set to True to use segmented view, False to use original image
 
 
7
 
8
  # 主界面两列宽度比例 (Keypoint Selection : Right Panel)
9
  KEYPOINT_SELECTION_SCALE = 1
 
4
  # --- Configuration ---
5
  VIDEO_PLAYBACK_FPS = 30.0 # Frame rate for demonstration video playback
6
  USE_SEGMENTED_VIEW = False # Set to True to use segmented view, False to use original image
7
+ LIVE_OBS_REFRESH_HZ = 20.0 # Live observation refresh frequency in Hz
8
+ KEYFRAME_DOWNSAMPLE_FACTOR = 1 # Keep 1 frame out of every N streamed frames
9
 
10
  # 主界面两列宽度比例 (Keypoint Selection : Right Panel)
11
  KEYPOINT_SELECTION_SCALE = 1
gradio-web/gradio_callbacks.py CHANGED
@@ -31,7 +31,14 @@ from state_manager import (
31
  )
32
  from image_utils import draw_marker, save_video, concatenate_frames_horizontally
33
  from user_manager import user_manager
34
- from config import USE_SEGMENTED_VIEW, should_show_demo_video, SESSION_TIMEOUT, EXECUTE_LIMIT_OFFSET
 
 
 
 
 
 
 
35
  from process_session import ScrewPlanFailureError, ProcessSessionProxy
36
  from note_content import get_task_hint
37
 
@@ -43,6 +50,15 @@ _LIVE_OBS_REFRESH_LOCK = threading.Lock()
43
  LOGGER = logging.getLogger("robomme.callbacks")
44
 
45
 
 
 
 
 
 
 
 
 
 
46
  def _uid_for_log(uid):
47
  if not uid:
48
  return "<none>"
@@ -199,7 +215,7 @@ def switch_to_execute_phase(uid):
199
  _LIVE_OBS_REFRESH[uid] = {
200
  "frame_queue": queue.Queue(),
201
  "last_base_count": base_count,
202
- "take_next": True, # downsample x2 by enqueueing every other frame
203
  }
204
  return (
205
  gr.update(interactive=False), # options_radio
@@ -233,14 +249,14 @@ def _get_live_obs_refresh_state(uid, base_count=0):
233
  _LIVE_OBS_REFRESH[uid] = {
234
  "frame_queue": queue.Queue(),
235
  "last_base_count": int(base_count),
236
- "take_next": True, # downsample x2 by enqueueing every other frame
237
  }
238
  return _LIVE_OBS_REFRESH[uid]
239
 
240
 
241
  def _enqueue_live_obs_frames(uid, base_frames):
242
  """
243
- Push newly appended base_frames into per-uid FIFO queue with x2 downsampling.
244
  """
245
  if not uid:
246
  return 0
@@ -255,22 +271,22 @@ def _enqueue_live_obs_frames(uid, base_frames):
255
  with _LIVE_OBS_REFRESH_LOCK:
256
  state["frame_queue"] = queue.Queue()
257
  state["last_base_count"] = current_count
258
- state["take_next"] = True
259
  return 0
260
 
261
  if current_count <= last_count:
262
  return frame_queue.qsize()
263
 
264
  new_frames = frames[last_count:current_count]
265
- take_next = bool(state.get("take_next", True))
266
  for frame in new_frames:
267
- if take_next and frame is not None:
268
  frame_queue.put(frame)
269
- take_next = not take_next
270
 
271
  with _LIVE_OBS_REFRESH_LOCK:
272
  state["last_base_count"] = current_count
273
- state["take_next"] = take_next
274
  return frame_queue.qsize()
275
 
276
 
@@ -285,8 +301,8 @@ def _wait_for_live_obs_queue_drain(uid, max_wait_sec=None, empty_grace_sec=0.2,
285
  queue0 = state0.get("frame_queue") if state0 else None
286
  initial_qsize = int(queue0.qsize()) if queue0 is not None else 0
287
  if max_wait_sec is None:
288
- # 0.1s tick playback + small buffer, capped to keep UI responsive.
289
- max_wait_sec = min(30.0, max(2.0, initial_qsize * 0.12 + 1.0))
290
 
291
  start = time.time()
292
  empty_since = None
@@ -329,7 +345,7 @@ def _prepare_refresh_frame(frame):
329
  def refresh_live_obs(uid, ui_phase):
330
  """
331
  Poll latest cached frame during execute phase.
332
- Updates live_obs every 0.1s via gr.Timer.
333
  """
334
  if ui_phase != "execution_playback":
335
  return gr.update()
@@ -1108,12 +1124,12 @@ def execute_step(uid, option_idx, coords_str):
1108
  )
1109
 
1110
  # Execute frames are produced in batch when execute_action returns from worker process.
1111
- # Enqueue them now, then wait briefly for the 0.1s timer to drain FIFO playback.
1112
  _enqueue_live_obs_frames(uid, getattr(session, "base_frames", None))
1113
  _wait_for_live_obs_queue_drain(uid)
1114
  LOGGER.debug("execute_step playback drain complete uid=%s", _uid_for_log(uid))
1115
 
1116
- # 注意:执行阶段画面由 live_obs 的 0.1s 轮询刷新。
1117
 
1118
  progress_update = gr.update() # 默认不更新 progress
1119
  task_update = gr.update()
 
31
  )
32
  from image_utils import draw_marker, save_video, concatenate_frames_horizontally
33
  from user_manager import user_manager
34
+ from config import (
35
+ EXECUTE_LIMIT_OFFSET,
36
+ KEYFRAME_DOWNSAMPLE_FACTOR,
37
+ LIVE_OBS_REFRESH_HZ,
38
+ SESSION_TIMEOUT,
39
+ USE_SEGMENTED_VIEW,
40
+ should_show_demo_video,
41
+ )
42
  from process_session import ScrewPlanFailureError, ProcessSessionProxy
43
  from note_content import get_task_hint
44
 
 
50
  LOGGER = logging.getLogger("robomme.callbacks")
51
 
52
 
53
+ def _should_enqueue_sample(sample_index: int) -> bool:
54
+ factor = max(1, int(KEYFRAME_DOWNSAMPLE_FACTOR))
55
+ return sample_index % factor == 0
56
+
57
+
58
+ def _live_obs_refresh_interval_sec() -> float:
59
+ return 1.0 / max(float(LIVE_OBS_REFRESH_HZ), 1.0)
60
+
61
+
62
  def _uid_for_log(uid):
63
  if not uid:
64
  return "<none>"
 
215
  _LIVE_OBS_REFRESH[uid] = {
216
  "frame_queue": queue.Queue(),
217
  "last_base_count": base_count,
218
+ "sample_index": 0,
219
  }
220
  return (
221
  gr.update(interactive=False), # options_radio
 
249
  _LIVE_OBS_REFRESH[uid] = {
250
  "frame_queue": queue.Queue(),
251
  "last_base_count": int(base_count),
252
+ "sample_index": 0,
253
  }
254
  return _LIVE_OBS_REFRESH[uid]
255
 
256
 
257
  def _enqueue_live_obs_frames(uid, base_frames):
258
  """
259
+ Push newly appended base_frames into per-uid FIFO queue with configurable downsampling.
260
  """
261
  if not uid:
262
  return 0
 
271
  with _LIVE_OBS_REFRESH_LOCK:
272
  state["frame_queue"] = queue.Queue()
273
  state["last_base_count"] = current_count
274
+ state["sample_index"] = 0
275
  return 0
276
 
277
  if current_count <= last_count:
278
  return frame_queue.qsize()
279
 
280
  new_frames = frames[last_count:current_count]
281
+ sample_index = int(state.get("sample_index", 0))
282
  for frame in new_frames:
283
+ if _should_enqueue_sample(sample_index) and frame is not None:
284
  frame_queue.put(frame)
285
+ sample_index += 1
286
 
287
  with _LIVE_OBS_REFRESH_LOCK:
288
  state["last_base_count"] = current_count
289
+ state["sample_index"] = sample_index
290
  return frame_queue.qsize()
291
 
292
 
 
301
  queue0 = state0.get("frame_queue") if state0 else None
302
  initial_qsize = int(queue0.qsize()) if queue0 is not None else 0
303
  if max_wait_sec is None:
304
+ # Timer-driven playback + small buffer, capped to keep UI responsive.
305
+ max_wait_sec = min(30.0, max(2.0, initial_qsize * (_live_obs_refresh_interval_sec() + 0.02) + 1.0))
306
 
307
  start = time.time()
308
  empty_since = None
 
345
  def refresh_live_obs(uid, ui_phase):
346
  """
347
  Poll latest cached frame during execute phase.
348
+ Updates live_obs using the configured gr.Timer interval.
349
  """
350
  if ui_phase != "execution_playback":
351
  return gr.update()
 
1124
  )
1125
 
1126
  # Execute frames are produced in batch when execute_action returns from worker process.
1127
+ # Enqueue them now, then wait briefly for the configured timer to drain FIFO playback.
1128
  _enqueue_live_obs_frames(uid, getattr(session, "base_frames", None))
1129
  _wait_for_live_obs_queue_drain(uid)
1130
  LOGGER.debug("execute_step playback drain complete uid=%s", _uid_for_log(uid))
1131
 
1132
+ # 注意:执行阶段画面由 live_obs 的配置化轮询间隔刷新。
1133
 
1134
  progress_update = gr.update() # 默认不更新 progress
1135
  task_update = gr.update()
gradio-web/ui_layout.py CHANGED
@@ -10,6 +10,7 @@ import gradio as gr
10
 
11
  from config import (
12
  CONTROL_PANEL_SCALE,
 
13
  KEYPOINT_SELECTION_SCALE,
14
  RIGHT_TOP_ACTION_SCALE,
15
  RIGHT_TOP_LOG_SCALE,
@@ -159,7 +160,7 @@ def create_ui_blocks():
159
 
160
  uid_state = gr.State(value=None)
161
  ui_phase_state = gr.State(value=PHASE_INIT)
162
- live_obs_timer = gr.Timer(value=0.1, active=True)
163
 
164
  task_info_box = gr.Textbox(visible=False, elem_id="task_info_box")
165
  progress_info_box = gr.Textbox(visible=False)
 
10
 
11
  from config import (
12
  CONTROL_PANEL_SCALE,
13
+ LIVE_OBS_REFRESH_HZ,
14
  KEYPOINT_SELECTION_SCALE,
15
  RIGHT_TOP_ACTION_SCALE,
16
  RIGHT_TOP_LOG_SCALE,
 
160
 
161
  uid_state = gr.State(value=None)
162
  ui_phase_state = gr.State(value=PHASE_INIT)
163
+ live_obs_timer = gr.Timer(value=1.0 / LIVE_OBS_REFRESH_HZ, active=True)
164
 
165
  task_info_box = gr.Textbox(visible=False, elem_id="task_info_box")
166
  progress_info_box = gr.Textbox(visible=False)