Your Robot’s First Medical Skill: Posture Monitoring

First time at MonkeyTaco? This post builds on our earlier projects — particularly Part 4, Fall Detection. You don’t need to read them first — but if something looks unfamiliar, that’s a great place to start.


Here’s a question worth sitting with — ideally with your back straight.

How are you sitting right now?

If you had to guess, there’s a reasonable chance your neck is slightly forward, your shoulders are rolling inward, and your lower back has quietly given up and is doing something it probably shouldn’t. This is not a judgment. This is just what happens when humans spend hours in front of screens.

The numbers are genuinely uncomfortable. According to research compiled by the National Center for Biotechnology Information, more than 80% of the global population experiences posture-related problems at some point — a trend accelerating sharply with the rise of smartphones, remote work, and the general habit of staring at rectangles all day.

The consequences aren’t just cosmetic. Chronic bad posture causes:

  • Spinal problems — loss of natural curvature, herniated discs, early joint degeneration, and chronic pain in the neck, shoulders, and lower back
  • Circulatory issues — compressed blood vessels leading to numbness, dizziness, and increased cardiovascular risk
  • Digestive problems — a hunched position compresses the abdominal organs, slowing digestion and contributing to reflux and bloating
  • Reduced lung capacity — a rounded back physically restricts the chest cavity, limiting how fully the lungs can expand
  • Reduced productivity — chronic pain plus reduced oxygen to the brain means worse focus, more fatigue, more headaches

The global market for posture correction devices is growing rapidly — predicted to reach billions of dollars in the coming years. Which tells you two things: the problem is real, and a lot of people are willing to pay to fix it.

We’re going to build something that helps. For free.


Important note before we start: everything we build at MonkeyTaco is for learning and exploration. If you’re thinking about deploying any monitoring system in a real setting — especially involving elderly or vulnerable individuals — please take privacy, consent, and applicable regulations seriously. This is a prototype, not a medical device.


How Do We Actually Detect Bad Posture?

Before writing any code, let’s think about this the way we actually perceive posture in real life.

When you look at someone and immediately sense that something is “off” about how they’re standing or sitting, what are you actually noticing? You’re not consciously running a biomechanical analysis. You’re making an almost instantaneous judgment based on alignment — whether the key points of the body (ears, shoulders, hips, knees, ankles) appear to line up the way they naturally should.

The spine has three natural curves:

  • Cervical (neck): Curves gently forward — like a reverse C
  • Thoracic (upper back): Curves gently backward — like a C
  • Lumbar (lower back): Curves gently forward — like a reverse C again

When these curves are maintained, the body is in balance. When they’re exaggerated, collapsed, or reversed — that’s bad posture.

For our system, we focus on two things that are both measurable with a webcam and meaningful in practice:

1. Forward lean (sagittal plane) When someone slouches at a desk, their nose moves forward relative to their shoulder, and their shoulder moves forward relative to their hip. We can measure this as the angle formed by three points: nose → shoulder → hip. When everything is aligned and upright, this angle is close to 180°. When someone is hunching forward, the angle drops. Below about 160° is worth a gentle warning. Below 130° is a real problem.

2. Lateral lean (frontal plane) When someone habitually leans to one side — often caused by crossing legs, sitting on a wallet, or just a bad habit — the shoulders tilt. We detect this by measuring the angle of the line connecting the left and right shoulders. Level shoulders sit at 0°. Beyond about 10° of tilt, something asymmetric is happening.

Two angles. Two checks. A laptop webcam. Let’s build it.


The Code

This project uses the same yolov8n-pose.pt model from our fall detection post — no new installs needed if you’ve been following along. If this is your first project, open PyCharm Terminal and run:

pip install opencv-python ultralytics numpy

Create a new Python file — right-click your project folder, New → Python File, name it postureMonitor — and paste in the following:

import cv2
import math
import time
import numpy as np
from ultralytics import YOLO
import pygame

# --- Initialize ---
pygame.mixer.init()
model = YOLO("yolov8n-pose.pt")

# --- Settings ---
SOUND_FILE = "welcome.mp3"          # Replace with your alert audio
FORWARD_LEAN_THRESHOLD = 160        # Below this angle = forward lean warning
LATERAL_LEAN_THRESHOLD = 10         # Above this angle = lateral lean warning
MIN_CONFIDENCE = 0.3                # Ignore low-confidence keypoints
COOLDOWN_SECONDS = 8                # Gap between audio alerts

# YOLOv8 keypoint indices (COCO format)
NOSE          = 0
LEFT_SHOULDER = 5
RIGHT_SHOULDER= 6
LEFT_HIP      = 11
RIGHT_HIP     = 12

cap = cv2.VideoCapture(0)
if not cap.isOpened():
    print("Cannot open webcam")
    exit()

last_alert_time = 0
print("MonkeyTaco Posture Monitor running... Press 'q' to quit")
print("Tip: Position the camera so your full upper body is visible from the side or slightly angled.")


def calculate_angle(p1, p2, p3):
    """
    Calculate the angle at point p2, formed by the lines p2->p1 and p2->p3.
    Returns degrees (0-180).
    Good upright posture: nose-shoulder-hip angle is close to 180 degrees.
    Slouching forward: angle decreases toward 130 or lower.
    """
    v1 = np.array(p1) - np.array(p2)
    v2 = np.array(p3) - np.array(p2)
    cosine = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2) + 1e-6)
    return float(np.degrees(np.arccos(np.clip(cosine, -1.0, 1.0))))


def shoulder_tilt(left_shoulder, right_shoulder):
    """
    Calculate the tilt angle of the shoulder line from horizontal.
    Level shoulders = 0 degrees. Lateral lean = increasing angle.
    """
    dx = right_shoulder[0] - left_shoulder[0]
    dy = right_shoulder[1] - left_shoulder[1]
    return abs(math.degrees(math.atan2(dy, dx + 1e-6)))


while True:
    ret, frame = cap.read()
    if not ret:
        break

    results = model(frame, verbose=False)

    posture_ok = True
    warnings = []
    forward_angle = None
    lateral_angle = None

    if results[0].keypoints is not None and len(results[0].keypoints.data) > 0:
        kp_data  = results[0].keypoints.data[0].cpu().numpy()   # (17, 3): x, y, conf
        kp_xy    = results[0].keypoints.xy[0].cpu().numpy()     # (17, 2): x, y only

        # --- Check 1: Forward lean (nose - shoulder - hip angle) ---
        nose      = kp_data[NOSE]
        l_shoulder= kp_data[LEFT_SHOULDER]
        r_shoulder= kp_data[RIGHT_SHOULDER]
        l_hip     = kp_data[LEFT_HIP]

        # Use whichever shoulder is more visible
        shoulder = l_shoulder if l_shoulder[2] > r_shoulder[2] else r_shoulder
        shoulder_idx = LEFT_SHOULDER if l_shoulder[2] > r_shoulder[2] else RIGHT_SHOULDER
        hip = kp_data[LEFT_HIP] if shoulder_idx == LEFT_SHOULDER else kp_data[RIGHT_HIP]

        if min(nose[2], shoulder[2], hip[2]) > MIN_CONFIDENCE:
            forward_angle = calculate_angle(
                kp_xy[NOSE],
                kp_xy[shoulder_idx],
                kp_xy[LEFT_HIP if shoulder_idx == LEFT_SHOULDER else RIGHT_HIP]
            )
            if forward_angle < FORWARD_LEAN_THRESHOLD:
                posture_ok = False
                if forward_angle < 130:
                    warnings.append(f"Severe slouch! ({forward_angle:.0f} deg)")
                else:
                    warnings.append(f"Forward lean detected ({forward_angle:.0f} deg)")

        # --- Check 2: Lateral lean (shoulder tilt) ---
        if l_shoulder[2] > MIN_CONFIDENCE and r_shoulder[2] > MIN_CONFIDENCE:
            lateral_angle = shoulder_tilt(kp_xy[LEFT_SHOULDER], kp_xy[RIGHT_SHOULDER])
            if lateral_angle > LATERAL_LEAN_THRESHOLD:
                posture_ok = False
                warnings.append(f"Lateral lean ({lateral_angle:.0f} deg tilt)")

    # --- Draw skeleton ---
    annotated_frame = results[0].plot()

    # --- Display status ---
    y_pos = 50
    if forward_angle is not None:
        cv2.putText(annotated_frame, f"Forward angle: {forward_angle:.0f} deg",
                    (10, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
        y_pos += 35
    if lateral_angle is not None:
        cv2.putText(annotated_frame, f"Shoulder tilt: {lateral_angle:.0f} deg",
                    (10, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
        y_pos += 35

    if posture_ok and (forward_angle is not None or lateral_angle is not None):
        cv2.putText(annotated_frame, "Posture: GOOD",
                    (10, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
    elif warnings:
        for w in warnings:
            cv2.putText(annotated_frame, f"WARNING: {w}",
                        (10, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
            y_pos += 35

        # Audio alert with cooldown
        current_time = time.time()
        if not pygame.mixer.music.get_busy() and (current_time - last_alert_time) > COOLDOWN_SECONDS:
            pygame.mixer.music.load(SOUND_FILE)
            pygame.mixer.music.play()
            last_alert_time = current_time
            print(f"Posture alert: {', '.join(warnings)}")

    cv2.imshow("MonkeyTaco — Posture Monitor", annotated_frame)

    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()
cv2.destroyAllWindows()

Hit Run, sit in front of your webcam so your upper body is visible, and watch the angle readings appear.

Now hunch forward deliberately. The forward angle drops, the warning turns red.


Understanding the Two Angles

Forward lean angle (nose → shoulder → hip)

When your body is perfectly upright, these three points form a nearly straight line — angle close to 180°. As you lean or hunch forward, your nose moves ahead of your shoulder, and the angle shrinks.

Think of it like a folding ruler. Fully extended = 180°. Start folding it = angle decreases. Our threshold of 160° gives an early warning before the problem becomes severe.

AngleMeaning
160° – 180°Good posture
130° – 160°Mild forward lean — gentle warning
Below 130°Significant slouch — stronger warning

Shoulder tilt angle

A horizontal line between your left and right shoulders should be, well, horizontal — 0°. When you habitually lean to one side, that line tilts. Beyond 10°, it’s worth paying attention to.


Camera Position Matters

This system works best when the camera captures your upper body from a side angle or slight angle — not directly face-on. Here’s why: the forward lean calculation relies on seeing the nose in front of the shoulder, which requires some lateral perspective. Face-on, both points appear at the same horizontal position regardless of lean.

For a desk setup, try positioning the webcam slightly to your left or right rather than directly in front. A 20–30° angle is enough.


One Honest Limitation

The nose-shoulder-hip angle works well for detecting forward head posture and general slouching. What it doesn’t do well is distinguish between someone who is actively leaning forward to read something versus someone whose default resting posture is collapsed.

A production posture monitoring system would add a calibration step — capturing the user’s personal “good posture” baseline at the start of each session and measuring deviation from that. For our prototype, we use fixed thresholds. It’s a reasonable starting point, and the thresholds are easy to adjust in the settings at the top of the code.


Tuning Your Thresholds

Every person and every camera setup is slightly different. Here’s how to tune:

  • Sit in what feels like good posture, note the forward angle reading on screen
  • Now deliberately slouch — note how far the angle drops
  • Set FORWARD_LEAN_THRESHOLD to a value between those two readings, closer to the good posture value

The same principle applies to LATERAL_LEAN_THRESHOLD. Watch what angle corresponds to your natural sitting variation vs. a real lean, and set accordingly.


What Just Happened?

We built a posture monitoring system that:

  • Tracks body keypoints in real time using YOLOv8-pose
  • Calculates forward lean using a three-point angle (nose, shoulder, hip)
  • Detects lateral lean using shoulder line tilt
  • Issues visual and audio warnings with a cooldown to avoid alert fatigue
  • Runs entirely on a laptop webcam, costs nothing

The same principles — keypoint extraction, angle calculation, threshold comparison — are used in physical therapy software, ergonomics assessment tools, and sports performance systems that sell for significant money. Our version has limitations. It also genuinely works.


What’s Next?

So far, our laptop robot has learned to see, react, count, detect falls, and now monitor posture. All from a single webcam. All for $0.

But here’s something worth noticing: in every project so far, our robot treats every person it sees exactly the same way. A person is a person. It doesn’t know who that person is — just that a person is there.

That’s a meaningful limitation in healthcare contexts. A system that can tell the difference between a registered patient and an unfamiliar face, or recognize that a specific individual has been sitting in the same position for three hours, is significantly more useful than one that just sees “person.”

That gap — between detecting a face and recognizing whose face it is — turns out to be one of the most interesting conceptual distinctions in computer vision. And it’s exactly where we’re headed next.

Part 6 — Face Detection vs. Face Recognition: What’s the Difference? breaks down the two concepts clearly, with working demo code for both. Same laptop. Still $0. Considerably more interesting.


MonkeyTaco — Serious Robots. Zero Budget. Maximum Chaos.