SkillAgentSearch skills...

Trainbot

Watches a piece of train track, detects trains, and stitches together images of them.

Install / Use

/learn @jo-m/Trainbot
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Onlytrains

⚠️ Onlytrains is no longer being maintained due to time constraints. ⚠️

<img src="frontend/src/assets/logo-day.svg" height="100" width="100">

Watches a piece of train track, detects passing trains, and stitches together images of them. Should work with any video4linux USB cam, or Raspberry Pi camera v3 modules.

Frontend: https://trains.jo-m.ch/

Other deployments:

A collection of some "special" sightings.

The name Onlytrains is credited to @timethy.

<img src="internal/pkg/stitch/testdata/set0/day.jpg"> <img src="internal/pkg/stitch/testdata/set0/night.jpg"> <img src="internal/pkg/stitch/testdata/set0/rain.jpg"> <img src="internal/pkg/stitch/testdata/set0/snow.jpg"> <img src="demo.gif">

It also contains some packages which might be useful for other purposes:

The binaries are currently built and tested on X86_64 and a Raspberry Pi 4 B.

Assumptions and notes on computer vision

The computer vision used in trainbot is fairly naive and simple. There is no camera calibration, image stabilization, undistortion, perspective mapping, or "real" object tracking. This allows us to stay away from complex dependencies like OpenCV, and keeps the computational requirements low. All processing happens on CPU.

The assumptions are (there might be more implicit ones):

  1. Trains only appear in a (manually) pre-cropped region.
  2. The camera is stable and the image does not move around in any direction.
  3. There are no large fast brightness changes.
  4. Trains have a given min and max speed (configurable).
  5. We are looking at the tracks more or less perpendicularly in the chosen image crop region.
  6. Trains are coming from one direction at a time, crossings are not handled properly
  7. In practice, they happen and lead to the result of one train being chopped up, e.g. https://trains.jo-m.ch/#/trains/19212.
  8. Trains have a constant acceleration (might be 0) and do not stop and turn around while in front of the camera.
  9. In reality, this is often not true, there happens to be a stop signal right in front of my balcony...

Documentation

As this is just a hobby project for me, the documentation is pretty sparse. This very README is the most important part of it. To deploy this project yourself, you should have some basic sysadmin, web servers, and ideally Go knowledge. When in doubt, the source of truth is ... the source code.

All config options can be passed as ENV vars or CLI flags. See config struct on top of cmd/trainbot/main.go, or run trainbot --help to see all options.

The two Makefiles (root and frontend/) also contain some hints.

Deployment

There are two parts to deploy: First, the Go binary which detects trains, and second the web frontend.

How to get binaries? There are multiple options:

  1. go install jo-m.ch/go/trainbot/cmd/trainbot@latest
  2. Grab a binary from the latest CI run at https://github.com/jo-m/trainbot/actions
  3. Build via tooling in this repo - see Development

Raspberry Pi

Run the interactive tool to adjust camera and select a crop rectangle:

# On the host machine
make deploy_confighelper host=TRAINBOT_DEPLOY_TARGET_SSH_HOST
# Example:
make deploy_confighelper host=pi@10.20.0.12

# On the raspberry pi
sudo usermod -a -G video pi
# The --input arg has to be adapted to your actual camera config.
./confighelper-arm64 --log-pretty --input=picam3 --listen-addr=0.0.0.0:8080

Example "Production" deployment to a remote host (will install a systemd user unit):

First, you need to create a env file (copy env.example). Then, from the host machine:

make deploy_trainbot host=TRAINBOT_DEPLOY_TARGET_SSH_HOST

# To see logs, on the target device:
journalctl --user -eu trainbot.service

Download latest data from Raspberry Pi:

ssh "$TRAINBOT_DEPLOY_TARGET_SSH_HOST" sqlite3 trainbot/data/db.sqlite3
.backup trainbot/data/db.sqlite3.bak
# Ctrl+D
rsync --verbose --archive --rsh=ssh "$TRAINBOT_DEPLOY_TARGET_SSH_HOST:trainbot/data/" data/
rm data/db.sqlite3-shm data/db.sqlite3-wal
mv data/db.sqlite3.bak data/db.sqlite3

Frontend

Web frontend

The frontend is a VueJS SPA app written in Typescript. It consists of only static files (after the JS build process). There is no web backend, the frontend simply loads the entire SQLite database from the server, and then does all the queries itself. This means that the frontend can be deployed entirely independently from the trainbot binary, as long as there is some way for the date (db + pics) to get to the web server.

My setup

My Raspberry Pi is not exposed to the internet, and I also already had a web hosting account with FTP access available. Thus, in my setup, the binary and the frontend are running on two entirely different machines in two different networks.

The frontend is built and deployed via:

export FRONTEND_DEPLOY_TARGET_SSH_HOST=myuser@mywebserver:/var/www/trains/
cd frontend
make deploy

The binary on the Raspberry Pi in my home network will upload pictures and the updated db file via FTP to this webspace whenever a new train is detected. This is configured via the ENABLE_UPLOAD=true and UPLOAD_... env vars (or the corresponding CLI flags).

Alternative uploaders (e.g. SFTP, SCP, WebDAV, ...) could be pretty easily implemented (but they are not because I do not need them). For this, the Uploader interface from internal/pkg/upload/upload.go needs to be implemented, and corresponding configuration options added.

Hosting the frontend on the same machine

It is possible to deploy the fronted on the same machine where trainbot runs. There is no finished solution provided in this repo, but some hints are here:

  • Install an arbitrary static web server (Nginx, Apache, Caddy, ...).
    • A webserver could also added to the trainbot binary itself, see e.g. here, PRs welcome.
    • As wwwroot, this webserver needs the build output of the frontend, i.e. cd frontend; make build; [s]cp dist /var/www/trains.
  • Set up the trainbot binary to have its data directory somewhere inside the wwwroot via --data-dir / DATA_DIR.
    • Assuming the wwwroot is /var/www/trains, trainbot would be running with `--data-di
View on GitHub
GitHub Stars497
CategoryDevelopment
Updated9d ago
Forks11

Languages

Go

Security Score

100/100

Audited on Mar 17, 2026

No findings