DEV Community

vmodal_ai
vmodal_ai

Posted on

Android + ROS 2: Building a Robot Control App with Kotlin

Android + ROS 2: Building a Robot Control App with Kotlin

Introduction

Physical AI is bringing together robotics, edge computing, computer vision, and intelligent mobile interfaces. Android is a useful companion platform because modern phones and tablets provide touch interfaces, cameras, sensors, networking, and strong edge-computing capabilities.

In this tutorial, we will design an Android application in Kotlin that communicates with a ROS 2 robot. The app will provide a simple control interface for sending movement commands and receiving robot telemetry.

Architecture

A practical architecture can look like this:

Android App
   |
   | ROS 2 bridge / WebSocket / MQTT
   v
ROS 2 Middleware
   |
   +---- /cmd_vel ----> Robot Base
   |
   +---- /odom -------> Telemetry
   |
   +---- /battery ----> Battery Status
Enter fullscreen mode Exit fullscreen mode

The Android application should not directly control motors. Instead, it communicates with a ROS 2 node or bridge responsible for validating commands and interfacing with the robot.

Project Setup

Create a Kotlin Android project using Android Studio.

A clean package structure is:

com.example.robotcontroller
├── ui
├── ros
├── model
├── network
└── MainActivity.kt
Enter fullscreen mode Exit fullscreen mode

Keep the ROS communication layer separate from the Compose UI so that the application can later switch between a simulator, development robot, or production robot.

Robot Command Model

Create a simple command model:

data class VelocityCommand(
    val linearX: Double,
    val angularZ: Double
)
Enter fullscreen mode Exit fullscreen mode

The UI can map buttons or a virtual joystick to these values.

For example:

fun moveForward() = VelocityCommand(
    linearX = 0.5,
    angularZ = 0.0
)

fun stop() = VelocityCommand(
    linearX = 0.0,
    angularZ = 0.0
)
Enter fullscreen mode Exit fullscreen mode

The communication layer then converts the command into the message format expected by your ROS 2 bridge.

Jetpack Compose Control UI

A simple control panel can be created with Jetpack Compose:

@Composable
fun RobotControls(
    onForward: () -> Unit,
    onBackward: () -> Unit,
    onLeft: () -> Unit,
    onRight: () -> Unit,
    onStop: () -> Unit
) {
    Column {
        Button(onClick = onForward) {
            Text("Forward")
        }

        Row {
            Button(onClick = onLeft) {
                Text("Left")
            }

            Button(onClick = onStop) {
                Text("Stop")
            }

            Button(onClick = onRight) {
                Text("Right")
            }
        }

        Button(onClick = onBackward) {
            Text("Backward")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

For a real robot, replace these buttons with a joystick or gesture-based controller.

ROS 2 Communication Layer

The Android app needs a communication mechanism between the mobile device and ROS 2. A common architecture is to expose selected ROS 2 topics through a bridge or gateway.

The Android client can then publish commands such as:

/cmd_vel
Enter fullscreen mode Exit fullscreen mode

and subscribe to telemetry topics such as:

/odom
/battery_state
/robot_status
Enter fullscreen mode Exit fullscreen mode

Avoid exposing the entire ROS graph directly to an untrusted mobile client. Expose only the topics and services required by the application.

Receiving Telemetry

Represent telemetry in Kotlin:

data class RobotTelemetry(
    val battery: Float,
    val x: Double,
    val y: Double,
    val connected: Boolean
)
Enter fullscreen mode Exit fullscreen mode

Use Kotlin coroutines and StateFlow to expose updates to Compose:

private val _telemetry = MutableStateFlow(
    RobotTelemetry(0f, 0.0, 0.0, false)
)

val telemetry: StateFlow<RobotTelemetry> = _telemetry
Enter fullscreen mode Exit fullscreen mode

Compose can collect this state and update the dashboard automatically.

Safety Features

A robot-control application should include a reliable stop mechanism.

Implement:

  • Emergency stop
  • Automatic command timeout
  • Connection-loss detection
  • Maximum velocity limits
  • Authentication between the app and robot
  • TLS for network communication where supported

A particularly useful technique is a command watchdog: if the robot does not receive a valid command within a defined interval, the control node should command zero velocity.

Testing with Simulation

Before connecting physical hardware, test the Android application against a simulated ROS 2 robot.

A simulator lets you verify:

  • Movement commands
  • Telemetry
  • Network failures
  • Emergency stop behavior
  • UI responsiveness
  • Command limits

This dramatically reduces the risk of testing incorrect commands on physical hardware.

Production Architecture

For a production system, consider:

Android / Kotlin
       |
   Secure Gateway
       |
      ROS 2
       |
 Navigation / Perception
       |
 Robot Hardware
Enter fullscreen mode Exit fullscreen mode

This separation allows the Android application to remain a user interface while ROS 2 handles robotics workloads.

Conclusion

Android and Kotlin can provide a powerful human interface for Physical AI systems. By combining Jetpack Compose, Kotlin coroutines, secure networking, and ROS 2, you can build mobile applications that monitor and control robots without coupling the Android UI directly to robot hardware.

The same architecture can later be extended with camera streaming, AI perception, voice commands, autonomous navigation, and LLM-based robot control.

Useful Links

SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter

SDK Android: https://github.com/v-modal/vmodal_sdk_android

Discord: https://discord.gg/K72z28KUx

Top comments (0)