Skip to content

Add laydown-person detection and rescue approach (Unitree Go2) - #2635

Open
shicaih wants to merge 61 commits into
mainfrom
add-laydown-people-detection
Open

Add laydown-person detection and rescue approach (Unitree Go2)#2635
shicaih wants to merge 61 commits into
mainfrom
add-laydown-people-detection

Conversation

@shicaih

@shicaih shicaih commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a safety behavior to the Go2 patrol robot: detect a person lying on the ground, approach and lock on at a safe distance, and deliver spoken first-aid guidance until the person gets up. Driven by a new VLM-based input plugin plus an mppi-planner movement path. Lands as the unitree_go2_laydown_rescue config.

The control loop, per emergency: VLM verdict → cortex/LLM → one move → mppi executes → lock on near → hold & speak, leaving emergency mode only when a fresh "No person" verdict clears the alert.

Key changes

New input — PersonLaydownDetector (plugins/inputs/vlm/laydown_detector.go)

  • RTSP frames → VLM; emits a one-line verdict with Location in view (left/center/right, judged by the head in the robot's image frame) and Distance (near/far).
  • Latch + debounce so a downed person stays ALERT across brief VLM dropouts; triggers an immediate cortex tick on each fresh verdict.
  • Optional image/verdict debug logging (image_log_dir).

mppi autonomy connector (plugins/actions/unitree/go2/autonomy/mppi.go)

  • turn … slightly re-center rotations (short arc) and gentle (smallest-angle) path selection.
  • Arrival lock: once a near verdict latches, hold position and ignore all moves for the rest of the emergency — turns only happen while approaching.
  • One move per VLM verdict during an alert, so the dog doesn't act on the stale latched verdict while the multi-second VLM call is still running.
  • Goal-frame sign handling (pathAngles is +right; the planner goal frame is +left/CCW, so the angle is negated).

Providers (internal/providers/alert_state.go): PersonDownAlert, PersonDownArrived, and a VisionSeq perception-frame counter.

Supporting

  • VLM Describer: multi-image requests + configurable detail.
  • TTS: Priority flag (emergency speech bypasses the patrol silence-rate) and skip_when_busy.
  • Cortex LLM call timeout (15s) and a default HTTP client timeout (60s) so a hung call can't stall the loop.

move.go: reverted to main except the two new turn … slightly enum values + description — that's the shared action schema the mppi connector relies on. The move connector itself is unused by this config.

Testing

  • go build, go vet, and the autonomy package tests pass.
  • Behavior tuned over many on-robot runs (approach distance, head-based left/right, lock-on-near, gentle turns, alert stickiness).

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 17, 2026 18:52
@shicaih
shicaih requested review from a team as code owners June 17, 2026 18:52
@github-actions github-actions Bot added tests Test files config Configuration files plugins go core labels Jun 17, 2026
@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “person lying down” safety/rescue behavior for the Unitree Go2 by introducing a VLM-based input detector, new alert/vision sequencing providers, and MPPI autonomy connector behavior to approach, lock-on near, and continue guidance until cleared.

Changes:

  • Added PersonLaydownDetector RTSP+VLM input with latched alert/debounce behavior and optional debug logging.
  • Updated MPPI autonomy connector to support gentle path selection, “turn … slightly” recenter moves, one-move-per-vision-verdict gating, and arrival lock-on behavior.
  • Added supporting plumbing: alert/vision sequence providers, multi-image VLM describe support, TTS priority/skip-when-busy behavior, and default timeouts.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
plugins/inputs/vlm/laydown_detector.go New VLM+RTSP sensor that latches ALERT and publishes vision verdicts into the cortex loop.
plugins/actions/unitree/go2/autonomy/mppi.go Adds gentle-turn selection, recenter rotation goals, alert/arrival gating, and per-vision-step motion constraints.
plugins/actions/unitree/go2/autonomy/mppi_test.go Updates unit tests for the new issueGoal signature.
plugins/actions/unitree/go2/autonomy/move.go Extends shared Move action schema with “turn left/right slightly”.
plugins/actions/speak/elevenlabs_tts.go Adds skip_when_busy and uses tts.Priority to bypass silence throttling.
internal/runtime/runtime.go Adds a timeout around cortex LLM calls.
internal/providers/vlm/describe.go Adds multi-image requests and configurable image detail.
internal/providers/tts/state.go Adds a global Priority flag for emergency speech behavior.
internal/providers/alert_state.go New alert/arrival and VisionSeq state for coordinating emergency behavior.
internal/httpclient/httpclient.go Adds a default HTTP client timeout.
config/unitree_go2_laydown_rescue.json5 New config wiring the detector + MPPI + TTS behavior for the rescue mode.

Comment thread plugins/inputs/vlm/laydown_detector.go Outdated
Comment on lines +308 to +321
if strings.Contains(strings.ToUpper(text), "ALERT") {
if !s.alerted {
s.alertedAt = time.Now()
}
s.alerted = true
s.clearCount = 0
s.lastAlert = text
tts.Priority.Store(true)
providers.SetPersonDownAlert(true)
if strings.Contains(strings.ToLower(text), "distance: near") {
providers.SetPersonDownArrived(true)
}
return text
}
Comment on lines +377 to +391
func (s *laydownDetector) FormattedLatestBuffer() string {
s.mu.Lock()
defer s.mu.Unlock()

if !s.hasLatest {
return ""
}

result := fmt.Sprintf("\n%s: '%s'\n", vlmDescriptor, s.latest.Message)

ts := time.Unix(0, int64(s.latest.Timestamp*1e9))
providers.IO().AddInput(s.name, s.latest.Message, ts)

return result
}
Comment on lines +175 to +178
if providers.PersonDownArrived() {
c.log.Info("arrived - locked on, holding position", zap.String("action", action))
return nil, nil
}
Comment on lines 229 to 233
// issueGoal selects a path (smallest-magnitude heading when gentlest, else random)
// and publishes a goal toward it. pathAngles is +right but the mppi goal frame is
// +left (CCW), so the angle is negated.
func (c *MPPIConnector) issueGoal(options []uint32, label string, gentlest bool) {
if len(options) == 0 {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Configuration files core go plugins tests Test files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants