Logic Gates & Composed Events

In the previous recipes, we triggered actions based on single, isolated conditions – “If Mapper Fails” or “If Person Detected”. But real-world autonomy is rarely that simple. A robot shouldn’t stop every time it sees an obstacle – maybe only if it’s moving fast. It shouldn’t return home just because the battery is low – maybe only after finishing its current task.

In this recipe, we use logic operators to compose multiple conditions into smarter, more robust event triggers.


Logic Operators

EMOS lets you compose complex triggers using standard Python bitwise operators. This turns your Event definitions into a high-level logic circuit.

Logic

Operator

Description

Use Case

AND

&

All conditions must be True

Speed > 0 AND Obstacle Close

OR

|

At least one condition is True

Lidar Blocked OR Bumper Hit

NOT

~

Inverts the condition

Target Seen AND NOT Low Battery



Intelligence Example: Conditional Cognition

We can apply the same logic to the intelligence layer. Consider the Event-Driven Cognition recipe where a Vision detector triggers a VLM. What if we only want the VLM to run when the robot has sufficient battery?

from agents.ros import Topic, Event

# Detection output from the Vision component
detections = Topic(name="/detections", msg_type="Detections")

# Battery level topic
battery = Topic(name="/battery_state", msg_type="Float32")

# Condition A: Person detected (with debounce)
person_detected = detections.msg.labels.contains_any(["person"])

# Condition B: Battery above 20%
battery_ok = battery.msg.data > 20.0

# Composed Event: person detected AND battery sufficient
event_describe_person = Event(
    event_condition=(person_detected & battery_ok),
    on_change=True,
    keep_event_delay=5
)

The VLM only wakes up when both conditions are met – saving compute when the battery is low.


OR Logic: Redundant Sensors

The | operator is useful for sensor redundancy. If either the front lidar detects a close obstacle or the bumper is pressed, trigger an emergency stop:

from kompass.ros import Topic, Event

lidar = Topic(name="/scan_front", msg_type="Float32")
bumper = Topic(name="/bumper", msg_type="Bool")

is_lidar_blocked = lidar.msg.data < 0.2
is_bumper_pressed = bumper.msg.data == True

event_any_collision = Event(
    event_condition=(is_lidar_blocked | is_bumper_pressed),
    on_change=True
)

NOT Logic: Exclusion

The ~ operator inverts a condition. Use it to exclude scenarios:

from kompass.ros import Topic, Event

# Only track the target if the robot is NOT in manual override mode
manual_mode = Topic(name="/manual_override", msg_type="Bool")
target_seen = detections.msg.labels.contains_any(["person"])

event_auto_track = Event(
    event_condition=(target_seen & ~manual_mode.msg.data),
    on_change=True
)

Event Configuration Reference

All composed events support these parameters:

Parameter

Description

Default

on_change

Trigger only when the condition transitions to True (edge-triggered)

False

handle_once

Fire only once during the system’s lifetime

False

keep_event_delay

Minimum seconds between consecutive triggers (debounce)

0

See also

For the full list of event types and configuration options, see the Events & Actions reference.