ספירת חזרות נשמעת פשוטה, אבל נשברת במקרי קצה. משתמש מבצע סקוואט לאט — חזרה אחת. רוטט בחלק העליון — המודל סופר שתיים. הטלפון נוטה — הקואורדינטות זזות, המונה קורא לא נכון. בניסיון שלנו, נתקלנו בפרויקט אפליקציית כושר שבו דיוק הספירה היה רק 60% בגלל רעידות מצלמה ומבני גוף משתנים. לאחר יישום כיול אדפטיבי והחלקת סדרות זמן, הדיוק עלה ל-92%. אלגוריתם זה מעבד כעת עד 30 פריימים בשנייה במכשירים בני חמש שנים. תיעוד Apple Vision ממליץ על תצוגת מצלמה צדדית לדיוק טוב יותר בשכיבות סמיכה, אבל התאמנו את הפתרון למצלמה קדמית — נוח יותר למשתמש.
איך פועלת ספירת חזרות בינה מלאכותית
הגישה זהה לניתוח יציבה: VNDetectHumanBodyPoseRequest (iOS) או ML Kit Pose Detection (אנדרואיד) מפיקים נקודות מפתח שלד בכל פריים. אבל לספירת חזרות, אנחנו מתעניינים לא ביציבה סטטית אלא בתנועה של נקודת מפתח לאורך זמן.
לכל תרגיל, אנו בוחרים נקודת מעקב:
| תרגיל | נקודת מעקב | ציר |
|---|---|---|
| סקוואט | ירך (leftHip / rightHip) | Y |
| שכיבת סמיכה | פרק כף יד או כתף | Y |
| כפיפת מרפקים | פרק כף יד | Y |
| ברפי | פרק כף יד + ראש | Y מורכב |
| לונג' | ברך | Y |
class RepCounter {
private var positionHistory: [Double] = []
private var repCount: Int = 0
private var state: MovementState = .neutral
private let minAmplitude: Double = 0.08 // 8% высоты экрана
enum MovementState {
case neutral, goingDown, bottom, goingUp, top
}
func update(normalizedY: Double) {
positionHistory.append(normalizedY)
if positionHistory.count > 30 {
positionHistory.removeFirst()
}
let smoothed = positionHistory.suffix(5).reduce(0, +) / 5
detectRep(smoothedY: smoothed)
}
private func detectRep(smoothedY: Double) {
let baseline = positionHistory.prefix(10).reduce(0, +) / 10
let deviation = smoothedY - baseline
switch state {
case .neutral where deviation > minAmplitude:
state = .goingDown
case .goingDown where deviation < minAmplitude * 0.3:
state = .bottom
case .bottom where deviation > minAmplitude * 0.7:
state = .goingUp
case .goingUp where deviation < 0:
state = .top
repCount += 1
onRepCompleted?(repCount)
state = .neutral
default:
break
}
}
}ממוצע נע על פני 5 פריימים (class RepCounter { private var positionHistory: [Double] = [] private var repCount: Int = 0 private var state: MovementState = .neutral private let minAmplitude: Double = 0.08 // 8% высоты экрана enum MovementState { case neutral, goingDown, bottom, goingUp, top } func update(normalizedY: Double) { positionHistory.append(normalizedY) if positionHistory.count > 30 { positionHistory.removeFirst() } let smoothed = positionHistory.suffix(5).reduce(0, +) / 5 detectRep(smoothedY: smoothed) } private func detectRep(smoothedY: Double) { let baseline = positionHistory.prefix(10).reduce(0, +) / 10 let deviation = smoothedY - baseline switch state { case .neutral where deviation > minAmplitude: state = .goingDown case .goingDown where deviation < minAmplitude * 0.3: state = .bottom case .bottom where deviation > minAmplitude * 0.7: state = .goingUp case .goingUp where deviation < 0: state = .top repCount += 1 onRepCompleted?(repCount) state = .neutral default: break } } } ) מחליק רעש של הערכת יציבה. בלעדיו, המונה רועד מתנודות נקודות מפתח בין פריימים.
למה כיול חשוב
suffix(5) הוא אחוז מגובה המסך. זה עובד לסקוואטים, אבל לכפיפות מרפקים נדרש ערך גדול יותר (טווח רחב יותר), ולשכיבות סמיכה ערך קטן יותר עם ציר שונה.
אנו מבצעים כיול או אנליטית (אימון על סרטונים מתויגים) או באמצעות קו בסיס אדפטיבי: 3 השניות הראשונות של התרגיל מודדות את משרעת התנועה ומתאימות ספים.
class AdaptiveRepCounter {
private var calibrationPhase = true
private var calibrationSamples: [Double] = []
private var dynamicAmplitude: Double = 0.05
func calibrate(y: Double) {
calibrationSamples.append(y)
if calibrationSamples.count >= 75 { // ~3 секунды при 25fps
let range = calibrationSamples.max()! - calibrationSamples.min()!
dynamicAmplitude = range * 0.4 // 40% от наблюдённой амплитуды
calibrationPhase = false
}
}
} נוסף: סינון חיובי שווא
תנועות פועמות (למשל, רעידות) יכולות להפעיל חזרה בטעות. כדי להתמודד עם זה, הכנסנו עיכוב זמן מינימלי בין שלבים — לפחות 200 אלפיות השנייה. זה מסנן תנודות מהירות מבלי לאבד דיוק במהירות רגילה.
איך לבחור נקודת מעקב לתרגיל
בחירת הנקודה תלויה במשרעת התנועה. לסקוואטים, הירך היא הטובה ביותר כי היא זזה משמעותית למעלה ולמטה. לשכיבות סמיכה, פרק כף יד או כתף עובדים מכיוון שהגוף נשאר ישר. לכפיפות מרפקים, פרק כף יד מספיק. לתרגילים מורכבים (ברפי), אנו משתמשים בשתי נקודות ומנתחים את השילוב שלהן. המהנדסים שלנו יבחרו את התצורה האופטימלית לסט התרגילים שלך — צור קשר.
מה כלול בעבודה
- ניתוח דרישות: הגדרת סט תרגילים ותנאי צילום
- שילוב הערכת יציבה: VNDetectHumanBodyPoseRequest ל-iOS או ML Kit לאנדרואיד
- יישום אלגוריתם ספירת חזרות: החלקה, זיהוי שלבים, כיול אדפטיבי
- ממשק שלד ומונה: הצגת נקודות ואנימציית חזרות
- בדיקות עם משתמשים אמיתיים: אימות דיוק בטלפונים שונים ותנאי תאורה
- תיעוד והדרכה: מסירת קוד והוראות כיול
כיוון מצלמה: תצוגה צדדית מול קדמית. אותה משימה דורשת פתרונות שונים בהתאם למיקום המצלמה:
| מיקום | יתרונות | חסרונות |
|---|---|---|
| תצוגה קדמית (מצלמה מול המשתמש) | שתי הכתפיים, הירכיים והראש נראים. טוב לסקוואטים, לונג'ים וברפי. | פחות מדויק לשכיבות סמיכה — פרקי כף יד לפעמים מוסתרים. |
| תצוגה צדדית | טוב יותר לניתוח זווית ברך (טכניקת סקוואט), מדויק יותר לשכיבות סמיכה. | דורש חצובה/מעמד, לא נוח בתנאים אמיתיים. |
רוב האפליקציות מייעלות לתצוגה קדמית עם הוראות "הנח את הטלפון 2 מטר לפניך בגובה המותן." צור קשר להערכת פרויקט — ננתח את הדרישות שלך ונציע פתרון אופטימלי.
תהליך עבודה
- אנליטיקה: דיון בתרגילי מטרה ותרחישי שימוש
- עיצוב: בחירת גישה (פרונטאנד/בקאנד) וסטאק
- יישום: שילוב הערכת יציבה, כתיבת אלגוריתם ספירה, כיול אדפטיבי, שכבת ממשק
- בדיקות: אימות במכשירים אמיתיים עם משתמשים שונים
- השקה: מסירת קוד, תיעוד, תמיכה
הערכות זמן
ספירת 3-5 תרגילים בסיסיים — משבוע עד שבועיים. זיהוי תרגילים אוטומטי + מעקב אימון מלא — מ-3 עד 5 שבועות. תקציב הפיתוח משתנה בהתאם למורכבות ומספר התרגילים. אנו עובדים עם פלטפורמות כושר גדולות ומבטיחים לפחות 90% דיוק לאחר כיול. עם למעלה מ-10 שנות ניסיון בפיתוח מובייל ולמעלה מ-50 פרויקטים מוצלחים. צור קשר לייעוץ על יישום ספירת חזרות בינה מלאכותית באפליקציה שלך.
משוב וממשק משתמש
�ונה מונפש גדול עם ניגודיות גבוהה, minAmplitude = 0.08, והבזק ויזואלי בכל חזרה. שכבת קווי שלד בין נקודות מפתח על הזנת המצלמה. שלד אדום כאשר הביטחון נמוך.







