import gradio as gr import numpy as np from robot import TrajectoryPlanner import logging import plotly.graph_objects as go from plotly.subplots import make_subplots logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") import time import threading # Initialize trajectory planner try: planner = TrajectoryPlanner() PLANNER_AVAILABLE = True except Exception as e: logging.error(f"Failed to initialize trajectory planner: {e}") PLANNER_AVAILABLE = False animation_running = False animation_thread = None css = """ .control-panel .gr-slider { margin-top: 4px !important; margin-bottom: 4px !important; } .control-panel .gr-form { gap: 6px !important; } .label-override { font-weight: 500 !important; font-size: 1.1em !important; color: #333 !important; } """ def create_comparison_plots(plot_data): """Create comparison plots for the trajectories""" if plot_data is None: return None ours = plot_data['ours'] toppra = plot_data['toppra'] # Create subplots fig = make_subplots( rows=3, cols=2, subplot_titles=('Position', 'Velocity', 'Acceleration', 'Jerk', 'Position Error', 'Orientation Error'), vertical_spacing=0.12, horizontal_spacing=0.1 ) # Position fig.add_trace(go.Scatter(x=ours['t'], y=ours['x'], name='Ours', line=dict(color='#6C8EBF', width=2), showlegend=True), row=1, col=1) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['x'], name='TOPPRA', line=dict(color='#B85450', width=2), showlegend=True), row=1, col=1) # Velocity fig.add_trace(go.Scatter(x=ours['t'], y=ours['dx'], name='Ours', line=dict(color='#6C8EBF', width=2), showlegend=False), row=1, col=2) fig.add_trace(go.Scatter(x=ours['t'], y=ours['dx_max'], name='Ours Limits', line=dict(color='#6C8EBF', width=1, dash='dash'), showlegend=True), row=1, col=2) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['dx'], name='TOPPRA', line=dict(color='#B85450', width=2), showlegend=False), row=1, col=2) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['ds_max'], name='TOPPRA Limits', line=dict(color='#B85450', width=1, dash='dash'), showlegend=True), row=1, col=2) # Acceleration fig.add_trace(go.Scatter(x=ours['t'], y=ours['ddx'], name='Ours', line=dict(color='#6C8EBF', width=2), showlegend=False), row=2, col=1) fig.add_trace(go.Scatter(x=ours['t'], y=ours['ddx_max'], name='Ours Limits', line=dict(color='#6C8EBF', width=1, dash='dash'), showlegend=False), row=2, col=1) fig.add_trace(go.Scatter(x=ours['t'], y=ours['ddx_min'], name='Ours Limits', line=dict(color='#6C8EBF', width=1, dash='dash'), showlegend=False), row=2, col=1) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['ddx'], name='TOPPRA', line=dict(color='#B85450', width=2), showlegend=False), row=2, col=1) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['dds_max'], name='TOPPRA Limits', line=dict(color='#B85450', width=1, dash='dash'), showlegend=False), row=2, col=1) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['dds_min'], name='TOPPRA Limits', line=dict(color='#B85450', width=1, dash='dash'), showlegend=False), row=2, col=1) # Jerk fig.add_trace(go.Scatter(x=ours['t'], y=ours['dddx'], name='Ours', line=dict(color='#6C8EBF', width=2), showlegend=False), row=2, col=2) fig.add_trace(go.Scatter(x=ours['t'], y=ours['dddx_max'], name='Ours Limits', line=dict(color='#6C8EBF', width=1, dash='dash'), showlegend=False), row=2, col=2) fig.add_trace(go.Scatter(x=ours['t'], y=ours['dddx_min'], name='Ours Limits', line=dict(color='#6C8EBF', width=1, dash='dash'), showlegend=False), row=2, col=2) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['dddx'], name='TOPPRA', line=dict(color='#B85450', width=2), showlegend=False), row=2, col=2) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['ddds_max'], name='TOPPRA Limits', line=dict(color='#B85450', width=1, dash='dash'), showlegend=False), row=2, col=2) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['ddds_min'], name='TOPPRA Limits', line=dict(color='#B85450', width=1, dash='dash'), showlegend=False), row=2, col=2) # Position Error fig.add_trace(go.Scatter(x=ours['t'], y=ours['e_pos'], name='Ours', line=dict(color='#6C8EBF', width=2), showlegend=False), row=3, col=1) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['e_pos'], name='TOPPRA', line=dict(color='#B85450', width=2), showlegend=False), row=3, col=1) # Orientation Error fig.add_trace(go.Scatter(x=ours['t'], y=ours['e_rot'], name='Ours', line=dict(color='#6C8EBF', width=2), showlegend=False), row=3, col=2) fig.add_trace(go.Scatter(x=toppra['t'], y=toppra['e_rot'], name='TOPPRA', line=dict(color='#B85450', width=2), showlegend=False), row=3, col=2) # Update axes fig.update_xaxes(title_text="Time [s]", row=3, col=1) fig.update_xaxes(title_text="Time [s]", row=3, col=2) fig.update_yaxes(title_text="s [m]", row=1, col=1) fig.update_yaxes(title_text="ds [m/s]", row=1, col=2) fig.update_yaxes(title_text="dds [m/s²]", row=2, col=1) fig.update_yaxes(title_text="ddds [m/s³]", row=2, col=2) fig.update_yaxes(title_text="Position Error [mm]", row=3, col=1) fig.update_yaxes(title_text="Orientation Error [deg]", row=3, col=2) fig.update_layout(height=600, showlegend=True, legend=dict(x=0.5, y=1.1, orientation='h')) return fig def animate_trajectory(): """Background thread to animate the trajectory""" global animation_running anim_data = planner.get_animation_data() if anim_data is None: return t_max = anim_data['t_max'] for i in range(10): if not animation_running: return t0 = time.time() while animation_running and (time.time() - t0) < t_max: t_current = time.time() - t0 planner.update_animation(t_current) time.sleep(0.01) animation_running = False def generate_and_compute(traj_length, capacity_scale, progress=gr.Progress()): """Generate trajectory and compute both algorithms""" if not PLANNER_AVAILABLE: return "Planner not available", None, None progress(0, desc="Generating trajectory...") # try: result = planner.generate_trajectory(traj_length, capacity_scale, progress) info = f""" ✅ **Trajectory Generated Successfully** - **Waypoints**: {result['waypoints']} - **Trajectory Length**: {traj_length:.2f} m - **Capacity Scale**: {capacity_scale:.2f} - **TOPPRA Duration**: {result['toppra_duration']:.3f} s - **Ours Duration**: {result['ours_duration']:.3f} s - **Speedup**: {(result['toppra_duration'] / result['ours_duration']):.2f}x """ progress(0.8, desc="Creating plots...") plot_data = planner.get_plot_data() plots = create_comparison_plots(plot_data) progress(1.0, desc="Done!") return info, plots, gr.update(interactive=True) #except Exception as e: # logging.error(f"Error generating trajectory: {e}") # return f"❌ Error: {str(e)}", None, gr.update(interactive=False) def start_animation(): """Start the animation""" global animation_running, animation_thread if not PLANNER_AVAILABLE or planner.data is None: return gr.update(value="⚠️ Generate trajectory first") animation_running = True animation_thread = threading.Thread(target=animate_trajectory, daemon=True) animation_thread.start() return gr.update(value="⏸️ Stop Animation", variant="stop") def stop_animation(): """Stop the animation""" global animation_running animation_running = False return gr.update(value="▶️ Play Animation", variant="primary") def toggle_animation(btn_state): """Toggle animation on/off""" if "Stop" in btn_state: return stop_animation() else: return start_animation() with gr.Blocks(css=css, title="Capacity-Aware Trajectory Planning") as demo: gr.Markdown(""" # 🤖 Capacity-Aware Trajectory Planning Comparison 📄 This demo implements the paper: **Online approach to near time-optimal task-space trajectory planning** by Antun Skuric, Nicolas Torres Alberto, Lucas Joseph, Vincent Padois, David Daney Link: https://inria.hal.science/hal-04576076 Compare **capacity-aware real-time trajectory planning** vs **TOPPRA** for the Panda robot. **Left Robot**: Our approach | **Right Robot**: TOPPRA """) with gr.Row(equal_height=True): # LEFT: MeshCat viewer with gr.Column(scale=2, min_width=500): if PLANNER_AVAILABLE: viewer = gr.HTML(planner.iframe(height=600)) else: viewer = gr.HTML("