Developing a Robot Mission-Orchestration SDK with Kotlin
Complex robot operations—such as multi-point inspection, payload delivery, and security patrols—require robust task orchestration engines. The orchestration layer must handle sequential actions, concurrent sub-tasks, error recovery, and instant safety preemption (pause, resume, abort).
In this tutorial, we will write a Kotlin-first Mission Orchestration SDK utilizing Coroutines, Channels, and Finite State Machines (FSM).
1. Architecture of the Mission Engine
+-------------------------------------------------------------+
| Mission Executor |
+-------------------------------------------------------------+
| |
v v
+-----------------------+ +-----------------------+
| Waypoint Task | -------> | Manipulate Payload |
+-----------------------+ +-----------------------+
| |
v v
+-----------------------+ +-----------------------+
| Safety Guard Check | | Return to Dock |
+-----------------------+ +-----------------------+
2. Defining Task Primitives and Mission States
package com.vmodal.sdk.mission.model
sealed class TaskResult {
object Success : TaskResult()
data class Failure(val reason: String) : TaskResult()
object Aborted : TaskResult()
}
interface MissionTask {
val taskId: String
val description: String
suspend fun execute(context: MissionContext): TaskResult
}
data class MissionContext(
val robotId: String,
val variables: MutableMap<String, Any> = mutableMapOf()
)
sealed class MissionStatus {
object Idle : MissionStatus()
data class Running(val currentTaskIndex: Int, val currentTaskId: String) : MissionStatus()
object Paused : MissionStatus()
data class Completed(val totalTasksExecuted: Int) : MissionStatus()
data class Failed(val failedTaskId: String, val reason: String) : MissionStatus()
object Aborted : MissionStatus()
}
3. Implementing Concrete Mission Tasks
package com.vmodal.sdk.mission.tasks
import com.vmodal.sdk.mission.model.MissionContext
import com.vmodal.sdk.mission.model.MissionTask
import com.vmodal.sdk.mission.model.TaskResult
import kotlinx.coroutines.delay
class MoveToWaypointTask(
override val taskId: String,
private val x: Double,
private val y: Double,
private val heading: Double
) : MissionTask {
override val description: String = "Navigating to ($x, $y) at $heading rad"
override suspend fun execute(context: MissionContext): TaskResult {
println("Exec [$taskId]: Starting navigation to x=$x, y=$y")
// Simulate navigation loop delay
delay(1500)
println("Exec [$taskId]: Reached waypoint successfully.")
return TaskResult.Success
}
}
class WaitTask(
override val taskId: String,
private val durationMs: Long
) : MissionTask {
override val description: String = "Pausing for ${durationMs}ms"
override suspend fun execute(context: MissionContext): TaskResult {
println("Exec [$taskId]: Waiting ${durationMs}ms...")
delay(durationMs)
return TaskResult.Success
}
}
4. Building the Core Mission Orchestrator
package com.vmodal.sdk.mission
import com.vmodal.sdk.mission.model.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class MissionOrchestrator(
private val scope: CoroutineScope
) {
private val _status = MutableStateFlow<MissionStatus>(MissionStatus.Idle)
val status: StateFlow<MissionStatus> = _status.asStateFlow()
private var missionJob: Job? = null
private var isPaused = false
fun executeMission(context: MissionContext, tasks: List<MissionTask>) {
if (_status.value is MissionStatus.Running) return
missionJob = scope.launch(Dispatchers.Default) {
println("Starting Mission execution for Robot ${context.robotId} with ${tasks.size} tasks.")
for ((index, task) in tasks.withIndex()) {
// Check for pause condition loop
while (isPaused) {
_status.value = MissionStatus.Paused
delay(500)
}
if (!isActive) {
_status.value = MissionStatus.Aborted
return@launch
}
_status.value = MissionStatus.Running(index, task.taskId)
when (val result = task.execute(context)) {
is TaskResult.Success -> {
println("Task ${task.taskId} completed successfully.")
}
is TaskResult.Failure -> {
_status.value = MissionStatus.Failed(task.taskId, result.reason)
return@launch
}
TaskResult.Aborted -> {
_status.value = MissionStatus.Aborted
return@launch
}
}
}
_status.value = MissionStatus.Completed(tasks.size)
}
}
fun pause() {
if (_status.value is MissionStatus.Running) {
isPaused = true
}
}
fun resume() {
if (isPaused) {
isPaused = false
}
}
fun abort() {
missionJob?.cancel()
_status.value = MissionStatus.Aborted
isPaused = false
}
}
5. Execution Script Example
import com.vmodal.sdk.mission.MissionOrchestrator
import com.vmodal.sdk.mission.model.MissionContext
import com.vmodal.sdk.mission.tasks.MoveToWaypointTask
import com.vmodal.sdk.mission.tasks.WaitTask
import kotlinx.coroutines.*
fun main() = runBlocking {
val orchestrator = MissionOrchestrator(this)
val context = MissionContext(robotId = "AMR-9021")
val plan = listOf(
MoveToWaypointTask("TASK_01", 12.5, 4.0, 0.0),
WaitTask("TASK_02", 1000),
MoveToWaypointTask("TASK_03", 20.0, 15.2, 1.57)
)
orchestrator.executeMission(context, plan)
delay(2000)
println("Operator presses PAUSE button...")
orchestrator.pause()
delay(2000)
println("Operator presses RESUME button...")
orchestrator.resume()
delay(3000)
println("Final Mission State: ${orchestrator.status.value}")
}
Conclusion
By leveraging Kotlin’s structured concurrency, suspendable tasks, and state flow models, building resilient mission execution pipelines for autonomous robots becomes clean and maintainable.
Useful Links
- Website: www.v-modal.com
- SDK Flutter: v-modal/vmodal_sdk_flutter
- SDK Android: v-modal/vmodal_sdk_android
- Discord: https://discord.gg/K72z28KUx











