Control software for an Autonomous Guided Vehicle (AGV), written in C++20 with an optional Qt UI. Targets Linux (the vehicle PC runs Linux; develop on Windows via WSL2).
A single SI (System Integration) program that drives the robot manually (Qt UI) and automatically (commands from a fleet / supervisory system), talks to the hardware, raises alarms, surfaces status, keeps logs and fault history — the things production robots do.
A Qt-free core with the control logic, and optional front-ends, so the core is unit-testable headlessly and reusable (e.g. a future ROS2 node).
Fleet (TCP) ──MISSION/MOVE/MODE──► MissionRunner ─► Orchestrator (20 Hz safety state machine)
│ ▲ E-stop / obstacle / critical alarm → inhibit motion
▼ │
Battery · Motor · IO · LiDAR modules (each: simulator + real backend)
│ EventBus (pub/sub)
├─► Qt UI dashboard
├─► Logging (console / journald / rotating file)
└─► SQLite alarm history
Every hardware module sits behind an interface with a simulator backend, so the whole system runs and is tested end-to-end without hardware. Real backends (serial / CAN / TCP) drop in by config without touching the logic.
| Layer | Contents |
|---|---|
core |
logging, config (INI), event bus, alarms, shared types |
comm |
transports: TCP, serial (termios), CAN (SocketCAN) + virtual bus |
modules |
Battery, Motor, IO, LiDAR — each PolledModule + simulator |
app |
Orchestrator (safety state machine), MissionRunner, HealthMonitor, fleet protocol/server, AgvSystem composition root |
persistence |
SQLite alarm-history store (optional) |
ui |
Qt Widgets operator dashboard (optional) |
- Safety state machine: Idle / Moving / Charging / Paused / Error / EmergencyStop. E-stop and critical alarms inhibit motion; a LiDAR protective-field obstacle pauses and auto-resumes.
- Manual + automatic control: UI drive-pad, or fleet velocity/missions.
- Mission sequencer: autonomous task sequences (
MISSION DEMO). - Alarms: severity, active set, bounded history, EventBus integration, optional SQLite persistence.
- Watchdog: data-freshness monitor catches stalled-but-alive modules.
- Logging: levelled, thread-safe; console, rotating file, and
systemd-journald sinks (
journalctl AGV_TAG=motor). - Config-driven: backends, ports, thresholds, rates in
agv.ini.
Requirements: CMake ≥ 3.16, a C++20 compiler (GCC 11+/Clang 14+), Linux.
Optional: libsystemd-dev (journald), libsqlite3-dev (alarm history),
qt6-base-dev (UI). Absent optionals degrade gracefully.
sudo apt-get install -y cmake ninja-build pkg-config \
libsystemd-dev libsqlite3-dev qt6-base-dev
cmake -S . -B build -G Ninja
cmake --build build
ctest --test-dir build --output-on-failureOptions: AGV_BUILD_TESTS, AGV_BUILD_UI, AGV_USE_JOURNALD,
AGV_USE_SQLITE (all ON by default).
./build/bin/agv_console --config docs/agv.example.ini # headless SI
./build/bin/agv_console --scenario # scripted E-stop demo
./build/bin/agv_ui # Qt dashboardDrive it from a "fleet" over TCP (default port 9000):
MODE AUTO MOTION ON MOVE 0.5 0.1
MISSION DEMO MISSION ABORT PING
The robot replies with ACK/PONG and streams STATE ... telemetry.
- systemd: see
deploy/agv-control.service(auto-start, restart, journald). - Docker:
docker build -t agv-control ./docker compose up.
See docs/agv.example.ini for every configuration key.
Runs end-to-end on simulators; CI builds and tests on Ubuntu 22.04 (including a real vcan SocketCAN check) and compiles the Qt UI. Real hardware backends are stubbed against documented placeholder protocols, ready to be replaced when parts and protocols are chosen.