File size: 5,978 Bytes
ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b aa41bac ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 20ad86b ba4f7c6 aa41bac ba4f7c6 20ad86b ba4f7c6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | """
Minimum-Violation LTL Planning β Interactive Demo
Reproduces: Tumova et al., "Minimum-Violation LTL Planning with Conflicting
Specifications", ACC 2013 (arXiv:1303.3679)
Change spec priorities β watch the plan change to satisfy the highest-priority rules.
"""
import gradio as gr
from src.grid_world import make_scenario
from src.automata import parse_spec
from src.planner import plan
from src.visualize import render_static, render_animation, spec_table_html
SCENARIO_SPECS = {
"road": [
("G(!danger)", "Never cross double line (danger zone)", 80),
("GF(zone_a)", "Periodically visit pickup zone A", 50),
("GF(zone_b)", "Periodically visit dropoff zone B", 30),
("F(goal)", "Eventually reach the goal", 10),
],
"patrol": [
("G(!danger)", "Stay away from danger zones", 100),
("GF(zone_a)", "Patrol zone A repeatedly", 60),
("GF(zone_b)", "Patrol zone B repeatedly", 40),
],
"rescue": [
("G(!danger)", "Avoid hazardous areas", 90),
("F(zone_a)", "Reach survivor site A", 70),
("F(zone_b)", "Reach survivor site B", 50),
("GF(zone_c)", "Return to base (zone C) always", 20),
],
}
SCENARIO_DESCRIPTIONS = {
"road": "π Road network β navigate around a danger zone to reach pickup/dropoff areas and a destination.",
"patrol": "π Warehouse patrol β periodically cover two inspection zones while avoiding a hazardous area.",
"rescue": "π Rescue mission β reach two survivor sites and periodically return to base, avoiding hazards.",
}
def run_planning(scenario, r0, r1, r2, r3, animate):
grid = make_scenario(scenario)
specs_raw = SCENARIO_SPECS[scenario]
n_specs = len(specs_raw)
rewards_input = [r0, r1, r2, r3][:n_specs]
automata, rewards = [], []
for i, (formula, _, _) in enumerate(specs_raw):
try:
automata.append(parse_spec(formula, label=formula))
rewards.append(float(rewards_input[i]))
except ValueError as e:
return None, None, f"<p style='color:red'>Error: {e}</p>"
result = plan(grid, automata, rewards)
html = spec_table_html(result)
static = render_static(grid, result)
if animate and result.success:
gif = render_animation(grid, result, fps=3)
# Embed as base64 so Gradio 5.x HTML component plays it natively
import base64
with open(gif, "rb") as f:
b64 = base64.b64encode(f.read()).decode()
gif_html = (
f"<img src='data:image/gif;base64,{b64}' "
f"style='max-width:100%;border-radius:8px'/>"
)
return static, gif_html, html
return static, "", html
def update_scenario_ui(scenario):
specs = SCENARIO_SPECS[scenario]
desc = SCENARIO_DESCRIPTIONS[scenario]
n = len(specs)
slider_updates = []
for i in range(4):
if i < n:
_, label, val = specs[i]
slider_updates.append(gr.Slider(label=label, value=val, visible=True))
else:
slider_updates.append(gr.Slider(visible=False))
return [gr.Markdown(value=f"**{desc}**")] + slider_updates
def load_defaults():
ui = update_scenario_ui("road")
plan_out = run_planning("road", 80, 50, 30, 10, False)
return ui + list(plan_out)
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
with gr.Blocks(title="Minimum-Violation LTL Planner", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# Minimum-Violation LTL Planning
**Reproducing:** Tumova et al., *"Minimum-Violation LTL Planning with Conflicting Specifications"*, ACC 2013
When robot specs conflict, instead of failing, this planner finds the path that satisfies
the **highest-priority** rules and minimally violates the rest.
> **Try it:** drag the reward sliders to swap priorities β watch the planned path change.
""")
with gr.Row():
with gr.Column(scale=1):
scenario_dd = gr.Dropdown(
choices=["road", "patrol", "rescue"],
value="road",
label="Scenario",
)
scenario_desc = gr.Markdown("**Loading...**")
gr.Markdown("### Spec Priorities (higher reward = harder to violate)")
sliders = []
for i, (_, lbl, val) in enumerate(SCENARIO_SPECS["road"]):
sliders.append(gr.Slider(minimum=0, maximum=200, step=5,
value=val, label=lbl, visible=True))
animate_cb = gr.Checkbox(label="Generate animation (GIF)", value=True)
plan_btn = gr.Button("βΆ Synthesize Plan", variant="primary")
gr.Markdown("""
---
**How it works:**
1. Each spec Οα΅’ becomes a BΓΌchi automaton
2. Product automaton = grid Γ autβ Γ autβ Γ ...
3. SCCs with accepting states for each spec are found
4. Max-reward SCC β lasso path reconstructed
π΅ Prefix path π΄ Repeating cycle π£ Start π Robot
""")
with gr.Column(scale=2):
grid_img = gr.Image(label="Planned Path", type="pil", height=420)
anim_img = gr.HTML(label="Animation (GIF)")
result_md = gr.HTML(label="Spec Satisfaction")
scenario_dd.change(
fn=update_scenario_ui,
inputs=[scenario_dd],
outputs=[scenario_desc] + sliders,
)
plan_btn.click(
fn=run_planning,
inputs=[scenario_dd] + sliders + [animate_cb],
outputs=[grid_img, anim_img, result_md],
)
demo.load(
fn=load_defaults,
outputs=[scenario_desc] + sliders + [grid_img, anim_img, result_md],
)
if __name__ == "__main__":
demo.launch()
|