Skip to content

Repository files navigation

MOPSO-WSN

Multi-Objective Particle Swarm Optimization (MOPSO) for energy-efficient clustering in a Wireless Sensor Network (WSN), implemented in MATLAB/GNU Octave.


The problem, from scratch

What is a wireless sensor network? Imagine scattering 400 tiny battery-powered sensors across a 100 × 100 metre field — measuring temperature, detecting intruders, whatever. Each one has to report its reading to a base station (also called the sink). In this project the sink is mobile: it wanders around the field on a random walk.

Why is this hard? The batteries can't be recharged. Radio transmission is by far the biggest drain, and the energy a node spends is roughly proportional to the square of the distance it transmits:

cost ≈ (electronics) + (amplifier) × distance²

Doubling the distance costs about four times the energy. So a node far from the sink burns through its battery much faster than one sitting right next to it.

Why that matters more than it sounds. A sensor network is usually only useful while it still covers its whole area. If the far-flung nodes all die first, you lose coverage of the edges of the field even though most nodes are still alive. So the number people actually care about is not average energy but how long until the first node dies — and the network is only as healthy as its weakest node.

The standard fix: clustering. Instead of every node shouting all the way to the sink, nodes are grouped into clusters. Each cluster has a cluster head (CH). Members make a short, cheap hop to their nearby head; the head merges all those readings into one packet (aggregation) and makes the single expensive trip to the sink.

   Without clustering              With one cluster head
   ─────────────────────          ──────────────────────
   node ──────────► sink          node ──┐
   node ──────────► sink          node ──┼─► CH ──────► sink
   node ──────────► sink          node ──┘
   (3 expensive trips)            (3 cheap hops + 1 expensive trip)

So why not make everything a cluster head? Because being a head is expensive — a head pays the long trip to the sink plus aggregation overhead, on top of its own reading. Too many heads and you're back to everyone transmitting far. Too few and each head is overloaded and dies quickly. There is a sweet spot, it depends on where the sink currently is, and finding it is what this project does.

The classic protocol for this is LEACH, which just picks heads at random (about 5% of nodes per round) and rotates the role. This project replaces that guesswork with an optimizer — and uses LEACH as the benchmark to beat.


The two objectives

Every simulation step, the optimizer chooses a set of cluster heads to score on two goals at once:

# Objective Direction Meaning
1 Network lifetime maximize The residual energy of the weakest node still alive after this round. Pushes toward more clusters — heads shield the distant nodes from long transmissions.
2 Minimal clustering minimize The number of cluster heads elected. Pushes toward fewer clusters — each head pays an extra trip to the sink plus aggregation cost.

These genuinely conflict: you cannot improve one without hurting the other. That is what makes this a multi-objective problem rather than an ordinary one.

Implementation note: internally both objectives are maximized; minimized quantities are stored negated (objective 2 is -clusterCount). Objective 1 is the minimum over living nodes only — dead nodes hold negative energy forever, and including them would freeze the objective.

What "optimal" means when there are two objectives

With a single objective there's one best answer. With two conflicting ones there is a whole set of equally-defensible answers, called the Pareto front. A solution is on the front if you can't improve either objective without sacrificing the other.

  min residual
  energy (obj 1)
      ▲
 high │   ●───●──●
      │            ●──●         each ● is a Pareto-optimal
      │                 ●        clustering: no other solution
      │                  ●●      beats it on both objectives
  low │                     ●
      └────────────────────────►
        many          few    number of clusters

The two ends of that curve are extremes: one end elects lots of heads for maximum lifetime; the other elects zero and just has everyone transmit directly. Both are technically "optimal". Neither is a useful answer.

So the code reports the knee of the front — the point furthest from the straight line joining the two extremes. That's the best-balanced trade-off, where you get the most lifetime per cluster head spent. (See KneePoint in PSO.m.)

The answer: roughly 6 clusters

Across the 191 steps before nodes start dying:

clusters elected 0–1 2–3 4–9 10–14 15–20
number of steps 4 18 125 31 13

Median 6, mean 7.2, most common value 4. Roughly two-thirds of steps land between 4 and 9. It varies step to step because the sink keeps moving — the best clustering when the sink is in the north-east corner is not the best clustering when it drifts south.

Why ~6 and not ~39? The textbook LEACH formula for the optimal head count predicts about 39 here:

k_opt = √(N / 2π) · √(Efs / Emp) · M / E[d²]  ≈ 39

That formula does not apply to this scenario, and the reason is worth understanding. The radio model has two regimes split at a threshold d0 = 87.7 m: short hops cost d² (free-space), long hops cost d⁴ (multipath). The textbook derivation assumes members reach their head in the cheap d² regime while heads reach the sink in the punishing d⁴ regime — that gap is where clustering's payoff comes from.

Here the sink sits inside a 100 × 100 m field, so the longest node-to-sink distance across the entire run is 89.7 m — barely over the threshold. Only 0.01% of all links ever enter the d⁴ regime. Essentially everything is d², so a cluster head pays nearly as much to reach the sink as its members would have paid going direct, while still adding aggregation overhead. Each head buys much less than the formula assumes, so far fewer are worth electing.

There's a second reason too: this optimizer maximizes the weakest node's energy, not the network's total energy consumption. Those are different targets with different optima — shielding the single worst-off node takes fewer heads than minimizing network-wide expenditure would.


How a run unfolds

A full run simulates 400 nodes for up to 400 rounds, and every alive node reports once per round. It stops early — at step 214 — when the last node dies. The run has three distinct phases:

Phase 1 — Steady drain (steps 1–191)

All 400 nodes alive. Energy declines smoothly and the optimizer elects heads every round, mostly 4–9 of them. This is the phase the cluster-count statistics above describe.

Phase 2 — Die-off (steps 192–214)

At step 192 the first node hits zero. From here the network collapses quickly — 400 → 0 alive nodes in 22 rounds.

The collapse is fast for a structural reason, not a bug: because every node reports every round and they started with identical batteries, nodes at similar distances from the sink drain at similar rates. They therefore reach empty at around the same time. You'd see a longer tail with heterogeneous initial energy or unequal traffic.

Two things worth noticing in this phase:

  • The elected cluster count falls off (9 → 4 → 3 → …). As the network thins out, the surviving nodes are increasingly the ones near the sink, which don't benefit from a relay — direct transmission genuinely becomes the better answer. The optimizer is correctly reporting that clustering has stopped paying.
  • min_node_energy goes slightly negative (e.g. -0.000211). That's expected: a node is charged for the transmission that kills it, so its final balance overshoots zero. The node is then marked dead and excluded from all later rounds.

Phase 3 — Dead network (step 214)

Every node is flat. The final printed cluster count is 0 and hypervolume is 0 — these are not the answer, they're just the state of an empty network. Read the cluster statistics from Phase 1.

Lifetime metrics

Two standard WSN benchmarks, both reported for MOPSO and LEACH:

  • FND (First Node Death) — the round the first node dies. The most important one, since it's when the network starts losing coverage.
  • HND (Half Node Death) — the round half the nodes are gone.

Results

Identical field, identical traffic model, identical radio model and mobile sink; independent energy bookkeeping for each protocol. Seeded (Seed=42), so these numbers reproduce exactly.

Metric MOPSO LEACH Improvement
First Node Death 192 22 8.7×
Half Node Death 203 109 1.9×
Network fully dead step 214 step 182 1.2×
Packets delivered to sink 80,906 — —

The FND gap is the headline result. LEACH picks heads at random, so it regularly hands the job to a node in a far corner that then burns out; the first casualty arrives at round 22. MOPSO elects heads deliberately against the weakest-node objective and holds off the first death until round 192.

See outputs/alive_nodes.png for the two curves side by side.


How the optimizer works

Each particle in the swarm is a candidate clustering: one activation value per node, where a node is elected a cluster head if its value is ≥ 0. The swarm flies through this 400-dimensional space looking for good trade-offs.

  • Senders transmit to their nearest elected head, which aggregates and forwards one packet to the sink. With no heads elected, senders transmit directly.
  • Fitness uses the first-order radio energy model (free-space d² / multipath d⁴, split at d0).
  • Activations are initialized sparsely (~5% active) and particle 1 is seeded with the zero-cluster direct-transmission solution. Both matter: uniform initialization would activate ~200 nodes and the swarm could never prune back down to a handful.
  • Non-dominated solutions are kept in a bounded repository (positions stored alongside fitness); leaders are drawn from it by roulette wheel, and the repository is truncated by NSGA-II crowding distance to preserve front diversity.
  • Coello-style non-uniform mutation fights premature convergence, with strength decaying over the iterations; the search stops early once the global best stalls.
  • The returned decision is the knee of the final Pareto front, not the running global best — the global best drifts randomly along the front, which would make the reported cluster count jump between meaningless extremes.
  • Per-step front quality is tracked with the 2-D hypervolume (S-metric).
  • A LEACH baseline is co-simulated with independent energy bookkeeping for a fair comparison.

A legacy path mode (Cfg.Mode='path' in Init.m) instead optimizes a per-sender multi-hop relay route, using a second priority value per node for hop ordering.


Files

File Purpose
Init.m Entry point: parameters, main simulation loop, LEACH baseline, metrics, plots, and exports
PSO.m The MOPSO core: swarm loop, repository, leader selection, knee selection, convergence check
ClusterFitness.m Cluster-mode objectives: min residual energy of living nodes, and negated cluster count
MultObjFitness.m Path-mode objectives: min residual node energy and negated hop count
Update.m PSO velocity/position update with clamping and dead-node handling
Mutate.m Coello-style non-uniform mutation (strength decays over iterations)
ParetoFilter.m Vectorized pairwise Pareto (non-dominance) screening
LocalDominanceTest.m Strict Pareto dominance test between fitness vectors
CheckNonDominated.m Non-dominance check of one particle against a population
RepUpdate.m Repository maintenance: merge, dedupe, rank, crowding-distance truncation
CrowdingDistance.m NSGA-II crowding distance (front diversity measure)
Hypervolume.m 2-D hypervolume (S-metric) front-quality indicator
NodeMovement.m Random-walk movement of the mobile sink, clamped to the field

Output

All results are written to the outputs/ folder (created automatically on first run):

File Contents
results.csv One row per step: mean energy, min node energy, cluster count, alive nodes (MOPSO & LEACH), hypervolume
results.mat Full arrays (per-node energies, metrics, node/sink positions, config) for programmatic analysis
energy.png / energy.txt Per-node energy traces / mean energy per step
fitness.png / clusters.txt Cluster count per step (the knee of each step's Pareto front)
optimality.txt Objective-1 value (min residual energy of living nodes) per step
alive_nodes.png / alive_nodes.txt Alive-node curves, MOPSO vs LEACH — the headline result
hypervolume.png / hypervolume.txt Pareto front quality per step
energy_vs_optimality.png Mean energy vs objective-1 value
network.png Node positions, dead nodes, sink track, and the last step's cluster structure

A lifetime-metrics summary (packets delivered, FND/HND rounds for both protocols) is printed to the console at the end of the run.

Reading hypervolume.png: it decays to 0 during the die-off phase. The hypervolume is measured against a fixed worst-case reference point of zero energy, so once objective 1 goes negative there is no volume left to measure. That's the metric behaving as defined, not a failure.

Installation and Usage

Works with MATLAB or free GNU Octave (tested with Octave 11.3.0). No toolboxes required.

Install Octave (Windows):

winget install --id GNU.Octave -e

Run (from the repository folder):

octave --no-gui --eval "Init"

Or open Init.m in MATLAB / Octave GUI and run it. Figures are rendered off-screen and saved to outputs/ — no windows pop up. A full run takes roughly 15 minutes.

Note: use octave --no-gui, not octave-cli. The CLI-only binary ships without the Qt graphics toolkit, and its fltk fallback cannot render the off-screen plots. If plots do fail, all data files (.txt, .csv, .mat) are still written — only the .png files are skipped, with a warning.

Tuning: all parameters live at the top of Init.m:

Parameter Default Meaning
Seed 42 RNG seed; set to [] for a non-reproducible run
Step 400 Maximum rounds (the loop exits early once all nodes are dead)
iteration 50 PSO iterations per clustering decision
NodeNum 400 Number of sensor nodes
Eo 0.05 J Initial battery per node — sized so the network dies inside the run
Cfg.SparseRate 0.05 Initial fraction of nodes activated as heads
Cfg.* — Remaining algorithm constants (swarm size, repository limit, radio model)

For a quick smoke test, set Step = 15 and iteration = 20 (≈ 20 s).

Resources

The resources/ folder contains the project documentation and reference papers:

  • proposal.docx — the project proposal (problem statement, methodology, algorithm outline)
  • Reference papers on PSO-based energy-efficient routing and secure routing models in WSNs, including the paper behind the base implementation

Attributions

https://github.com/Mohamadnet/MOPSO-WSN for providing the base logic and implementation of the project.

Contributors

Languages