@@ -805,7 +805,7 @@ def plot_global_execution_view(self, step_idx: int):
805805 with col4 :
806806 st .metric ("当前Chunk位置" , f"{ current_chunk_start } -{ current_chunk_end } " )
807807
808- def render_inference_details_panel (self , step_idx : int ):
808+ def render_inference_details_panel (self , step_idx : int , show_language_prompt : bool = True ):
809809 """Render detailed inference information panel."""
810810 if step_idx >= len (self .steps ):
811811 return
@@ -815,12 +815,13 @@ def render_inference_details_panel(self, step_idx: int):
815815 tags = step .get ("tags" , {})
816816
817817 # Language Prompt
818- if prompt :
819- st .markdown ("#### 🗣️ 语言指令" )
820- st .info (f"**Prompt:** { prompt } " )
821- elif self .meta .get ("task_prompt" ):
822- st .markdown ("#### 🗣️ 语言指令" )
823- st .info (f"**Task Prompt:** { self .meta .get ('task_prompt' )} " )
818+ if show_language_prompt :
819+ if prompt :
820+ st .markdown ("#### 🗣️ 语言指令" )
821+ st .info (f"**Prompt:** { prompt } " )
822+ elif self .meta .get ("task_prompt" ):
823+ st .markdown ("#### 🗣️ 语言指令" )
824+ st .info (f"**Task Prompt:** { self .meta .get ('task_prompt' )} " )
824825
825826 # Global trends (Now Interactive) - Two views: Inference vs Execution
826827 st .markdown ("#### 📈 全局状态与动作趋势" )
@@ -900,6 +901,7 @@ def render_model_config_panel(self):
900901 st .markdown ("**Client Config:**" )
901902 st .json (client_config )
902903
904+
903905 def plot_replay_frame (self , step_idx : int , show_details : bool = True ):
904906 """Plot replay frame for a step."""
905907 if not self .valid or step_idx >= len (self .steps ):
@@ -910,13 +912,12 @@ def plot_replay_frame(self, step_idx: int, show_details: bool = True):
910912 pred_action = self .get_step_action (step_idx )
911913 imgs = self .get_step_images (step_idx )
912914
913- # Layout
914- c1 , c2 = st .columns ([1 , 1.5 ])
915+ # Layout: 左侧“视觉观测 + 动作规划”;右侧上提“状态/动作视图”
916+ left_col , right_col = st .columns ([1.5 , 1.0 ])
915917
916- with c1 :
918+ with left_col :
917919 st .markdown ("#### 👁️ 模型视觉观测" )
918920 if imgs :
919- # Display in a grid (up to 3 columns) to support multi-camera runs
920921 cam_names = list (imgs .keys ())
921922 n_cols = min (3 , max (1 , len (cam_names )))
922923 cols = st .columns (n_cols )
@@ -925,60 +926,99 @@ def plot_replay_frame(self, step_idx: int, show_details: bool = True):
925926 img = imgs [cam ]
926927 st .image (
927928 img ,
928- caption = f"{ cam } | Step { step_idx } | { img . shape } " ,
929+ caption = f"{ cam } " ,
929930 use_container_width = True ,
930931 )
931932 else :
932933 st .warning ("无图像数据" )
933-
934- with c2 :
934+
935935 st .markdown ("#### 🗺️ 3D 动作规划" )
936936
937937 if len (current_state ) >= 3 :
938- fig = plt . figure ( figsize = ( 8 , 6 ) )
939- ax = fig . add_subplot ( 111 , projection = '3d' )
940-
941- # Plot history
938+ fig = go . Figure ( )
939+
940+ # --- 修复部分开始 ---
941+ # 1. 历史轨迹 (History)
942942 all_states = self .get_all_states ()
943943 if len (all_states ) > 0 and all_states .shape [1 ] >= 3 :
944- start = max (0 , step_idx - 50 )
945- hist = all_states [start :step_idx + 1 ]
944+ # 获取从开始到当前步的所有轨迹
945+ hist = all_states [:step_idx + 1 ]
946+
946947 if len (hist ) > 1 :
947- ax .plot (hist [:,0 ], hist [:,1 ], hist [:,2 ], 'k-' , alpha = 0.3 , label = 'History' )
948-
949- # Current position
950- ax .scatter (current_state [0 ], current_state [1 ], current_state [2 ],
951- c = 'b' , s = 100 , label = 'Current' )
952-
953- # Predicted trajectory
948+ fig .add_trace (go .Scatter3d (
949+ x = hist [:, 0 ], y = hist [:, 1 ], z = hist [:, 2 ],
950+ mode = 'lines' ,
951+ # 【修改点】:颜色改为纯深灰(#555555),不透明,宽度加粗到4
952+ line = dict (color = '#555555' , width = 4 ),
953+ name = 'History' ,
954+ hoverinfo = 'skip'
955+ ))
956+ # --- 修复部分结束 ---
957+
958+ # 2. 预测轨迹 (Pred)
954959 if len (pred_action ) > 0 and pred_action .ndim == 2 and pred_action .shape [1 ] >= 3 :
955- ax .plot (pred_action [:,0 ], pred_action [:,1 ], pred_action [:,2 ],
956- 'r--' , linewidth = 2 , label = 'Pred' )
957- ax .scatter (pred_action [- 1 ,0 ], pred_action [- 1 ,1 ], pred_action [- 1 ,2 ],
958- c = 'r' , marker = 'x' , s = 100 )
959-
960- ax .set_xlabel ('X' )
961- ax .set_ylabel ('Y' )
962- ax .set_zlabel ('Z' )
963- ax .legend ()
964-
965- # Set axis limits
966- if len (all_states ) > 0 and all_states .shape [1 ] >= 3 :
967- margin = 0.1
968- ax .set_xlim (all_states [:,0 ].min ()- margin , all_states [:,0 ].max ()+ margin )
969- ax .set_ylim (all_states [:,1 ].min ()- margin , all_states [:,1 ].max ()+ margin )
970- ax .set_zlim (all_states [:,2 ].min ()- margin , all_states [:,2 ].max ()+ margin )
960+ fig .add_trace (go .Scatter3d (
961+ x = pred_action [:, 0 ], y = pred_action [:, 1 ], z = pred_action [:, 2 ],
962+ mode = 'lines+markers' ,
963+ line = dict (color = 'rgba(255, 50, 50, 0.8)' , width = 4 , dash = 'dot' ),
964+ marker = dict (size = 2 , color = 'rgba(255, 50, 50, 0.8)' ),
965+ name = 'Prediction'
966+ ))
967+ # 目标点
968+ fig .add_trace (go .Scatter3d (
969+ x = [pred_action [- 1 , 0 ]], y = [pred_action [- 1 , 1 ]], z = [pred_action [- 1 , 2 ]],
970+ mode = 'markers' ,
971+ marker = dict (size = 5 , color = 'red' , symbol = 'diamond' ),
972+ name = 'Target'
973+ ))
974+
975+ # 3. 当前位置 (Current)
976+ fig .add_trace (go .Scatter3d (
977+ x = [current_state [0 ]], y = [current_state [1 ]], z = [current_state [2 ]],
978+ mode = 'markers' ,
979+ marker = dict (size = 6 , color = '#2196F3' , symbol = 'circle' , line = dict (width = 1 , color = 'white' )),
980+ name = 'Current'
981+ ))
982+
983+ fig .update_layout (
984+ height = 500 ,
985+ margin = dict (l = 0 , r = 0 , b = 0 , t = 10 ),
986+ scene = dict (
987+ xaxis_title = 'X' ,
988+ yaxis_title = 'Y' ,
989+ zaxis_title = 'Z' ,
990+ aspectmode = 'data' ,
991+ ),
992+ legend = dict (
993+ x = 0 , y = 1 ,
994+ bgcolor = 'rgba(255,255,255,0.6)' ,
995+ itemsizing = 'constant'
996+ ),
997+ uirevision = 'constant_scene_view'
998+ )
971999
972- st .pyplot (fig )
973- plt . close ( fig )
1000+ st .plotly_chart (fig , use_container_width = True )
1001+
9741002 else :
9751003 st .warning ("状态维度不足,无法绘制3D轨迹" )
1004+
1005+ # 左下方:语言指令
1006+ prompt = step .get ("prompt" )
1007+ task_prompt = self .meta .get ("task_prompt" ) if self .meta else None
1008+ if prompt or task_prompt :
1009+ st .divider ()
1010+ st .markdown ("#### 🗣️ 语言指令" )
1011+ if prompt :
1012+ st .info (f"**Prompt:** { prompt } " )
1013+ else :
1014+ st .info (f"**Task Prompt:** { task_prompt } " )
9761015
977- # Inference Details Panel
978- if show_details :
979- st .divider ()
980- self .render_inference_details_panel (step_idx )
981-
1016+ with right_col :
1017+ if show_details :
1018+ self .render_inference_details_panel (step_idx , show_language_prompt = False )
1019+ else :
1020+ st .info ("已关闭推理详情(侧边栏勾选“显示推理详情”可查看状态/动作视图)" )
1021+
9821022 def plot_latency_analysis (self ):
9831023 """Plot latency analysis chart."""
9841024 if not self .steps :
0 commit comments