code wiki / _hdl_build / nx_pose_coach_gate.nx

nx_pose_coach_gate.nx source

↩ module page · 81 lines · 4882 B

1// nx_pose_coach_gate.nx -- prove the COACH (R8): compare a user's motion to a reference and (T1) score a clean 2// attempt near-perfect, (T2) score a FORM ERROR low AND name the off joint, (T3) be TEMPO-INVARIANT (a slower 3// attempt with the same form scores near-perfect under DTW alignment though frame-aligned looks off). This is 4// "help people achieve their goals": name the mistake, forgive the tempo. expect_exit:0 ORIGINAL 5import "nx_syscalls.nx" 6import "nx_figure_render.nx" 7import "nx_pose_coach.nx" 8 9func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 10func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 11func pdg(d: i64) -> i64 { return d * 12868 / 180 } 12 13const T: i64 = 16 14const NJ: i64 = 3 // shoulder, elbow, wrist (the arm chain) 15 16// drive the arm through an arm-raise and record SHR/ELR/WRR positions per frame. 17// mode 0=clean, 1=elbow-bent error, 2=tempo warp (ease-in: same poses, different timing). 18func gen_track(sk: i64, track: *i64, mode: i64) -> i64 { 19 rig_build(sk) 20 var f: i64 = 0 21 while f < T { 22 var phase: i64 = f * 1000 / (T - 1) 23 if mode == 2 { phase = phase * phase / 1000 } // ease-in tempo warp 24 let pitch: i64 = phase * 90 / 1000 25 var elbow: i64 = 0 26 if mode == 1 { elbow = 65 } // form error: elbow bent 27 sk_pose(sk, RG_SHR, 0, pdg(pitch)) 28 sk_pose(sk, RG_ELR, pdg(elbow), 0) 29 sk_update(sk) 30 let b0: *i64 = sk_bone(sk, RG_SHR); track[(f*NJ+0)*3]=b0[15]; track[(f*NJ+0)*3+1]=b0[16]; track[(f*NJ+0)*3+2]=b0[17] 31 let b1: *i64 = sk_bone(sk, RG_ELR); track[(f*NJ+1)*3]=b1[15]; track[(f*NJ+1)*3+1]=b1[16]; track[(f*NJ+1)*3+2]=b1[17] 32 let b2: *i64 = sk_bone(sk, RG_WRR); track[(f*NJ+2)*3]=b2[15]; track[(f*NJ+2)*3+1]=b2[16]; track[(f*NJ+2)*3+2]=b2[17] 33 f = f + 1 34 } 35 return 0 36} 37func jname(j: i64) -> *u8 { if j == 0 { return "shoulder" as *u8 } if j == 1 { return "elbow" as *u8 } return "wrist" as *u8 } 38 39func main() -> i64 { 40 hw("=== nx_pose_coach_gate -- the coach reads your form (R8) ===\n" as *u8) 41 let sk: i64 = sys_mmap(sk_bytes()) as i64 42 let reft: *i64 = sys_mmap(T * NJ * 3 * 8) as *i64 43 let clean: *i64 = sys_mmap(T * NJ * 3 * 8) as *i64 44 let errt: *i64 = sys_mmap(T * NJ * 3 * 8) as *i64 45 let tempo: *i64 = sys_mmap(T * NJ * 3 * 8) as *i64 46 gen_track(sk, reft, 0) 47 gen_track(sk, clean, 0) 48 gen_track(sk, errt, 1) 49 gen_track(sk, tempo, 2) 50 let jdev: *i64 = sys_mmap(NJ * 8) as *i64 51 let tol: i64 = 220 // per-joint-frame deviation that scores 0 (bone units) 52 var fails: i64 = 0 53 54 // T1 clean attempt -> near perfect. Coaching score is driven by the WORST joint (one bad joint = bad form), 55 // not the average (which would dilute a single-joint error into a passing grade). 56 pc_score(reft, clean, T, NJ, jdev) 57 let wj1: i64 = pc_worst_joint(jdev, NJ) 58 let sc: i64 = pc_form_score(jdev[wj1], 1, tol) 59 hw(" clean attempt: worst-joint-dev="); pn(jdev[wj1]); hw(" form-score="); pn(sc); hw("/1000\n" as *u8) 60 if sc >= 950 { hw("T1 PASS clean attempt scores near-perfect\n" as *u8) } 61 else { fails = fails + 1; hw("T1 FAIL clean not perfect\n" as *u8) } 62 63 // T2 form error -> low score AND names the off joint 64 pc_score(reft, errt, T, NJ, jdev) 65 let wj: i64 = pc_worst_joint(jdev, NJ) 66 let se: i64 = pc_form_score(jdev[wj], 1, tol) 67 hw(" elbow-bent attempt: form-score="); pn(se); hw("/1000 worst-joint="); hw(jname(wj)); hw(" (dev="); pn(jdev[wj]); hw(")\n" as *u8) 68 if se < 700 { if wj == 2 { hw("T2 PASS form error caught + coach names the wrist (the hand landed wrong)\n" as *u8) } else { fails = fails + 1; hw("T2 FAIL named wrong joint\n" as *u8) } } 69 else { fails = fails + 1; hw("T2 FAIL error not penalised (score "); pn(se); hw(")\n" as *u8) } 70 71 // T3 tempo-invariance: frame-aligned looks off, DTW says form is fine 72 let tt_fa: i64 = pc_score(reft, tempo, T, NJ, jdev) 73 let tt_dtw: i64 = pc_dtw(reft, tempo, T, NJ) 74 hw(" slower attempt: frame-aligned dev="); pn(tt_fa); hw(" DTW-aligned dev="); pn(tt_dtw); hw("\n" as *u8) 75 if tt_dtw * 2 < tt_fa { hw("T3 PASS TEMPO-INVARIANT (DTW forgives the timing; same form -> DTW dev << frame-aligned)\n" as *u8) } 76 else { fails = fails + 1; hw("T3 FAIL DTW not << frame-aligned\n" as *u8) } 77 78 if fails == 0 { hw("POSE-COACH-GATE 3/3 GREEN -- coach scores form, names the error, forgives tempo (R8 engine up)\n" as *u8); sys_exit(0); return 0 } 79 hw("POSE-COACH-GATE RED fails="); pn(fails); hw("\n" as *u8) 80 sys_exit(1); return 1 81}