YashsharmaPhD commited on
Commit
5aa941e
·
verified ·
1 Parent(s): fb31512

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +172 -81
app.py CHANGED
@@ -1,105 +1,196 @@
 
 
 
 
1
  import random
2
  import threading
3
  import time
4
- import matplotlib.pyplot as plt
5
- import gradio as gr
6
 
 
7
  class SLAMSimulation:
8
  def __init__(self):
9
- self.robot_pos = [5, 5] # start position
10
- self.grid_size = 10
 
 
 
 
 
11
  self.auto_mode = False
12
- self.lock = threading.Lock()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  def render_env(self):
15
- fig, ax = plt.subplots()
16
- ax.set_xlim(0, self.grid_size)
17
- ax.set_ylim(0, self.grid_size)
18
- ax.grid(True)
19
- ax.plot(self.robot_pos[0], self.robot_pos[1], 'ro', markersize=15)
20
- ax.set_title("Environment")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  plt.close(fig)
22
  return fig
23
 
24
  def render_slam_map(self):
25
- # For demo, same as env
26
- return self.render_env()
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  def get_valid_moves(self):
29
- x, z = self.robot_pos
30
- moves = []
31
- if x > 0:
32
- moves.append("left")
33
- if x < self.grid_size - 1:
34
- moves.append("right")
35
- if z > 0:
36
- moves.append("down")
37
- if z < self.grid_size - 1:
38
- moves.append("up")
39
- return moves
 
 
 
 
 
 
40
 
41
- def move_robot(self, direction):
42
- with self.lock:
43
- x, z = self.robot_pos
44
- if direction == "left" and x > 0:
45
- self.robot_pos[0] -= 1
46
- elif direction == "right" and x < self.grid_size - 1:
47
- self.robot_pos[0] += 1
48
- elif direction == "up" and z < self.grid_size - 1:
49
- self.robot_pos[1] += 1
50
- elif direction == "down" and z > 0:
51
- self.robot_pos[1] -= 1
52
-
53
- def auto_move_step(self):
54
- if self.auto_mode:
55
- moves = self.get_valid_moves()
56
- if moves:
57
- direction = random.choice(moves)
58
- self.move_robot(direction)
59
- return f"Auto moved {direction}"
60
- else:
61
- return "No valid moves"
62
- return "Auto mode off"
63
 
 
64
  sim = SLAMSimulation()
65
 
66
- def toggle_auto_mode():
67
- sim.auto_mode = not sim.auto_mode
68
- if sim.auto_mode:
69
- return "🟢 Auto Mode ON"
70
- else:
71
- return "⚪ Auto Mode OFF"
72
 
73
- def manual_move(direction):
74
- sim.move_robot(direction)
75
- return sim.render_env(), sim.render_slam_map(), f"Moved {direction}"
76
 
77
- def update_plots():
78
- # This will be called repeatedly by gr.Timer
79
- msg = sim.auto_move_step()
80
- return sim.render_env(), sim.render_slam_map(), msg
81
 
82
- with gr.Blocks() as demo:
83
- env_plot = gr.Plot(value=sim.render_env())
84
- slam_plot = gr.Plot(value=sim.render_slam_map())
85
- status = gr.Textbox(value="Ready", interactive=False)
86
-
87
- btn_up = gr.Button("Up")
88
- btn_down = gr.Button("Down")
89
- btn_left = gr.Button("Left")
90
- btn_right = gr.Button("Right")
91
- btn_auto = gr.Button("Toggle Auto")
92
-
93
- btn_up.click(fn=lambda: manual_move("up"), inputs=None, outputs=[env_plot, slam_plot, status])
94
- btn_down.click(fn=lambda: manual_move("down"), inputs=None, outputs=[env_plot, slam_plot, status])
95
- btn_left.click(fn=lambda: manual_move("left"), inputs=None, outputs=[env_plot, slam_plot, status])
96
- btn_right.click(fn=lambda: manual_move("right"), inputs=None, outputs=[env_plot, slam_plot, status])
97
- btn_auto.click(fn=toggle_auto_mode, inputs=None, outputs=status)
98
-
99
- # Timer updates every 0.5 seconds
100
- timer = gr.Timer(interval=0.5,
101
- fn=update_plots,
102
- inputs=None,
103
- outputs=[env_plot, slam_plot, status])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
  demo.launch()
 
1
+ import gradio as gr
2
+ import matplotlib.pyplot as plt
3
+ import matplotlib.image as mpimg
4
+ import numpy as np
5
  import random
6
  import threading
7
  import time
 
 
8
 
9
+ # ---------- GLOBAL STATE ----------
10
  class SLAMSimulation:
11
  def __init__(self):
12
+ self.pose = {"x": 0, "z": 0, "angle": 0}
13
+ self.trajectory = [(0, 0)]
14
+ self.obstacle_hits = []
15
+ self.color_index = 0
16
+ self.rgb_colors = ['red', 'green', 'blue']
17
+ self.noise_enabled = True
18
+ self.obstacles = []
19
  self.auto_mode = False
20
+ self.reset(10)
21
+
22
+ def generate_obstacles(self, count=10):
23
+ return [{
24
+ "x": random.uniform(-8, 8),
25
+ "z": random.uniform(-8, 8),
26
+ "radius": random.uniform(0.5, 1.2)
27
+ } for _ in range(count)]
28
+
29
+ def toggle_noise(self):
30
+ self.noise_enabled = not self.noise_enabled
31
+ return f"Noise: {'ON' if self.noise_enabled else 'OFF'}"
32
+
33
+ def reset(self, obstacle_count):
34
+ self.pose = {"x": 0, "z": 0, "angle": 0}
35
+ self.trajectory = [(0, 0)]
36
+ self.obstacle_hits.clear()
37
+ self.color_index = 0
38
+ self.obstacles = self.generate_obstacles(int(obstacle_count))
39
+ return self.render_env(), self.render_slam_map(), f"🔄 Simulation reset with {obstacle_count} obstacles"
40
+
41
+ def check_collision(self, x, z):
42
+ return any(np.hypot(obs["x"] - x, obs["z"] - z) <= obs["radius"] + 0.2 for obs in self.obstacles)
43
+
44
+ def move_robot(self, direction):
45
+ step = 1
46
+ direction = direction.upper()
47
+ dx, dz, angle = {
48
+ "W": (0, step, 90),
49
+ "S": (0, -step, -90),
50
+ "A": (-step, 0, 180),
51
+ "D": (step, 0, 0)
52
+ }.get(direction, (0, 0, self.pose["angle"]))
53
+
54
+ if direction not in "WASD":
55
+ return self.render_env(), self.render_slam_map(), "❌ Invalid direction"
56
+
57
+ new_x, new_z = self.pose["x"] + dx, self.pose["z"] + dz
58
+
59
+ if self.check_collision(new_x, new_z):
60
+ return self.render_env(), self.render_slam_map(), "🚫 Collision detected!"
61
+
62
+ self.pose.update({"x": new_x, "z": new_z, "angle": angle})
63
+
64
+ noisy_x = new_x + random.uniform(-0.1, 0.1) if self.noise_enabled else new_x
65
+ noisy_z = new_z + random.uniform(-0.1, 0.1) if self.noise_enabled else new_z
66
+ self.trajectory.append((noisy_x, noisy_z))
67
+
68
+ return self.render_env(), self.render_slam_map(), f"✅ Moved {direction}"
69
 
70
  def render_env(self):
71
+ fig, ax = plt.subplots(figsize=(5, 5))
72
+ ax.set_xlim(-10, 10)
73
+ ax.set_ylim(-10, 10)
74
+ ax.set_title("SLAM Environment View")
75
+
76
+ try:
77
+ bg = mpimg.imread("map.png")
78
+ ax.imshow(bg, extent=(-10, 10, -10, 10), alpha=0.2)
79
+ except FileNotFoundError:
80
+ pass
81
+
82
+ for obs in self.obstacles:
83
+ circle = plt.Circle((obs["x"], obs["z"]), obs["radius"], facecolor="gray", alpha=0.6, edgecolor="black")
84
+ ax.add_patch(circle)
85
+
86
+ ax.plot(self.pose["x"], self.pose["z"], 'ro', markersize=8)
87
+ self.obstacle_hits.clear()
88
+
89
+ angles = np.linspace(0, 2 * np.pi, 24)
90
+ for ang in angles:
91
+ for r in np.linspace(0, 3, 30):
92
+ scan_x = self.pose["x"] + r * np.cos(ang)
93
+ scan_z = self.pose["z"] + r * np.sin(ang)
94
+ if self.check_collision(scan_x, scan_z):
95
+ ax.plot([self.pose["x"], scan_x], [self.pose["z"], scan_z], 'g-', linewidth=0.5)
96
+ self.obstacle_hits.append((scan_x, scan_z))
97
+ break
98
+
99
  plt.close(fig)
100
  return fig
101
 
102
  def render_slam_map(self):
103
+ fig, ax = plt.subplots(figsize=(5, 5))
104
+ ax.set_title("SLAM Trajectory Map")
105
+ if self.trajectory:
106
+ x_vals, z_vals = zip(*self.trajectory)
107
+ ax.plot(x_vals, z_vals, 'bo-', markersize=3)
108
+ ax.grid(True)
109
+
110
+ if self.obstacle_hits:
111
+ hit_color = self.rgb_colors[self.color_index % len(self.rgb_colors)]
112
+ for hit in self.obstacle_hits[-20:]:
113
+ ax.plot(hit[0], hit[1], 'o', color=hit_color, markersize=6)
114
+ self.color_index += 1
115
+
116
+ plt.close(fig)
117
+ return fig
118
 
119
  def get_valid_moves(self):
120
+ deltas = {
121
+ "W": (0, 1),
122
+ "S": (0, -1),
123
+ "A": (-1, 0),
124
+ "D": (1, 0)
125
+ }
126
+ return [
127
+ dir for dir, (dx, dz) in deltas.items()
128
+ if not self.check_collision(self.pose["x"] + dx, self.pose["z"] + dz)
129
+ ]
130
+
131
+ def auto_move(self):
132
+ valid_moves = self.get_valid_moves()
133
+ if not valid_moves:
134
+ return self.render_env(), self.render_slam_map(), "⚠️ No valid moves"
135
+ direction = random.choice(valid_moves)
136
+ return self.move_robot(direction)
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
+ # ---------- GRADIO UI ----------
140
  sim = SLAMSimulation()
141
 
142
+ with gr.Blocks() as demo:
143
+ gr.Markdown("## 🤖 SLAM Simulation with Auto Mode + Obstacle Avoidance")
 
 
 
 
144
 
145
+ auto_mode_state = gr.State(value=False)
 
 
146
 
147
+ obstacle_slider = gr.Slider(1, 20, value=10, step=1, label="Number of Obstacles")
148
+ direction_input = gr.Textbox(label="Type W / A / S / D and press Enter", placeholder="e.g., W")
149
+ status_text = gr.Textbox(label="Status", interactive=False)
 
150
 
151
+ with gr.Row():
152
+ with gr.Column():
153
+ env_plot = gr.Plot(label="Robot View")
154
+ with gr.Column():
155
+ slam_plot = gr.Plot(label="SLAM Map")
156
+
157
+ with gr.Row():
158
+ w_btn = gr.Button("⬆️ W")
159
+ a_btn = gr.Button("⬅️ A")
160
+ s_btn = gr.Button("⬇️ S")
161
+ d_btn = gr.Button("➡️ D")
162
+ reset_btn = gr.Button("🔄 Reset")
163
+ toggle_noise_btn = gr.Button("🎲 Toggle Noise")
164
+ auto_btn = gr.Button("🤖 Start/Stop Auto")
165
+
166
+ # Button click handlers
167
+ w_btn.click(fn=lambda: sim.move_robot("W"), outputs=[env_plot, slam_plot, status_text])
168
+ a_btn.click(fn=lambda: sim.move_robot("A"), outputs=[env_plot, slam_plot, status_text])
169
+ s_btn.click(fn=lambda: sim.move_robot("S"), outputs=[env_plot, slam_plot, status_text])
170
+ d_btn.click(fn=lambda: sim.move_robot("D"), outputs=[env_plot, slam_plot, status_text])
171
+
172
+ reset_btn.click(fn=sim.reset, inputs=[obstacle_slider], outputs=[env_plot, slam_plot, status_text])
173
+ toggle_noise_btn.click(fn=sim.toggle_noise, outputs=status_text)
174
+ direction_input.submit(fn=lambda text: sim.move_robot(text.strip().upper()), inputs=direction_input, outputs=[env_plot, slam_plot, status_text])
175
+
176
+ # Auto mode toggle logic
177
+ def toggle_auto_mode(state):
178
+ state.value = not state.value
179
+ if state.value:
180
+ return "🟢 Auto Mode: ON", state
181
+ else:
182
+ return "⚪ Auto Mode: OFF", state
183
+
184
+ auto_btn.click(fn=toggle_auto_mode, inputs=auto_mode_state, outputs=[status_text, auto_mode_state])
185
+
186
+ # Timer to do auto moves every 0.5s if enabled
187
+ def auto_update():
188
+ if auto_mode_state.value:
189
+ return sim.auto_move()
190
+ else:
191
+ # just update current plots and status without moving
192
+ return sim.render_env(), sim.render_slam_map(), status_text.value
193
+
194
+ timer = gr.Timer(every=0.5, fn=auto_update, outputs=[env_plot, slam_plot, status_text])
195
 
196
  demo.launch()