malansi commited on
Commit
37046a6
·
verified ·
1 Parent(s): f43b4be

Update hand_module/collect_hand_data.py — 2026-07-07 18:04

Browse files
Files changed (1) hide show
  1. code/collect_hand_data.py +234 -0
code/collect_hand_data.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # collect_hand_data.py
2
+ # Guided EMG data collection for prosthetic hand control
3
+ # 10 gestures x 10 rounds x 5 seconds
4
+
5
+ import asyncio
6
+ import myo
7
+ from myo import ClassifierMode, EMGMode, IMUMode
8
+ import csv
9
+ import os
10
+ import time
11
+ from datetime import datetime
12
+
13
+ FS = 200
14
+
15
+ GESTURES = {
16
+ 0: {
17
+ 'name': 'rest',
18
+ 'instruction': 'Rest your hand completely flat — do not move anything',
19
+ },
20
+ 1: {
21
+ 'name': 'fist',
22
+ 'instruction': 'Close ALL fingers into a tight fist',
23
+ },
24
+ 2: {
25
+ 'name': 'grasp',
26
+ 'instruction': 'Curl fingers into a C-shape — like holding a cup or bottle. NOT a full fist, leave space in the middle',
27
+ },
28
+ 3: {
29
+ 'name': 'index',
30
+ 'instruction': 'Extend INDEX finger only — keep all others closed in a fist',
31
+ },
32
+ 4: {
33
+ 'name': 'middle',
34
+ 'instruction': 'Extend MIDDLE finger only — keep all others closed in a fist',
35
+ },
36
+ 5: {
37
+ 'name': 'ring',
38
+ 'instruction': 'Extend RING finger only — keep all others closed. Take your time, this is hard',
39
+ },
40
+ 6: {
41
+ 'name': 'pinky',
42
+ 'instruction': 'Extend PINKY finger only — keep all others closed in a fist',
43
+ },
44
+ 7: {
45
+ 'name': 'thumb',
46
+ 'instruction': 'Extend THUMB only — keep all other fingers closed in a fist',
47
+ },
48
+ 8: {
49
+ 'name': 'wrist_rotate_out',
50
+ 'instruction': 'Rotate wrist so palm faces DOWN toward the table — arm stays still',
51
+ },
52
+ 9: {
53
+ 'name': 'wrist_rotate_in',
54
+ 'instruction': 'Rotate wrist so palm faces UP toward you — arm stays still',
55
+ },
56
+ }
57
+
58
+ ROUNDS = 10
59
+ HOLD_SECONDS = 5
60
+ REST_SECONDS = 4
61
+ COUNTDOWN = 4
62
+
63
+ class State:
64
+ emg_buffer = []
65
+ is_recording = False
66
+ sample_count = 0
67
+
68
+ STATE = State()
69
+
70
+
71
+ class Collector(myo.MyoClient):
72
+ async def on_emg_data(self, emg: myo.EMGData):
73
+ for sample in [emg.sample1, emg.sample2]:
74
+ if STATE.is_recording:
75
+ STATE.emg_buffer.append(list(sample))
76
+ STATE.sample_count += 1
77
+
78
+ async def on_imu_data(self, _): pass
79
+ async def on_classifier_event(self, _): pass
80
+ async def on_aggregated_data(self, _): pass
81
+ async def on_emg_data_aggregated(self, _): pass
82
+ async def on_fv_data(self, _): pass
83
+ async def on_motion_event(self, _): pass
84
+
85
+ def save_session(session_dir, all_data):
86
+ os.makedirs(session_dir, exist_ok=True)
87
+ filepath = f"{session_dir}/emg_data.csv"
88
+
89
+ with open(filepath, 'w', newline='') as f:
90
+ writer = csv.writer(f)
91
+ writer.writerow([
92
+ 'emg_0', 'emg_1', 'emg_2', 'emg_3',
93
+ 'emg_4', 'emg_5', 'emg_6', 'emg_7',
94
+ 'label', 'gesture', 'timestamp'
95
+ ])
96
+ for row in all_data:
97
+ writer.writerow(row)
98
+
99
+ print(f"\n Saved {len(all_data):,} samples → {filepath}\n")
100
+ return filepath
101
+
102
+ async def countdown_display(seconds, message):
103
+ for i in range(seconds, 0, -1):
104
+ print(f"\r ⏳ {message} — {i}s ", end='', flush=True)
105
+ await asyncio.sleep(1)
106
+ print(f"\r ✅ GO! ")
107
+
108
+ async def run_collection():
109
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
110
+ session_dir = f"hand_module/sessions/session_{timestamp}"
111
+
112
+ print("\n" + "═" * 64)
113
+ print(" PROSTHETIC HAND — EMG DATA COLLECTION")
114
+ print("═" * 64)
115
+ print(f"\n Gestures : {len(GESTURES)}")
116
+ print(f" Rounds : {ROUNDS} per gesture")
117
+ print(f" Hold : {HOLD_SECONDS}s each")
118
+ print(f" Rest : {REST_SECONDS}s between gestures")
119
+ print(f"\n Place your arm comfortably on the table.")
120
+ print(f" Only your hand/wrist moves — keep your arm still.\n")
121
+ print(" Starting in 8 seconds — get ready!")
122
+ await asyncio.sleep(8)
123
+
124
+ all_data = []
125
+ total_steps = len(GESTURES) * ROUNDS
126
+ step = 0
127
+
128
+ for gesture_id, gesture_info in GESTURES.items():
129
+ name = gesture_info['name']
130
+ instruction = gesture_info['instruction']
131
+
132
+ print(f"\n{'═'*64}")
133
+ print(f" GESTURE: {name.upper()}")
134
+ print(f" {instruction}")
135
+ print(f"{'═'*64}")
136
+ print(f" Study this gesture — starting in {COUNTDOWN} seconds...")
137
+
138
+ await countdown_display(COUNTDOWN, f"Prepare for {name.upper()}")
139
+
140
+ for round_num in range(1, ROUNDS + 1):
141
+ step += 1
142
+ progress = f"[{step}/{total_steps}]"
143
+
144
+ print(f"\n {progress} Round {round_num}/{ROUNDS} — {name.upper()}")
145
+ print(f" 👉 {instruction}\n")
146
+
147
+ # countdown قبل التسجيل
148
+ await countdown_display(3, "Get ready")
149
+
150
+ # ابدأ التسجيل
151
+ print(f" 🟢 RECORDING — hold steady for {HOLD_SECONDS} seconds!\n")
152
+ STATE.emg_buffer = []
153
+ STATE.sample_count = 0
154
+ STATE.is_recording = True
155
+
156
+ start = time.time()
157
+ while time.time() - start < HOLD_SECONDS:
158
+ elapsed = time.time() - start
159
+ progress_bar = int(elapsed / HOLD_SECONDS * 20)
160
+ bar = '█' * progress_bar + '░' * (20 - progress_bar)
161
+ print(f"\r [{bar}] {elapsed:.1f}s / {HOLD_SECONDS}s "
162
+ f"({STATE.sample_count} samples)",
163
+ end='', flush=True)
164
+ await asyncio.sleep(0.1)
165
+
166
+ STATE.is_recording = False
167
+ print()
168
+
169
+ # احفظ الـ samples مع الـ label
170
+ ts = datetime.now().isoformat()
171
+ for sample in STATE.emg_buffer:
172
+ row = sample + [gesture_id, name, ts]
173
+ all_data.append(row)
174
+
175
+ samples = len(STATE.emg_buffer)
176
+ print(f" ✅ Captured {samples} samples "
177
+ f"({samples/FS:.1f}s)")
178
+
179
+ # راحة بين الـ rounds (إلا آخر round في كل gesture)
180
+ if round_num < ROUNDS:
181
+ print(f"\n 😌 Rest...")
182
+ await countdown_display(REST_SECONDS, "Relax your hand")
183
+
184
+ # راحة أطول بين الـ gestures
185
+ if gesture_id < len(GESTURES) - 1:
186
+ print(f"\n 💤 Gesture complete! Rest for 6 seconds before next gesture.")
187
+ await countdown_display(6, "Relax completely")
188
+
189
+ # احفظ كل الداتا
190
+ filepath = save_session(session_dir, all_data)
191
+
192
+ # ملخص
193
+ print("\n" + "═" * 64)
194
+ print(" SESSION COMPLETE")
195
+ print("═" * 64)
196
+
197
+ print(f"\n {'Gesture':<20} {'Samples':<12} {'Duration'}")
198
+ print(" " + "─" * 45)
199
+ for gesture_id, info in GESTURES.items():
200
+ name = info['name']
201
+ samples = sum(1 for r in all_data if r[8] == gesture_id)
202
+ dur = samples / FS
203
+ print(f" {name:<20} {samples:<12,} {dur:.1f}s")
204
+
205
+ total = len(all_data)
206
+ print(f"\n Total: {total:,} samples ({total/FS:.0f}s)")
207
+ print(f" Saved: {filepath}")
208
+ print("═" * 64)
209
+
210
+
211
+ async def main():
212
+ print("🔍 Scanning for Myo Armband...")
213
+ client = await Collector.with_device()
214
+ print(f"✅ Connected: {client.device.name}\n")
215
+
216
+ await client.setup(
217
+ classifier_mode=ClassifierMode.DISABLED,
218
+ emg_mode=EMGMode.SEND_EMG,
219
+ imu_mode=IMUMode.SEND_DATA,
220
+ )
221
+ await client.start()
222
+
223
+ try:
224
+ await run_collection()
225
+ except KeyboardInterrupt:
226
+ print("\n\n Interrupted — saving collected data so far...")
227
+ finally:
228
+ await client.stop()
229
+ await client.disconnect()
230
+ print(" Done.")
231
+
232
+
233
+ if __name__ == "__main__":
234
+ asyncio.run(main())