Skip to content

Commit 0ec48f6

Browse files
committed
improve layout
1 parent 4e70bf5 commit 0ec48f6

2 files changed

Lines changed: 102 additions & 55 deletions

File tree

src/vlalab/apps/streamlit/app.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,17 +231,24 @@ def main():
231231

232232
# Navigation
233233
pages = {
234-
"🏠 首页": "home",
234+
"🚀 Get Started": "home",
235235
"🔬 推理回放": "inference",
236236
"📊 数据集浏览": "dataset",
237237
"📈 延迟分析": "latency",
238238
"🎯 开环评估": "eval",
239239
}
240+
241+
# 默认页:Inference Viewer(仅首次进入生效,之后保持用户选择)
242+
nav_options = list(pages.keys())
243+
default_nav = "🔬 推理回放"
244+
if "vlalab_nav" not in st.session_state:
245+
st.session_state.vlalab_nav = default_nav if default_nav in nav_options else nav_options[0]
240246

241247
selection = st.sidebar.radio(
242248
"导航",
243-
list(pages.keys()),
249+
nav_options,
244250
label_visibility="collapsed",
251+
key="vlalab_nav",
245252
)
246253

247254
page_name = pages[selection]
@@ -273,9 +280,9 @@ def main():
273280
def show_home_page():
274281
# Hero section
275282
st.markdown("""
276-
<div class="hero-title">VLA-Lab</div>
283+
<div class="hero-title">Get Started</div>
277284
<div class="hero-subtitle">
278-
专为 VLA (Vision-Language-Action) 模型设计的实机部署追踪与可视化工具箱
285+
VLA (Vision-Language-Action) 实机部署追踪与可视化工具箱 · 快速上手指南
279286
</div>
280287
""", unsafe_allow_html=True)
281288

@@ -369,7 +376,7 @@ def show_home_page():
369376
# vlalab view
370377
""", language="python")
371378

372-
st.info("👈 从左侧导航栏选择功能开始使用")
379+
st.info("👈 默认已为你选中「推理回放」。如需查看指南或其它功能,可从左侧导航切换。")
373380

374381

375382
if __name__ == "__main__":

src/vlalab/apps/streamlit/pages/inference_viewer.py

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)