edge-fall-vlm-2.2b โ€” single-stage VLM for fall / danger detection

A fine-tune of SmolVLM2-2.2B-Instruct that detects human falls, being down, and distress from a short strip of consecutive video frames, designed to run on a Raspberry Pi 5 (4 GB) at Q6_K GGUF. Trained almost entirely on synthetic 3D renders plus a small real-negative set.

Code & full write-up: https://github.com/vieenrose/edge-fall-vlm

What it does

Input: a few consecutive frames (oldestโ†’newest). Output: JSON, e.g. {"posture":"horizontal-on-floor","status":"down","confidence":0.9,"person_down":true,"n_people":1} where status โˆˆ {down, distress, normal} (3-class down3 scheme).

Results

Test Metric
In-the-wild falls (OOPS) 0.83 recall โ€” vs VideoMAE 0.21, frozen-I3D 0.68 baselines
Real overhead falls (URFD held-out) 0.90 recall / 1.0 specificity
Synthetic-only โ†’ real transfer 0.95 recall before any real data
On-device Q6_K GGUF โ‰ˆ 2.2 GB, ~1.8 GB RAM, ~4โ€“6 s/inference projected on RPi5

Files

  • model.safetensors + config/processor โ€” transformers (bf16).
  • GGUF for llama.cpp / edge: model-Q6_K.gguf (recommended, fits RPi5 4 GB) and model-Q8_0.gguf, plus mmproj-f16.gguf (vision projector). Note: Q4 breaks this fine-tune โ€” use Q6_K or higher.

Usage (transformers)

from transformers import AutoModelForImageTextToText, AutoProcessor
import torch
proc = AutoProcessor.from_pretrained("Luigi/edge-fall-vlm-2.2b", do_image_splitting=False,
                                     size={"longest_edge": 384})
model = AutoModelForImageTextToText.from_pretrained("Luigi/edge-fall-vlm-2.2b",
                                                    dtype=torch.bfloat16).to("cuda").eval()
prompt = ("You are a safety monitor. These are consecutive video frames (oldest first), "
          "possibly with more than one person. Report whether ANYONE has fallen, fainted, "
          "is lying immobile, or is in distress; else normal. Answer with JSON only.")
frames = [...]  # list of PIL images, ~6 frames spanning ~3.5s
msgs = [{"role":"user","content":[{"type":"image"} for _ in frames]+[{"type":"text","text":prompt}]}]
text = proc.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
batch = proc(text=[text], images=[frames], return_tensors="pt").to("cuda")
print(proc.batch_decode(model.generate(**batch, max_new_tokens=64, do_sample=False)[:, batch["input_ids"].shape[1]:], skip_special_tokens=True)[0])

Usage (llama.cpp, edge)

llama-mtmd-cli -m model-Q6_K.gguf --mmproj mmproj-f16.gguf \
  --image f0.png --image f1.png ... -t 4 -n 64 --temp 0 -p "<prompt above>"

Training data & recipe

  • Synthetic: 3D humans (Blender skin-mesh mannequin) rendered from many viewpoints and lens models incl. native fisheye, with exact labels; hard-negative normals (crouch/sit/kneel/bend). Camera pose is a free parameter โ†’ perspective robustness.
  • Real negatives: a small set of real clips (UR Fall Detection, research-use) added to fix specificity โ€” synthetic falls transfer, but synthetic "normal" does not.

Limitations (read before use)

  • Research prototype, NOT a medical or safety-certified device. Do not rely on it for actual safety monitoring without a verification layer and real-site validation.
  • Validated on small test sets (40โ€“300 clips). Cross-view generalization on real data is partial (0.55 on an untrained frontal view vs 0.90 overhead).
  • Raw specificity is far below the commercial false-alarm bar โ€” needs the verification stack (motion gate โ†’ temporal confirm โ†’ persistence timer โ†’ human review) in the repo.
  • Trained on synthetic + young-actor real falls; not validated on elderly falls.

License

Apache-2.0 (inherits SmolVLM2). Evaluation datasets (URFD, OmniFall/OOPS) are not redistributed; see their sources for terms.

Downloads last month
102
Safetensors
Model size
2B params
Tensor type
BF16
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for Luigi/edge-fall-vlm-2.2b