/BotvacCenter

This is a Kotlin Multiplatform app for controlling and experimenting with Neato Botvac D85 robot vacuums.

Primary LanguageKotlinMIT LicenseMIT

BotvacCenter

Overview

Overview

This is a Kotlin Multiplatform app for controlling and experimenting with Neato Botvac D85 robot vacuums. It is loosely based on btvccntrl-ng and focuses on making everyday tasks easier (like setting the clock or starting the robot) as well as exploring SLAM (Simultaneous Localization And Mapping).

Prebuilt packages for Linux (.deb) and Android (.apk) can be found under Releases.

Also take a look at the project video on YouTube:

Making the WORLD'S SMARTEST ROBOT vacuum DUMBER by building my own crude SLAM stack

Dependencies

I generally try to minimize dependencies, but I'm a one man crew and can therefore only support Debian-based Linux distributions as I'm running one myself. Anyway, you need to have the following packages installed for everything to work properly:

  • SDKMAN! for managing all the JVM dependencies. Install it via the installation guide.
  • JDK for running the bytecode. Install it with sdk install java.
  • Kotlin for developing the program. Install it with sdk install kotlin.
  • Gradle for building the whole thing. Install it with sdk install gradle.
  • Android Studio as the necessary IDE for Android development. Install it via the installation guide.
  • Kotlin Multiplatform plugin for Android Studio. Install it from here.

Features

  • Start and stop house or spot cleanings remotely.
  • Access crucial statistics (battery charge and total runtime) at a moments glance.
  • Manually drive the robot like a RC car from one location to another.
  • Check, modify and enable/disable the cleaning schedule.
  • Quickly set the current date and time with a single button press.
  • Get extensive diagnostics data for troubleshooting.
  • Easily experiment with SLAM via inbuilt sensors (LIDAR and wheel odometry).

SLAM

Proposed algorithm

  1. Initial scan of the environment.
  2. If the target position isn't equal to the current position:
    1. Calculate the path from current to target via a modified A* path finding algorithm including a cost map (prefer routes far away from obstacles).
    2. Follow the first 50 cm (or whatever distance between scans proves to be reliable) of the path.
    3. Perform another scan.
    4. If needed, perform some post-processing to clean the scan.
    5. Match the new scan to the last scan with the ICP (Iterative Closest Point) algorithm to get a LIDAR transformation estimate.
    6. Combine the LIDAR and wheel odometry transformation estimates to update the robot's absolute position.
    7. Add the current scan to the map based on the determined robot position.
    8. If needed, perform some post-processing to clean the map.
  3. Wait for the user to either set a new target position and then go the step 2 or stop the mapping process.

Example mapping runs

Mapping early

mapping-early.mp4

Mapping later

mapping-later.mp4

Useful resources

How to use it

After modding your Neato Botvac D85 with btvcbrdg and writing down its IP address as well as login credentials (username and password), you can immediately run the desktop version via gradle desktopRun -DmainClass=MainKt --quiet and enter your specific information in the Settings to get started. Alternatively you can create a packaged version of the app via gradle packageDistributionForCurrentOS and install that - the final binary will be placed under composeApp/build/compose/binaries/main/deb. To run it as an Android app you can either just run a debug build in Android Studio's inbuilt emulator, create an Android package by selecting debug under Build/Select Build Variant and then running Build App Bundle(s) / APK(s)/Build APK(s) or build a fully-secure release build with your own signing keys via Build/Generate Signed App Bundle / APK.

Helpful stuff

  • Convert .png to .ico via convert file.png -define icon:auto-resize=256,64,48,32,16 file.ico.
  • Convert .png to .icns via convert file.png file.icns.
  • If the app throws a ClassNotFoundException, see this section in the native distributions tutorial.