
Elk pose-model logt 15 goede squats op een knie die inzakt. Ik bouwde de AI die herhaling 9 vangt.
Ik bouwde de eerste versie van deze demo om te bewijzen dat een pose-model squats kon tellen, en binnen een dag had ik het verkeerde bewezen. Ik voerde het een synthetische patiënt na een voorste-kruisbandoperatie in die ik Maria noemde, 62 jaar oud, acht weken na kniereconstructie, vijftien voorgeschreven squats op eigen lichaamsgewicht. De gratis pose-bibliotheek deed precies wat gratis pose-bibliotheken doen. Ze registreerde vijftien herhalingen, telde ze allemaal, en ging door. Vijftien van de vijftien. Een schone sessie.
Het probleem was herhaling 9. Bij herhaling 9 zakt Maria's knie naar binnen en vertraagt ze om te compenseren, precies de beweging waar een fysiotherapeut op let na een voorste-kruisbandreconstructie omdat mensen zo het transplantaat opnieuw scheuren. Het pose-model zag een squat. Het zag geen risico op herblessering, want dat zien is niet waar een pose-model voor is. De herhaling die het had moeten markeren was de herhaling die het telde.
Dat was het moment waarop het echte product voor mij helder werd. Ik had de pose-estimatie behandeld als het moeilijke deel. Dat is het moeilijke deel niet meer. Het is gratis.
Pose-estimatie is gratis, dus ik probeerde er niet langer mee te concurreren
Ik besteedde de eerste week aan het bouwen van een betere keypoint-tracker, en het was de meest verspilde week van het project. BlazePose en MoveNet draaien al op een telefoon, met 30 frames per seconde, en geven je een skelet van 33 keypoints voor niets. Elk PT-platform en elke bedrijfs-wellness-app heeft dit al of kan het morgen invoegen. Daar concurreren is concurreren om iets weg te geven dat al wordt weggegeven.
Wat niemand je aanreikt is de laag na de keypoints. Een ruwe pose-stroom nemen en die omzetten in iets waarop een behandelaar kan handelen en waarvoor een betaler vergoedt. Dat is de laag die Kinetiq end-to-end zou bouwen, besloot ik, en de stelling waar ik steeds op terugkwam is deze.
Pose-estimatie is gratis. De waarde zit in het brein erboven, en het factureerbare bewijs.
Dus gooide ik de tracker weg en bouwde in plaats daarvan de interpretatie-engine. Hij neemt de keypoint-stroom (synthetisch voor drie van mijn testpatiënten, en voor een vierde offline geëxtraheerd uit een echt opgenomen squatclip zodat ik kon bewijzen dat de pijplijn op echt beeldmateriaal draait), gladstrijkt hem met een 1-Euro-filter, past een confidence-gate toe, segmenteert herhalingen op temporele zelfgelijkheid, en berekent de features waar een behandelaar écht over redeneert: gewrichtshoek en bewegingsbereik, een knievalgus-index, een tempo-ratio afdaling-tot-opgang, bewegingsvloeiendheid als Log Dimensionless Jerk, en een links-rechts-symmetrie-index. Gewoon, unit-geteste NumPy. Geen model in het scoringspad. Je kunt het hele ding bouwen op veriprajna.com/nl/demos/ai-biomechanica-oefenverificatie-en-rtm-bewijs en zien hoe hij een sessie beoordeelt.
Toen ik Maria opnieuw door deze engine liet lopen, kwam herhaling 9 terug zoals die de eerste keer had moeten terugkomen.

De engine zegt niet alleen "slechte herhaling." Hij schrijft de clausule die hij controleerde: valgus 0.054 op of boven 0.03, tempo 1.4 boven 1.25. Een behandelaar kan dat kruisverhoren. Je kunt een black box niet kruisverhoren, en na een voorste-kruisbandoperatie wil je dat heel graag.
De herhaling die ik geneigd was te vervalsen
Ik weet precies waar ik hebzuchtig werd, en dat was herhaling 12. Bij herhaling 12 van Maria's sessie zakt de heup-keypoint onder de zichtbaarheidsgate. De tracker-confidence op dat gewricht daalt te laag om te vertrouwen. Voor een paar frames weet de engine simpelweg niet waar haar heup is.
De verleidelijke zet, die je benchmark beter laat lijken, is interpoleren. De heuppositie raden uit de frames eromheen, toch een hoek berekenen, de herhaling scoren, het getal schoon houden. Ik schreef die interpolatie. Hij werkte. En toen las ik de literatuur over monoculaire nauwkeurigheid en verwijderde hem.
De fout bij kniehoekmeting met één camera bedraagt een gemiddelde absolute fout van 9.3 tot 21.9 graden (Nature Scientific Reports, 2025). Dat is het eerlijke plafond voor dit soort meting. Als ik een heuppositie verzin op een frame met lage confidence en daar vervolgens een gewrichtshoek van rapporteer, meet ik geen knie — ik genereer een plausibel ogend getal en noem het klinisch. Voor een bedrijf genaamd Veriprajna, wat ware wijsheid betekent, was dat geen twijfelgeval.

Dus onthoudt de engine zich. Hij zegt, op het scherm, "herhaling niet gescoord, heup niet zichtbaar." Hij maakt het skelet grijs. Hij doet niet alsof.
We raden nooit gewrichtshoeken uit keypoints met lage confidence.
Ik zette die zin op het scherm omdat ik wilde dat de discipline zichtbaar was, niet begraven in een config-bestand. Over de afgedekte herhalingen in mijn gelabelde set onthielden drie van de drie zich en werden nul hoeken verzonnen, en dat is geen claim die ik beweer — het is een unit-geteste invariant. Een afgedekte herhaling die toch werd gescoord zou, per definitie, een verzonnen hoek zijn. De test zou het vangen. Weigeren te antwoorden is een feature die ik tegen mijn eigen instinct in moest behouden.
Waarom slaagt dezelfde squat voor de ene patiënt en zakt hij voor de volgende?
Ik had bijna één enkele globale drempel uitgeleverd, en een fysiotherapeut aan wie ik een vroege build liet zien praatte me er in ongeveer dertig seconden van af. Haar punt was eenvoudig. Een squat van 80 graden van een vrouw acht weken na een voorste-kruisbandoperatie is goede vooruitgang. Dezelfde squat van 80 graden van een gezonde 30-jarige wellnesscliënt is een luie herhaling. Als je engine ze hetzelfde scoort, begrijpt hij revalidatie niet — hij begrijpt geometrie.
Dus werd de regel-engine populatie-adaptief. Drempels zijn gekoppeld aan het patiëntprofiel: leeftijdsgroep, aandoening, herstelweek. Maria, week 8 na voorste-kruisbandoperatie, krijgt een flexiedoel van 75 graden. Jordan, een 30-jarige bedrijfs-wellness-atleet met gezonde knieën, krijgt 95. Ik bouwde een bediening in de demo om het punt onweerlegbaar te maken: wissel het profiel-dropdown, houd exact dezelfde beweging, en zie het oordeel veranderen.

De eerste keer dat ik dat dropdown voor iemand omschakelde en de hele kolom oordelen veranderde onder een beweging die dat niet deed, zag ik dat ze het begrepen. Dezelfde beweging krijgt een ander oordeel per profiel, en dat is klinisch oordeel, geen bug. Dit is ook het deel van de stelling dat niet veroudert. Wanneer pose-estimatie perfect wordt, en dat zal ze, kan de sensor je nog steeds niet vertellen dat 80 graden het ene betekent voor een herstellende knie en het andere voor een gezonde. Dat begrip leeft in de laag erboven.
Agents adviseren, code beslist
Ik liet een AI-agent me één keer in verlegenheid brengen, en dat is de reden dat de architectuur eruitziet zoals hij eruitziet. Iedereen aan wie ik pitchte wilde de trendy mogelijkheid: een agent die de sessie leest en de klinische notitie schrijft, een agent die de trend bewaakt en escaleert. Prima. Ik koppelde een Clinical Scribe en een Longitudinal Monitor aan een andere patiënt, Eleanor, 70, na knievervanging, van wie het bewegingsbereik stilaan daalt over haar sessiegeschiedenis.
Het eerste concept dat de scribe schreef was vloeiend en zelfverzekerd en bevatte een getal voor bewegingsbereik dat niet in het bewijs stond. Hij had afgerond, of gladgestreken, of simpelweg een getal verzonnen dat lekker las. Als ik hem had vertrouwd, had ik een klinische notitie uitgeleverd met een verzonnen meting erin. Dat is de hele faalmodus van een taalmodel in een medische lus zetten, en ik had het zojuist op mijn eigen scherm zien gebeuren.
De oplossing was om de agents structureel onbekwaam te maken om dat te doen. Elk cijfer dat een agent introduceert wordt gecontroleerd tegen het deterministische bewijs. Elk getal dat niet in de output van de engine staat wordt afgewezen, en in plaats daarvan wordt de deterministische template getoond. Beide agents onthouden zich volledig wanneer er geen API-sleutel is. Zij interpreteren en communiceren. Zij beslissen niet.

Agents adviseren, code beslist.
Die zin is de dragende ontwerpbeslissing van de hele demo. De vertrouwenskern, elk oordeel per herhaling en de factureringsbepaling, is gewone unit-geteste code zonder taalmodel in de buurt. De agents zitten erbovenop, begrensd en grounding-checked, en doen het ene waar ze echt goed in zijn: geverifieerde getallen omzetten in voor behandelaars klaargemaakte tekst. Op het moment dat een agent een stem krijgt over het oordeel, ben je de controleerbaarheid kwijt die het hele punt was.
Wat een betaler werkelijk vergoedt is een bewijs, geen herhalingstelling
De commerciële reden waarom ik dit überhaupt bouwde is iets wat ik leerde bij het lezen van CMS-factureringsregels, wat niet is hoe ik had verwacht een maand door te brengen. Behandelaars kunnen Remote Therapeutic Monitoring factureren onder CPT 98975-98981, plus de 2026-codes 98979 en 98985, en de kwalificatiedrempel van 2026 daalde tot slechts 2 dagen en 10 minuten data (Veriprajna WP29-onderzoek, 2026). Maar CMS vergoedt geen ruwe coördinaten. Het vereist door het apparaat verzamelde data gekoppeld aan een behandelbeslissing. Een stapel keypoints is niet factureerbaar. Een gedocumenteerde, auditbare bepaling wel.
Die kloof is niet klein, want het onderliggende probleem is niet klein. De therapietrouw bij PT-thuisoefeningen ligt rond de 35 procent, en 65 procent van de patiënten verlaat hun programma binnen de eerste maand (Veriprajna WP29-onderzoek, 2026). Zelfrapportage overschat de naleving, dus factureert en behandelt de behandelaar vaak op data die hij niet kan vertrouwen. Aan de werkgeverskant kosten musculoskeletale aandoeningen ruwweg 3.591 dollar per werknemer per jaar, naar schatting 36 procent van de MSK-operaties wordt als onnodig beschouwd (90 miljard dollar), en meer dan de helft van de werknemers verzet zich ertegen om gezondheidsdata te delen vanwege privacy (Veriprajna WP29-onderzoek, 2026). Geverifieerde, privacyveilige, factureerbare oefendata is wat iedereen in die keten mist.
Dus het laatste wat de engine per sessie doet is beslissen: factureerbaar, heeft beoordeling door behandelaar nodig, of onvoldoende apparaatdata, en hij exporteert een FHIR-vormig sessierapport. Observaties per herhaling, de drempelclausule die voor elk is gecontroleerd, de sessiesamenvatting, de RTM-bepaling, en de documentatievelden die CMS vraagt. Ik zeg FHIR-vormig, nooit FHIR-gevalideerd, omdat het gestructureerde JSON is die de veldvormen van Observation en DocumentReference weerspiegelt, geen payload die naar een live EPD wordt gepost. Ik schrijf hem uit. Ik doe niet alsof ik hem POST.
Maria's sessie, met de valgus-vlag bij herhaling 9, factureert niet automatisch. Hij gaat eerst naar een behandelaar, omdat een signaal van herblessering door een mens moet worden gezien voordat het een declaratie wordt. Dat is de eerlijke nuance waarop ik bleef aandringen: factureerbaar betekent niet dat alles goed is, het betekent goed genoeg gedocumenteerd om erachter te staan.

De cijfers op dat paneel zijn de cijfers waar ik om geef, en ik wil precies zijn over hun reikwijdte. Ground truth hier is het oordeel dat volgt uit de geplante fysieke parameters versus de profieldrempels, onafhankelijk van de engine berekend, en daarna worden de herhalingen naar keypoints gerenderd met meetruis toegevoegd nadat de labels vastliggen, zodat de engine het oordeel door de ruis heen moet terugvinden. Op die set is de overeenstemming 100 procent op 25 duidelijke gevallen en 100 procent op 10 grensgevallen die binnen ongeveer één standaardafwijking van een drempel zitten. Dat is een onderscheidingsresultaat op een gelabelde synthetische set, geen open-wereldgarantie, en de grensgevallen zijn precies die welke onder naïeve drempelzetting zouden omslaan, daarom rapporteer ik ze apart. Dit zijn duurzame maten: taaknauwkeurigheid, automatiseringsdoorvoer en eerlijkheid. Ze verouderen niet wanneer het pose-model verbetert, omdat geen van hen een foutpercentage van het pose-model is.
De vraag waarmee ik blijf zitten
Ik begon hieraan denkend dat ik een computer-vision-product bouwde, en ik eindig ervan overtuigd dat ik een verantwoordingsproduct bouwde dat toevallig met een camera begint. Het interessante werk was nooit het krijgen van de keypoints. Het was beslissen wat het systeem mag claimen, wanneer het zich moet onthouden, wiens drempels gelden, en waar een behandelaar zijn licentie achter kan zetten. Als je wilt zien hoe de engine een inzakkende knie beoordeelt, een afgedekte herhaling weigert, en het bewijs exporteert, draait hij zonder sleutel op veriprajna.com/nl/demos/ai-biomechanica-oefenverificatie-en-rtm-bewijs.
En als je het liever bekijkt dan mij het te laten beschrijven, hier is het hele ding end-to-end draaiend.
De vraag die ik blijf omdraaien, en waarover ik oprecht graag met andere builders zou willen debatteren, is deze. Naarmate de sensoren asymptotisch perfect worden, zal de verleiding zijn om het model meer te laten beslissen, omdat het zo zelden fout zal zitten. Maar zelden-fout is precies de conditie waaronder een verzonnen getal de meeste schade aanricht, omdat je bent gestopt met controleren. Dus waar, in je eigen systemen, ligt de lijn die je het model weigert te laten overschrijden, en kun je wijzen naar de code die dat afdwingt?


