Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
99408de
Add apple tree collision-aware build script and camera capture rig
yogesh2850 Jul 22, 2026
4f9885b
Add Gaussian Splatting pipeline: per-tree camera rig, fruit-only, dua…
yogesh2850 Jul 28, 2026
49282e3
Sweep capture density and viewing-plane count as separate design axes
yogesh2850 Jul 28, 2026
4f4ccc0
overall md file
yogesh2850 Jul 28, 2026
bcb1a0a
helios setup
yogesh2850 Jul 28, 2026
1053f56
yogesh_dev
yogesh2850 Jul 29, 2026
5ca80ea
Add Phase 0-4 deliverables (yogesh_dev/): radiation cameras, ground t…
yogesh2850 Jul 29, 2026
3429d7e
Add yogesh_dev/COMPLETE_SETUP.md as an index into Phase 0-5 progress
yogesh2850 Jul 29, 2026
4c655c8
Expand COMPLETE_SETUP.md to cover the full plan (Phase 0-8), not just…
yogesh2850 Jul 29, 2026
6bb17e8
Add Phase 5-6 deliverables (yogesh_dev/): baselines/oracles, metrics …
yogesh2850 Jul 29, 2026
96a2622
Add Phase 7 deliverables (yogesh_dev/): foundation model diagnostics …
yogesh2850 Jul 30, 2026
9574821
Add yogesh_dev/FINDINGS_SUMMARY.md — synthesis of real findings acros…
yogesh2850 Jul 30, 2026
ad174df
Add yogesh_dev/PHASE_BY_PHASE_FINDINGS.md — detailed what-we-did/what…
yogesh2850 Jul 30, 2026
d4bd146
Add Phase 8 deliverables and close out the full plan (Phases 0-8 comp…
yogesh2850 Jul 30, 2026
a3de7ab
world_model: W0-W2 -- orchard factory, batched render rig, action space
yogesh2850 Jul 31, 2026
7f67dbd
world_model: W1 fixes -- ground plane, orientation-only-on-RGB, repro…
yogesh2850 Jul 31, 2026
d4efbee
world_model: W4 model + evaluation harness + gsplat reference baseline
yogesh2850 Jul 31, 2026
b5da2b5
world_model: W4 acceptance PASSED, plus an Adam-epsilon divergence fo…
yogesh2850 Jul 31, 2026
102230e
world_model: chain script for W3-verify/W5/W6, log through W4, COMPLE…
yogesh2850 Jul 31, 2026
4678b96
world_model: W3 dataset done (64k frames) + verification, and the RGB…
yogesh2850 Jul 31, 2026
b316ad6
world_model: W5 training + W6 evaluation at 30k steps, with an honest…
yogesh2850 Jul 31, 2026
b982904
world_model: W5/W6 final results with corrected checkpoint selection,…
yogesh2850 Jul 31, 2026
8955257
world_model: record that the Slack notification could not be delivered
yogesh2850 Jul 31, 2026
a618f61
World model Round 2: growth-channel diagnostics + dataset-scaling sca…
yogesh2850 Jul 31, 2026
267448d
World model Round 2: error attribution says capacity, not data
yogesh2850 Jul 31, 2026
e91a220
World model Round 2: mIoU 0.266 is class collapse, not a plateau
yogesh2850 Jul 31, 2026
73a06a8
World model Round 2: quantify the blur and locate the depth error
yogesh2850 Jul 31, 2026
671ed20
World model Round 2: L1 depth option and the combined-fix run
yogesh2850 Jul 31, 2026
1052078
World model Round 2: seed the recon-floor evaluation
yogesh2850 Jul 31, 2026
151bb78
World model Round 2: separate in- and out-of-distribution dt in the g…
yogesh2850 Jul 31, 2026
67e482b
World model Round 2: a blurred-ground-truth control bounds what sharp…
yogesh2850 Jul 31, 2026
34eb47d
World model Round 2: FINDINGS sections 12-15 (the diagnostics)
yogesh2850 Jul 31, 2026
c906a0d
World model Round 2: expose the KL knobs and add the bottleneck run
yogesh2850 Jul 31, 2026
9aaaf82
World model Round 2: chronological log through the diagnostics phase
yogesh2850 Jul 31, 2026
aebe2f1
World model Round 2: fix a garbled step reference in the log
yogesh2850 Jul 31, 2026
acae96e
World model Round 2: correct the growth RGB SNR with a same-pose meas…
yogesh2850 Jul 31, 2026
b118bbb
World model Round 2: sections 16-17 -- data scaling result and the gr…
yogesh2850 Jul 31, 2026
6b660a9
World model Round 2: retract and correct the blur bound in section 14
yogesh2850 Jul 31, 2026
a4fd2b2
World model Round 2: section 18 -- what actually moved the held-out n…
yogesh2850 Jul 31, 2026
7172f50
World model Round 2: task-by-task status with acceptance against the …
yogesh2850 Jul 31, 2026
4d73c7e
World model Round 2: log sections 13-16 -- run chronology, the turnin…
yogesh2850 Jul 31, 2026
9e1833a
World model Round 2: run stdout logs
yogesh2850 Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
286 changes: 286 additions & 0 deletions GAUSSIAN_SPLATTING.md

Large diffs are not rendered by default.

830 changes: 830 additions & 0 deletions active_vision_design.md

Large diffs are not rendered by default.

146 changes: 146 additions & 0 deletions apple_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import sys

from pyhelios import Context, PlantArchitecture, Visualizer
from pyhelios.types import RGBcolor, vec3


def build_apple_tree(plantarch, position=None, age_days=365.0, build_parameters=None):
"""Load the apple library model and build one tree instance.

Args:
plantarch: PlantArchitecture instance bound to a Context.
position: Base of the trunk (default: origin).
age_days: Plant age in days. Older = larger / more developed.
build_parameters: Optional training overrides, e.g.
{'trunk_height': 0.8, 'num_scaffolds': 4, 'scaffold_angle': 40}

Returns:
plant_id for the created tree.
"""
if position is None:
position = vec3(0, 0, 0)

if build_parameters and build_parameters.get("trunk_height", 0.8) > 0.8:
raise ValueError(
"The built-in apple model requires trunk_height <= 0.80 m "
"(20 nodes at 0.04 m per internode)."
)

plantarch.loadPlantModelFromLibrary("apple")
plant_id = plantarch.buildPlantInstanceFromLibrary(
base_position=position,
age=age_days,
build_parameters=build_parameters,
)
return plant_id


def visualize_trees(context, plantarch, plant_ids):
"""Open one interactive window showing all built trees together."""
all_uuids = []
for plant_id in plant_ids:
uuids = plantarch.getAllPlantUUIDs(plant_id)
if not uuids:
raise RuntimeError(f"Plant {plant_id} has no geometry to visualize")
all_uuids.extend(uuids)

if not all_uuids:
raise RuntimeError("No tree geometry to visualize")

# Fit the camera to the combined scene bounding box
x_bounds, y_bounds, z_bounds = context.getDomainBoundingBox(all_uuids)
center = vec3(
0.5 * (x_bounds.x + x_bounds.y),
0.5 * (y_bounds.x + y_bounds.y),
0.5 * (z_bounds.x + z_bounds.y),
)
extent = max(
x_bounds.y - x_bounds.x,
y_bounds.y - y_bounds.x,
z_bounds.y - z_bounds.x,
1.0,
)

with Visualizer(width=1000, height=800) as visualizer:
visualizer.buildContextGeometry(context, uuids=all_uuids)
visualizer.setCameraPosition(
position=vec3(
center.x + 1.8 * extent,
center.y + 1.8 * extent,
center.z + 0.6 * extent,
),
lookAt=center,
)
visualizer.setBackgroundColor(RGBcolor(0.70, 0.85, 1.0))
visualizer.setLightingModel("phong_shadowed")
visualizer.plotInteractive()


if __name__ == "__main__":
# Age is the main "size" control for library plants (days).
# Try ~90 for a young tree, ~365 for a year-old tree.
AGE_DAYS = [720.0] * 10

POSITIONS = [
vec3(0, 0, 0),
vec3(1.5, 0, 0),
vec3(3, 0, 0),
vec3(9, 0, 0),
vec3(12, 0, 0),
vec3(15, 0, 0),
vec3(18, 0, 0),
vec3(21, 0, 0),
vec3(24, 0, 0),
vec3(27, 0, 0),
]

# Optional apple training-system overrides (omit to use defaults):
BUILD_PARAMS_LIST = [
{"trunk_height": 0.72, "num_scaffolds": 4, "scaffold_angle": 33},
{"trunk_height": 0.78, "num_scaffolds": 6, "scaffold_angle": 47},
{"trunk_height": 0.79, "num_scaffolds": 5, "scaffold_angle": 39},
{"trunk_height": 0.74, "num_scaffolds": 4, "scaffold_angle": 44},
{"trunk_height": 0.70, "num_scaffolds": 6, "scaffold_angle": 31},
{"trunk_height": 0.80, "num_scaffolds": 5, "scaffold_angle": 50},
{"trunk_height": 0.76, "num_scaffolds": 4, "scaffold_angle": 36},
{"trunk_height": 0.77, "num_scaffolds": 5, "scaffold_angle": 42},
{"trunk_height": 0.71, "num_scaffolds": 6, "scaffold_angle": 35},
{"trunk_height": 0.74, "num_scaffolds": 5, "scaffold_angle": 48},
]

with Context() as context:
with PlantArchitecture(context) as plantarch:
plantarch.loadPlantModelFromLibrary("apple")

# Include fruit in collision detection and steer growth away from
# overlaps (fruit is excluded by default) so apples on neighboring
# trees/branches don't grow into each other.
plantarch.setCollisionRelevantOrgans(
include_internodes=True,
include_leaves=True,
include_fruit=True,
)
plantarch.enableSoftCollisionAvoidance(enable_fruit_collision=True)

plant_ids = []

for i in range(3):
plant_id_i = build_apple_tree(
plantarch,
position=POSITIONS[i],
age_days=AGE_DAYS[i],
build_parameters=BUILD_PARAMS_LIST[i],
)
plant_ids.append(plant_id_i)

object_ids = plantarch.getAllPlantObjectIDs(plant_id_i)
uuids = plantarch.getAllPlantUUIDs(plant_id_i)
x_bounds, y_bounds, z_bounds = context.getDomainBoundingBox(uuids)
print(f"Apple tree plant_id={plant_id_i}")
print(f" objects: {len(object_ids)}")
print(f" primitives: {len(uuids)}")
print(f" age: {AGE_DAYS[i]} days")
print(f" extent: x={x_bounds.y - x_bounds.x:.3f}m, y={y_bounds.y - y_bounds.x:.3f}m, z={z_bounds.y - z_bounds.x:.3f}m")

if "--no-visualization" not in sys.argv:
visualize_trees(context, plantarch, plant_ids)
76 changes: 76 additions & 0 deletions apple_tree_cameras.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import os
import sys

from apple_tree import build_apple_tree
from pyhelios import Context, PlantArchitecture, Visualizer
from pyhelios.types import RGBcolor, vec3

# Gaussian-splat capture rig: all 3 cameras sit at the same horizontal
# distance in front of the tree (same x, same y offset) and differ only in
# elevation (z) -- one above the tree looking down, one level with the
# tree's center, one low near the ground looking up. The same rig is
# reused for every tree, just re-centered on that tree's position/height.
CAMERA_DISTANCE = 2.5 # front offset from the tree, in meters, same for all rigs

CAMERA_RIGS = [
{"name": "above", "height_frac": 1.15},
{"name": "level", "height_frac": 0.5},
{"name": "below", "height_frac": 0.05},
]

OUTPUT_DIR = "renders"


def camera_position_for_tree(context, plantarch, plant_id, base_position, rig):
"""Compute a camera position/lookAt in front of one tree for a given rig."""
uuids = plantarch.getAllPlantUUIDs(plant_id)
x_bounds, y_bounds, z_bounds = context.getDomainBoundingBox(uuids)
tree_height = z_bounds.y - z_bounds.x

look_at = vec3(base_position.x, base_position.y, z_bounds.x + 0.5 * tree_height)
position = vec3(
base_position.x,
base_position.y - CAMERA_DISTANCE,
z_bounds.x + rig["height_frac"] * tree_height,
)
return position, look_at


if __name__ == "__main__":
AGE_DAYS = 365.0
POSITIONS = [vec3(0, 0, 0), vec3(3, 0, 0), vec3(6, 0, 0)]

os.makedirs(OUTPUT_DIR, exist_ok=True)

with Context() as context:
with PlantArchitecture(context) as plantarch:
plant_ids = []
for i, position in enumerate(POSITIONS):
plant_id = build_apple_tree(plantarch, position=position, age_days=AGE_DAYS)
plant_ids.append(plant_id)
print(f"Built apple tree plant_id={plant_id} at {position}")

all_uuids = []
for plant_id in plant_ids:
all_uuids.extend(plantarch.getAllPlantUUIDs(plant_id))

with Visualizer(width=1000, height=800, headless=True) as visualizer:
visualizer.buildContextGeometry(context, uuids=all_uuids)
visualizer.setBackgroundColor(RGBcolor(0.70, 0.85, 1.0))
visualizer.setLightingModel("phong_shadowed")

for plant_id, position in zip(plant_ids, POSITIONS):
for rig in CAMERA_RIGS:
camera_pos, look_at = camera_position_for_tree(
context, plantarch, plant_id, position, rig
)
visualizer.setCameraPosition(position=camera_pos, lookAt=look_at)
visualizer.plotUpdate()

filename = os.path.join(
OUTPUT_DIR, f"tree{plant_id}_{rig['name']}.png"
)
visualizer.printWindow(filename)
print(f" Saved {filename}")

print(f"\nDone. Images written to {OUTPUT_DIR}/")
Loading