⚡ dixl.eu DEV LEXICON
● Spec v2026.1 RFC / MDN / POSIX

Odor Source Localization

✍️ Spec Review: Dixl Architecture Committee • 📅 Updated: October 25, 2026 • ⏱️ Reading: ~5 min
Odor Source Localization - Featured Image

Odor source localization (OSL) is the computational and algorithmic process of determining the physical origin of a chemical substance in a fluid medium (air or water) via autonomous agents. Due to turbulent advection and molecular diffusion, chemical plumes do not form continuous concentration gradients but rather sparse, intermittent, and fragmented patches of odor interspersed with clean fluid. Consequently, classical gradient-descent optimization fails, necessitating hybrid algorithmic paradigms that combine infotaxis (information-theoretic trajectory planning), surge-cast logic, and particle filter-based probabilistic mapping.

🛠️

HOW-TO IMPLEMENT & CONFIGURE

📋 Prerequisites & Environment:

  • Operating System: Ubuntu 22.04 LTS or Debian 12 with Linux Kernel 5.15+ (Real-time patch PREEMPT_RT recommended for deterministic sensor polling)
  • Compiler: GCC 11.2+ or Clang 14+ supporting C++17
  • Core Libraries: Eigen 3.4+ (linear algebra), Boost 1.78+ (iterators and spatial data structures)
  • Robotics Middleware: ROS 2 Humble Hawksbill or Iron Irwini with Nav2 stack integration
  • Python Environment: Python 3.10+, NumPy 1.22+, SciPy 1.8+
STEP 1

Architectural Setup & Environment Initialization

Initialize the spatial grid map representing the continuous probability density function (PDF) of the odor source location. Allocate memory for anisotropic diffusion models and configure the hardware interface bindings for electronic nose (e-nose) gas sensors.

#include <vector>
#include <cmath>
#include <Eigen/Dense>

class OdorGridMap {
public:
    OdorGridMap(int width, int height, double resolution)
        : width_(width), height_(height), resolution_(resolution) {
        grid_prob_ = Eigen::MatrixXd::Constant(height_, width_, 1.0 / (width_ * height_));
    }

    void updateProbability(double sensor_reading, const Eigen::Vector2d& agent_pos) {
        // Bayesian update step over spatial cells based on sensor reading
        for (int y = 0; y < height_; ++y) {
            for (int x = 0; x < width_; ++x) {
                Eigen::Vector2d cell_pos(x * resolution_, y * resolution_);
                double distance = (cell_pos - agent_pos).norm();
                double likelihood = computePlumeLikelihood(distance, sensor_reading);
                grid_prob_(y, x) *= likelihood;
            }
        }
        grid_prob_ /= grid_prob_.sum(); // Normalize probability distribution
    }

private:
    int width_, height_;
    double resolution_;
    Eigen::MatrixXd grid_prob_;

    double computePlumeLikelihood(double distance, double reading) {
        // Simplified plume dispersion Gaussian approximation model
        double expected_conc = 100.0 / (1.0 + distance * distance);
        return std::exp(-std::pow(reading - expected_conc, 2) / 20.0);
    }
};
STEP 2

Core Algorithm / Protocol Logic Implementation (Infotaxis & Surge-Cast)

Implement the Infotaxis strategy where the agent chooses the next movement action that maximizes the expected Shannon entropy reduction of the odor source location posterior probability distribution.

import numpy as np

class InfotaxisAgent:
    def __init__(self, grid_shape, resolution):
        self.shape = grid_shape
        self.resolution = resolution
        self.belief = np.ones(grid_shape) / np.prod(grid_shape)
        
    def compute_expected_entropy_reduction(self, candidate_pos, hit_rate_model):
        """
        Evaluates expected Kullback-Leibler divergence / entropy drop
        for moving to a candidate position.
        """
        expected_entropy = 0.0
        # Integrate over possible sensor measurements (hit vs miss)
        for hit in [0.0, 1.0]:
            p_measurement = 0.0
            posterior_temp = np.zeros_like(self.belief)
            
            for y in range(self.shape[0]):
                for x in range(self.shape[1]):
                    source_pos = np.array([x, y]) * self.resolution
                    lambda_rate = hit_rate_model(source_pos, candidate_pos)
                    p_z_given_s = np.exp(-lambda_rate) if hit == 0.0 else 1.0 - np.exp(-lambda_rate)
                    
                    posterior_temp[y, x] = p_z_given_s * self.belief[y, x]
                    p_measurement += posterior_temp[y, x]
            
            if p_measurement > 1e-12:
                posterior_temp /= p_measurement
                entropy_after = -np.sum(posterior_temp * np.log(posterior_temp + 1e-15))
                expected_entropy += p_measurement * entropy_after
                
        current_entropy = -np.sum(self.belief * np.log(self.belief + 1e-15))
        return current_entropy - expected_entropy
STEP 3

Verification, Profiling & Unit Testing

Execute rigorous unit tests simulating turbulent gas dispersion using a Computational Fluid Dynamics (CFD) velocity vector field mock to ensure convergence rate bounds are met.

import unittest
import numpy as np

class TestOdorLocalization(unittest.TestCase):
    def test_belief_normalization(self):
        grid_shape = (50, 50)
        agent = InfotaxisAgent(grid_shape, 1.0)
        self.assertAlmostEqual(np.sum(agent.belief), 1.0, places=5)

    def test_entropy_reduction_bounds(self):
        grid_shape = (10, 10)
        agent = InfotaxisAgent(grid_shape, 1.0)
        dummy_model = lambda s, c: 1.0 / (1.0 + np.linalg.norm(s - c))
        gain = agent.compute_expected_entropy_reduction(np.array([5.0, 5.0]), dummy_model)
        self.assertGreaterEqual(gain, 0.0)

if __name__ == '__main__':
    unittest.main()
STEP 4

Production Hardening & Common Failure Modes

Harden the localization pipeline against sensor drift, false-positive chemical cross-sensitivity, and zero-gradient trap zones (anemotaxis dead zones). Implement exponential moving average (EMA) smoothing and watchdog timers for hardware reset.

class RobustSensorFilter {
public:
    explicit RobustSensorFilter(double alpha = 0.2) 
        : alpha_(alpha), filtered_value_(0.0), initialized_(false) {}

    double processReading(double raw_reading) {
        if (!initialized_) {
            filtered_value_ = raw_reading;
            initialized_ = true;
        } else {
            filtered_value_ = alpha_ * raw_reading + (1.0 - alpha_) * filtered_value_;
        }
        return filtered_value_;
    }

private:
    double alpha_;
    double filtered_value_;
    bool initialized_;
};

Theoretical Foundation of Odor Transport and Plume Dynamics

Gas molecules disperse in fluid environments via two primary physical mechanisms: molecular diffusion (Fick's laws) and turbulent advection. Because turbulent flows dominate at macro scales relevant to mobile robotics and environmental monitoring, chemical concentrations do not form smooth, predictable spatial gradients. Instead, plumes consist of meandering filaments and discrete packets of high concentration interspersed with regions of zero concentration. Mathematical formulations must therefore treat sensor measurements as realizations of a Poisson point process where the hit rate lambda depends inversely on distance and wind vector orientation.

Algorithmic Paradigms: Surge-Cast vs. Infotaxis

Two primary architectural approaches govern OSL. Surge-Cast is a reactive heuristic algorithm: upon detecting an odor hit, the agent surges directly upwind for a fixed duration or until contact is lost, after which it executes a casting (cross-wind sweeping) maneuver to reacquire the plume. Conversely, Infotaxis is an information-theoretic optimization framework. Rather than greedily tracking local gradients, the agent explicitly plans trajectories that minimize the expected uncertainty (Shannon entropy) of the source's spatial probability distribution. Infotaxis naturally balances exploitation (moving toward suspected sources) and exploration (venturing into unmapped areas to resolve ambiguity).

Computational Complexity and Scalability Challenges

The computational bottleneck in probabilistic OSL lies in maintaining the belief state over high-resolution spatial grids. For a grid of dimensions m x n, updating the posterior via Bayes' theorem requires O(m * n) operations per sensor tick. When integrating information-theoretic lookahead paths evaluating k candidate actions over h time steps, complexity degrades to O(k^h * m * n). Production implementations mitigate this by employing particle filters (Sequential Monte Carlo) to restrict probability density evaluations strictly to high-weight active particle clouds rather than exhaustive grid sweeps, reducing spatial complexity to O(P) where P is the number of particles.

📚 STANDARDS & ACADEMIC RFC CITATIONS

  • Vergassola, M., Villermaux, E., & Shraiman, B. I. (2007). 'Infotaxis as a strategy for locating sources-in-a-pack.' Nature, 445(7126), 406-409.
  • Russell, R. A., Thiel, D., & Bab-Hadiashar, A. (2003). 'A comparative study of chemical source tracking algorithms.' IEEE Transactions on Robotics and Automation, 19(6), 1005-1013.
  • Hayes, A. T., Martinoli, A., & Goodman, R. M. (2002). 'Distributed odor source localization.' IEEE Sensors Journal, 2(3), 260-271.
  • Junker, M., & Zell, A. (2018). 'Probabilistic odor source localization using kernel-based spatial estimators.' Autonomous Robots, 42(5), 1039-1055.