Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

50 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ARP Drone Simulator — Multi-Process Drone Control in C

A 2D drone simulation built as a set of independent Linux processes that talk to each other only through POSIX anonymous pipes, coordinated by a central blackboard server. The drone is not moved directly by the keyboard: key presses become force inputs to a mass–damper model, and targets and obstacles exert attractive and repulsive forces on the drone through an artificial potential field.

Written for the Advanced and Robot Programming course at the University of Genoa (UniGe), winter semester 2023/2024.

Amir Mahdi Matin — s5884715


Table of Contents


What this project actually does

main forks seven child processes, each wrapped in its own Konsole terminal window so that its stdout is visible while the simulation runs. The processes exchange fixed-size tagged text messages over eleven anonymous pipes created by main before the fork()s; each child receives its own pipe file descriptors as a space-separated string in argv[1].

The interface draws an ncurses window; the drone integrates its own equations of motion; targets and obstacles are generated by separate processes; and the server routes every message to the processes that need it.

Tech stack

Area What is used
Language C (C17, GCC)
Concurrency model Multi-process — fork() + execvp()
IPC POSIX anonymous pipes (pipe()), non-blocking multiplexing with select()
Shared state POSIX shared memory (shm_open / mmap) + named semaphores (sem_open) for watchdog PID handover
Signals sigaction with SA_SIGINFO (SIGINT, SIGUSR1, SIGUSR2)
UI ncurses
Process display Konsole (one terminal window per process)
Build GNU Make

Architecture

                    ┌──────────┐
                    │   main   │  creates 11 pipes, forks 7 children
                    └──────────┘
                          │
     ┌──────────┬─────────┼──────────┬───────────┬──────────┐
     ▼          ▼         ▼          ▼           ▼          ▼
┌─────────┐ ┌────────┐ ┌─────┐ ┌──────────┐ ┌─────────┐ ┌────────┐
│interface│ │key_mgr │ │drone│ │ targets  │ │obstacles│ │watchdog│
└─────────┘ └────────┘ └─────┘ └──────────┘ └─────────┘ └────────┘
     │  ▲        │        │ ▲        │ ▲         │ ▲
     │  │        │        │ │        │ │         │ │
     │  └────────┼────────┴─┴────────┴─┴─────────┴─┘
     │           │              ┌────────┐
     └───────────┴─────────────▶│ server │  (blackboard: routes all traffic)
                                └────────┘
     ── two "serverless" pipes bypass the server ──
     interface ──raw keypress──▶ key_manager
     interface ──lowest target──▶ drone

Processes

main (src/main.c) — Creates all pipes with pipe(), then forks each child and execvp()s konsole -e ./build/<process> "<fds>". It installs a SIGINT handler that kills every child and closes every pipe, then wait()s for all children to exit.

server (src/server.c) — The blackboard. It holds the read ends of the five inbound pipes (key manager, interface, drone, obstacles, targets) and polls each with select() and a zero timeout in one loop. Routing is decided by the leading tag of each message: keyboard actions go to the drone, interface messages go to the drone (and, for resize events, to targets and obstacles), drone positions go to the interface, and obstacle data goes to both the interface and the drone. The server also creates the shared memory segments and semaphores used for watchdog PID registration.

interface (src/interface.c) — The ncurses front end and the game-logic owner. It reports the initial drone position and terminal size, redraws the window each iteration (drone + in blue, obstacles * in red, targets as digits in green, score in the title bar), captures key presses with non-blocking getch(), tracks the score, detects target capture and obstacle collision, and re-scales target coordinates when the terminal is resized. It also sends the currently active target straight to the drone over a dedicated pipe.

key_manager (src/key_manager.c) — Blocks on select() waiting for a key character from the interface, maps it to a force pair through determine_action(), and forwards non-empty actions to the server.

drone (src/drone.c) — Owns the physics. It integrates position and velocity with an explicit Euler step, sums the keyboard force with the potential-field force from the active target and all known obstacles, clamps the drone to the window bounds, and publishes its rounded integer position back to the server every D_T (0.1 s).

targets (src/targets.c) — Waits for the screen dimensions, then generates MAX_TARGETS (8) targets once. Placement combines randomness with structure: a shuffled order distributes targets across a 3×2 sector grid, so no region of the screen is left empty.

obstacles (src/obstacles.c) — Waits for the screen dimensions, then adds one obstacle per second up to MAX_OBSTACLES (7). Each obstacle is given a lifetime between MIN_SPAWN_TIME (4 s) and MAX_SPAWN_TIME (10 s); when it expires the obstacle is regenerated at a new random location, so the obstacle field keeps changing. The full obstacle list is re-sent to the server on every cycle.

watchdog (src/watchdog.c) — Collects the PIDs of the server, interface, key manager and drone. Because every process actually runs inside a Konsole wrapper, the PID returned by fork() in main is not the PID of the simulation process, so each process registers its own PID by writing it into the /wd_pid_1 shared memory segment under a three-semaphore handshake (publish_pid_to_wd() in src/util.c). See Known limitations for what the watchdog does not do.

logger (src/logger.c) — A shared-memory log consumer that writes timestamped entries to a file. It is not launched — the call in main is commented out. See Known limitations.

Message protocol

All messages are fixed-length (MSG_LEN = 240 bytes) ASCII strings, dispatched on their first characters.

Message From → To Meaning
I1:x,y,sx,sy interface → server → drone Initial drone position and screen size
I2:sx,sy interface → server → drone, targets, obstacles Screen size changed (resize)
K:x,y key_manager → server → drone Keyboard force input
D:x,y drone → server → interface Current drone position
T[n]x,y|x,y|… targets → server → interface The full target list
O[n]x,y|x,y|… obstacles → server → interface, drone The full obstacle list
<char> interface → key_manager Raw key press (bypasses the server)
x,y interface → drone Coordinates of the lowest-ID target (bypasses the server)

Drone dynamics

The drone is a point mass with viscous damping, integrated with an explicit Euler step (differential_equations() in src/drone.c):

a = (F_input + F_external − DAMPING · v) / MASS
v ← v + a · Δt
p ← p + v · Δt

with MASS = 1.0, DAMPING = 1, Δt = 0.1 s, input force capped at F_MAX = 30.0 and external force capped at EXT_FORCE_MAX = 40.0. Position is clamped to the window, so the window borders act as walls.

F_external is an artificial potential field in the style of Khatib's model (calculate_extenal_force()): targets attract, obstacles repel, and both use the gradient form

F = Coefficient · (1/d − 1/ρ) · (1/d²)

with Coefficient = 400.0 and an influence radius start_distance = 5.0; distances below min_distance = 1.0 are clamped to avoid a singularity. Only obstacles and the single active (lowest-ID) target contribute.

Because forces accumulate rather than setting velocity directly, the drone has inertia — it keeps drifting after the keys are released.

Game rules and scoring

Targets must be reached in ID order; the interface always sends only the lowest remaining target ID to the drone, and only that one attracts it. Scoring is implemented in src/interface.c:

Time to reach the target (loop ticks of ~10 ms) Points
under ~5 s +10
~5–10 s +8
~10–20 s +6
over ~20 s +2

Overlapping an obstacle costs −5. When the last target is collected the interface draws a final window with the score and closes after a 5-second countdown.

Build and run

Dependencies

sudo apt-get install build-essential libncurses-dev konsole

konsole is required: main launches every process inside a Konsole window. ncurses headers are required to build the interface.

Build

make          # builds all executables into ./build

Run

make run      # equivalent to ./build/main

Seven Konsole windows open — one per process. Play in the interface window. Ctrl+C in the main terminal shuts everything down.

Clean

make clean

Controls

The nine keys around s map to the eight compass directions plus stop:

   q  w  e
   a  s  d
   z  x  c
Key Action Key Action
w up q up-left
x down e up-right
a left z down-left
d right c down-right

s cancels all input force. Because of inertia and damping the drone coasts to a stop rather than halting instantly. Any other key is ignored.

Repository layout

src/        Process sources — main, server, interface, key_manager,
            drone, targets, obstacles, watchdog, logger, util
include/    Headers; constants.h holds the IPC names and shared structs,
            drone.h the physics constants, targets.h / obstacles.h the
            generation parameters
Backup/     Earlier revisions of server.c and targets.c (not built)
Test/       Scratch file (empty, not built)
Makefile    One target per executable

Known limitations

These are honest notes about the state of the code, not planned features:

  • The watchdog does not monitor anything. It successfully collects the four PIDs at startup, but its main loop is an empty usleep() — no SIGUSR1 heartbeat is ever sent, and the per-process counters are never incremented. The SIGUSR1 → SIGUSR2 reply handlers are implemented in every monitored process, so only the polling half is missing. The watchdog also does not track the targets and obstacles processes, which never register a PID.
  • The logger is not wired up. Its launch in main is commented out, write_message_to_logger() is never called from any running code path, and the build/logs directory the logger writes into is not created by the Makefile.
  • Leftover shared-memory design. constants.h still defines shared memory and semaphore names for keyboard, position and action state from an earlier shared-memory-based version. They are unused: the current build communicates through pipes.
  • make clean does not remove the targets and obstacles binaries.
  • The drone must run in a terminal large enough for the ncurses window; there is no minimum size check.

Other branches

The main branch holds the pipe-based version described above. Two further branches exist and have not been merged:

  • Improve-Assignment-2 — a refactored version of this same design.
  • Assignment-3 — a substantially larger version that adds TCP socket communication, splitting the system so that targets and obstacles can run as clients against a remote server, selected through a configuration.txt file. Its own README marks it as incomplete.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages