update pip install .copy manifest for developments
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# Non-Python files needed for sdist -> wheel builds from an unpacked sdist.
|
||||||
|
include pyproject.toml
|
||||||
|
include setup.py
|
||||||
|
include README.md
|
||||||
|
recursive-include config *.json
|
||||||
|
recursive-include sensors *
|
||||||
|
recursive-include build/common *.py
|
||||||
|
recursive-include build/wheel *.py
|
||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import ast
|
import ast
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -102,7 +103,11 @@ def bundle_items(repo_root: Path) -> list[dict]:
|
|||||||
|
|
||||||
|
|
||||||
def bundle_data_files(repo_root: Path) -> list[tuple[str, list[str]]]:
|
def bundle_data_files(repo_root: Path) -> list[tuple[str, list[str]]]:
|
||||||
"""Map bundle items marked dev_install=true to setuptools data_files entries."""
|
"""Map bundle items marked ``dev_install=true`` to install destinations.
|
||||||
|
|
||||||
|
Kept for tests / tooling. Prefer :func:`stage_dev_bundle_into` for pip
|
||||||
|
installs so files land inside ``site-packages/orisys/``.
|
||||||
|
"""
|
||||||
resolve_bundle_source_files = _load_wheel_layout_module().resolve_bundle_source_files
|
resolve_bundle_source_files = _load_wheel_layout_module().resolve_bundle_source_files
|
||||||
|
|
||||||
grouped: dict[str, list[str]] = {}
|
grouped: dict[str, list[str]] = {}
|
||||||
@@ -123,3 +128,33 @@ def bundle_data_files(repo_root: Path) -> list[tuple[str, list[str]]]:
|
|||||||
grouped.setdefault(dest, []).append(rel_src)
|
grouped.setdefault(dest, []).append(rel_src)
|
||||||
|
|
||||||
return [(dest, sorted(files)) for dest, files in sorted(grouped.items())]
|
return [(dest, sorted(files)) for dest, files in sorted(grouped.items())]
|
||||||
|
|
||||||
|
|
||||||
|
def stage_dev_bundle_into(repo_root: Path, pkg_dir: Path) -> list[str]:
|
||||||
|
"""Copy ``dev_install`` bundle files under ``pkg_dir`` (the orisys package).
|
||||||
|
|
||||||
|
Returns package-relative paths that were written (e.g. ``sensors/box/...``).
|
||||||
|
"""
|
||||||
|
resolve_bundle_source_files = _load_wheel_layout_module().resolve_bundle_source_files
|
||||||
|
packaged: list[str] = []
|
||||||
|
|
||||||
|
for item in bundle_items(repo_root):
|
||||||
|
if not item.get("dev_install", False):
|
||||||
|
continue
|
||||||
|
|
||||||
|
src_dir = repo_root / item["source"]
|
||||||
|
dst_dir = pkg_dir / item["target"]
|
||||||
|
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for src_file in resolve_bundle_source_files(repo_root, item):
|
||||||
|
if item.get("preserve_tree", False):
|
||||||
|
rel = src_file.relative_to(src_dir)
|
||||||
|
dst_file = dst_dir / rel
|
||||||
|
else:
|
||||||
|
rel = Path(src_file.name)
|
||||||
|
dst_file = dst_dir / src_file.name
|
||||||
|
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(src_file, dst_file)
|
||||||
|
packaged.append(f"{item['target']}/{rel.as_posix()}")
|
||||||
|
|
||||||
|
return packaged
|
||||||
|
|||||||
+12
-1
@@ -6,12 +6,19 @@ import shutil
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Required files that must ship with the wheel for ORY-FINGER runtime/GUI.
|
# Required files that must ship with the wheel for ORY-FINGER runtime/GUI.
|
||||||
|
# Accept both the flat layout and a nested assets/finger/ checkout tree.
|
||||||
REQUIRED_ASSET_RELPATHS = (
|
REQUIRED_ASSET_RELPATHS = (
|
||||||
"finger/assets/30fps-0703/mesh.npz",
|
"finger/assets/30fps-0703/mesh.npz",
|
||||||
"finger/assets/30fps-0703/camera_intrinsics.npz",
|
"finger/assets/30fps-0703/camera_intrinsics.npz",
|
||||||
"finger/assets/30fps-0703/world_to_cam.npy",
|
"finger/assets/30fps-0703/world_to_cam.npy",
|
||||||
"finger/assets/visualization/world_to_cam.npy",
|
"finger/assets/visualization/world_to_cam.npy",
|
||||||
)
|
)
|
||||||
|
REQUIRED_ASSET_RELPATH_ALTERNATES = {
|
||||||
|
"finger/assets/30fps-0703/mesh.npz": "finger/assets/finger/30fps-0703/mesh.npz",
|
||||||
|
"finger/assets/30fps-0703/camera_intrinsics.npz": "finger/assets/finger/30fps-0703/camera_intrinsics.npz",
|
||||||
|
"finger/assets/30fps-0703/world_to_cam.npy": "finger/assets/finger/30fps-0703/world_to_cam.npy",
|
||||||
|
"finger/assets/visualization/world_to_cam.npy": "finger/assets/finger/visualization/world_to_cam.npy",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def resolve_bundle_source_files(repo_root: Path, item: dict) -> list[Path]:
|
def resolve_bundle_source_files(repo_root: Path, item: dict) -> list[Path]:
|
||||||
@@ -61,7 +68,11 @@ def verify_required_assets(repo_root: Path, items: list[dict]) -> None:
|
|||||||
continue
|
continue
|
||||||
resolved.add(rel)
|
resolved.add(rel)
|
||||||
|
|
||||||
missing = [rel for rel in REQUIRED_ASSET_RELPATHS if rel not in resolved]
|
missing = []
|
||||||
|
for rel in REQUIRED_ASSET_RELPATHS:
|
||||||
|
alt = REQUIRED_ASSET_RELPATH_ALTERNATES.get(rel)
|
||||||
|
if rel not in resolved and (alt is None or alt not in resolved):
|
||||||
|
missing.append(rel)
|
||||||
if missing:
|
if missing:
|
||||||
joined = ", ".join(missing)
|
joined = ", ".join(missing)
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
|
|||||||
@@ -49,8 +49,23 @@ def main():
|
|||||||
sensor.compute_contact()
|
sensor.compute_contact()
|
||||||
|
|
||||||
|
|
||||||
# 步骤 2.4:读取结果
|
# 步骤 2.4:读取结果(FNORMAL/FSHEAR* 在有 sensors/box/force_calibration.json 时为标定后牛顿力;
|
||||||
fps, flow, vnormal, img, contour, centroid, depth_map, fnormal, fshearx, fsheary = sensor.read_info(
|
# *_RAW 始终为原始积分量)
|
||||||
|
(
|
||||||
|
fps,
|
||||||
|
flow,
|
||||||
|
vnormal,
|
||||||
|
img,
|
||||||
|
contour,
|
||||||
|
centroid,
|
||||||
|
depth_map,
|
||||||
|
fnormal,
|
||||||
|
fshearx,
|
||||||
|
fsheary,
|
||||||
|
fnormal_raw,
|
||||||
|
fshearx_raw,
|
||||||
|
fsheary_raw,
|
||||||
|
) = sensor.read_info(
|
||||||
sensor.info.FPS,
|
sensor.info.FPS,
|
||||||
sensor.info.VRAW,
|
sensor.info.VRAW,
|
||||||
sensor.info.VNORMAL,
|
sensor.info.VNORMAL,
|
||||||
@@ -60,7 +75,10 @@ def main():
|
|||||||
sensor.info.DEPTH,
|
sensor.info.DEPTH,
|
||||||
sensor.info.FNORMAL,
|
sensor.info.FNORMAL,
|
||||||
sensor.info.FSHEARX,
|
sensor.info.FSHEARX,
|
||||||
sensor.info.FSHEARY
|
sensor.info.FSHEARY,
|
||||||
|
sensor.info.FNORMAL_RAW,
|
||||||
|
sensor.info.FSHEARX_RAW,
|
||||||
|
sensor.info.FSHEARY_RAW,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 步骤 3:可视化显示
|
# 步骤 3:可视化显示
|
||||||
@@ -89,10 +107,13 @@ def main():
|
|||||||
# 应用伪彩色映射
|
# 应用伪彩色映射
|
||||||
cv2.imshow("depth", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET))
|
cv2.imshow("depth", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET))
|
||||||
|
|
||||||
# 步骤 3.4:打印关键结果
|
# 步骤 3.4:打印关键结果(标定力 + 原始力)
|
||||||
print(
|
print(
|
||||||
f"FPS={fps:.2f}, 法向力={fnormal:.4f}, 切向力X={fshearx:.4f}, "
|
f"FPS={fps:.2f}, "
|
||||||
f"切向力Y={fsheary:.4f}, 深度图尺寸={depth_map.shape}, 原始分辨率={sensor.img_size_raw}"
|
f"法向力={fnormal:.4f} N (raw={fnormal_raw:.4f}), "
|
||||||
|
f"切向力X={fshearx:.4f} N (raw={fshearx_raw:.4f}), "
|
||||||
|
f"切向力Y={fsheary:.4f} N (raw={fsheary_raw:.4f}), "
|
||||||
|
f"深度图尺寸={depth_map.shape}, 原始分辨率={sensor.img_size_raw}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 步骤 3.5:键盘控制
|
# 步骤 3.5:键盘控制
|
||||||
|
|||||||
+10
-1
@@ -79,7 +79,16 @@ dev_install = true
|
|||||||
name = "sensors"
|
name = "sensors"
|
||||||
source = "sensors"
|
source = "sensors"
|
||||||
target = "sensors"
|
target = "sensors"
|
||||||
patterns = ["**/*.json", "finger/assets/30fps*/*", "finger/assets/visualization/*"]
|
# JSON bundles (sensor.json, force_calibration.json) plus finger mesh/viz assets.
|
||||||
|
# Prefer the flat layout sensors/finger/assets/{30fps-*,visualization}/;
|
||||||
|
# also accept a nested assets/finger/ tree if present in a checkout.
|
||||||
|
patterns = [
|
||||||
|
"**/*.json",
|
||||||
|
"finger/assets/30fps*/*",
|
||||||
|
"finger/assets/visualization/*",
|
||||||
|
"finger/assets/finger/30fps*/*",
|
||||||
|
"finger/assets/finger/visualization/*",
|
||||||
|
]
|
||||||
preserve_tree = true
|
preserve_tree = true
|
||||||
dev_install = true
|
dev_install = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"normal_force": {
|
|
||||||
"model": "linear",
|
|
||||||
"slope": 3.92685554668795e-05,
|
|
||||||
"offset": 0.0217596967278535,
|
|
||||||
"unit": "N"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,42 @@
|
|||||||
"""Setuptools entry point; bundle rules are read from pyproject.toml."""
|
"""Setuptools entry point; bundle rules are read from pyproject.toml.
|
||||||
|
|
||||||
|
``dev_install`` bundle items (configs/sensors) are staged into the ``orisys``
|
||||||
|
package during build so ``pip install .`` places them at
|
||||||
|
``site-packages/orisys/...`` (importlib / configs resolvers), not under
|
||||||
|
``sys.prefix/orisys`` via deprecated ``data_files``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
from setuptools.command.build_py import build_py as _build_py
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parent
|
REPO_ROOT = Path(__file__).resolve().parent
|
||||||
sys.path.insert(0, str(REPO_ROOT / "build" / "common"))
|
sys.path.insert(0, str(REPO_ROOT / "build" / "common"))
|
||||||
|
|
||||||
from pyproject_utils import bundle_data_files
|
from pyproject_utils import stage_dev_bundle_into # noqa: E402
|
||||||
|
|
||||||
setup(data_files=bundle_data_files(REPO_ROOT))
|
|
||||||
|
class build_py(_build_py):
|
||||||
|
def run(self) -> None:
|
||||||
|
super().run()
|
||||||
|
# After pure modules are copied, stage configs/sensors into build/lib/orisys.
|
||||||
|
pkg_dir = Path(self.build_lib) / "orisys"
|
||||||
|
pkg_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
stage_dev_bundle_into(REPO_ROOT, pkg_dir)
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
cmdclass={"build_py": build_py},
|
||||||
|
# Declared so wheels record the staged files; actual copy happens in build_py.
|
||||||
|
package_data={
|
||||||
|
"orisys": [
|
||||||
|
"configs/*.json",
|
||||||
|
"sensors/**/*",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include_package_data=True,
|
||||||
|
)
|
||||||
|
|||||||
+62
-7
@@ -11,6 +11,7 @@ import numpy as np
|
|||||||
import cv2
|
import cv2
|
||||||
import time
|
import time
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
|
from pathlib import Path
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
# from threading import Thread, Event
|
# from threading import Thread, Event
|
||||||
# from queue import Queue, Empty
|
# from queue import Queue, Empty
|
||||||
@@ -22,7 +23,12 @@ from . import configs
|
|||||||
from .get_depth import Contact3D
|
from .get_depth import Contact3D
|
||||||
from .backend_availability import (check_cupy_available, check_opencl_available)
|
from .backend_availability import (check_cupy_available, check_opencl_available)
|
||||||
from .frame_processors import create_frame_processor
|
from .frame_processors import create_frame_processor
|
||||||
from .signal_processing import DEFAULT_SAMPLE_RATE_HZ, LowpassFilter
|
from .signal_processing import (
|
||||||
|
DEFAULT_SAMPLE_RATE_HZ,
|
||||||
|
LowpassFilter,
|
||||||
|
calibrate_normal_force,
|
||||||
|
calibrate_shear_components,
|
||||||
|
)
|
||||||
from .capture import (
|
from .capture import (
|
||||||
CaptureConfig,
|
CaptureConfig,
|
||||||
CaptureWorker,
|
CaptureWorker,
|
||||||
@@ -148,6 +154,7 @@ class Sensor:
|
|||||||
self.n_boxes = None
|
self.n_boxes = None
|
||||||
self.fnormal = self.fshearx = self.fsheary = self.ftor = 0.0
|
self.fnormal = self.fshearx = self.fsheary = self.ftor = 0.0
|
||||||
self._force_raw = (0.0, 0.0, 0.0)
|
self._force_raw = (0.0, 0.0, 0.0)
|
||||||
|
self._force_cal_sensor_name = self._resolve_force_calibration_sensor_name()
|
||||||
self._lowpass_enabled = False
|
self._lowpass_enabled = False
|
||||||
self._lowpass_cutoff_hz = 0.0
|
self._lowpass_cutoff_hz = 0.0
|
||||||
self._lowpass_sample_rate_hz = float(DEFAULT_SAMPLE_RATE_HZ)
|
self._lowpass_sample_rate_hz = float(DEFAULT_SAMPLE_RATE_HZ)
|
||||||
@@ -158,6 +165,13 @@ class Sensor:
|
|||||||
order=self._lowpass_order,
|
order=self._lowpass_order,
|
||||||
n_channels=3,
|
n_channels=3,
|
||||||
)
|
)
|
||||||
|
if self.verbose:
|
||||||
|
if self._force_cal_sensor_name:
|
||||||
|
print(
|
||||||
|
f"Force calibration: sensors/{self._force_cal_sensor_name}/force_calibration.json"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Force calibration: raw (no force_calibration.json)")
|
||||||
|
|
||||||
# Contact Data Variables (public)
|
# Contact Data Variables (public)
|
||||||
self.centroid = (0, 0)
|
self.centroid = (0, 0)
|
||||||
@@ -790,17 +804,58 @@ class Sensor:
|
|||||||
self._update_hhd_mask_cache()
|
self._update_hhd_mask_cache()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def _resolve_force_calibration_sensor_name(self) -> Optional[str]:
|
||||||
|
"""Return sensors/<name> when force_calibration.json exists; else None.
|
||||||
|
|
||||||
|
Lookup order:
|
||||||
|
1. ``config_name`` stem / product alias (e.g. ``box``)
|
||||||
|
2. ``sensors/<name>/...`` path segment in ``config_name``
|
||||||
|
Missing JSON means ``FNORMAL/FSHEAR*`` stay raw (no hardcoded fallback).
|
||||||
|
"""
|
||||||
|
text = str(self.config_name or "").strip().replace("\\", "/")
|
||||||
|
if not text:
|
||||||
|
return None
|
||||||
|
|
||||||
|
candidates: list[str] = []
|
||||||
|
stem = Path(text).stem if text else ""
|
||||||
|
if stem:
|
||||||
|
candidates.append(stem)
|
||||||
|
parts = [p for p in text.split("/") if p]
|
||||||
|
if "sensors" in parts:
|
||||||
|
idx = parts.index("sensors")
|
||||||
|
if idx + 1 < len(parts):
|
||||||
|
candidates.append(parts[idx + 1])
|
||||||
|
for name in configs.PRODUCT_BUNDLES:
|
||||||
|
if text == name or text.endswith(f"/sensors/{name}") or text.endswith(f"/sensors/{name}/sensor.json"):
|
||||||
|
candidates.append(name)
|
||||||
|
|
||||||
|
seen: set[str] = set()
|
||||||
|
for candidate in candidates:
|
||||||
|
alias = configs.CONFIG_ALIASES.get(candidate, candidate)
|
||||||
|
for name in (alias, candidate):
|
||||||
|
if name in seen:
|
||||||
|
continue
|
||||||
|
seen.add(name)
|
||||||
|
if configs.load_force_calibration(name) is not None:
|
||||||
|
return name
|
||||||
|
return None
|
||||||
|
|
||||||
def _apply_force_filter(self):
|
def _apply_force_filter(self):
|
||||||
raw = np.asarray(self._force_raw, dtype=np.float64)
|
raw = np.asarray(self._force_raw, dtype=np.float64)
|
||||||
if self._lowpass_enabled and self._lowpass_cutoff_hz > 0:
|
if self._lowpass_enabled and self._lowpass_cutoff_hz > 0:
|
||||||
filtered = self._force_filter.process(raw)
|
filtered = self._force_filter.process(raw)
|
||||||
self.fnormal, self.fshearx, self.fsheary = (float(v) for v in filtered)
|
fn, fx, fy = (float(v) for v in filtered)
|
||||||
else:
|
else:
|
||||||
self.fnormal, self.fshearx, self.fsheary = (
|
fn, fx, fy = float(raw[0]), float(raw[1]), float(raw[2])
|
||||||
float(raw[0]),
|
|
||||||
float(raw[1]),
|
# Apply sensors/<name>/force_calibration.json when present; otherwise keep raw.
|
||||||
float(raw[2]),
|
name = self._force_cal_sensor_name
|
||||||
)
|
if name is not None:
|
||||||
|
fn = float(calibrate_normal_force(fn, sensor_name=name))
|
||||||
|
fx, fy = calibrate_shear_components(fx, fy, sensor_name=name)
|
||||||
|
fx, fy = float(fx), float(fy)
|
||||||
|
|
||||||
|
self.fnormal, self.fshearx, self.fsheary = fn, fx, fy
|
||||||
|
|
||||||
# def export_config(self, path="./config/exported_config.json"):
|
# def export_config(self, path="./config/exported_config.json"):
|
||||||
# """Return current configuration dict."""
|
# """Return current configuration dict."""
|
||||||
|
|||||||
+22
-2
@@ -57,13 +57,33 @@ def test_pyproject_bundle_manifest(packaging_utils):
|
|||||||
assert all("/" in path and "*" not in path for path in config_files)
|
assert all("/" in path and "*" not in path for path in config_files)
|
||||||
assert any(path.endswith(".json") for path in config_files)
|
assert any(path.endswith(".json") for path in config_files)
|
||||||
|
|
||||||
sensor_dirs = [dest for dest in data_files if dest.startswith("orisys/sensors/")]
|
sensor_dirs = [dest for dest in data_files if dest.startswith("orisys/sensors")]
|
||||||
assert sensor_dirs
|
assert sensor_dirs
|
||||||
assert any("finger/assets" in dest for dest in sensor_dirs)
|
all_sensor_files = [path for files in (data_files[d] for d in sensor_dirs) for path in files]
|
||||||
|
assert any(path.endswith("force_calibration.json") for path in all_sensor_files)
|
||||||
|
assert any("finger" in path and path.endswith(".json") for path in all_sensor_files)
|
||||||
|
# Finger mesh/viz assets (flat or nested assets/finger/ checkout).
|
||||||
|
assert any("finger/assets" in dest or any("finger/assets" in p for p in data_files[dest]) for dest in sensor_dirs)
|
||||||
assert all("*" not in path for files in data_files.values() for path in files)
|
assert all("*" not in path for files in data_files.values() for path in files)
|
||||||
assert "examples" not in data_files
|
assert "examples" not in data_files
|
||||||
|
|
||||||
|
|
||||||
|
def test_stage_dev_bundle_into_writes_sensors_under_package(tmp_path):
|
||||||
|
import sys
|
||||||
|
|
||||||
|
build_dir = REPO_ROOT / "build"
|
||||||
|
if str(build_dir) not in sys.path:
|
||||||
|
sys.path.insert(0, str(build_dir))
|
||||||
|
from common.pyproject_utils import stage_dev_bundle_into
|
||||||
|
|
||||||
|
pkg = tmp_path / "orisys"
|
||||||
|
packaged = stage_dev_bundle_into(REPO_ROOT, pkg)
|
||||||
|
assert any(p.replace("\\", "/").endswith("sensors/box/force_calibration.json") for p in packaged)
|
||||||
|
assert (pkg / "sensors" / "box" / "force_calibration.json").is_file()
|
||||||
|
assert (pkg / "configs").is_dir()
|
||||||
|
assert any((pkg / "configs").glob("*.json"))
|
||||||
|
|
||||||
|
|
||||||
def test_bundle_explicit_files():
|
def test_bundle_explicit_files():
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,16 @@ def test_configure_lowpass_filters_read_info_and_keeps_raw():
|
|||||||
sensor.info.FSHEARX,
|
sensor.info.FSHEARX,
|
||||||
sensor.info.FSHEARY,
|
sensor.info.FSHEARY,
|
||||||
)
|
)
|
||||||
assert (fn, fx, fy) == pytest.approx((1000.0, -200.0, 50.0))
|
fn_raw, fx_raw, fy_raw = sensor.read_info(
|
||||||
|
sensor.info.FNORMAL_RAW,
|
||||||
|
sensor.info.FSHEARX_RAW,
|
||||||
|
sensor.info.FSHEARY_RAW,
|
||||||
|
)
|
||||||
|
assert (fn_raw, fx_raw, fy_raw) == pytest.approx((1000.0, -200.0, 50.0))
|
||||||
|
assert fn == pytest.approx(calibrate_normal_force(1000.0, sensor_name="box"))
|
||||||
|
assert (fx, fy) == pytest.approx(
|
||||||
|
calibrate_shear_components(-200.0, 50.0, sensor_name="box")
|
||||||
|
)
|
||||||
|
|
||||||
sensor.configure(lowpass_cutoff_hz=5.0, lowpass_sample_rate_hz=30.0)
|
sensor.configure(lowpass_cutoff_hz=5.0, lowpass_sample_rate_hz=30.0)
|
||||||
noisy = [(1000.0 + (200.0 if i % 2 else -200.0), -200.0, 50.0) for i in range(40)]
|
noisy = [(1000.0 + (200.0 if i % 2 else -200.0), -200.0, 50.0) for i in range(40)]
|
||||||
@@ -166,7 +175,10 @@ def test_configure_lowpass_disabled_or_zero_cutoff_returns_raw():
|
|||||||
sensor.info.FSHEARX,
|
sensor.info.FSHEARX,
|
||||||
sensor.info.FSHEARY,
|
sensor.info.FSHEARY,
|
||||||
)
|
)
|
||||||
assert (fn, fx, fy) == pytest.approx((111.0, 222.0, 333.0))
|
assert fn == pytest.approx(calibrate_normal_force(111.0, sensor_name="box"))
|
||||||
|
assert (fx, fy) == pytest.approx(
|
||||||
|
calibrate_shear_components(222.0, 333.0, sensor_name="box")
|
||||||
|
)
|
||||||
|
|
||||||
sensor.configure(lowpass_cutoff_hz=5.0, lowpass_enabled=False)
|
sensor.configure(lowpass_cutoff_hz=5.0, lowpass_enabled=False)
|
||||||
sensor._force_raw = (7.0, 8.0, 9.0)
|
sensor._force_raw = (7.0, 8.0, 9.0)
|
||||||
@@ -177,12 +189,59 @@ def test_configure_lowpass_disabled_or_zero_cutoff_returns_raw():
|
|||||||
sensor.info.FSHEARY,
|
sensor.info.FSHEARY,
|
||||||
)
|
)
|
||||||
fn_raw = sensor.read_info(sensor.info.FNORMAL_RAW)[0]
|
fn_raw = sensor.read_info(sensor.info.FNORMAL_RAW)[0]
|
||||||
assert (fn, fx, fy) == pytest.approx((7.0, 8.0, 9.0))
|
assert fn == pytest.approx(calibrate_normal_force(7.0, sensor_name="box"))
|
||||||
|
assert (fx, fy) == pytest.approx(
|
||||||
|
calibrate_shear_components(8.0, 9.0, sensor_name="box")
|
||||||
|
)
|
||||||
assert fn_raw == pytest.approx(7.0)
|
assert fn_raw == pytest.approx(7.0)
|
||||||
finally:
|
finally:
|
||||||
sensor.disconnect()
|
sensor.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
def test_box_read_info_applies_force_calibration_json():
|
||||||
|
sensor = _external_sensor()
|
||||||
|
try:
|
||||||
|
assert sensor._force_cal_sensor_name == "box"
|
||||||
|
sensor._force_raw = (10000.0, 36000.0, -36000.0)
|
||||||
|
sensor._apply_force_filter()
|
||||||
|
fn, fx, fy = sensor.read_info(
|
||||||
|
sensor.info.FNORMAL,
|
||||||
|
sensor.info.FSHEARX,
|
||||||
|
sensor.info.FSHEARY,
|
||||||
|
)
|
||||||
|
fn_raw, fx_raw, fy_raw = sensor.read_info(
|
||||||
|
sensor.info.FNORMAL_RAW,
|
||||||
|
sensor.info.FSHEARX_RAW,
|
||||||
|
sensor.info.FSHEARY_RAW,
|
||||||
|
)
|
||||||
|
assert (fn_raw, fx_raw, fy_raw) == pytest.approx((10000.0, 36000.0, -36000.0))
|
||||||
|
assert fn == pytest.approx(0.377)
|
||||||
|
assert fx == pytest.approx(1.19)
|
||||||
|
assert fy == pytest.approx(-1.19)
|
||||||
|
finally:
|
||||||
|
sensor.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
def test_read_info_force_stays_raw_without_calibration_json(monkeypatch):
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"orisys.sensor.configs.load_force_calibration",
|
||||||
|
lambda _name: None,
|
||||||
|
)
|
||||||
|
sensor = Sensor("external", config_name="box", verbose=False, backend="cpu")
|
||||||
|
try:
|
||||||
|
assert sensor._force_cal_sensor_name is None
|
||||||
|
sensor._force_raw = (111.0, -22.0, 33.0)
|
||||||
|
sensor._apply_force_filter()
|
||||||
|
fn, fx, fy = sensor.read_info(
|
||||||
|
sensor.info.FNORMAL,
|
||||||
|
sensor.info.FSHEARX,
|
||||||
|
sensor.info.FSHEARY,
|
||||||
|
)
|
||||||
|
assert (fn, fx, fy) == pytest.approx((111.0, -22.0, 33.0))
|
||||||
|
finally:
|
||||||
|
sensor.disconnect()
|
||||||
|
|
||||||
|
|
||||||
def test_configure_hhd_downsample_invalidates_solver_cache():
|
def test_configure_hhd_downsample_invalidates_solver_cache():
|
||||||
sensor = _external_sensor()
|
sensor = _external_sensor()
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user