YORU Your Optimal Recognition Utility

Beta: Custom Trigger Plugins

Applies to v2.0.0-beta.4. This page covers the API changes that affect user-written code. If you only use the bundled plugins, you can skip it — but see Real-time Process for the trigger_pin and threshold changes, which affect everyone.

Plugins for the YORU project are listed under Trigger Plugins.


Detection rows have 13 entries (Beta 3)

The results argument of trigger() is m_dict["yolo_results"]: one row per detection. In Beta 3 each row gained five entries at the end:

Index 0–3 4 5 6 7 8–11 12
Value x1, y1, x2, y2 confidence class class_name total_time cx, cy, w, h angle (radians)

The first eight entries keep their names and positions, and none of the bundled trigger plugins is affected.

  • Indexing still works: row[4], row[6].
  • Unpacking exactly eight values breaks:

      x1, y1, x2, y2, conf, cls, name, t = row          # ValueError in Beta 3
    

    When that happens, the trigger stops firing. Unpack the rest into a list, or index instead:

      x1, y1, x2, y2, conf, cls, name, t, *rest = row   # works in Beta 2 and Beta 3
    
  • For an OBB model, cx, cy, w, h, angle are the rotated box. For any other model they describe the same upright box as x1..y2, with angle = 0.
  • Rows passed to yoru.libs.drawing.draw_detections changed the same way.

Fresh results only (Beta 4)

trigger() now receives only fresh results. When detection is off, the model is reloading, capture has stopped, or the frame is older than trigger.result_max_age seconds (default 1.0), results is an empty list, exactly as if nothing had been detected. A plugin that switches its output off when it sees no detection of the trigger class therefore switches it off in all of these cases too.


Shutting down (Beta 4)

When the real-time process ends, YORU:

  1. calls trigger() once more with no detections — an empty class list and empty results — which the bundled plugins take as output OFF, and then
  2. calls the plugin’s close(), if it has one.

close() is optional and takes no arguments. Use it to reset outputs and release what the plugin opened — a serial port, a window, a DAQ task. The bundled serial, display and NI-DAQ plugins implement it. An exception in either call is written to yoru.log and does not stop the shutdown.

For example, the bundled standard_serial plugin:

def close(self):
    try:
        self.ser.write(b"0")
    finally:
        self.ser.close()

The trigger plugin contract changed

A plugin written for Beta 1 or v1.1.x will not run unchanged on the beta (changed in Beta 2). Check all four points, plus the 13-entry rows above:

1. The constructor must accept m_dict

def __init__(self, m_dict=None):   # not: def __init__(self):

2. The 3rd argument of trigger() is a pyfirmata board, not a serial port

arduino.writeDO_all(1)   # not: ser.write(b"1")
arduino.writeDO_all(0)   # not: ser.write(b"0")

3. Handle a missing board

Condition files may set Arduino_COM: "None", in which case no board is passed:

if arduino is None:
    return

4. Fix the import path

import yoru.libs.arduino as ard   # not: import libs.arduino as ard

Libraries were reorganized into a yoru/ package in Beta 1, so the top-level libs import no longer resolves.

For the full signature, use a bundled plugin under trigger_plugins/ in the repository as the reference implementation — standard_arduino is the simplest one.

Why this matters

In Beta 1, three of the five bundled plugins — standard_nidaq, state_convert and state_convert_for_copulation_attempts — hit exactly these problems and could not be constructed at all, so the trigger silently never engaged. If your own plugin was written against the same pattern, it was likely affected too.


Using YORU from your own scripts

yoru.libs.yolo_wrapper is not part of the beta (it was deleted in Beta 2). Replace:

from yoru.libs.yolo_wrapper import load_yolo_model

with:

from yoru.libs.plugins import get_detector

det = get_detector("auto", model_path)   # or "yolov5", "ultralytics", "rtdetr", "torchvision", "onnx"

yoru.libs.plugins.list_detector_backends() (new in Beta 4) returns the names get_detector accepts on this machine, "auto" first. For a YOLOv5 model, get_detector(..., rgb_input=True) gives it RGB frames instead of the BGR frames v1 used (or set YORU_YOLOV5_RGB=1).

The detector exposes:

  • .names — the class-name table.
  • .detect(image) — takes a BGR image and returns a list of dicts with the keys x1, y1, x2, y2, conf, class_id, class_name.
  • New in Beta 3: an OBB model adds the keys cx, cy, w, h, angle (radians) for the rotated box. x1..y2 is still the upright box around it. Other models leave these keys out.

yoru.libs.file_operation_evaluation was also removed; it was a duplicate of yoru.libs.file_operation_create_label.

The external API (Beta 4)

docs/external_api.md in the repository lists the YORU names that code outside YORU may rely on — the supported surface, where a rename is a breaking change — and tests/test_public_api.py pins each one. It is written for sister applications such as YORU Tracker, and is the safest set of names to use from your own scripts as well. Anything not listed there may change between betas.

Detection defaults

All backends use the same thresholds: confidence 0.25, IoU 0.45 (changed in Beta 2). Previously each backend used its own default, so scripts built around torchvision models in particular will see a different number of detections than on Beta 1.


Other paths that moved

Item Beta 1 Beta 2 and later
Trained weights <project>/train/weights/best.pt <project>/exp_<model>/weights/best.pt
Saved window layouts config/custom_layout_*.ini logs/custom_layout_*.ini (plus logs/yoru_windows.ini from Beta 3)
YOLOv5 sources yoru/libs/yolov5/ Removed in Beta 2 and 3; bundled again in yoru/libs/yolov5/ from Beta 4

The stale config/custom_layout_*.ini files can be deleted; saved window layouts reset once.


Packaging note

The packaged source distribution now actually contains config/, trigger_plugins/ and web/, and yoru --version reports the real version instead of a placeholder.



Previous