diff --git a/README.md b/README.md
index f5027e7..de5ede7 100644
--- a/README.md
+++ b/README.md
@@ -163,6 +163,7 @@ DYNAMIC_GESTURE = {
| Название | Описание | Ссылка | Количество примеров | Классы |
|----------|----------|--------|---------------------|--------|
| **special_gestures_v1** | Набор фотографий для распознавания специальных жестов (домик, крест, none). Собран с помощью `capture_photo.py` и стоковых изображений, размечен через `annotate.py`. | [Скачать](https://disk.yandex.ru/d/F25kMjrmZ8xwRA) | 77 (после балансировки, 13 на класс dome/cross, 51 none) | `dome`, `cross`, `none` |
+| **dynamic_data_v1** | csv файл с последовательностью точек скелета для 14 кадров | [Скачать](https://disk.yandex.ru/d/hnm6apfuxJsEyw) | 81 (по 20 на махи правой и левой рукой, 61 на none) | `none`, `wave_left`, `wave_right` |
## Модели
| Название | Тип | Архитектура / параметры | Вход | Выход | Точность (test) | Precision / Recall (по классам) | Ссылка | Распознаваемые жесты |
@@ -170,3 +171,4 @@ DYNAMIC_GESTURE = {
| **special_gestures_mlp** | MLP (scikit-learn) | Скрытые слои: (64, 32), активация ReLU, Adam, early stopping, 500 эпох | 99 нормализованных координат скелета (33 точки × 3) | 3 класса (dome, cross, none) | **0.56** | **dome**: P=0.00, R=0.00
**cross**: P=0.00, R=0.00
**none**: P=0.60, R=0.90 | [Скачать](https://disk.yandex.ru/d/I1WyAfN3PJM9fw) | `dome` (руки над головой домиком)
`cross` (предплечья скрещены на груди)
`none` (остальные) |
| **special_gestures_rf** | Random Forest | 50 деревьев, max_depth=10, random_state=42 | 99 нормализованных координат скелета (33 точки × 3) | 3 класса (dome, cross, none) | **0.87** | **dome**: P=0.67, R=1.00
**cross**: P=0.750, R=1.00
**none**: P=0.90, R=0.90 | [Скачать](https://disk.yandex.ru/d/fcXxQh4LGjqA1g) |`dome` (руки над головой домиком)
`cross` (предплечья скрещены на груди)
`none` (остальные) |
| **special_gestures_lin** | Logistic Regression | max_iter=1000, random_state=42 | 99 нормализованных координат скелета (33 точки × 3) | 3 класса (dome, cross, none) | **0.94** | **dome**: P=1.00, R=1.00
**cross**: P=0.750, R=1.00
**none**: P=1.00, R=0.90 | [Скачать](https://disk.yandex.ru/d/4_XCPEeOGvNX6g) |`dome` (руки над головой домиком)
`cross` (предплечья скрещены на груди)
`none` (остальные) |
+| **dynamic_model** | LSTM | 2 слоя LSTM по 64 нейрона, Dropout(0.3), Dense(3, softmax), optimizer=Adam, loss=sparse_categorical_crossentropy, epochs=50, batch_size=16 | Последовательность из 14 кадров, каждый кадр – 99 признаков (нормализованные координаты скелета) | 3 класса (none, wave_left, wave_right) | 0.8235 | Precision / Recall (по классам) | [Скачать](https://disk.yandex.ru/d/yDol79iB2ZlS6Q) | `wave_left` (махание левой рукой) `wave_right` (махание правой рукой) `none` (отсутствие жеста) |
diff --git a/config.py b/config.py
index 6e7c125..ef5edfe 100644
--- a/config.py
+++ b/config.py
@@ -16,18 +16,19 @@ class Config:
# ===== Специальные жесты =====
SPECIAL_GESTURE_MODE = 'ml' #ml #geometric
- ML_GESTURE_MODEL = '/home/ubuntu/sirius/models/rf/special_model.pkl'
+ ML_GESTURE_MODEL = '/home/ubuntu/sirius/models/rf/special_gestures_rf.pkl'
ML_GESTURE_CLASSES = ['dome', 'cross', 'none']
DYNAMIC_GESTURE = {
'enabled': True, # включить/выключить
- 'model_path': 'dynamic_model.h5',
- 'classes_path': 'dynamic_model_classes.pkl',
+ 'model_path': '/home/ubuntu/sirius/models/lstm/dynamic_model.h5',
+ 'classes_path': '/home/ubuntu/sirius/models/lstm/dynamic_model_classes.pkl',
'window_size': 14, # длина буфера
- 'threshold': 0.7, # порог уверенности
+ 'threshold': 0.8, # порог уверенности
'actions': {
'wave_left': 'reset', # при жесте wave_left – перезапуск симулятора
- 'wave_right': 'restart', # при wave_right – рестарт
+ 'wave_right': 'restart' # при wave_right – рестарт
+ }
}
# ===== Робот =====
diff --git a/main.py b/main.py
index 13d0141..0d5e2be 100644
--- a/main.py
+++ b/main.py
@@ -53,6 +53,9 @@ def main():
print("Линейная скорость: правая рука в сторону, угловая: левая рука")
print("В симуляторе: R – перезапуск после Game Over/победы")
print("q – выход")
+
+ last_dynamic_gesture = None
+ dynamic_gesture_counter = 0
while True:
ret, frame = cap.read()
@@ -67,16 +70,17 @@ def main():
if result['success']:
landmarks = result['landmarks']
- if result['success']:
- if cfg.DYNAMIC_GESTURE['enabled']:
- dynamic_predictor.add_frame(landmarks)
- gesture = dynamic_predictor.predict()
- if gesture:
- action = cfg.DYNAMIC_GESTURE['actions'].get(gesture)
- if action == 'reset':
- robot.reset()
- elif action == 'restart':
- robot.reset()
+ if cfg.DYNAMIC_GESTURE['enabled']:
+ dynamic_predictor.add_frame(landmarks)
+ gesture = dynamic_predictor.predict()
+ if gesture:
+ action = cfg.DYNAMIC_GESTURE['actions'].get(gesture)
+ if action == 'reset':
+ robot.reset()
+ elif action == 'restart':
+ robot.reset()
+ last_dynamic_gesture = gesture
+ dynamic_gesture_counter = 30
vis_frame = detector.draw_landmarks(frame, result['pose_landmarks'])
@@ -99,6 +103,12 @@ def main():
if special != 'none':
cv2.putText(vis_frame, f"Special: {special}", (10, 90),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,255,0), 2)
+
+ if dynamic_gesture_counter > 0 and last_dynamic_gesture:
+ cv2.putText(vis_frame, f"Dynamic: {last_dynamic_gesture}", (10, 120),
+ cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
+ dynamic_gesture_counter -= 1
+
else:
vis_frame = frame
cv2.putText(vis_frame, "No pose detected", (10, 30),
diff --git a/ml_gestures_dynamic/predict.py b/ml_gestures_dynamic/predict.py
index a9c59ca..412eae6 100644
--- a/ml_gestures_dynamic/predict.py
+++ b/ml_gestures_dynamic/predict.py
@@ -14,6 +14,8 @@ class DynamicGesturePredictor:
self.threshold = threshold
def add_frame(self, landmarks):
+ if landmarks is None or np.isnan(landmarks).any():
+ landmarks = np.zeros(99)
self.buffer.append(landmarks)
def predict(self):