Robotic mode (pyobs.robotic)
Contents:
pyobs can operate a telescope fully autonomously. In robotic mode, a pool of pending observation tasks is continuously scheduled and executed without human intervention. This section describes the architecture of the robotic system and how its components fit together.
Architecture
The robotic system is built around five concepts:
Concept |
Role |
|---|---|
Task |
A unit of work: what to observe, how long it takes, when it may run, and what script to execute. |
Observation |
A scheduled instance of a task: a task assigned a concrete start and end time. |
Scheduler |
Evaluates the pool of tasks and calculates which should run next, producing a sequence of
|
Mastermind |
Watches the schedule and, at the right moment, hands each |
Script |
The observing logic itself: move the telescope, take exposures, save images. |
The flow through the system looks like this:
┌─────────────┐
│ TaskArchive │ ← task pool (files, backend, LCO portal)
└──────┬──────┘
│ schedulable tasks
▼
┌──────────────────────────────────────────────┐
│ Scheduler module │
│ │
│ for each time slot: │
│ evaluate Constraints → gate (yes/no) │
│ evaluate Merits → rank (0..N) │
│ pick highest-ranked eligible task │
└──────────────────┬───────────────────────────┘
│ Observation (task + start + end)
▼
┌─────────────────────┐
│ ObservationArchive │ ← persists the schedule
└──────────┬──────────┘
│ next pending Observation
▼
┌────────────────────────────────────┐
│ Mastermind module │
│ │
│ at scheduled time: │
│ TaskRunner.run_task(task) │
└────────────────┬───────────────────┘
│
▼
┌─────────────┐
│ Script │ ← the actual observing logic
└─────────────┘
Module layer vs. robotic layer
It helps to distinguish two layers:
The module layer (pyobs.modules.robotic) contains the long-running pyobs processes you
start from YAML config files — Scheduler and
Mastermind. These are full Module subclasses
with comm, background tasks, and event subscriptions.
The robotic layer (pyobs.robotic) contains the data and logic objects that the modules
orchestrate — Task, Script, Constraint, Merit, OnDemandScheduler, and the
archive classes. These are either Object subclasses or pydantic models,
not modules, and are configured as nested objects inside the module YAML.
This means a typical deployment YAML for the scheduler module looks like:
class: pyobs.modules.robotic.Scheduler # ← module layer
scheduler:
class: pyobs.robotic.scheduler.OnDemandScheduler # ← robotic layer
twilight: astronomical
tasks:
class: pyobs.robotic.filesystem.YamlTaskArchive # ← robotic layer
path: /robotic/tasks/
schedule:
class: pyobs.robotic.filesystem.YamlObservationArchive # ← robotic layer
path: /opt/pyobs/robotic/observations/
Tasks and scheduling
A Task carries four kinds of information:
Identity —
id,name,project,priorityScheduling metadata —
duration,constraints,merits,targetScript config — the
scriptblock that defines what to do when the task runsState — tracked indirectly via
Observationobjects in theObservationArchive
Constraints answer a binary question: may this task run right now? If any constraint returns
False, the task is excluded from consideration for that time slot entirely. Examples: airmass
too high, sun still up, outside the allowed time window.
Merits answer a continuous question: how desirable is it to run this task right now? All merit
values for a task are multiplied together (along with priority and project priority) to produce
a single score. The task with the highest score in each time slot wins. Examples: transit timing,
time since last observation, distance from a preferred window.
The clean separation between constraints (hard gates) and merits (soft ranking) means you can express complex scheduling policies entirely in YAML without writing any code. See Scheduling (pyobs.robotic.scheduler) for the full list of built-in constraints and merits.
Scheduler re-triggering
The Scheduler module recalculates the schedule whenever
_need_update is set. This happens automatically in several situations:
The task pool changes (a task is added, removed, or modified in the
TaskArchive)A
GoodWeatherEventarrives, carrying an ETA for when observing can resumeA
TaskStartedEventarrives (iftrigger_on_task_started: true)A
TaskFinishedEventarrives (iftrigger_on_task_finished: true)The
run()method is called manually (e.g. from the GUI)
To avoid submitting a stale schedule while a new one is being calculated, the scheduler submits the first task immediately as soon as it is found, then submits the rest once the full run completes. If a new update is requested mid-run, the partial results are discarded.
Scripts
A Script is the leaf of the system — it does the actual work.
Scripts are pydantic models (not modules), so they are fully described by their YAML config and
have access to comm, vfs, and observer injected at runtime.
A Script has two methods:
can_run(data)— called by the scheduler to check whether the script’s hardware is currently available. ReturnFalseif a required module is offline.run(data)— called by the mastermind to execute the script. Receives aTaskDataobject giving access to the current task, theObservationArchive, and theTaskArchive.
Built-in scripts cover common observing tasks (autofocus, sky flats, dark/bias frames) and control flow (sequential, parallel, conditional). See Scripts (pyobs.robotic.scripts) for details.
Archive implementations
Both TaskArchive and ObservationArchive are abstract base classes. pyobs-core ships
three concrete implementations:
Implementation |
Use case |
|---|---|
|
Tasks and observations stored as YAML files on disk. The simplest setup — no external services required. Good for single-telescope systems. |
|
Tasks and observations managed by the pyobs-robotic-backend HTTP service. Enables multi-telescope coordination and a web UI for queue management. |
|
Integration with the Las Cumbres Observatory observation portal. Used for LCO-connected telescopes. |
Further reading
Scheduling (pyobs.robotic.scheduler) — full API reference for
Task,Observation, constraints, merits, targets, and scheduler implementationsScripts (pyobs.robotic.scripts) — full API reference for
Scriptand all built-in script classesSerialization (pyobs.utils.serialization) — how
BaseModelandPolymorphicBaseModelenable YAML-driven instantiation of pydantic modelsSetting up a minimal robotic observation system — step-by-step recipe for setting up a minimal robotic system