Promethist Control

The presence camera

A depth camera on the ceiling watches two ellipses of floor in front of the avatar, a small one inside a bigger one. When somebody steps into the small one, the conversation starts; a second after they have left the bigger one, it stops. Nobody presses anything.

It is one portable Windows program, vision.exe, built from vision/ in this repository. Nothing is installed on the kiosk — no Python, and no packages that can drift on a machine nobody watches.

Just another client

The camera is not a second way into Unreal. It connects to the same control channel the tablet uses and sends the same session command as the tablet’s Start and Stop buttons. That gives it everything the tablet already has:

  • A kiosk that is out of service refuses it, exactly as it refuses the tablet.

  • Every tablet shows the session starting, because they all draw the same published state.

  • Your level’s Blueprint does the connecting. If the tablet’s Start button works, the camera needs nothing more on the Unreal side — see Wiring a button on the Unreal side for how session is split between C++ and the graph.

It sends changes only — somebody arrived, somebody left — never a repeated "still here". So an operator who presses Stop is not overruled a moment later by a visitor who has not walked away yet. When its connection comes back, after Unreal restarts for instance, it states the current answer once more, so a visitor who arrived during the restart is not left facing a kiosk that never woke up.

Like any client, it counts against MaxClients (four by default).

What you need

  • An Intel RealSense depth camera on the ceiling, looking straight down. We use a D415. There is no model to set: vision.exe asks the camera what it is. It must be a D400-series camera — D415, D435, D455 and the like. Older models Intel has dropped from its camera library, such as the L515 and the SR300 family, are not supported.

  • A Windows 11 x64 machine it plugs into over USB — normally the Unreal machine itself.

  • vision.exe, attached to every release of this repository: open the project’s Releases page on GitLab and download it from the version you want.

Running it

On the Unreal machine, run it with no arguments. It finds the control channel on that machine, ws://127.0.0.1:9010/link, and a connection from the Unreal machine needs no token.

Supervise it like the rest of the kiosk: point Promethist Watchdog, or whatever restarts your programs, straight at vision.exe. No launcher script is needed. It keeps retrying until Unreal answers, so the start-up order does not matter. When it cannot run at all — no camera, say — it exits with an error rather than sitting idle, so the supervisor sees a failure.

Started hidden — Watchdog’s hidden visibility — it opens no window at all, not even for a moment. An uncalibrated camera then still runs, detecting nothing and telling Unreal nothing, rather than refusing to start as it does with --no-window. To aim or calibrate, start it visibly.

It starts by naming the camera it found, Camera: Intel RealSense D415 (serial …, firmware …, USB 3.2), and then prints one line per change, PRESENT inner=… outer=… or NOT_PRESENT inner=… outer=…, the moment it happens. Under Watchdog that lands in the app’s app.out.log (see Logs), and it is the local record of what the camera saw and when — even while Unreal is unreachable.

If the camera is on another machine, tell it where the channel is and give it the token, as any client on the network must:

vision.exe --ws ws://<unreal-machine>:9010/link --token <AuthToken>

The token is the AuthToken from the Unreal machine, the same value tablet.bat carries. See Locking the control channel.

Aiming and calibrating

The window shows what the camera sees, the two ellipses of floor it watches, and what it currently decides. You aim the running detector: the kiosk does not stop for it, and what you see is what is deciding.

Input Does

Click

moves the centre of both ellipses

Tab

switches between shaping the inner and the outer ellipse. The one being shaped is drawn thicker

Mouse wheel

grows or shrinks that ellipse

W / X

stretches or squeezes it vertically

D / A

stretches or squeezes it horizontally

C

calibrates now: measures the empty floor for five seconds. The floor must be clear.

+ / -

widens or narrows the margin — how far a reading must move from the empty floor to count as somebody. Wider is less sensitive

Every change saves itself.

The inner ellipse starts the conversation, the outer one ends it, a second after nobody is left in either. So walking through the ring between them starts nothing, and a visitor who steps back half a pace is not cut off. Make the inner one the spot where a visitor stands to talk, and the outer one the area they may move around in. The inner one always stays inside the outer.

It calibrates by itself two seconds after you last move or reshape an ellipse, just as C does — so keep the floor clear while you aim. Press C to calibrate at any other time, after the lighting has changed, say.

Until it is calibrated it detects nothing and tells Unreal nothing — nor while it calibrates, when the floor is deliberately empty — so it never ends a conversation somebody started from the tablet. Moving or reshaping an ellipse drops the calibration: through other ellipses the empty floor would look like somebody standing there. A margin change keeps it.

The settings belong to this installation. They are saved to visionConfig.json beside vision.exe, because the ceiling height and the camera’s angle are particular to this place. Do not copy that file to another kiosk. To update, replace vision.exe and leave visionConfig.json where it is.

Changing the camera

The calibration remembers the camera it was measured with. A different camera — another model, or another unit of the same one — sees the floor differently, and reading it through the old calibration would look like somebody standing there. So with a different camera plugged in, vision.exe sets the calibration aside: the window says CAMERA CHANGED - press C, the log names both cameras, and it detects nothing and tells Unreal nothing until it is calibrated again — press C, or re-aim it. With --no-window it refuses to start, as an uncalibrated camera does.

Nothing is deleted. Put the original camera back and its calibration applies again.

Options

All optional. On the Unreal machine it usually takes none.

Option Use it when

--ws ws://HOST:9010/link

the camera is not on the Unreal machine. The default is ws://127.0.0.1:9010/link

--token VALUE

the same case: a connection from another machine must present the token

--no-window

you want no window at all. Detection is unchanged, but nothing can be aimed or calibrated — so a camera that has never been calibrated refuses to start this way

--no-ws

you want it to detect and print only, telling Unreal nothing

--client-name NAME

you want it to name itself differently to the kiosk. The default is vision

--version

you need to know which release this is

When it does not work

Two more options each take the other half of the system out of the picture. That is the only way to tell a camera fault from a wiring one, so on a new machine run them in this order:

  1. vision.exe --send session=true — Unreal, with no camera. If this starts a conversation, the connection, the token and your level’s Blueprint all work, and whatever is still wrong is the camera. --send session=false stops it again. If it does not start one, there is no point looking at the camera yet. Add --ws and --token when Unreal is on another machine.

  2. vision.exe --preview — the camera, with no Unreal. Raw depth and colour side by side: is it plugged in, looking at the right patch of floor, and sending frames?

ERROR: no RealSense camera found means none is plugged in, or it is a model this version cannot use. When Windows can see one of the retired models, the error names it instead: ERROR: an Intel RealSense L515 is plugged in, but this version cannot use it.

ERROR: the depth camera is not available means it found a camera but could not stream from it — almost always the USB cable, or another program holding the camera. The Camera: line it prints when it starts says which camera it found and whether it is connected over USB 3 or USB 2.