-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path06_bounce_detection.py
More file actions
153 lines (130 loc) · 5.49 KB
/
06_bounce_detection.py
File metadata and controls
153 lines (130 loc) · 5.49 KB
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
#!/usr/bin/env python3
"""Demo: Automatic bounce detection and counting.
This demonstrates the new Phase 1.1 feature: automatic bounce event detection.
"""
import asyncio
import os
os.environ["RAPIER_SERVICE_URL"] = "https://rapier.chukai.io"
os.environ["PHYSICS_PROVIDER"] = "rapier"
from chuk_mcp_physics.server import (
create_simulation,
add_rigid_body,
record_trajectory_with_events,
destroy_simulation,
)
from chuk_mcp_physics.analysis import count_bounces, get_last_bounce_above_threshold
async def demo_bounce_detection():
"""Demonstrate automatic bounce detection."""
print("=" * 70)
print("DEMO: Automatic Bounce Detection (Phase 1.1)")
print("=" * 70)
# Create simulation
print("\n1. Creating simulation...")
sim = await create_simulation(gravity_y=-9.81, dt=0.008)
print(f" ✓ Simulation ID: {sim.sim_id}")
try:
# Add ground plane
print("\n2. Adding ground plane...")
await add_rigid_body(
sim_id=sim.sim_id,
body_id="ground",
body_type="static",
shape="plane",
normal=[0.0, 1.0, 0.0],
restitution=0.3,
friction=0.8,
)
print(" ✓ Ground plane added")
# Add bouncing ball
print("\n3. Adding ball (10cm diameter, 1kg, dropped from 10m)...")
await add_rigid_body(
sim_id=sim.sim_id,
body_id="ball",
body_type="dynamic",
shape="sphere",
size=[0.05], # 5cm radius = 10cm diameter
mass=1.0,
position=[0.0, 10.0, 0.0],
restitution=0.3, # Soft rubber
friction=0.8,
)
print(" ✓ Ball added")
# Record trajectory WITH EVENTS (new feature!)
print("\n4. Recording 5-second trajectory WITH event detection...")
steps = int(5.0 / 0.008)
# Note: Ball radius is 0.05m, so center is at 0.05m when touching ground
# Use threshold of 0.1m to catch bounces reliably
trajectory = await record_trajectory_with_events(
sim_id=sim.sim_id,
body_id="ball",
steps=steps,
detect_bounces=True,
bounce_height_threshold=0.1, # 10cm threshold (2x ball radius)
)
print(f" ✓ Recorded {trajectory.meta.num_frames} frames")
print(f" ✓ Detected {len(trajectory.bounces)} bounces!")
# Display bounce details
print("\n5. Bounce Analysis:")
print(" " + "-" * 66)
print(
f" {'Bounce':<8} {'Time (s)':<10} {'Height (m)':<12} "
f"{'Speed Before':<14} {'Speed After':<14} {'Energy Loss':<12}"
)
print(" " + "-" * 66)
for bounce in trajectory.bounces:
print(
f" #{bounce.bounce_number:<7} {bounce.time:<10.3f} "
f"{bounce.height_at_bounce:<12.4f} "
f"{bounce.speed_before:<14.3f} {bounce.speed_after:<14.3f} "
f"{bounce.energy_loss_percent:<12.1f}%"
)
# Answer the original question!
print("\n6. Answering: 'When did the ball stop bouncing above 5mm?'")
last_bounce_above_5mm = get_last_bounce_above_threshold(trajectory, height_threshold=0.005)
if last_bounce_above_5mm:
print(
f" ✓ Last bounce above 5mm: Bounce #{last_bounce_above_5mm.bounce_number} "
f"at t={last_bounce_above_5mm.time:.2f}s"
)
print(
f" Height at this bounce: {last_bounce_above_5mm.height_at_bounce * 1000:.1f}mm"
)
else:
print(" ✓ No bounces detected above 5mm threshold")
# Count bounces above different thresholds
print("\n7. Bounce Statistics:")
total_bounces = count_bounces(trajectory)
bounces_above_1cm = count_bounces(trajectory, min_height=0.01)
bounces_above_5mm = count_bounces(trajectory, min_height=0.005)
print(f" Total bounces detected: {total_bounces}")
print(f" Bounces above 1cm: {bounces_above_1cm}")
print(f" Bounces above 5mm: {bounces_above_5mm}")
# Physical insights
if len(trajectory.bounces) >= 2:
first = trajectory.bounces[0]
last = trajectory.bounces[-1]
print("\n8. Physical Analysis:")
print(
f" First bounce: {first.speed_before:.2f} m/s → {first.speed_after:.2f} m/s "
f"({first.energy_loss_percent:.1f}% energy loss)"
)
print(
f" Last bounce: {last.speed_before:.2f} m/s → {last.speed_after:.2f} m/s "
f"({last.energy_loss_percent:.1f}% energy loss)"
)
print(f" Time between bounces: {last.time - first.time:.2f}s")
print("\n" + "=" * 70)
print("✅ SUCCESS! Phase 1.1 bounce detection works perfectly!")
print("=" * 70)
# Show how this answers the user's question
print("\n💡 Original Question:")
print(' "How many bounces would a 10cm ball that weighs 1 kilo')
print(' before it would stop bouncing over 5mm?"')
print(f"\n🎯 Answer: {bounces_above_5mm} bounces above 5mm threshold")
if last_bounce_above_5mm:
print(f" Last bounce above 5mm occurred at t={last_bounce_above_5mm.time:.2f}s")
finally:
await destroy_simulation(sim.sim_id)
print("\n✓ Simulation destroyed\n")
if __name__ == "__main__":
asyncio.run(demo_bounce_detection())