A-SW Hub / Repositories
Local Gitea · READ ONLY
← 저장소 Repositories
REPOSITORY

unist/crop-structure-mapping Private

[참조 MVP] 작물구조 3D 매핑 — 작물과 장애물 관측을 작업공간 지도로 집계하는 위탁연구 참조 MVP입니다.

https://git.agrithing.ai/unist/crop-structure-mapping.git
4 commits
COMMIT DETAIL

feat: 작물구조 3D 매핑 구체 MVP 구현

기관 승인 및 자동 품질검사를 통과한 구체 MVP 0.2.0 병합 Co-authored-by: unist-dev <[email protected]>

unist-dev <[email protected]> · 2026. 8. 3. 오후 3:13:00
d699d3923c7889bddcd076720fbf52510543dcfa
8 changed files +67 −7 parent e30994c
modified README.md +7−1 View file
@@ -11,6 +11,12 @@
11 11
12 12 작물과 장애물 관측을 작업공간 지도로 집계하는 위탁연구 참조 MVP입니다.
13 13
14 + ## MVP 동작 범위
15 +
16 + - 3차원 점군 유효성 검사
17 + - 클래스별 개체 집계
18 + - 작업공간 경계와 장애물 목록 생성
19 +
14 20 ## 실행
15 21
16 22 ```bash
@@ -18,4 +24,4 @@ python run.py examples/input.json
18 24 python -m unittest discover -s tests -v
19 25 ```
20 26
21 실제 기관 구현은 `contracts/component.interface.json`의 저장소 식별자와 제품 기준선 연결 규칙을 유지하면서 교체할 수 있습니다.
27 + 정상 입력은 `examples/input.json`, 차단·안전 경로는 `examples/failure-input.json`으로 재현합니다. 실제 기관 구현은 `contracts/component.interface.json`의 저장소 식별자와 제품 기준선 연결 규칙을 유지하면서 교체할 수 있습니다.
modified asw_component/component.py +15−3 View file
@@ -2,6 +2,18 @@ from __future__ import annotations
2 2
3 3
4 4 def execute(payload: dict) -> dict:
5 if not isinstance(payload, dict) or not payload:
6 raise ValueError("a non-empty object is required")
7 return {"component": "unist/crop-structure-mapping", "accepted": True, "status": "READY", "target": "vpu", "input_fields": sorted(payload.keys())}
5 + valid = []
6 + for point in payload.get("points", []):
7 + try:
8 + valid.append({"class": str(point["class"]), "x": float(point["x"]), "y": float(point["y"]), "z": float(point["z"])})
9 + except (KeyError, TypeError, ValueError):
10 + continue
11 + counts = {}
12 + for point in valid: counts[point["class"]] = counts.get(point["class"], 0) + 1
13 + bounds = None if not valid else {axis: [min(point[axis] for point in valid), max(point[axis] for point in valid)] for axis in ("x", "y", "z")}
14 + obstacles = [{"x": point["x"], "y": point["y"], "z": point["z"]} for point in valid if point["class"] == "obstacle"]
15 + accepted = bool(valid)
16 + return {"component": "unist/crop-structure-mapping", "accepted": accepted,
17 + "status": "MAP_READY" if accepted else "NO_POINTS", "target": "vpu",
18 + "mapId": str(payload.get("mapId", "unknown")), "counts": counts,
19 + "bounds": bounds, "obstacles": obstacles}
modified contracts/component.interface.json +21−1 View file
@@ -5,7 +5,12 @@
5 5 "required": [
6 6 "component",
7 7 "accepted",
8 "status"
8 + "status",
9 + "target",
10 + "mapId",
11 + "counts",
12 + "bounds",
13 + "obstacles"
9 14 ],
10 15 "properties": {
11 16 "component": {
@@ -19,6 +24,21 @@
19 24 },
20 25 "target": {
21 26 "const": "vpu"
27 + },
28 + "mapId": {
29 + "type": "string"
30 + },
31 + "counts": {
32 + "type": "object"
33 + },
34 + "bounds": {
35 + "type": [
36 + "object",
37 + "null"
38 + ]
39 + },
40 + "obstacles": {
41 + "type": "array"
22 42 }
23 43 }
24 44 }
added examples/failure-input.json +4−0 View file
@@ -0,0 +1,4 @@
1 + {
2 + "mapId": "row-04-map",
3 + "points": []
4 + }
modified examples/input.json +6−0 View file
@@ -12,6 +12,12 @@
12 12 "x": 2.1,
13 13 "y": 0.7,
14 14 "z": 0.3
15 + },
16 + {
17 + "class": "fruit",
18 + "x": 1.4,
19 + "y": 0.6,
20 + "z": 1.2
15 21 }
16 22 ]
17 23 }
modified pyproject.toml +1−1 View file
@@ -1,5 +1,5 @@
1 1 [project]
2 2 name = "crop-structure-mapping"
3 version = "0.1.0"
3 + version = "0.2.0"
4 4 description = "작물과 장애물 관측을 작업공간 지도로 집계하는 위탁연구 참조 MVP입니다."
5 5 requires-python = ">=3.11"
modified release/manifest.json +1−1 View file
@@ -4,7 +4,7 @@
4 4 "displayName": "작물구조 3D 매핑",
5 5 "owner": "울산과학기술원",
6 6 "implementationAuthor": "주식회사 비아 (연동 검증용 참조 구현)",
7 "version": "0.1.0-mvp",
7 + "version": "0.2.0-mvp",
8 8 "domain": "3D Mapping",
9 9 "status": "REFERENCE_MVP",
10 10 "compatibility": {
modified tests/test_component.py +12−0 View file
@@ -12,6 +12,18 @@ class ComponentTest(unittest.TestCase):
12 12 result = execute(payload)
13 13 self.assertTrue(result["accepted"])
14 14 self.assertEqual(result["component"], "unist/crop-structure-mapping")
15 + self.assertEqual(result["target"], "vpu")
16 + self.assertEqual(result["counts"], {"stem": 1, "obstacle": 1, "fruit": 1})
17 + self.assertEqual(result["bounds"]["x"], [1.2, 2.1])
18 + self.assertEqual(len(result["obstacles"]), 1)
19 +
20 + def test_safety_or_failure_path(self):
21 + payload = json.loads((ROOT / "examples/failure-input.json").read_text(encoding="utf-8"))
22 + result = execute(payload)
23 + self.assertEqual(result["component"], "unist/crop-structure-mapping")
24 + self.assertFalse(result["accepted"])
25 + self.assertEqual(result["status"], "NO_POINTS")
26 + self.assertIsNone(result["bounds"])
15 27
16 28 if __name__ == "__main__":
17 29 unittest.main()