feat(perception): add autonomous vegetation shadow lab

This commit is contained in:
DCCONSTRUCTIONS
2026-08-27 23:26:35 +03:00
parent 7594c71dd1
commit 57204b3b0b
26 changed files with 2494 additions and 5 deletions
@@ -0,0 +1,230 @@
[CmdletBinding()]
param(
[ValidateSet("Build", "Probe", "Validate", "Ravnoves", "Status")]
[string]$Mode = "Status",
[ValidateSet("Ddrnet", "Ppliteseg")]
[string]$Candidate = "Ddrnet",
[string]$AssetRoot = "D:\NDC_MISSIONCORE\datasets\vegetation-v1\observed-2026-08-27",
[string]$ToolRoot = "D:\NDC_MISSIONCORE\datasets\tooling\lab-v1-vegetation-goose",
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\experiments\lab-v1-vegetation",
[string]$RavnovesVideo = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$datasetPrefix = "D:\NDC_MISSIONCORE\datasets\"
$runtimePrefix = "D:\NDC_MISSIONCORE\runtime\experiments\"
if (-not $AssetRoot.StartsWith($datasetPrefix, [StringComparison]::OrdinalIgnoreCase)) {
throw "AssetRoot must stay under $datasetPrefix"
}
if (-not $ToolRoot.StartsWith($datasetPrefix, [StringComparison]::OrdinalIgnoreCase)) {
throw "ToolRoot must stay under $datasetPrefix"
}
if (-not $OutputRoot.StartsWith($runtimePrefix, [StringComparison]::OrdinalIgnoreCase)) {
throw "OutputRoot must stay under $runtimePrefix"
}
$image = "ndc/mission-core-lab-v1-goose:sg3.2.0-cu117-v1"
$canonicalContainer = "ndc-mission-core-triton"
$candidateKey = $Candidate.ToLowerInvariant()
$contextRoot = Join-Path $ToolRoot "context"
$configRoot = Join-Path $ToolRoot "config"
$benchmarkConfig = Join-Path $configRoot "lab-v1-goose-vegetation-benchmark-v1.json"
$policyConfig = Join-Path $configRoot "lab-v1-vegetation-mission-policy-v1.json"
$providerMapConfig = Join-Path $configRoot "lab-v1-vegetation-provider-label-map-v1.json"
$datasetRoot = Join-Path $AssetRoot "goose-2d\validation"
$checkpointRelative = if ($candidateKey -eq "ddrnet") {
"models\goose\ddrnet_class_512.pth"
} else {
"models\goose\ppliteseg_class_512.pth"
}
$checkpoint = Join-Path $AssetRoot $checkpointRelative
$expectedCheckpointSha256 = if ($candidateKey -eq "ddrnet") {
"b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6"
} else {
"6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd"
}
$expectedCheckpointBytes = if ($candidateKey -eq "ddrnet") { 259419077 } else { 98208249 }
$ravnovesSha256 = "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8"
$frameIndices = @(0, 253, 512, 768, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4488)
$dockerConfig = "D:\NDC_MISSIONCORE\datasets\state\lab-v1-vegetation\docker-config"
function Get-CanonicalTritonIdentity {
$identity = & docker inspect $canonicalContainer --format "{{.Id}}|{{.Config.Image}}|{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}"
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($identity)) {
throw "Canonical Triton is unavailable"
}
$parts = $identity.Split("|")
if ($parts.Count -ne 4 -or $parts[2] -ne "running" -or $parts[3] -ne "healthy") {
throw "Canonical Triton is not running and healthy: $identity"
}
return $identity
}
function Assert-FileIdentity {
param([string]$Path, [long]$ExpectedBytes, [string]$ExpectedSha256)
$file = Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue
if ($null -eq $file -or $file.Length -ne $ExpectedBytes) {
throw "File identity changed: $Path"
}
$actualSha256 = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actualSha256 -ne $ExpectedSha256) {
throw "File digest changed: $Path"
}
}
function Assert-RunnerInputs {
foreach ($path in @($benchmarkConfig, $policyConfig, $providerMapConfig)) {
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
throw "Runner config is unavailable: $path"
}
}
if (-not (Test-Path -LiteralPath (Join-Path $contextRoot "Dockerfile") -PathType Leaf)) {
throw "Runner Dockerfile is unavailable"
}
if (-not (Test-Path -LiteralPath (Join-Path $contextRoot "run_goose_vegetation_benchmark.py") -PathType Leaf)) {
throw "Runner source is unavailable"
}
Assert-FileIdentity -Path $checkpoint -ExpectedBytes $expectedCheckpointBytes -ExpectedSha256 $expectedCheckpointSha256
}
function New-RunRoot {
param([string]$Kind)
$stamp = [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssfffZ")
$path = Join-Path $OutputRoot ("{0}-{1}-{2}" -f $Kind, $candidateKey, $stamp)
New-Item -ItemType Directory -Path $path | Out-Null
return $path
}
function Invoke-IsolatedRun {
param(
[ValidateSet("goose", "ravnoves")][string]$RunMode,
[string]$RunRoot,
[int]$Limit,
[string]$FramesRoot = ""
)
$containerName = "ndc-lab-v1-goose-$candidateKey-$([Guid]::NewGuid().ToString('N').Substring(0, 10))"
$arguments = @(
"run", "--rm", "--name", $containerName,
"--gpus", "all",
"--network", "none",
"--read-only",
"--cap-drop", "ALL",
"--security-opt", "no-new-privileges",
"--memory", "10g",
"--cpus", "8",
"--pids-limit", "512",
"--tmpfs", "/tmp:rw,noexec,nosuid,size=2g",
"--env", "HOME=/tmp",
"--mount", "type=bind,src=$datasetRoot,dst=/data/goose,readonly",
"--mount", "type=bind,src=$checkpoint,dst=/models/candidate.pth,readonly",
"--mount", "type=bind,src=$configRoot,dst=/config,readonly",
"--mount", "type=bind,src=$RunRoot,dst=/output",
$image,
"--mode", $RunMode,
"--candidate", $candidateKey,
"--config", "/config/lab-v1-goose-vegetation-benchmark-v1.json",
"--policy", "/config/lab-v1-vegetation-mission-policy-v1.json",
"--provider-map", "/config/lab-v1-vegetation-provider-label-map-v1.json",
"--checkpoint", "/models/candidate.pth",
"--dataset-root", "/data/goose",
"--output", "/output/result",
"--limit", $Limit.ToString(),
"--visual-count", "12"
)
if ($RunMode -eq "ravnoves") {
$arguments = @($arguments[0..($arguments.Count - 1)])
$arguments += @("--frames-root", "/input")
$mountIndex = [Array]::IndexOf($arguments, $image)
$head = @($arguments[0..($mountIndex - 1)])
$tail = @($arguments[$mountIndex..($arguments.Count - 1)])
$arguments = $head + @("--mount", "type=bind,src=$FramesRoot,dst=/input,readonly") + $tail
}
& docker @arguments
if ($LASTEXITCODE -ne 0) {
throw "LAB V1 container failed with exit code $LASTEXITCODE"
}
}
function Export-RavnovesFrames {
param([string]$Destination)
Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $ravnovesSha256
New-Item -ItemType Directory -Path $Destination | Out-Null
$expression = ($frameIndices | ForEach-Object { "eq(n\,$_ )" }) -join "+"
$temporaryPattern = Join-Path $Destination "selected-%03d.png"
& ffmpeg -hide_banner -loglevel error -i $RavnovesVideo -vf "select='$expression'" -fps_mode vfr $temporaryPattern
if ($LASTEXITCODE -ne 0) {
throw "RAVNOVES exact frame extraction failed"
}
$selected = @(Get-ChildItem -LiteralPath $Destination -Filter "selected-*.png" | Sort-Object Name)
if ($selected.Count -ne $frameIndices.Count) {
throw "RAVNOVES frame island changed: expected $($frameIndices.Count), got $($selected.Count)"
}
for ($index = 0; $index -lt $selected.Count; $index++) {
$target = Join-Path $Destination ("frame-{0:D6}.png" -f $frameIndices[$index])
Move-Item -LiteralPath $selected[$index].FullName -Destination $target
}
}
if ($Mode -eq "Status") {
$imageIdentity = & docker image inspect $image --format "{{.Id}}" 2>$null
[ordered]@{
schema_version = "missioncore.lab-v1-goose-runner-status/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
worker_id = "worker-006"
image = $image
image_id = if ($LASTEXITCODE -eq 0) { $imageIdentity } else { $null }
canonical_triton = Get-CanonicalTritonIdentity
asset_root = $AssetRoot
output_root = $OutputRoot
candidate = $candidateKey
} | ConvertTo-Json -Depth 6
exit 0
}
Assert-RunnerInputs
$canonicalBefore = Get-CanonicalTritonIdentity
try {
if ($Mode -eq "Build") {
if (-not (Test-Path -LiteralPath (Join-Path $dockerConfig "config.json") -PathType Leaf)) {
throw "Isolated Docker client configuration is unavailable"
}
$previousDockerConfig = $env:DOCKER_CONFIG
try {
$env:DOCKER_CONFIG = $dockerConfig
& docker build --pull=false --label "com.nodedc.component=mission-core-lab-v1-goose" --label "com.nodedc.authority=shadow-only" --tag $image $contextRoot
if ($LASTEXITCODE -ne 0) {
throw "LAB V1 image build failed with exit code $LASTEXITCODE"
}
}
finally {
$env:DOCKER_CONFIG = $previousDockerConfig
}
}
elseif ($Mode -eq "Probe") {
$runRoot = New-RunRoot -Kind "probe"
Invoke-IsolatedRun -RunMode "goose" -RunRoot $runRoot -Limit 8
}
elseif ($Mode -eq "Validate") {
$runRoot = New-RunRoot -Kind "validation"
Invoke-IsolatedRun -RunMode "goose" -RunRoot $runRoot -Limit 0
}
elseif ($Mode -eq "Ravnoves") {
$runRoot = New-RunRoot -Kind "ravnoves"
$framesRoot = Join-Path $runRoot "input-frames"
Export-RavnovesFrames -Destination $framesRoot
Invoke-IsolatedRun -RunMode "ravnoves" -RunRoot $runRoot -Limit 0 -FramesRoot $framesRoot
}
}
finally {
$canonicalAfter = Get-CanonicalTritonIdentity
if ($canonicalAfter -ne $canonicalBefore) {
throw "Canonical Triton identity changed during LAB V1 work"
}
}