YashsharmaPhD commited on
Commit
0a30948
Β·
verified Β·
1 Parent(s): 289bd82

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -133
app.py CHANGED
@@ -1,145 +1,201 @@
1
  import gradio as gr
2
  import matplotlib.pyplot as plt
 
 
3
  import numpy as np
 
4
  import threading
5
  import time
6
- import random
7
- from io import BytesIO
8
-
9
- class SLAMSimulation:
10
- def __init__(self):
11
- self.reset(10)
12
-
13
- def reset(self, obstacle_count=10):
14
- self.robot_pos = [0, 0]
15
- self.robot_path = [tuple(self.robot_pos)]
16
- self.map = set()
17
- self.obstacles = []
18
- self.auto_mode = False
19
-
20
- for _ in range(obstacle_count):
21
- self.obstacles.append({
22
- "x": random.randint(-10, 10),
23
- "z": random.randint(-10, 10),
24
- "radius": random.uniform(0.5, 1.5),
25
- })
26
-
27
- return self.render_env(), self.render_slam_map(), f"πŸ”„ Simulation reset with {obstacle_count} obstacles"
28
-
29
- def move(self, direction):
30
- dx, dz = 0, 0
31
- if direction == "up":
32
- dz = 1
33
- elif direction == "down":
34
- dz = -1
35
- elif direction == "left":
36
- dx = -1
37
- elif direction == "right":
38
- dx = 1
39
- self.robot_pos[0] += dx
40
- self.robot_pos[1] += dz
41
- self.robot_path.append(tuple(self.robot_pos))
42
- self.update_map()
43
- return self.render_env(), self.render_slam_map(), f"πŸ“ Moved {direction}"
44
-
45
- def auto_move(self):
46
- direction = random.choice(["up", "down", "left", "right"])
47
- return self.move(direction)
48
-
49
- def update_map(self):
50
- x, z = self.robot_pos
51
- for dx in range(-2, 3):
52
- for dz in range(-2, 3):
53
- self.map.add((x + dx, z + dz))
54
-
55
- def render_env(self):
56
- fig, ax = plt.subplots()
57
- ax.set_xlim(-15, 15)
58
- ax.set_ylim(-15, 15)
59
- ax.set_title("Environment")
60
-
61
- for obs in self.obstacles:
62
- circle = plt.Circle((obs["x"], obs["z"]), obs["radius"],
63
- color="gray", alpha=0.6, edgecolor="black")
64
- ax.add_patch(circle)
65
-
66
- path = np.array(self.robot_path)
67
- ax.plot(path[:, 0], path[:, 1], linestyle='--', color='blue')
68
- ax.plot(self.robot_pos[0], self.robot_pos[1], 'ro', label='Robot')
69
- ax.legend()
70
-
71
- return self.fig_to_image(fig)
72
-
73
- def render_slam_map(self):
74
- fig, ax = plt.subplots()
75
- ax.set_xlim(-15, 15)
76
- ax.set_ylim(-15, 15)
77
- ax.set_title("SLAM Map")
78
-
79
- for (x, z) in self.map:
80
- ax.plot(x, z, 'ks', markersize=4)
81
-
82
- ax.plot(self.robot_pos[0], self.robot_pos[1], 'ro', label='Robot')
83
- ax.legend()
84
-
85
- return self.fig_to_image(fig)
86
-
87
- def fig_to_image(self, fig):
88
- buf = BytesIO()
89
- fig.savefig(buf, format='png')
90
- buf.seek(0)
91
- plt.close(fig)
92
- return buf
93
-
94
- sim = SLAMSimulation()
95
- auto_mode_state = {"value": False}
96
-
97
- def manual_control(direction):
98
- return sim.move(direction)
99
-
100
- def auto_toggle():
101
- auto_mode_state["value"] = not auto_mode_state["value"]
102
- return f"πŸ€– Auto mode {'ON' if auto_mode_state['value'] else 'OFF'}"
103
-
104
- def reset_sim():
105
- return sim.reset(10)
106
-
107
- def background_loop(update_fn, delay=0.5):
108
- def loop():
109
- while True:
110
- if auto_mode_state["value"]:
111
- env, slam, status = sim.auto_move()
112
- update_fn(env, slam, status)
113
- time.sleep(delay)
114
- threading.Thread(target=loop, daemon=True).start()
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  with gr.Blocks() as demo:
117
- env_plot = gr.Image(label="Environment")
118
- slam_plot = gr.Image(label="SLAM Map")
119
- status_text = gr.Textbox(label="Status")
 
 
120
 
121
  with gr.Row():
122
- btn_up = gr.Button("⬆️ Up")
123
- btn_down = gr.Button("⬇️ Down")
124
- btn_left = gr.Button("⬅️ Left")
125
- btn_right = gr.Button("➑️ Right")
126
  with gr.Row():
127
- btn_auto = gr.Button("πŸ€– Toggle Auto")
128
- btn_reset = gr.Button("πŸ”„ Reset")
129
-
130
- def update_ui(env, slam, status):
131
- env_plot.update(env)
132
- slam_plot.update(slam)
133
- status_text.update(status)
134
-
135
- btn_up.click(fn=lambda: sim.move("up"), outputs=[env_plot, slam_plot, status_text])
136
- btn_down.click(fn=lambda: sim.move("down"), outputs=[env_plot, slam_plot, status_text])
137
- btn_left.click(fn=lambda: sim.move("left"), outputs=[env_plot, slam_plot, status_text])
138
- btn_right.click(fn=lambda: sim.move("right"), outputs=[env_plot, slam_plot, status_text])
139
- btn_reset.click(fn=reset_sim, outputs=[env_plot, slam_plot, status_text])
140
- btn_auto.click(fn=auto_toggle, outputs=[status_text])
141
-
142
- # Start background loop for auto mode
143
- background_loop(update_ui)
144
 
145
  demo.launch()
 
1
  import gradio as gr
2
  import matplotlib.pyplot as plt
3
+ import matplotlib.patches as patches
4
+ import matplotlib.image as mpimg
5
  import numpy as np
6
+ import random
7
  import threading
8
  import time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ # Global State
11
+ pose = {"x": 0, "z": 0, "angle": 0}
12
+ trajectory = [(0, 0)]
13
+ obstacle_hits = []
14
+ color_index = 0
15
+ rgb_colors = ['#FF5733', '#33FF57', '#3357FF'] # More distinguishable colors
16
+ noise_enabled = True
17
+ auto_mode = False
18
+ obstacles = []
19
+
20
+ # Thread lock for safety in auto mode
21
+ lock = threading.Lock()
22
+
23
+ # --- Obstacle Generation ---
24
+ def generate_obstacles(count=10):
25
+ return [{
26
+ "x": random.uniform(-8, 8),
27
+ "z": random.uniform(-8, 8),
28
+ "radius": random.uniform(0.5, 1.2)
29
+ } for _ in range(count)]
30
+
31
+ obstacles = generate_obstacles(10)
32
+
33
+ # --- Collision Check ---
34
+ def check_collision(x, z):
35
+ for obs in obstacles:
36
+ dist = np.sqrt((obs["x"] - x)**2 + (obs["z"] - z)**2)
37
+ if dist <= obs["radius"] + 0.2:
38
+ return True
39
+ return False
40
+
41
+ # --- Movement ---
42
+ def move_robot(direction):
43
+ global pose, trajectory
44
+ step = 1
45
+ direction = direction.upper()
46
+ dx, dz, angle = 0, 0, 0
47
+
48
+ if direction == "W":
49
+ dz, angle = step, 90
50
+ elif direction == "S":
51
+ dz, angle = -step, -90
52
+ elif direction == "A":
53
+ dx, angle = -step, 180
54
+ elif direction == "D":
55
+ dx, angle = step, 0
56
+ else:
57
+ return render_env(), render_slam_map(), "❌ Invalid Key"
58
+
59
+ new_x, new_z = pose["x"] + dx, pose["z"] + dz
60
+
61
+ if check_collision(new_x, new_z):
62
+ return render_env(), render_slam_map(), "🚫 Collision detected!"
63
+
64
+ pose["x"], pose["z"], pose["angle"] = new_x, new_z, angle
65
+ noisy_x = new_x + random.uniform(-0.1, 0.1) if noise_enabled else new_x
66
+ noisy_z = new_z + random.uniform(-0.1, 0.1) if noise_enabled else new_z
67
+ trajectory.append((noisy_x, noisy_z))
68
+
69
+ return render_env(), render_slam_map(), f"Moved {direction}"
70
+
71
+ # --- Rendering ---
72
+ def render_env():
73
+ global obstacle_hits
74
+ fig, ax = plt.subplots(figsize=(5, 5))
75
+ ax.set_xlim(-10, 10)
76
+ ax.set_ylim(-10, 10)
77
+ ax.set_title("SLAM Environment View")
78
+
79
+ # Background
80
+ try:
81
+ bg = mpimg.imread("map.png")
82
+ ax.imshow(bg, extent=(-10, 10, -10, 10), alpha=0.2)
83
+ except:
84
+ pass
85
+
86
+ # Obstacles
87
+ for obs in obstacles:
88
+ circ = plt.Circle((obs["x"], obs["z"]), obs["radius"], color="gray", alpha=0.6)
89
+ ax.add_patch(circ)
90
+
91
+ # Robot
92
+ ax.plot(pose["x"], pose["z"], 'ro', markersize=8)
93
+
94
+ # LIDAR Simulation
95
+ obstacle_hits.clear()
96
+ for ang in np.linspace(0, 2*np.pi, 24):
97
+ for r in np.linspace(0, 3, 30):
98
+ scan_x = pose["x"] + r * np.cos(ang)
99
+ scan_z = pose["z"] + r * np.sin(ang)
100
+ if check_collision(scan_x, scan_z):
101
+ ax.plot([pose["x"], scan_x], [pose["z"], scan_z], 'g-', linewidth=0.5)
102
+ obstacle_hits.append((scan_x, scan_z))
103
+ break
104
+
105
+ plt.close(fig)
106
+ return fig
107
+
108
+ def render_slam_map():
109
+ global color_index
110
+ fig, ax = plt.subplots(figsize=(5, 5))
111
+ ax.set_title("SLAM Trajectory Map")
112
+ ax.grid(True)
113
+
114
+ x_vals, z_vals = zip(*trajectory)
115
+ ax.plot(x_vals, z_vals, 'bo-', markersize=3)
116
+
117
+ if obstacle_hits:
118
+ current_color = rgb_colors[color_index % len(rgb_colors)]
119
+ for hit in obstacle_hits[-20:]:
120
+ ax.plot(hit[0], hit[1], 'o', color=current_color, markersize=6)
121
+ color_index += 1
122
+
123
+ plt.close(fig)
124
+ return fig
125
+
126
+ # --- Handlers ---
127
+ def toggle_noise():
128
+ global noise_enabled
129
+ noise_enabled = not noise_enabled
130
+ return "Noise: ON" if noise_enabled else "Noise: OFF"
131
+
132
+ def reset_sim(count):
133
+ global pose, trajectory, obstacles, obstacle_hits, color_index
134
+ pose = {"x": 0, "z": 0, "angle": 0}
135
+ trajectory = [(0, 0)]
136
+ obstacle_hits.clear()
137
+ color_index = 0
138
+ obstacles = generate_obstacles(int(count))
139
+ return render_env(), render_slam_map(), f"Simulation Reset with {count} obstacles"
140
+
141
+ def handle_text_input(direction):
142
+ return move_robot(direction.strip().upper())
143
+
144
+ def auto_movement(update_callback):
145
+ global auto_mode
146
+ directions = ['W', 'A', 'S', 'D']
147
+ while auto_mode:
148
+ with lock:
149
+ direction = random.choice(directions)
150
+ env, slam, msg = move_robot(direction)
151
+ update_callback(env, slam, msg)
152
+ time.sleep(1)
153
+
154
+ def toggle_auto_mode(env_plot, slam_plot, status_text):
155
+ global auto_mode
156
+ auto_mode = not auto_mode
157
+
158
+ if auto_mode:
159
+ def update_ui(e, s, t):
160
+ env_plot.update(value=e)
161
+ slam_plot.update(value=s)
162
+ status_text.update(value=t)
163
+
164
+ thread = threading.Thread(target=auto_movement, args=(update_ui,), daemon=True)
165
+ thread.start()
166
+ return "🟒 Auto Mode: ON"
167
+ else:
168
+ return "βšͺ Auto Mode: OFF"
169
+
170
+ # --- Gradio Interface ---
171
  with gr.Blocks() as demo:
172
+ gr.Markdown("## πŸ€– SLAM Simulation: Noise, Collision, and Auto Mode")
173
+
174
+ obstacle_slider = gr.Slider(1, 20, value=10, step=1, label="Obstacle Count")
175
+ direction_input = gr.Textbox(label="Manual Control (W/A/S/D)", placeholder="Type a direction...")
176
+ status_text = gr.Textbox(label="Status", interactive=False)
177
 
178
  with gr.Row():
179
+ env_plot = gr.Plot(label="Environment View")
180
+ slam_plot = gr.Plot(label="SLAM Map")
181
+
 
182
  with gr.Row():
183
+ w = gr.Button("⬆️ W")
184
+ a = gr.Button("⬅️ A")
185
+ s = gr.Button("⬇️ S")
186
+ d = gr.Button("➑️ D")
187
+ reset = gr.Button("πŸ”„ Reset")
188
+ toggle = gr.Button("πŸ”€ Toggle Noise")
189
+ auto = gr.Button("πŸ€– Toggle Auto")
190
+
191
+ w.click(fn=lambda: move_robot("W"), outputs=[env_plot, slam_plot, status_text])
192
+ a.click(fn=lambda: move_robot("A"), outputs=[env_plot, slam_plot, status_text])
193
+ s.click(fn=lambda: move_robot("S"), outputs=[env_plot, slam_plot, status_text])
194
+ d.click(fn=lambda: move_robot("D"), outputs=[env_plot, slam_plot, status_text])
195
+
196
+ reset.click(fn=reset_sim, inputs=[obstacle_slider], outputs=[env_plot, slam_plot, status_text])
197
+ toggle.click(fn=lambda: (None, None, toggle_noise()), outputs=[env_plot, slam_plot, status_text])
198
+ auto.click(fn=toggle_auto_mode, inputs=[env_plot, slam_plot, status_text], outputs=auto)
199
+ direction_input.submit(fn=handle_text_input, inputs=direction_input, outputs=[env_plot, slam_plot, status_text])
200
 
201
  demo.launch()