34cb83d4a4
避免未标定读数被示例标成 N,并让 Finger 示例读取 SDK 的每轴单位。已通过相关测试和语法检查。
120 lines
5.3 KiB
Python
120 lines
5.3 KiB
Python
"""ORY-BOX / ORY-MINI: read a sensor and get tactile information, in one loop.
|
|
|
|
The whole recommended flow:
|
|
|
|
sensor -> StreamPipeline -> FrameSnapshot per frame
|
|
|
|
One object reads the camera, pre-processes (stitch, grey, resize), computes the deformation field
|
|
and the forces, and hands back one immutable snapshot per frame. There is no stage sequence to
|
|
remember and no tuple to unpack by position.
|
|
|
|
What you do NOT need to touch: the compute backend, the capture queue, the stitch mode, the
|
|
low-pass filter, or the order in which the stages run. Those are the SDK's concern and
|
|
``StreamPipeline`` sequences them on three concurrent threads. If you later need that control, the
|
|
manual form is preserved in ``examples/archive/basic_pipeline_manual.py``.
|
|
|
|
Run:
|
|
|
|
python examples/basic_pipeline.py # first camera, box config
|
|
python examples/basic_pipeline.py -v 0 -c mini
|
|
python examples/basic_pipeline.py -v recording.mp4 -c box
|
|
python examples/basic_pipeline.py --no-show # headless: print only
|
|
|
|
Keys: 'q' quit, 'r' reset the tracker.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
# Set before OpenCV is imported: stops the capture backend applying its own transform on top of
|
|
# the SDK's preprocessing. This is a capture detail, not a tuning knob you need to own - it is
|
|
# here so the file can be copied as a whole.
|
|
os.environ.setdefault("OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS", "0")
|
|
|
|
import cv2
|
|
|
|
import orisys
|
|
from _display import depth_to_color, draw_contact, draw_flow_arrows
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Read a sensor and print its tactile information.")
|
|
parser.add_argument("-v", "--video", default="0",
|
|
help="camera index or video file path (default: 0)")
|
|
parser.add_argument("-c", "--config", default="box",
|
|
help="product config: box, mini, finger, or a config path")
|
|
parser.add_argument("--cal", default=None,
|
|
help="force calibration file; omit to use the sensor bundle's own")
|
|
parser.add_argument("--no-show", dest="show", action="store_false",
|
|
help="do not open the display windows")
|
|
parser.add_argument("-verbose", action="store_true", help="SDK verbose logs")
|
|
args = parser.parse_args()
|
|
|
|
# ------------------------------------------------------------------ create the object
|
|
# That is the whole setup. The source may be a camera index or a video path.
|
|
source = int(args.video) if args.video.isdigit() else args.video
|
|
sensor = orisys.Sensor(source, config_name=args.config, cal_path=args.cal,
|
|
verbose=args.verbose)
|
|
|
|
# contact=True runs contact analysis. include=... selects which arrays each snapshot carries;
|
|
# arrays that are not included are None, so you pay only for what you use.
|
|
include = ("image", "flow", "depth", "centroid", "contour") if args.show else ("image",)
|
|
|
|
if args.show:
|
|
print("Keys: 'q' quit, 'r' reset tracker.")
|
|
|
|
try:
|
|
with orisys.StreamPipeline(sensor, sensor.get_img, contact=True,
|
|
include=include) as pipe:
|
|
while True:
|
|
# ------------------------------------------------------ read one frame
|
|
# Blocks until a frame newer than the last one is ready. Everything in the
|
|
# snapshot belongs to the same frame.
|
|
snap = pipe.wait()
|
|
if snap is None:
|
|
print("\nInput ended.")
|
|
break
|
|
|
|
# ------------------------------------------------------ use the results
|
|
# The *_raw pair is always the unfiltered integrator output.
|
|
touching = "yes" if snap.centroid is not None else "no"
|
|
fn_unit, fx_unit, fy_unit = sensor.force_units
|
|
print(
|
|
f"FPS={snap.fps:5.1f} "
|
|
f"normal={snap.normal_force:8.4f} {fn_unit} (raw {snap.normal_force_raw:10.2f}) "
|
|
f"shear=({snap.shear_x:7.4f} {fx_unit}, {snap.shear_y:7.4f} {fy_unit}) "
|
|
f"contact={touching} age={snap.age_ms:4.1f} ms",
|
|
end="\r",
|
|
)
|
|
|
|
if args.show and snap.image is not None:
|
|
if snap.flow is not None:
|
|
cv2.imshow("deformation", draw_flow_arrows(
|
|
snap.image, snap.flow, threshold=2, grid_spacing=20))
|
|
if snap.contour is not None and snap.centroid is not None:
|
|
cv2.imshow("contact", draw_contact(
|
|
snap.image, snap.contour, snap.centroid))
|
|
colour = depth_to_color(snap.depth)
|
|
if colour is not None:
|
|
cv2.imshow("depth", colour)
|
|
|
|
key = cv2.waitKey(1) & 0xFF
|
|
if key == ord("q"):
|
|
print("\nExiting.")
|
|
break
|
|
if key == ord("r"):
|
|
# Queued onto the flow stage, so this is safe from the consumer loop.
|
|
pipe.reset()
|
|
print("\nTracker reset.")
|
|
except KeyboardInterrupt:
|
|
print("\nInterrupted.")
|
|
finally:
|
|
sensor.disconnect()
|
|
if args.show:
|
|
cv2.destroyAllWindows()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |