DEV Community

vmodal_ai
vmodal_ai

Posted on Originally published at v-modal.com

Android SDK for Robot Geofencing and Safety-Zone Management

Android SDK for Robot Geofencing and Safety-Zone Management

When autonomous mobile devices navigate human environments, keeping them out of hazardous areas (keep-out zones, stairwells, restricted storage) is critical for industrial safety compliance (ISO 3691-4).

This tutorial demonstrates how to implement a spatial geofencing engine in Kotlin using spatial indexing, ray-casting point-in-polygon (PIP) algorithms, and instant safety interlocks.


1. Safety Zone Taxonomy

+-------------------------------------------------------------+
|                     Operating Environment                   |
|  +-------------------------------------------------------+  |
|  |                 Keep-Out Zone (Polygon)              |  |
|  |   [Robots Prohibited - Trigger E-Stop / Speed 0.0]    |  |
|  +-------------------------------------------------------+  |
|                                                             |
|  +-------------------------------------------------------+  |
|  |                 Warning Zone (Polygon)               |  |
|  |   [Speed Limited to <= 0.3 m/s]                      |  |
|  +-------------------------------------------------------+  |
+-------------------------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

2. Spatial Data Structures

package com.vmodal.sdk.geofence.model

data class Point2D(val x: Double, val y: Double)

enum class ZoneType {
    KEEP_OUT,       // Hard stop zone
    SPEED_RESTRICTED, // Soft slow-down zone
    AUTHORIZED_ONLY   // Requires special operator permission
}

data class SafetyZone(
    val zoneId: String,
    val name: String,
    val type: ZoneType,
    val vertices: List<Point2D>,
    val maxSpeedLimitMps: Double = 0.0
)
Enter fullscreen mode Exit fullscreen mode

3. Implementing Ray-Casting Point-in-Polygon Engine

The Ray-Casting algorithm determines whether a given 2D coordinate $(x, y)$ resides inside an arbitrary polygon by drawing a ray to infinity and counting edge intersections.

package com.vmodal.sdk.geofence.geometry

import com.vmodal.sdk.geofence.model.Point2D

class PolygonGeometryEvaluator {

    /**
     * Standard Ray-Casting Algorithm for Point-in-Polygon
     */
    fun isPointInsidePolygon(point: Point2D, polygon: List<Point2D>): Boolean {
        if (polygon.size < 3) return false

        var inside = false
        var j = polygon.size - 1

        for (i in polygon.indices) {
            val pi = polygon[i]
            val pj = polygon[j]

            val intersect = ((pi.y > point.y) != (pj.y > point.y)) &&
                    (point.x < (pj.x - pi.x) * (point.y - pi.y) / (pj.y - pi.y) + pi.x)

            if (intersect) inside = !inside
            j = i
        }

        return inside
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Building the Geofence Evaluation Engine

package com.vmodal.sdk.geofence

import com.vmodal.sdk.geofence.geometry.PolygonGeometryEvaluator
import com.vmodal.sdk.geofence.model.Point2D
import com.vmodal.sdk.geofence.model.SafetyZone
import com.vmodal.sdk.geofence.model.ZoneType

sealed class SafetyEvaluationResult {
    object SafeState : SafetyEvaluationResult()
    data class SpeedLimitEnforced(val maxSpeed: Double, val zoneName: String) : SafetyEvaluationResult()
    data class EmergencyStopRequired(val violatedZoneId: String, val zoneName: String) : SafetyEvaluationResult()
}

class GeofenceSafetyManager {

    private val activeZones = mutableListOf<SafetyZone>()
    private val evaluator = PolygonGeometryEvaluator()

    fun updateZones(zones: List<SafetyZone>) {
        synchronized(activeZones) {
            activeZones.clear()
            activeZones.addAll(zones)
        }
    }

    fun evaluateRobotPosition(robotPosition: Point2D): SafetyEvaluationResult {
        synchronized(activeZones) {
            for (zone in activeZones) {
                if (evaluator.isPointInsidePolygon(robotPosition, zone.vertices)) {
                    return when (zone.type) {
                        ZoneType.KEEP_OUT -> SafetyEvaluationResult.EmergencyStopRequired(
                            violatedZoneId = zone.zoneId,
                            zoneName = zone.name
                        )
                        ZoneType.SPEED_RESTRICTED -> SafetyEvaluationResult.SpeedLimitEnforced(
                            maxSpeed = zone.maxSpeedLimitMps,
                            zoneName = zone.name
                        )
                        ZoneType.AUTHORIZED_ONLY -> SafetyEvaluationResult.EmergencyStopRequired(
                            violatedZoneId = zone.zoneId,
                            zoneName = "${zone.name} (Unauthorized Access)"
                        )
                    }
                }
            }
        }
        return SafetyEvaluationResult.SafeState
    }
}
Enter fullscreen mode Exit fullscreen mode

5. End-to-End Test Execution

import com.vmodal.sdk.geofence.GeofenceSafetyManager
import com.vmodal.sdk.geofence.SafetyEvaluationResult
import com.vmodal.sdk.geofence.model.Point2D
import com.vmodal.sdk.geofence.model.SafetyZone
import com.vmodal.sdk.geofence.model.ZoneType

fun main() {
    val geofenceManager = GeofenceSafetyManager()

    // Define a Keep-Out zone around a open stairwell
    val stairwellZone = SafetyZone(
        zoneId = "ZONE_HAZARD_01",
        name = "Main Stairwell Pit",
        type = ZoneType.KEEP_OUT,
        vertices = listOf(
            Point2D(10.0, 10.0),
            Point2D(15.0, 10.0),
            Point2D(15.0, 15.0),
            Point2D(10.0, 15.0)
        )
    )

    geofenceManager.updateZones(listOf(stairwellZone))

    val safePose = Point2D(5.0, 5.0)
    val breachPose = Point2D(12.0, 12.0)

    println("Evaluating Pose 1 (5,5): ${geofenceManager.evaluateRobotPosition(safePose)}")
    println("Evaluating Pose 2 (12,12): ${geofenceManager.evaluateRobotPosition(breachPose)}")
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Enforcing spatial integrity using in-memory point-in-polygon evaluations gives Android-driven robots an instant, deterministic safety layer to satisfy strict operational requirements.


Useful Links

Top comments (0)