feat(perception): integrate vegetation policy review
This commit is contained in:
@@ -12,6 +12,10 @@ param(
|
||||
[string]$RunId,
|
||||
[ValidateRange(1.0, 120.0)]
|
||||
[double]$SourceRateHz = 12.0,
|
||||
[switch]$VegetationLoadGate,
|
||||
[string]$VegetationAssetRoot = (
|
||||
"D:\NDC_MISSIONCORE\datasets\vegetation-v1\observed-2026-08-27"
|
||||
),
|
||||
[string]$OutputRoot = (
|
||||
"D:\NDC_MISSIONCORE\runtime\results\m49-tgs-integrated-graph-shadow"
|
||||
)
|
||||
@@ -23,6 +27,8 @@ $TravelImageTag = "ndc/mission-core-m49-t3-travel:20260826"
|
||||
$TravelImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
|
||||
$ParityImageTag = "ndc-mission-core-m48t-upstream-parity:1.9.4-cu130"
|
||||
$ParityImageId = "sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0"
|
||||
$VegetationImageTag = "ndc/mission-core-lab-v1-goose:sg3.2.0-cu117-v1"
|
||||
$VegetationImageId = "sha256:591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"
|
||||
$RuntimeImage = (
|
||||
"nvcr.io/nvidia/tritonserver:26.06-py3@" +
|
||||
"sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
||||
@@ -98,12 +104,20 @@ function Wait-Healthy([string]$Name) {
|
||||
function Wait-SharedReady(
|
||||
[string]$GraphReady,
|
||||
[string]$TgsReady,
|
||||
[string]$VegetationReady,
|
||||
[string]$GraphName,
|
||||
[string]$TgsName
|
||||
[string]$TgsName,
|
||||
[string]$VegetationName
|
||||
) {
|
||||
$deadline = [DateTimeOffset]::UtcNow.AddMinutes(10)
|
||||
while (-not ((Test-Path -LiteralPath $GraphReady) -and (Test-Path -LiteralPath $TgsReady))) {
|
||||
foreach ($name in @($GraphName, $TgsName)) {
|
||||
$requiredFiles = @($GraphReady, $TgsReady)
|
||||
$requiredContainers = @($GraphName, $TgsName)
|
||||
if (-not [string]::IsNullOrWhiteSpace($VegetationReady)) {
|
||||
$requiredFiles += $VegetationReady
|
||||
$requiredContainers += $VegetationName
|
||||
}
|
||||
while ($requiredFiles.Where({ -not (Test-Path -LiteralPath $_) }).Count -gt 0) {
|
||||
foreach ($name in $requiredContainers) {
|
||||
$container = Get-Container $name
|
||||
if (-not $container.State.Running) {
|
||||
& docker logs $name
|
||||
@@ -128,15 +142,25 @@ $runCandidate = Join-Path $output $RunId
|
||||
if (Test-Path -LiteralPath $runCandidate) { throw "M49 integrated output already exists" }
|
||||
$null = New-Item -ItemType Directory -Path $runCandidate
|
||||
$runOutput = Resolve-DDirectory $runCandidate "M49 integrated run output" $false
|
||||
foreach ($directory in @("bin", "control", "graph", "tgs")) {
|
||||
foreach ($directory in @("bin", "control", "graph", "tgs", "vegetation")) {
|
||||
$null = New-Item -ItemType Directory -Path (Join-Path $runOutput $directory)
|
||||
}
|
||||
|
||||
$releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json
|
||||
$expectedReleaseSchema = if ($VegetationLoadGate) {
|
||||
"missioncore.lab-v1-vegetation-integrated-worker-release/v1"
|
||||
} else {
|
||||
"missioncore.m49-tgs-integrated-graph-worker-release/v1"
|
||||
}
|
||||
$expectedTransition = if ($VegetationLoadGate) {
|
||||
"lab-v1-vegetation-m49-integrated-shadow/v1"
|
||||
} else {
|
||||
"m49-tgs-native-risk-integrated-shadow/v1"
|
||||
}
|
||||
if (
|
||||
$releaseDocument.schema_version -cne "missioncore.m49-tgs-integrated-graph-worker-release/v1" -or
|
||||
$releaseDocument.schema_version -cne $expectedReleaseSchema -or
|
||||
$releaseDocument.worker_id -cne "worker-006" -or
|
||||
$releaseDocument.transition -cne "m49-tgs-native-risk-integrated-shadow/v1"
|
||||
$releaseDocument.transition -cne $expectedTransition
|
||||
) { throw "M49 integrated release contract changed" }
|
||||
foreach ($property in $releaseDocument.files.PSObject.Properties) {
|
||||
$path = Join-Path $payload $property.Name
|
||||
@@ -146,6 +170,9 @@ foreach ($property in $releaseDocument.files.PSObject.Properties) {
|
||||
}
|
||||
$wheelSha256 = [string]$releaseDocument.files."nodedc_mission_core-0.1.0-py3-none-any.whl".sha256
|
||||
$runnerSha256 = [string]$releaseDocument.files."run_m48s_reference_graph_shadow_worker.py".sha256
|
||||
$vegetationRunnerSha256 = if ($VegetationLoadGate) {
|
||||
[string]$releaseDocument.files."run_vegetation_integrated_load.py".sha256
|
||||
} else { "" }
|
||||
|
||||
$source = [ordered]@{
|
||||
CameraIndex = (
|
||||
@@ -179,6 +206,23 @@ if ((Get-Sha256 $source.SourcePack) -cne [string]$releaseDocument.source_pack_sh
|
||||
throw "RAVNOVES00 source pack digest changed"
|
||||
}
|
||||
|
||||
$vegetation = $null
|
||||
if ($VegetationLoadGate) {
|
||||
$vegetationRoot = Resolve-DDirectory $VegetationAssetRoot "vegetation asset root" $false
|
||||
$vegetation = [ordered]@{
|
||||
Dataset = Resolve-DDirectory (
|
||||
(Join-Path $vegetationRoot "goose-2d\validation")
|
||||
) "GOOSE validation root" $false
|
||||
Checkpoint = Resolve-DFile (
|
||||
(Join-Path $vegetationRoot "models\goose\ddrnet_class_512.pth")
|
||||
) "DDRNet checkpoint"
|
||||
}
|
||||
if (
|
||||
(Get-Sha256 $vegetation.Checkpoint) -cne
|
||||
"b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6"
|
||||
) { throw "DDRNet checkpoint SHA-256 changed" }
|
||||
}
|
||||
|
||||
$nativeConfig = Resolve-DFile (
|
||||
(Join-Path $payload "rf_detr_large_native_kb4_config.pbtxt")
|
||||
) "native RF-DETR config"
|
||||
@@ -207,12 +251,17 @@ $pillow = Resolve-DDirectory (
|
||||
|
||||
Assert-Image $TravelImageTag $TravelImageId
|
||||
Assert-Image $ParityImageTag $ParityImageId
|
||||
if ($VegetationLoadGate) { Assert-Image $VegetationImageTag $VegetationImageId }
|
||||
& docker image inspect $RuntimeImage *> $null
|
||||
Assert-LastExitCode "pinned runtime image inspection"
|
||||
$os = Get-CimInstance Win32_OperatingSystem
|
||||
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
|
||||
if ($freeMemoryGiB -lt 24.0) {
|
||||
throw ("M49 integrated shadow requires 24 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
|
||||
$requiredMemoryGiB = if ($VegetationLoadGate) { 32.0 } else { 24.0 }
|
||||
if ($freeMemoryGiB -lt $requiredMemoryGiB) {
|
||||
throw (
|
||||
"M49 integrated shadow requires {0:N0} GiB free memory; observed {1:N2} GiB" -f
|
||||
$requiredMemoryGiB, $freeMemoryGiB
|
||||
)
|
||||
}
|
||||
$canonicalBefore = Get-Container "ndc-mission-core-triton"
|
||||
if (-not $canonicalBefore.State.Running -or $canonicalBefore.State.Health.Status -cne "healthy") {
|
||||
@@ -225,9 +274,12 @@ $compileName = "ndc-mission-core-m49-integrated-compile-$RunId"
|
||||
$tritonName = "ndc-mission-core-m49-integrated-triton-$RunId"
|
||||
$graphName = "ndc-mission-core-m49-integrated-graph-$RunId"
|
||||
$tgsName = "ndc-mission-core-m49-integrated-tgs-$RunId"
|
||||
$vegetationName = "ndc-mission-core-m49-integrated-vegetation-$RunId"
|
||||
$analyzeName = "ndc-mission-core-m49-integrated-analyze-$RunId"
|
||||
$evidenceName = "ndc-mission-core-m49-integrated-evidence-$RunId"
|
||||
$vegetationEvidenceName = "ndc-mission-core-m49-integrated-vegetation-evidence-$RunId"
|
||||
$containers = @($prepareName, $compileName, $tritonName, $graphName, $tgsName, $analyzeName, $evidenceName)
|
||||
if ($VegetationLoadGate) { $containers += @($vegetationName, $vegetationEvidenceName) }
|
||||
foreach ($name in $containers) {
|
||||
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
|
||||
throw "M49 integrated container name already exists: $name"
|
||||
@@ -236,6 +288,24 @@ foreach ($name in $containers) {
|
||||
|
||||
$started = [DateTimeOffset]::UtcNow
|
||||
try {
|
||||
if ($VegetationLoadGate) {
|
||||
$vegetationFrames = Join-Path $runOutput "vegetation\input-frames"
|
||||
$null = New-Item -ItemType Directory -Path $vegetationFrames
|
||||
& ffmpeg -hide_banner -loglevel error -i $source.Video -map 0:v:0 -fps_mode passthrough (
|
||||
Join-Path $vegetationFrames "frame-%06d.png"
|
||||
)
|
||||
Assert-LastExitCode "RAVNOVES full-video frame extraction"
|
||||
$extractedFrames = @(
|
||||
Get-ChildItem -LiteralPath $vegetationFrames -File -Filter "frame-*.png" |
|
||||
Sort-Object Name
|
||||
)
|
||||
if (
|
||||
$extractedFrames.Count -ne 4489 -or
|
||||
$extractedFrames[0].Name -cne "frame-000001.png" -or
|
||||
$extractedFrames[-1].Name -cne "frame-004489.png"
|
||||
) { throw "RAVNOVES full-video frame sequence changed" }
|
||||
}
|
||||
|
||||
& docker run --rm --name $prepareName --network none --cpus 8 --memory 16g `
|
||||
--entrypoint python3 `
|
||||
--volume ((Convert-ToDockerPath $source.SourcePack) + ":/source/lidar-pack.npz:ro") `
|
||||
@@ -332,24 +402,71 @@ try {
|
||||
$TravelImageTag /release/run_tgs_integrated_shadow.sh *> $null
|
||||
Assert-LastExitCode "M49 integrated TGS creation"
|
||||
|
||||
if ($VegetationLoadGate) {
|
||||
$dockerVegetationDataset = Convert-ToDockerPath $vegetation.Dataset
|
||||
$dockerVegetationCheckpoint = Convert-ToDockerPath $vegetation.Checkpoint
|
||||
& docker create --name $vegetationName --network none --cpus 8 --memory 10g `
|
||||
--gpus all --read-only --security-opt "no-new-privileges:true" --cap-drop ALL `
|
||||
--pids-limit 512 --tmpfs "/tmp:rw,noexec,nosuid,size=2g" `
|
||||
-e "HOME=/tmp" `
|
||||
--entrypoint conda `
|
||||
--volume ($dockerRelease + ":/release:ro") `
|
||||
--volume ($dockerRun + ":/shared:rw") `
|
||||
--volume ($dockerVegetationDataset + ":/data/goose:ro") `
|
||||
--volume ($dockerVegetationCheckpoint + ":/models/candidate.pth:ro") `
|
||||
$VegetationImageTag run --no-capture-output --name goose python `
|
||||
/release/run_vegetation_integrated_load.py `
|
||||
--config /release/lab-v1-goose-vegetation-benchmark-v1.json `
|
||||
--policy /release/lab-v1-vegetation-mission-policy-v1.json `
|
||||
--provider-map /release/lab-v1-vegetation-provider-label-map-v1.json `
|
||||
--checkpoint /models/candidate.pth `
|
||||
--dataset-root /data/goose `
|
||||
--frames-root /shared/vegetation/input-frames `
|
||||
--source-rate-hz $rate `
|
||||
--minimum-effective-fps 11.209069 `
|
||||
--maximum-completion-p95-ms 125.0 `
|
||||
--shared-start-ready-file /shared/control/vegetation.ready `
|
||||
--shared-start-file /shared/control/start.signal `
|
||||
--frame-ledger /shared/vegetation/frames.jsonl `
|
||||
--output /shared/vegetation/result.json `
|
||||
--release-sha256 $ExpectedArtifactSha256 *> $null
|
||||
Assert-LastExitCode "M49 integrated vegetation creation"
|
||||
}
|
||||
|
||||
& docker start $graphName *> $null
|
||||
Assert-LastExitCode "M49 integrated graph start"
|
||||
& docker start $tgsName *> $null
|
||||
Assert-LastExitCode "M49 integrated TGS start"
|
||||
if ($VegetationLoadGate) {
|
||||
& docker start $vegetationName *> $null
|
||||
Assert-LastExitCode "M49 integrated vegetation start"
|
||||
}
|
||||
$graphReady = Join-Path $runOutput "control\graph.ready"
|
||||
$tgsReady = Join-Path $runOutput "control\tgs.ready"
|
||||
Wait-SharedReady $graphReady $tgsReady $graphName $tgsName
|
||||
$vegetationReady = if ($VegetationLoadGate) {
|
||||
Join-Path $runOutput "control\vegetation.ready"
|
||||
} else { "" }
|
||||
Wait-SharedReady $graphReady $tgsReady $vegetationReady $graphName $tgsName $vegetationName
|
||||
[DateTimeOffset]::UtcNow.ToString("o") | Set-Content -LiteralPath (
|
||||
Join-Path $runOutput "control\start.signal"
|
||||
) -Encoding utf8
|
||||
|
||||
$telemetryPath = Join-Path $runOutput "container-telemetry.jsonl"
|
||||
$m49TelemetryPath = if ($VegetationLoadGate) {
|
||||
Join-Path $runOutput "m49-container-telemetry.jsonl"
|
||||
} else { $telemetryPath }
|
||||
while ($true) {
|
||||
$graphState = Get-Container $graphName
|
||||
$tgsState = Get-Container $tgsName
|
||||
$vegetationState = if ($VegetationLoadGate) {
|
||||
Get-Container $vegetationName
|
||||
} else { $null }
|
||||
$running = @()
|
||||
if ($graphState.State.Running) { $running += $graphName }
|
||||
if ($tgsState.State.Running) { $running += $tgsName }
|
||||
if ($VegetationLoadGate -and $vegetationState.State.Running) {
|
||||
$running += $vegetationName
|
||||
}
|
||||
if ((Get-Container $tritonName).State.Running) { $running += $tritonName }
|
||||
if ($running.Count -gt 0) {
|
||||
$stats = @((& docker stats --no-stream --format "{{json .}}" @running))
|
||||
@@ -362,10 +479,12 @@ try {
|
||||
"tgs"
|
||||
} elseif ($value.Name -ceq $tritonName) {
|
||||
"triton"
|
||||
} elseif ($VegetationLoadGate -and $value.Name -ceq $vegetationName) {
|
||||
"vegetation"
|
||||
} else {
|
||||
throw "Unknown M49 telemetry container"
|
||||
}
|
||||
[ordered]@{
|
||||
$telemetryRow = [ordered]@{
|
||||
observed_utc = [DateTimeOffset]::UtcNow.ToString("o")
|
||||
role = $role
|
||||
name = [string]$value.Name
|
||||
@@ -373,23 +492,46 @@ try {
|
||||
memory_usage = [string]$value.MemUsage
|
||||
memory_percent = [string]$value.MemPerc
|
||||
pids = [string]$value.PIDs
|
||||
} | ConvertTo-Json -Compress | Out-File -LiteralPath $telemetryPath -Encoding utf8 -Append
|
||||
} | ConvertTo-Json -Compress
|
||||
$telemetryRow | Out-File -LiteralPath $telemetryPath -Encoding utf8 -Append
|
||||
if ($VegetationLoadGate -and $role -cne "vegetation") {
|
||||
$telemetryRow | Out-File -LiteralPath $m49TelemetryPath -Encoding utf8 -Append
|
||||
}
|
||||
}
|
||||
}
|
||||
if (-not $graphState.State.Running -and -not $tgsState.State.Running) { break }
|
||||
$vegetationStopped = -not $VegetationLoadGate -or -not $vegetationState.State.Running
|
||||
if (
|
||||
-not $graphState.State.Running -and
|
||||
-not $tgsState.State.Running -and
|
||||
$vegetationStopped
|
||||
) { break }
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
$graphExit = [int](Get-Container $graphName).State.ExitCode
|
||||
$tgsExit = [int](Get-Container $tgsName).State.ExitCode
|
||||
$vegetationExit = if ($VegetationLoadGate) {
|
||||
[int](Get-Container $vegetationName).State.ExitCode
|
||||
} else { 0 }
|
||||
$previousErrorAction = $ErrorActionPreference
|
||||
$ErrorActionPreference = "Continue"
|
||||
$graphLogs = & docker logs $graphName 2>&1
|
||||
$tgsLogs = & docker logs $tgsName 2>&1
|
||||
$vegetationLogs = if ($VegetationLoadGate) {
|
||||
& docker logs $vegetationName 2>&1
|
||||
} else { @() }
|
||||
$ErrorActionPreference = $previousErrorAction
|
||||
$graphLogs | Set-Content -LiteralPath (Join-Path $runOutput "graph.log") -Encoding utf8
|
||||
$tgsLogs | Set-Content -LiteralPath (Join-Path $runOutput "tgs.log") -Encoding utf8
|
||||
if ($VegetationLoadGate) {
|
||||
$vegetationLogs | Set-Content -LiteralPath (
|
||||
Join-Path $runOutput "vegetation.log"
|
||||
) -Encoding utf8
|
||||
}
|
||||
if ($graphExit -ne 0) { throw "M49 integrated graph failed with exit code $graphExit" }
|
||||
if ($tgsExit -ne 0) { throw "M49 integrated TGS failed with exit code $tgsExit" }
|
||||
if ($vegetationExit -ne 0) {
|
||||
throw "M49 integrated vegetation failed with exit code $vegetationExit"
|
||||
}
|
||||
|
||||
& docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g `
|
||||
--entrypoint python3 `
|
||||
@@ -401,6 +543,12 @@ try {
|
||||
--output-root /shared/tgs/evidence
|
||||
Assert-LastExitCode "M49 integrated TGS evidence analysis"
|
||||
|
||||
$m49ResultPath = if ($VegetationLoadGate) {
|
||||
"/shared/m49-result.json"
|
||||
} else { "/shared/result.json" }
|
||||
$dockerM49TelemetryPath = if ($VegetationLoadGate) {
|
||||
"/shared/m49-container-telemetry.jsonl"
|
||||
} else { "/shared/container-telemetry.jsonl" }
|
||||
& docker run --rm --name $evidenceName --network none --cpus 4 --memory 8g `
|
||||
--entrypoint python3 `
|
||||
--volume ($dockerRelease + ":/release:ro") `
|
||||
@@ -411,11 +559,33 @@ try {
|
||||
--graph-frames /shared/graph/frames.jsonl `
|
||||
--tgs-result /shared/tgs/evidence/result.json `
|
||||
--tgs-timing /shared/tgs/tgs-full-timing.tsv `
|
||||
--telemetry /shared/container-telemetry.jsonl `
|
||||
--output /shared/result.json `
|
||||
--telemetry $dockerM49TelemetryPath `
|
||||
--output $m49ResultPath `
|
||||
--release-sha256 $ExpectedArtifactSha256
|
||||
Assert-LastExitCode "M49 integrated evidence gate"
|
||||
|
||||
if ($VegetationLoadGate) {
|
||||
& docker run --rm --name $vegetationEvidenceName --network none --cpus 4 --memory 8g `
|
||||
--entrypoint python3 `
|
||||
--volume ($dockerRelease + ":/release:ro") `
|
||||
--volume ($dockerRun + ":/shared:rw") `
|
||||
$ParityImageTag /release/build_vegetation_integrated_graph_evidence.py `
|
||||
--profile /release/lab-v1-vegetation-integrated-shadow-v1.json `
|
||||
--m49-result /shared/m49-result.json `
|
||||
--graph-frames /shared/graph/frames.jsonl `
|
||||
--tgs-timing /shared/tgs/tgs-full-timing.tsv `
|
||||
--vegetation-result /shared/vegetation/result.json `
|
||||
--vegetation-frames /shared/vegetation/frames.jsonl `
|
||||
--telemetry /shared/container-telemetry.jsonl `
|
||||
--output /shared/result.json `
|
||||
--release-sha256 $ExpectedArtifactSha256
|
||||
Assert-LastExitCode "M49 integrated vegetation evidence gate"
|
||||
}
|
||||
} finally {
|
||||
$vegetationFrames = Join-Path $runOutput "vegetation\input-frames"
|
||||
if (Test-Path -LiteralPath $vegetationFrames -PathType Container) {
|
||||
Remove-Item -LiteralPath $vegetationFrames -Recurse -Force
|
||||
}
|
||||
foreach ($name in $containers) { Remove-ExactContainer $name }
|
||||
$canonicalAfter = Get-Container "ndc-mission-core-triton"
|
||||
if (
|
||||
@@ -432,7 +602,11 @@ if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
|
||||
}
|
||||
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
|
||||
$summary = [ordered]@{
|
||||
schema_version = "missioncore.m49-tgs-integrated-graph-worker-summary/v1"
|
||||
schema_version = if ($VegetationLoadGate) {
|
||||
"missioncore.lab-v1-vegetation-integrated-worker-summary/v1"
|
||||
} else {
|
||||
"missioncore.m49-tgs-integrated-graph-worker-summary/v1"
|
||||
}
|
||||
worker_id = "worker-006"
|
||||
run_id = $RunId
|
||||
code_revision = [string]$releaseDocument.code_revision
|
||||
@@ -443,6 +617,7 @@ $summary = [ordered]@{
|
||||
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
|
||||
result_id = [string]$result.result_id
|
||||
result_status = [string]$result.status
|
||||
vegetation_load_gate = [bool]$VegetationLoadGate
|
||||
canonical_triton_id = $canonicalId
|
||||
canonical_triton_health = "healthy"
|
||||
gauss_or_playcanvas_action = "none"
|
||||
|
||||
Reference in New Issue
Block a user