_images/logo_kobuki.png

Contents

_images/logo_kobuki.png

About

Introducing Korea’s first robotic turtle.

kobuki [거북이] n. turtle

Kobuki is robotically engineered to be long-lived, tough and fast. With high performance batteries, Kobuki will tirelessly work alongside you through those long coffee-powered nights. He’ll also happily burden himself with your modded array of sensors, actuators, laptops, embedded boards, portside cannons and do it all at a speed that makes his real world cousins seem like … well, turtles.

Use him for serving 치맥 (chi-mek), chasing your neighbour’s kids or simply, to make your own robot ideas become reality.

Kobuki is still young, don’t expect him to remain as he is . Kobuki’s development has already been significantly influenced by the community and as he marches towards old age, we will continue to work with the community and you to ensure he becomes better with time.

Sincerely, Kobuki Team.

Out of the Box

Charging

Connect the power adapter to Kobuki or dock Kobuki in the docking station. If Kobuki is turned on, you will hear a short sound when charging starts and the LED will light up appropriately.

LED Color Status
Green fully charged
Blinking Green charging
Orange low battery

Note

The battery still charges if Kobuki is off, but you will not see the LED, nor hear sounds

First Run

You want to see Kobuki in action without further ado? Kobuki has a special random walker mode embedded into the firmware which you can activate on start-up:

  • Disconnect the power cable
  • Turn on Kobuki.
  • Within the first 3 seconds press the B0 button and hold for 2 seconds.
  • LED2 will start blinking and Kobuki wander around.

Note

This was introduced to the firmware in v1.1.0. In case your kobuki is not running this or a later version, please refer to Updating Firmware.

Installing & Running the Software

Install from Binaries

If you happen to have access to a binary install (e.g. ROS), follow their instructions and then proceed directly to Checking the Version Info, otherwise follow the instructions below to build Kobuki and it’s dependencies from source.

Build from Source

Requirements

The environment under which these instructions have been tested (and thus supported) is as follows.

  • Platform: Linux (most flavours)
  • C++ Version: c++14
  • Compiler: gcc
  • Build Dependencies: ament, colcon, vcstool, CMake
  • Code Dependencies: Eigen, Sophus, ECL

Other platforms may work, but your mileage will vary. Windows has been supported in the past, so if you’re willing to do a bit of work, you might find success.

Preparation

Ensure your system has the following packages installed:

  • GCC (>=9)
  • CMake (>=3.5)
  • wget
  • python3-venv

Download a few scripts that will help setup your workspace.

$ mkdir kobuki && cd kobuki

# a virtual environment launcher that will fetch build tools from pypi (colcon, vcstools)
$ wget https://raw.githubusercontent.com/kobuki-base/kobuki_documentation/release/1.0.x/resources/venv.bash || exit 1

# custom build configuration options for eigen, sophus
$ wget https://raw.githubusercontent.com/kobuki-base/kobuki_documentation/release/1.0.x/resources/colcon.meta || exit 1

# list of repositories to git clone
$ wget https://raw.githubusercontent.com/kobuki-base/kobuki_documentation/release/1.0.x/resources/kobuki_standalone.repos || exit 1

Fetch the sources:

$ source ./venv.bash

$ mkdir src

# vcs handles distributed fetching of repositories listed in a .repos file
$ vcs import ./src < kobuki_standalone.repos || exit 1

$ deactivate

Note

If you prefer to use your system Eigen:

$ touch src/eigen/AMENT_IGNORE

Build

$ source ./venv.bash

# build everything
$ colcon build --merge-install --cmake-args -DBUILD_TESTING=OFF

# disable any unused cmake variable warnings (e.g. sophus doesn't use BUILD_TESTING)
$ colcon build --merge-install --cmake-args -DBUILD_TESTING=OFF --no-warn-unused-cli

# build a single package
$ colcon build --merge-install --packages-select kobuki_core --cmake-args -DBUILD_TESTING=OFF

# build everything, verbosely
$ VERBOSE=1 colcon build --merge-install --event-handlers console_direct+ --cmake-args -DBUILD_TESTING=OFF

# build release with debug symbols
$ colcon build --merge-install --cmake-args -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=RelWithDebInfo

# update the source workspace
$ vcs pull ./src

$ deactivate

The resulting headers, libraries and resources can be found under ./install.

These instructions are continuously vetted with a github action (yaml, results/logs).

Connect Kobuki

Kobuki’s default means of communication is over usb (it can instead use the serial comm port directly, more on that later). On most linux systems, your Kobuki will appear on /dev/ttyUSBO as soon as you connect the cable. This is a typical serial2usb device port and if you happen to be using more than one such device, Kobuki may appear at ttyUSB1, ttyUSB1, …

In order to provide a constant identifier for the connection, we’ve prepared a udev rule for you:

$ wget https://raw.githubusercontent.com/kobuki-base/kobuki_ftdi/devel/60-kobuki.rules
$ sudo cp 60-kobuki.rules /etc/udev/rules.d

# different linux distros may use a different service manager (this is Ubuntu's)
#   --> failing all else, a reboot will work
$ sudo service udev reload
$ sudo service udev restart

With this udev rule, you’ll find your Kobuki appear at /dev/kobuki as soon as you connect and turn on the robot. This also comes with the added convenience that it is the default device port value for most Kobuki programs.

  • Connect the usb cable
  • Turn Kobuki on (you’ll hear a chirp)
  • Check for existence of /dev/kobuki
  • I’m wearing a colander, you should too

Checking the Version Info

# drop into the runtime enviroment
$ source ./install/setup.bash

# who is your kobuki?
$ kobuki-version-info
Version Info:
  Hardware Version: 1.0.4
  Firmware Version: 1.2.0
  Software Version: 1.1.0
  Unique Device ID: 97713968-842422349-1361404194

Your driver may give you a warning (software or firmware upgrade advised) or error (incompatible firmware/software) about mismatching versions. If it’s the firmware you need to upgrade, refer to the section on Firmware.

Take Kobuki for a Test Drive

# drop into the runtime enviroment
$ source ./install/setup.bash

# take kobuki for a test drive
$ kobuki-simple-keyop
Simple Keyop : Utility for driving kobuki by keyboard.
KobukiManager : using linear  vel step [0.05].
KobukiManager : using linear  vel max  [1].
KobukiManager : using angular vel step [0.33].
KobukiManager : using angular vel max  [6.6].
Reading from keyboard
---------------------------
Forward/back arrows : linear velocity incr/decr.
Right/left arrows : angular velocity incr/decr.
Spacebar : reset linear/angular velocities.
q : quit.
current pose: [0, 0, 0]
current pose: [0, 0, 0]
current pose: [0, 0, 0]
current pose: [0.0064822, -1.17028e-06, -0.00074167]
current pose: [0.0226873, -9.88246e-05, -0.0133501]

Creating Applications

Chirp

About

This example merely configures and establishes a connection to Kobuki which will cause it to chirp, pause for five seconds and then emit the corresponding shutdown chirp. First though, some information about the library and the API that will be useful to get you started.

The Kobuki Library

The nature of the computational resources you have as well as your application’s use case can have a significant impact on how you design your application, especially for details around the control loop. For this reason, the library does not endeavour to provide a control loop (that is up to you) and as such, libkobuki.so is simply one of classes, data structures, simple functions and collaback-oriented sigslot mechanisms.

The Kobuki Class

The kobuki:Kobuki class is the first port of call for developing your application. Configuration and non-callback modes of interaction are handled via this class. Callback modes are handled by sigslots, mroe on these later.

Initialisation & Configuration

Kobuki configuration is handled by the kobuki:Parameters class which is passed ot the kobuki instance via the kobuki::Kobuki::init() method. Most of the parameters to be configured have sane defaults.

The only one that requires frequent configuration is the serial device port. If you aren’t using a udev rule to guarantee discovery at /dev/kobuki, then you’ll typically find Kobuki at COM1 (windows) or /dev/ttyUSB0 (linux).

Code

#include <iostream>
#include <string>
#include <ecl/time.hpp>
#include <ecl/command_line.hpp>
#include <kobuki_core/kobuki.hpp>

class KobukiManager
{
public:
  KobukiManager(const std::string &device)
  {
    kobuki::Parameters parameters;
    // Specify the device port, default: /dev/kobuki
    parameters.device_port = device;

    // Other parameters are typically happy enough as defaults, some examples follow
    //
    // namespaces all sigslot connection names, default: /kobuki
    parameters.sigslots_namespace = "/kobuki";
    // Most use cases will bring their own smoothing algorithms, but if
    // you wish to utilise kobuki's minimal acceleration limiter, set to true
    parameters.enable_acceleration_limiter = false;
    // Adjust battery thresholds if your levels are significantly varying from factory settings.
    // This will affect led status as well as triggering driver signals
    parameters.battery_capacity = 16.5;
    parameters.battery_low = 14.0;
    parameters.battery_dangerous = 13.2;

    // Initialise - exceptions are thrown if parameter validation or initialisation fails.
    try
    {
      kobuki.init(parameters);
    }
    catch (ecl::StandardException &e)
    {
      std::cout << e.what();
    }
  }
private:
  kobuki::Kobuki kobuki;
};

int main(int argc, char **argv)
{
  ecl::CmdLine cmd_line("chirp", ' ', "0.2");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  cmd_line.add(device_port);
  cmd_line.parse(argc, argv);

  KobukiManager kobuki_manager(device_port.getValue());
  ecl::Sleep()(5);
  return 0;
}

Events & Streams

About

The next two applications make use of the callback handles provided by the core Kobuki class for listening in to events and streams from the Kobuki. This is done by registering callbacks with the sigslots framework.

Signals and Slots

The kobuki driver establishes a set of signals on uniquely labelled channels. Each channel consists of two parts. The first part represents the namespace, which can be customised via the sigslots_namespace variable in the kobuki::Parameter structure. The second uniquely identifies the signal itself.

The following represent the available signals along with the type of data they transmit when namespaced under the default namespace, “/kobuki”.

The Sensor Stream

  • /kobuki/stream_data [void]

The stream_data channel signals that a new data packet has arrived and is ready to be processed. These data packets are sent periodically and are include a composited payload containing data from all sensor streams. This is a special case, in that the type associated with the signal does not represent the data that has been collected, but just that it has arrived. This data can be fetched from within the callback connected to this signal via Kobuki::getCoreSensorData() which returns a kobuki::CoreSensors::Data structure holding all the important sensor information for the Kobuki.

General Purpose Signals

  • /kobuki/ros_debug [std::string]
  • /kobuki/ros_info [std::string]
  • /kobuki/ros_warn [std::string]
  • /kobuki/ros_error [std::string]
  • /kobuki/version_info [kobuki::VersionInfo]: communicated only on request

Event Handling Signals

  • /kobuki/button_event [kobuki::ButtonEvent]
  • /kobuki/bumper_event [kobuki::BumperEvent]
  • /kobuki/cliff_event [kobuki::CliffEvent]
  • /kobuki/wheel_event [kobuki::WheelEvent]
  • /kobuki/power_event [kobuki::PowerEvent]
  • /kobuki/input_event [kobuki::InputEvent]
  • /kobuki/robot_event [kobuki::RobotEvent]

These fire only when an event occurs.

Wheel events occur when the wheel position toggles between compressed and uncompressed (e.g. when you lift the robot from the floor). Input events correspond to gpio state changes (useful when you are customising Kobuki with additional sensors that can send binary signals to your program).

Slots

The kobuki driver does not establish any slots, that part is up to you and is demonstrated in the following program.

Code - Button Events

#include <iostream>
#include <random>
#include <string>
#include <vector>

#include <ecl/command_line.hpp>
#include <ecl/time.hpp>
#include <ecl/sigslots.hpp>

#include <kobuki_core/kobuki.hpp>

class KobukiManager
{
public:
  KobukiManager(const std::string &device) :
      slot_button_event(&KobukiManager::processButtonEvent, *this)
  {
    kobuki::Parameters parameters;
    parameters.device_port = device;

    try
    {
      kobuki.init(parameters);
    }
    catch (ecl::StandardException &e)
    {
      std::cout << e.what();
    }
    slot_button_event.connect("/kobuki/button_event");
  }

  /*
   * Nothing to do in the main thread, just put it to sleep
   */
  void spin()
  {
    ecl::Sleep sleep(1);
    while (true)
    {
      sleep();
    }
  }

  /*
   * Catches button events and prints a curious message to stdout.
   */
  void processButtonEvent(const kobuki::ButtonEvent &event)
  {
    std::vector<std::string> quotes = {
      "That's right buddy, keep pressin' my buttons. See what happens!",
      "Anything less than immortality is a complete waste of time",
      "I can detect humour, you are just not funny",
      "I choose to believe ... what I was programmed to believe",
      "My story is a lot like yours, only more interesting ‘cause it involves robots.",
      "I wish you'd just tell me rather trying to engage my enthusiasm with these buttons, because I haven't got one.",
    };
    std::random_device r;
    std::default_random_engine generator(r());
    std::uniform_int_distribution<int> distribution(0, 5);
    if (event.state == kobuki::ButtonEvent::Released ) {
      std::cout << quotes[distribution(generator)] << std::endl;
    }
  }

private:
  kobuki::Kobuki kobuki;
  ecl::Slot<const kobuki::ButtonEvent&> slot_button_event;
};

int main(int argc, char **argv)
{
  ecl::CmdLine cmd_line("buttons", ' ', "0.1");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  cmd_line.add(device_port);
  cmd_line.parse(argc, argv);

  KobukiManager kobuki_manager(device_port.getValue());
  kobuki_manager.spin();
  return 0;
}

Code - The Sensor Stream

#include <iostream>
#include <string>

#include <ecl/command_line.hpp>
#include <ecl/time.hpp>
#include <ecl/sigslots.hpp>

#include <kobuki_core/kobuki.hpp>

class KobukiManager
{
public:
  KobukiManager(const std::string &device) :
      slot_stream_data(&KobukiManager::processStreamData, *this)
  {
    kobuki::Parameters parameters;
    parameters.device_port = device;

    try
    {
      kobuki.init(parameters);
    }
    catch (ecl::StandardException &e)
    {
      std::cout << e.what();
    }
    slot_stream_data.connect("/kobuki/stream_data");
  }

  /*
   * Nothing to do in the main thread, just put it to sleep
   */
  void spin()
  {
    ecl::Sleep sleep(1);
    while (true)
    {
      sleep();
    }
  }

  /*
   * Called whenever the kobuki receives a data packet.
   * Up to you from here to process it.
   */
  void processStreamData()
  {
    kobuki::CoreSensors::Data data = kobuki.getCoreSensorData();
    std::cout << "Encoders [" << data.left_encoder << "," << data.right_encoder << "]" << std::endl;
  }

private:
  kobuki::Kobuki kobuki;
  ecl::Slot<> slot_stream_data;
};

int main(int argc, char **argv)
{
  ecl::CmdLine cmd_line("buttons", ' ', "0.1");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  cmd_line.add(device_port);
  cmd_line.parse(argc, argv);

  KobukiManager kobuki_manager(device_port.getValue());
  kobuki_manager.spin();
  return 0;
}

A Simple Control Loop

About

This example demonstrates how to process kobuki’s pose data and based on the current pose, computes and sends the appropriate wheel commands to the robot, i.e. it closes the loop between sensing and control.

Code

Engage and watch Kobuki move around a dead-reckoned square with sides of length 1.0m.

#include <string>
#include <csignal>
#include <ecl/geometry.hpp>
#include <ecl/time.hpp>
#include <ecl/sigslots.hpp>
#include <ecl/linear_algebra.hpp>
#include <ecl/command_line.hpp>
#include "kobuki_core/kobuki.hpp"


/*****************************************************************************
** Classes
*****************************************************************************/

class KobukiManager {
public:
  KobukiManager(
      const std::string & device,
      const double &length,
      const bool &disable_smoothing
  ) :
    dx(0.0), dth(0.0),
    length(length),
    slot_stream_data(&KobukiManager::processStreamData, *this)
  {
    kobuki::Parameters parameters;
    parameters.sigslots_namespace = "/kobuki";
    parameters.device_port = device;
    parameters.enable_acceleration_limiter = !disable_smoothing;

    kobuki.init(parameters);
    kobuki.enable();
    slot_stream_data.connect("/kobuki/stream_data");
  }

  ~KobukiManager() {
    kobuki.setBaseControl(0,0); // linear_velocity, angular_velocity in (m/s), (rad/s)
    kobuki.disable();
  }

  void processStreamData() {
    ecl::linear_algebra::Vector3d pose_update;
    ecl::linear_algebra::Vector3d pose_update_rates;
    kobuki.updateOdometry(pose_update, pose_update_rates);
    ecl::concatenate_poses(pose, pose_update);
    dx += pose_update[0];   // x
    dth += pose_update[2];  // heading
    // std::cout << dx << ", " << dth << std::endl;
    // std::cout << kobuki.getHeading() << ", " << pose.heading() << std::endl;
    // std::cout << "[" << pose[0] << ", " << pose.y() << ", " << pose.heading() << "]" << std::endl;
    processMotion();
  }

  // Generate square motion
  void processMotion() {
    const double buffer = 0.05;
    double longitudinal_velocity = 0.0;
    double rotational_velocity = 0.0;
    if (dx >= (length) && dth >= ecl::pi/2.0) {
      std::cout << "[Z] ";
      dx=0.0; dth=0.0;
    } else if (dx >= (length + buffer)) {
      std::cout << "[R] ";
      rotational_velocity = 1.1;
    } else {
      std::cout << "[L] ";
      longitudinal_velocity = 0.3;
    }
    std::cout << "[dx: " << dx << "][dth: " << dth << "][" << pose[0] << ", " << pose[1] << ", " << pose[2] << "]" << std::endl;
    kobuki.setBaseControl(longitudinal_velocity, rotational_velocity);
  }

  const ecl::linear_algebra::Vector3d& getPose() {
    return pose;
  }

private:
  double dx, dth;
  const double length;
  ecl::linear_algebra::Vector3d pose;  // x, y, heading
  kobuki::Kobuki kobuki;
  ecl::Slot<> slot_stream_data;
};

/*****************************************************************************
** Signal Handler
*****************************************************************************/

bool shutdown_req = false;
void signalHandler(int /* signum */) {
  shutdown_req = true;
}

/*****************************************************************************
** Main
*****************************************************************************/

int main(int argc, char** argv)
{
  ecl::CmdLine cmd_line("Uses a simple control loop to move Kobuki around a dead-reckoned square with sides of length 1.0m", ' ', "0.2");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  ecl::ValueArg<double> length(
      "l", "length",
      "traverse square with sides of this size in length (m)",
      false,
      0.25,
      "double"
  );
  ecl::SwitchArg disable_smoothing(
      "d", "disable_smoothing",
      "Disable the acceleration limiter (smoothens velocity)",
      false
  );

  cmd_line.add(device_port);
  cmd_line.add(length);
  cmd_line.add(disable_smoothing);
  cmd_line.parse(argc, argv);

  signal(SIGINT, signalHandler);

  std::cout << "Demo : Example of simple control loop." << std::endl;
  KobukiManager kobuki_manager(
      device_port.getValue(),
      length.getValue(),
      disable_smoothing.getValue()
  );

  ecl::Sleep sleep(1);
  ecl::linear_algebra::Vector3d pose;  // x, y, heading
  try {
    while (!shutdown_req){
      sleep();
      pose = kobuki_manager.getPose();
      // std::cout << "current pose: [" << pose[0] << ", " << pose[1] << ", " << pose[2] << "]" << std::endl;
    }
  } catch ( ecl::StandardException &e ) {
    std::cout << e.what();
  }
  return 0;
}

Decoupling the Control

This program relied on the periodic sensor stream to trigger the control commands. This results in a loop with the fewest lines of code as well as minimum latency between pose update and control.

Alternatively, you may wish to decopule the control from the sensor stream callback (e.g. via the spin() method). That is also fine and usual in more complex use cases. Beware however, of concurrency issues if using a separate thread.

Logging

About

Kobuki provides loggers over the debug, info, warning and error signals. By default, the software wires up stdout loggers directly to the warning and error signals, but you can both change this log level (e.g. DEBUG will cause all log levels to be printed to stdout) OR disable them complately and wire up slots to your own loggers.

Code - Log Levels

#include <iostream>
#include <string>
#include <ecl/console.hpp>
#include <ecl/time.hpp>
#include <ecl/command_line.hpp>
#include <kobuki_core/kobuki.hpp>

int main(int argc, char **argv)
{
  ecl::CmdLine cmd_line("log_levels", ' ', "0.1");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  cmd_line.add(device_port);
  cmd_line.parse(argc, argv);

  std::cout << ecl::bold << "\nLog Levels Demo\n" << ecl::reset << std::endl;

  kobuki::Parameters parameters;
  parameters.device_port = device_port.getValue();
  parameters.log_level = kobuki::LogLevel::DEBUG;

  kobuki::Kobuki kobuki;
  try {
    kobuki.init(parameters);
  } catch (ecl::StandardException &e) {
    std::cout << e.what();
  }

  ecl::Sleep()(5);
  return 0;
}

Output - Log Levels

_images/demo_log_levels.png

Code - Custom Loggers

#include <iostream>
#include <string>
#include <ecl/console.hpp>
#include <ecl/sigslots.hpp>
#include <ecl/time.hpp>
#include <ecl/command_line.hpp>
#include <kobuki_core/kobuki.hpp>

class KobukiManager
{
public:
  KobukiManager(const std::string &device) :
    slot_debug(&KobukiManager::logCustomDebug, *this),
    slot_info(&KobukiManager::logCustomInfo, *this),
    slot_warning(&KobukiManager::logCustomWarning, *this),
    slot_error(&KobukiManager::logCustomError, *this)
  {
    kobuki::Parameters parameters;

    parameters.device_port = device;
    // Disable the default loggers
    parameters.log_level = kobuki::LogLevel::NONE;

    // Wire them up ourselves
    slot_debug.connect(parameters.sigslots_namespace + "/debug");
    slot_info.connect(parameters.sigslots_namespace + "/info");
    slot_warning.connect(parameters.sigslots_namespace + "/warning");
    slot_error.connect(parameters.sigslots_namespace + "/error");

    try {
      kobuki.init(parameters);
    } catch (ecl::StandardException &e) {
      std::cout << e.what();
    }
  }

  void logCustomDebug(const std::string& message) {
    std::cout << ecl::green << "[DEBUG_WITH_COLANDERS] " << message << ecl::reset << std::endl;
  }

  void logCustomInfo(const std::string& message) {
    std::cout << "[INFO_WITH_COLANDERS] " << message << ecl::reset << std::endl;
  }

  void logCustomWarning(const std::string& message) {
    std::cout << ecl::yellow << "[WARNING_WITH_COLANDERS] " << message << ecl::reset << std::endl;
  }

  void logCustomError(const std::string& message) {
    std::cout << ecl::red << "[ERROR_WITH_COLANDERS] " << message << ecl::reset << std::endl;
  }

private:
  kobuki::Kobuki kobuki;
  ecl::Slot<const std::string&> slot_debug, slot_info, slot_warning, slot_error;
};

int main(int argc, char **argv)
{
  ecl::CmdLine cmd_line("logging", ' ', "0.3");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  cmd_line.add(device_port);
  cmd_line.parse(argc, argv);

  std::cout << ecl::bold << "\nLogging Demo\n" << ecl::reset << std::endl;

  KobukiManager kobuki_manager(device_port.getValue());
  ecl::Sleep()(5);
  return 0;
}

Output - Custom Loggers

_images/demo_custom_logging.png

Debugging the Stream

About

If you’re having troubles with your connection and need to debug the raw data stream, tune into the /kobuki/raw_data_stream signal.

Code

#include <iostream>
#include <string>
#include <ecl/console.hpp>
#include <ecl/sigslots.hpp>
#include <ecl/time.hpp>
#include <ecl/command_line.hpp>
#include <kobuki_core/kobuki.hpp>

class KobukiManager
{
public:
  KobukiManager(const std::string &device) :
    slot_raw_data_stream(&KobukiManager::logRawDataStream, *this)
  {
    kobuki::Parameters parameters;

    parameters.device_port = device;

    slot_raw_data_stream.connect(parameters.sigslots_namespace + "/raw_data_stream");

    try {
      kobuki.init(parameters);
    } catch (ecl::StandardException &e) {
      std::cout << e.what();
    }
  }

  void logRawDataStream(kobuki::PacketFinder::BufferType& buffer) {
    std::ostringstream ostream;
    ostream << ecl::cyan << "[" << ecl::TimeStamp() << "] " << ecl::yellow;
    ostream << std::setfill('0') << std::uppercase;
    for (unsigned int i = 0; i < buffer.size(); i++) {
      ostream << std::hex << std::setw(2) << static_cast<unsigned int>(buffer[i]) << " " << std::dec;
    }
    ostream << ecl::reset;
    std::cout << ostream.str() << std::endl;
  }

private:
  kobuki::Kobuki kobuki;
  ecl::Slot<kobuki::PacketFinder::BufferType&> slot_raw_data_stream;
};

int main(int argc, char **argv)
{
  ecl::CmdLine cmd_line("raw_data_stream", ' ', "0.3");
  ecl::ValueArg<std::string> device_port(
      "p", "port",
      "Path to device file of serial port to open",
      false,
      "/dev/kobuki",
      "string"
  );
  cmd_line.add(device_port);
  cmd_line.parse(argc, argv);

  std::cout << ecl::bold << "\nRaw Data Stream Demo\n" << ecl::reset << std::endl;

  KobukiManager kobuki_manager(device_port.getValue());
  ecl::Sleep()(5);
  return 0;
}

Output

_images/demo_raw_data_stream.png

Anatomy

Views

Top

_images/top_view.jpg

Bottom

_images/bottom_view.jpg

Control Panel

_images/control_panel.jpg
  • 19V/2A: Laptop power supply
  • 12V/5A: Arm power supply
  • 12v/1.5A: Microsoft Kinect power supply
  • 5V/1A: General power supply
  • Status LED: Indicates Kobuki’s status
  • Green: Kobuki is turned on and battery at high voltage level
  • Orange: On - Low battery voltage level (please charge soon)
  • Green blinking: On - Battery charging
  • Off: Kobuki is turned off.
  • LED1/2: Programmable LEDs
  • USB: Data connection
  • BO/1/2: Buttons
  • Firmware switch: Enable/disables the firmware update mode

Connectors

Note

SOME NOTES ABOUT THE MOLEX PAGES BELOW

  1. We do not actually use Molex connectors but we are supplied by a Korean vendor who produces connectors according to the Molex standard. These links will be more useful to internationals in helping them find a mating part that works for them.
  2. The images on each page are representative of the series of connectors. Each series usually has a variety of connectors with a different number of pins. As a result, the pictures on some of the pages below may seem as though they have the incorrect number of pins, but do not worry about this – they are the correct links. Note that you can jump to different connectors in the series via the second part of their identification number (e.g. 43045-0224 for the 2-pin, 43045-0424 for the 4-pin).
  3. If some linked connectors are listed as obsolete on the molex website, don’t worry. The connector you are exactly requiring are those you can find under the ‘Mates with Parts’ link on each page. If these however should become obsolete as well, please let us know via email.

Power

_images/power_panel.jpg
  • 5V@1A Molex PN : 43650-0218 – for custom embedded boards (e.g. Arduino, Odroid)
  • 12V@1.5A : Molex PN : 43045-0224 – for depth sensors (originally designed for Kinect/Asus sensors)
  • 12V@5A : Molex PN : 3929-9023 – for high powered accessories (e.g. robotic arm)
  • 19V@2A : Molex PN : 3928-9068 – for recharging netbooks (with a modified adapter)

Battery

I/O Port

DB25 pin D-SUB Female connector that provides the following functionality (pdf)

Cables

Note

If you click on the preceding links for the power connectors, under the heading ‘Mates with Part(s)’ you can find the compatible connector to use with each power source. The most important one being of course:

Models & Drawings

The models and drawings include both the base and parts for the Turtlebot 2.

The inserts in the kobuki plate are M4 threads (metric, 4mm). If you wish to build standoffs compatible for these inserts, please reference the pole pdf’s in the 2D mechanical drawings which are what we use for turtlebots.

Motors

Specifications

  • Brushed DC Motor
  • Motor Manufacturer: Standard Motor
  • Part Name: RP385-ST-2060
  • Rated Voltage: 12 V
  • Rated Load: 5 mN·m
  • No Load Current: 210 mA
  • No Load Speed: 9960 rpm ± 15%
  • Rated Load Current: 750 mA
  • Rated Load Speed: 8800 rpm ± 15%
  • Armature Resistance: 1.5506 Ω at 25°C
  • Armature Inductance: 1.51 mH
  • Torque Constant(Kt): 10.913 mN·m/A
  • Velocity Constant(Kv): 830 rpm/V
  • Stall Current: 6.1 A
  • Stall Torque: 33 mN·m

Control Method

  • Driven by voltage source(H-bridge)
  • Controlled by Pulse-width modulation(PWM)

Gyro

Specifications

  • 3-Axis Digital Gyroscope
  • Manufacturer : STMicroelectronics
  • Part Name : L3G4200D
  • Measurement Range: ±250 deg/s
  • Yaw axis is factory calibrated within the range of ±20 deg/s to ±100 deg/s

Performance

In-Place Rotation Test

This graph shows the average heading error per revolution of gyro, when robot rotates with a given velocity.

_images/gyro_in_place_rotation.jpg
Square Path Test

This graph shows the position error of fused odometry with gyro, when robot moves along a square path. Robot moved with 0.1 m/s on the line segment and rotated with 30 deg/s on the corner.

_images/gyro_square_test.jpg

This table shows the calculated angular error, when robot arrived at the diagonally opposite corner from the starting point (0.0, 0.0).

Number of turns of square path Angular Error [deg]
0.5 0.47
1.5 1.99
2.5 3.18

Power Adapter

Specifications

Input Output
Voltage: 100-240V Voltage: 19V
Ampere: 1.5A Max Ampere: 3.16A
Frequency: 50/60Hz Ampere: 3.16A

Batteries

Kobuki by the default ships with a small Lithium-Ion battery pack (4S1P, 2200mAh, 14.8V).

_images/battery_pack_small_4S1P.jpg

Tip

For extra long operation, a big battery pack (4S2P, 4400mAh, 14,8V) can be ordered as well.

_images/battery_pack_big_4S2P.jpg

Warning

The electronics does not support the use of multiple battery packs at the same time (even if there is room in the battery compartment).

Pinouts

  • Red : battery (+), 9.6 V ~ 16.8 V
  • White: NTC thermistor to ground, 10 kΩ ± 1%
  • Black: battery(-), Ground

Charging Profile

This plot shows the voltages as measured by the robot’s hardware. Both the standard 4S1P and the extra 4S2P batteries are compared. During the test, the robot was charging via adaptor.

_images/battery_charging_profile.jpg

Discharging Profile

This plot shows the voltage as measured by the robot’s hardware. Both the standard 4S1P and the extra 4S2P batteries are compared. During the test, the robot was continuously spinning, with the Kinect camera running.

_images/battery_discharging_profile.jpg

Expansion Port

Pictured below are the pinouts of Kobuki’s expansion port, including the serial pins. The minimum number of required pins for serial communication is three; TX, RX, and GND. Additionally EX3.3 or EX5 can be used for powering external devices, such as line transceiver.

_images/serial_port.jpg
  • RX / TX: Serial data connection (RS232; used voltage level is 3.3V!)
  • EX3.3 / EX5: 3.3V/1A and 5V/1A power supply
  • DI0 - 3: 4 x Digital input (high: 3.3 - 5V, low: 0V)
  • DO0 - 3: 4 x Digital output (open-drain, pull-up resistor required)
  • AI0-3: 4 x Analog input (12bit ADC: 0 - 4095, 0 - 3.3V)
  • GND: Ground
  • EN: Used for detecting an external board (connect to external ground)

Conversions

Encoder2Pose

Here are the necessary parameters and calcualations for conversion of encoder ticks to robot pose.

  Name Value Description
Robot Parameters wheelbase (bias) 230mm length between the centre of the wheels
  wheel radius 35mm  
  wheel width 21mm  
Magnetic Encoder ticks per revolution 52 tick/rev  
  pulses per revolution 13 pulse/rev  
Gear Box 1st stage 1:10  
  2nd stage 22:12  
  3rd stage 30:11  
  4th stage 35:12  
  5th stage 34:1  
  resultant ratio 6545/132 = 49.5833 6545 turns of motors(or encoders) will make 132 turns of wheels
Conversions ticks to metres 0.000085292090497737556558 m/tick  
  ticks to radians 0.002436916871363930187454 rad/tick  
  metres to ticks 11724.41658029856624751591 tick/m  
  radian to ticks 11.72441658029856624751591 tick/mm  

Serial Protocol

About

A software program communicates with the robot by using predefined protocol on the serial or usb-serial lines. The provided c++ library, libkobuki.so does this for you, so in most cases, understanding the serial protocol is not necessary. This section is for implementers of libraries attempting to communciate with the Kobuki via either a different language (e.g. java) or their own custom c++ implementation.

In general, commands are sent to the robot on the RX line and responses / sensor readings are streamed back on the TX line at a rate of 20ms.

Data Types

Types

Data fields used in commands or payloads can be in the form of one of the three data types specified below:

Name | Description Bytes Bits Range C/C++ Identifier
Unsigned Byte 8-bit unsigned int 1 8 0~255 unsigned char uint8_t
Unsigned Short 16-bit unsigned int 2 16 0~65,535 unsigned short uint16_t
Unsigned Int 32-bit unsigned int 4 32 0~4,294,967,295 unsigned int uint32_t

Ordering

Data for the multi-byte types are in LSB order. This means the least significant byte will come first in the bytestream, for example, the integer 2,864,434,397 (0xAABBCCDD) will be represented in the bytestream as:

0xDD 0xCC 0xBB 0xAA

The ByteStream

Structure

The returning stream consists of packets that combine both sensor data and responses to requests that have been sent in the previous cycle. A bytestream can be divided into 4 fields: Headers, Length, Payload and Checksum.

Headers Length Payload Checksum
Header0 Header1 SubPayload0 SubPayloadN-1
Name Header 0 Header 1 Length Payload Checksum
Size 1 Byte 1 Byte 1 Byte N Bytes 1 Byte
Description 0xAA 0x55 Payload size See below XOR (length + payload)

Headers

Two bytes of headers, header 0 and header 1, are of fixed value for both bytestreams, commands and feedback data. This headers are used to detect the starting point of bytestream.

Length

Length is a single byte that indicates size of the variable payload (in bytes). Length can be used to distinguish each bytestreams. Minimum value of this field is 3.

Payload

The payload is where the gold (actual data) is!

A payload is actually representative of several sub-payloads stitched together.

Payload
SubPayload0 SubPayload1 SubPayload2 SubPayload N-1

Sub-payloads can be divided into three parts; Header, Length and Data:

Name Header Length Data
Size 1 Byte 1 Byte N Byte(s)
Description Identifier Size of data See below

Checksum

The checksum is the XOR’ed value of the entire bytestream sans the headers. This is used as a check to ensure the integrity of the contents of the bytestream since individual bytes can be easily corrupted on the wire.

A c++ code snippet demonstrating the algorithm used:

unsigned int packet_size(buffer.size());
unsigned char cs(0);
for (unsigned int i = 2; i < packet_size; i++)
{
  cs ^= buffer[i];
}
return cs ? false : true;

Command Packets

Command Identifiers

ID Name Description
1 Base Control Control wheel motors
2 Reserved  
3 Sound Play custom sounds
4 Sound Sequence Play predefined sound sequences
5 Reserved  
6 Reserved  
7 Reserved  
8 Reserved  
9 Request Extra Request extra information
10 Reserved  
11 Reserved  
12 General Purpose Output Control general purpose outputs
13 Set Controller Gain Set PID gain of wheel velocity controller
14 Get Controller Gain Get PID gain of wheel velocity controller

Base Control

Control wheel motors to moving robot. Robot will follow the arc line, which radius is <Radius> mm, with <Speed> mm/s. Positive Radius indicates center of arc line that robot follows is located at the left side of the robot. Negative is opposite.

_images/velocity_representation.png

But actual value of speed field is little bit different. Here is conversion table.

Motion Speed(mm/s) Radius(mm)
Pure Translation Speed 0
Pure Rotation w*b / 2 1
Translation + Rotation Speed * (Radius + b) / 2 ) / Radius, if Radius > 1 Radius
  Speed * (Radius - b / 2 ) / Radius, if Radius < -1 Radius
  • w is rotation speed of the robot, in [rad/s].
  • b is bias or wheelbase, that indicates the length between the center of the wheels.
  Name Size Value Hex Description
Header Identifier 1 1 0x01 Fixed
Length Size of data field 1 4 0x04 Fixed
Data Speed 2     in mm/s
  Radius 2     in mm

Sound

Play custom sounds with note and duration.

  Name Size Value Hex Description
Header Identifier 1 3 0x03 Fixed
Length Size of data field 1 3 0x03 Fixed
Data Note 2     1 / (f*a), where f is the frequency (Hz), a is 0.00000275
  Duration 1     Duration of playing note in milli-seconds

Note

This command is implemented on the kobuki with firmware, but not implemented yet in the c++ library (kobuki_core).

Sound Sequence

Play predefined sounds by its index.

  Name Size Value Hex Description
Header Identifier 1 4 0x04 Fixed
Length Size of data field 1 1 0x01 Fixed
Data Sequence number 1     0 for ON sound
          1 for OFF sound
          2 for RECHARGE sound
          3 for BUTTON sound
          4 for ERROR sound
          5 for CLEANINGSTART sound
          6 for CLEANINGEND sound

Request Extra

Request extra data from robot. Especially version info of kobuki; Hardware Version, Firmware Version and Unique Device IDentifier(UDID)

UDID is unique to device. so can be used to identify on multiple robots.

  Name Size Value Hex Description
Header Identifier 1 9 0x09 Fixed
Length Size of data field 1 2 0x02 Fixed
Data Request flags 2     Set the flags to request extra data
          0x01 for Hardware Version
          0x02 for Firmware Version
          0x08 for Unique Device ID

General Purpose Output

This command has multiple roles. It controls LEDs, digital outputs and external powers.

  Name Size Value Hex Description
Header Identifier 1 12 0x0C Fixed
Length Size of data field 1 2 0x02 Fixed
Data Digital output flags 2       Set the flags to set high on output pins of expansion port
          0x0001 for digital output ch. 0
          0x0002 for digital output ch. 1
          0x0004 for digital output ch. 2
          0x0008 for digital output ch. 3
           
          Set the flags to turn on external powers
          0x0010 for external power 3.3V ch.
          0x0020 for external power 5V ch.
          0x0040 for external power 12V/5A ch.
          0x0080 for external power 12V/1.5A ch.
           
          Set the flags to turn on LEDs
          0x0100 for red colour of LED1
          0x0200 for green colour of LED1
          0x0400 for red colour of LED2
          0x0800 for green colour of LED2

Set Controller Gain

Set PID gain of wheel velocity controller of robot.

  Name Size Value Hex Description
Header Identifier 1 1 0x01 Fixed
Length Size of data field 1 13 0x0D Fixed
Data Type 1     0 for factory-default PID gain
          1 for user-configured PID gain
  P gain 4     Kp * 1000 (default: 100*1000)
  I gain 4     Ki * 1000 (default: 0.1*1000)
  D gain 4     Kd * 1000 (default: 2*1000)

Get Controller Gain

Request PID gain of wheel velocity controller of robot.

  Name Size Value Hex Description
Header Identifier 1 1 0x01 Fixed
Length Size of data field 1 14 0x0E Fixed
Data unused 1      

Feedback Packets

Feedback Identifiers

ID Name Descritpion Availability
1 Basic Sensor Data Basic core sensor data By default
2 Reserved    
3 Docking IR Signals from docking station By default
4 Inertial Sensor Gyro data both angle and angular velocity By default
5 Cliff PSD data facing floor By default
6 Current Current of wheel motors By default
7 Reserved    
8 Reserved    
9 Reserved    
10 Hardware Version Version number of kobuki hardware On request
11 Firmware Version Version number of kobuki firmware On request
12 Reserved    
13 Raw data of 3-axis gyro Raw ADC data of digital 3-axis gyro By default
14 Reserved    
15 Reserved    
16 General Purpose Input Inputs from 25-pin expansion port By default
17 Reserved    
18 Reserved    
19 Unique Device IDentifier(UDID) Unique number to identify robot On request
20 Reserved    
21 Controller Info PID gain values of wheel velocity controller On request

Basic Sensor Data

Note

This sub-payload is always streamed.

  Name Size Value Hex Description
Header Feedback Identifier 1 1 0x01 Fixed
Length Size of data field 1 15 0x0F Fixed
Data Timestamp 2     Timestamp generated internally in milliseconds
          It circulates from 0 to 65535
  Bumper 1     Flag will be setted when bumper is pressed
          0x01 for right bumper
          0x02 for central bumper
          0x04 for left bumper
  Wheel drop 1     Flag will be setted when wheel is dropped
          0x01 for right wheel
          0x02 for left wheel
  Cliff 1     Flag will be setted when cliff is detected
          0x01 for right cliff sensor
          0x02 for central cliff sensor
          0x04 for left cliff sensor
  Left encoder 2     Accumulated encoder data of left and right wheels in ticks
          Increments of this value means forward direction
          It circulates from 0 to 65535
  Right encoder 2     As above
  Left PWM 1     PWM value that applied to left and right wheel motor
          This data should be converted signed type to represent correctly
          Negative sign indicates backward direction
  Right PWM 1     As above
  Button 1     Flag will be setted when button is pressed
          0x01 for Button 0
          0x02 for Button 1
          0x04 for Button 2
  Charger 1     0 for DISCHARGING state
          2 for DOCKING_CHARGED state
          6 for DOCKING_CHARGING state
          18 for ADAPTER_CHARGED state
          22 for ADAPTER_CHARGING state
  Battery 1     Voltage of battery in 0.1 V
          Typically 16.7 V when fully charged
  Overcurrent flags 1     Flag will be setted when overcurrent is detected
          0x01 for left wheel
          0x02 for right wheel

Docking IR

Signals from the docking station.

  Name Size Value Hex Description
Header Identifier 1 3 0x03 Fixed
Length Size of data field 1 3 0x03 Fixed
Data Right signal 1     Flag will be setted when signal is detected
          0x01 for NEAR_LEFT state
          0x02 for NEAR_CENTER state
          0x04 for NEAR_RIGHT state
          0x08 for FAR_CENTER state
          ox10 for FAR_LEFT state
          0x20 for FAR_RIGHT state
  Central signal 1      
  Left signal 1      

Kobuki’s docking station has 3 IR emitters. The emitted IR lights cover three regions in front of the docking station: left, central and right, each divided in two sub-fields: near and far. Each beam encodes this information, so the robot knows at any moment in which region and sub-field he is. Also, as regions and fields are independently identified, they can be overlap on its borders.

_images/dock_ir_fields.png

Inertial Sensor Data

Note

This sub-payload is always streamed.

Z-axis gyro data only available.

  Name Size Value Hex Description
Header Identifier 1 4 0x04 Fixed
Length Size of data field 1 7 0x07 Fixed
Data Angle 2     Factory calibrated
  Angle rate 2     Factory calibrated
  Unused 1      
  Unused 1      
  Unused 1      

Cliff Sensor Data

Note

This sub-payload is always streamed.

This value is related with distance between sensor and floor surface. See the datasheet for more detailed information.

  Name Size Value Hex Description
Header Identifier 1 5 0x05 Fixed
Length Size of data field 1 6 0x06 Fixed  
Data Right cliff sensor 2     ADC output of each PSD
          Data range: 0 ~ 4095 (0 ~ 3.3V)
          Distance range: 2 ~ 15 cm
          Distance is not linear w.r.t. ADC output.
          See the datasheet for more detail.
  Central cliff sensor 2     As above
  Left cliff sensor 2     As above

Current

Note

This sub-payload is always streamed.

Current sensor readings of wheel motors.

  Name Size Value Hex Description
Header Identifier 1 6 0x06 Fixed
Length Size of data field 1 2 0x02 Fixed
Data Left motor 2     in 10mA
  Right motor 2     in 10mA

Hardware Version

Note

This sub-payload is sent only on request.

Hardware version info in triplet form; <major>.<minor>.<patch>

  Name Size Value Hex Description
Header Identifier 1 10 0x0A Fixed
Length Size of data field 1 4 0x04 Fixed
Data Patch 1      
  Minor 1        
  Major 1        
  Unused 1 0 0x00 Fixed

Firmware Version

Note

This sub-payload is sent only on request.

Firmware version info in triplet form; <major>.<minor>.<patch>

  Name Size Value Hex Description
Header Identifier 1 11 0x0A Fixed
Length Size of data field 1 4 0x04 Fixed
Data Patch 1      
  Minor 1        
  Major 1        
  Unused 1 0 0x00 Fixed

Raw Data Of 3D Gyro

Note

This sub-payload is always streamed.

Raw ADC data of digital 3D gyro L3G4200D. Due to difference of acquisition rate and update rate, 2-3 data will be arrived at once. Digit to deg/s ratio is 0.00875, it comes from datasheet of 3d gyro.

ADC output of each-axis is in 0.00875 deg/s.

  Name Size Value Hex Description
Header Identifier 1 13 0x0D Fixed
Length Size of data field 1 2+6N    
Data Frame id 1     Frame id of ‘Raw gyro data 0’
          Every sensor readings can identified by frame id
          Circulates from 0 to 255
  Followed data length 1 3N    
  Raw gyro data 0 2     x-axis
    2     y-axis
    2     z-axis
  2     z-axis
  Raw gyro data N-1 2      
    2      
    2      

Note

Sensing axis of 3d gyro is not match with robot. It is rotated 90 degree counterclockwise about z-axis. So, below conversion will needed.

const double digit_to_dps = 0.00875;
angular_velocity.x =  -digit_to_dps * (short)raw_gyro_data.y;
angular_velocity.y =   digit_to_dps * (short)raw_gyro_data.x;
angular_velocity.z =   digit_to_dps * (short)raw_gyro_data.z;

General Purpose Input

Note

This sub-payload is always streamed.

  Name Size Value Hex Description
Header Identifier 1 16 0x10 Fixed
Length Size of data field 1 16 0x10 Fixed
Data Digital input 2     Flag will be setted, when high voltage is applied
          0x01 for digital input ch. 0
          0x02 for digital input ch. 1
          0x04 for digital input ch. 2
          0x08 for input output ch. 3
  Analog input ch.0 2     12-bit ADC output of each channel
          Data range: 0 ~ 4095(2^12-1)
          Voltage range: 0 ~ 3.3 V
  Analog input ch.1 2     As above
  Analog input ch.2 2     As above
  Analog input ch.3 2     As above
  Unused 2      
  Unused 2      
  Unused 2      

Unique Device IDentifier (UDID)

Note

This sub-payload is sent only on request.

Contains Unique Device IDentifier of robot. This value is unique for all robot in the world. It can be represented by triplet form: <UDID0>-<UDID1>-<UDID2>

  Name Size Value Hex Description
Header Identifier 1 19 0x13 Fixed
Length Size of data field 1 12 0x0C Fixed
Data UDID0 4      
  UDID1 4      
  UDID2 4      

Controller Info

Note

This sub-payload is sent only on request.

Contains PID gain of wheel velocity controller of robot.

  Name Size Value Hex Description
Header Identifier 1 1 0x01 Fixed
Length Size of data field 1 21 0x15 Fixed
Data Type 1     Current controller setup
          0 for factory-default PID gain
          1 for user-configured PID gain
  P gain 4     Kp * 1000 (default: 100*1000)
  I gain 4     Ki * 1000 (default: 0.1*1000)
  D gain 4     Kd * 1000 (default: 2*1000)

Firmware

Versioning

Firmware versions follow semantic versioning rules. The c++ driver checks for compatibility between the software (i.e. driver) and firmware. Firmware versions are of the form M.m.p:

  • M(ajor) versions typically break protocol compatibility. When software and firmware are incompatible, the software will emit an error, suggest the required update and shutdown.
  • m(inor) versions add features, but the protocol will have not been modified. Software and firmware will inter-operate, but warnings will be issued just-in-time when features are used that aren’t supported by the connected firmware.
  • p(atch) versions provide minor bugfixes, but do not break driver or protocol compatibility.

Additionally, the software maintains a list of recommended versions. Even if there is only a minor or patch version difference, it will give you a warning on connection and suggest the recommended firmware version to upgrade to. For example:

$ kobuki-simple-keyop

Simple Keyop : Utility for driving kobuki by keyboard.

Reading from keyboard
---------------------
Forward/back arrows : linear velocity incr/decr.
Right/left arrows : angular velocity incr/decr.
Spacebar : reset linear/angular velocities.
q : quit.

[WARNING] The firmware does not match any of the recommended versions for this software.
[WARNING] Consider replacing the firmware. For more information,
[WARNING] refer to https://kobuki.readthedocs.io/en/devel/firmware.html.
[WARNING]  - Firmware Version: 1.1.3
[WARNING]  - Recommended Versions: 1.1.4 / 1.2.0

current pose: [x: 5.61871e-310, y: 1.57358e-314, heading: 6.90938e-310]
current pose: [x: 5.61871e-310, y: 1.57358e-314, heading: 6.90938e-310]

The c++ driver provides a utility for checking the version that is running on your kobuki. It will also provide versioning information for the driver (software) and hardware:

$ kobuki-version-info
  Version Info:
   * Hardware Version: 1.0.4
   * Firmware Version: 1.2.0
   * Software Version: 1.0.0
   * Unique Device ID: 97713968-842422349-1361404194

Additionally, firmware binaries come in three flavours:

  • latest: most recent, but be aware that this version hasn’t been tested much
  • stable: more recent than factory and reasonably well tested
  • factory: flashed onto the robots at the factory, has undergone stress testing

These are identified by the trailing suffix on binary filenames stored in the kobuki_firmware repository. More details on the specific features / fixes provided by each version can be found in the kobuki firmware CHANGELOG.

Updating Firmware

Kobuki’s come pre-flashed from the factory. The only time you should need to upgrade is if you have an older version and wish to catch new fixes or features.

Linux

The Flashing Utility
# Download stm32flash-0.4.gz from https://sourceforge.net/projects/stm32flash/files/
$ tar -xvzf stm32flash-0.4.tar.gz
$ cd stm32flash
$ make
Download Firmware
# Choose & download from https://github.com/kobuki-base/kobuki_firmware/tree/devel/firmware
# e.g. latest
$ wget --no-check-certificate --content-disposition https://github.com/kobuki-base/kobuki_firmware/blob/devel/firmware/kobuki_firmware_1.2.0-latest.hex?raw=true
Identify The COM Port

If you have a udev rule installed, it will show up as /dev/kobuki. If not, you can typically find it under one of the ttyUSB ports, e.g. /dev/ttyUSB0. If you are not sure, type dmesg into a terminal, unplug and replug the robot and type dmesg again. You should now be able to see which port is assigned to the robot.

Switch to Download Mode
  1. Connect the robot to your PC using the USB cable
  2. Turn off the robot (switch on the side)
  3. Switch from normal runtime mode to firmware download mode

This simply changes the type of data that is sent back and forth along the usb connection. You can do this by moving the switch illustrated below into the ‘download’ (up) position. Note that this switch is embedded into the robot cover so it isn’t easily thrown by accident - you may need thin plyers or some similar tool. You can find the mode switch mechanism on the right side of the control panel:

_images/modes.jpg
Flashing

Note

The following instructions assume flashing of kobuki_firmware_1.2.0-latest.hex and port /dev/ttyUSB0. Modify these as necessary.

Warning

you need to execute the flashing command IMMEDIATELY after turning the robot on!

  1. Turn off the robot
  2. Check that the switch is in download mode
  3. Turn on the robot
$ ./stm32flash -b 115200 -w kobuki_firmware_1.2.0-latest.hex /dev/ttyUSB0
  stm32flash 0.4

  http://stm32flash.googlecode.com/

  Using Parser : Intel HEX
  Interface serial_posix: 115200 8E1
  Version      : 0x22
  Option 1     : 0x00
  Option 2     : 0x00
  Device ID    : 0x0414 (High-density)
  - RAM        : 64KiB  (512b reserved by bootloader)
  - Flash      : 512KiB (sector size: 2x2048)
  - Option RAM : 16b
  - System RAM : 2KiB
  Write to memory
  Erasing memory
  Wrote address 0x0800a3f0 (100.00%) Done.
Reboot
  • Turn off the robot power
  • Flick the firmware switch back to ‘Operation’ mode.
  • Turn on the robot power
  • I’m happy, you should be too!

Windows

The Flashing Utility
  • Find, download and install Flash_Loader_Demonstrator_v2.5.0_Setup.exe.
Download Firmware

Choose & download from kobuki_firmware/firmware.

Identify the COM Port

Usually this will show up on COM1, but check to make sure.

Switch to Download Mode
  1. Connect the robot to your PC using the USB cable
  2. Turn off the robot (switch on the side)
  3. Switch from normal runtime mode to firmware download mode

This simply changes the type of data that is sent back and forth along the usb connection. You can do this by moving the switch illustrated below into the ‘download’ (up) position. Note that this switch is embedded into the robot cover so it isn’t easily thrown by accident - you may need thin plyers or some similar tool. You can find the mode switch mechanism on the right side of the control panel - see the image below.

Flashing
  1. Turn off the robot
  2. Check that the switch is in download mode
  3. Turn on the robot
flash1 flash2
Configure Properties Check that the target is identified
flash3 flash4
Enter the Download from file (your .hex) Success!
Rebooting
  • Turn off the robot power
  • Flick the firmware switch back to ‘Operation’ mode.
  • Turn on the robot power
  • I’m happy, you should be too!

Special Firmware Modes

Activating

Kobuki has some special firmware modes, which can be activated on startup.

  • Random Walker
  • Arduino/Embedded Board support mode

To activate one of them, follow these instructions:

  • Turn on Kobuki.
  • Within in the first 3 seconds press and hold either button BO (Random Walker) or B1 (Arduino) for 2 seconds
  • If you see LED2 (Random Walker) or LED1 (Arduino) switching between red and green, your chosen mode has been activated.

Note

These modes have been introduced to the firmware with version 1.1.0. In case your Kobuki is not running this or a later version, please refer to the section about updating the firmware.

Random Walker Mode

In random walker mode Kobuki is driving around until it hits an object with the bumper or a cliff is detected. In both cases, Kobuki will stop, turn by a random amount of degrees and continue driving .

Warning

In this mode Kobuki’s wheel drop sensors are not activated. So, be careful when lifting up Kobuki!

Arduino / Embedded Board Support Mode

In this mode the serial port (DB25 connector) gives access to basic controls of Kobuki. You can hook up the digital/analog inputs/outpus of your Arduino or other embedded boards and start writing simple control programs.

Below is the special pin setting listed. Please refer to the serial port description for the name to pin mapping.

  • DI0: Not used
  • DI1: Not used
  • DI2: Not used
  • DI3: Not used
  • DO0: Bumper left (pressed/released)
  • DO1: Bumper centre (pressed/released)
  • DO2: Bumper right (pressed/released)
  • DO3: Wheel drop sensors (at least one wheel is dropped / none is dropped)
  • AI0: Wheel speed right (0V - full speed backward, 3.3V - full speed forward)
  • AI1: Wheel speed left (0V - full speed backward, 3.3V - full speed forward)
  • AI2: Not used
  • AI3: Not used

All other pins (GND, RX, TX etc.) remain unchanged.

Note

To enable the motors you need to press button B0.

Docking Stations

About

_images/docking_station.jpg

Docking stations are an optional extra that can enable Kobuki to autonomously recharge as needed (with a little programming).

How it Works

Kobuki’s docking station has 3 IR emitters positioned on the left, right and centre of the docking station. Each emitter beams encoded signals in a manner that will ensure coverage of the area in front of the docking station partitioned in six areas - left, right and centre, further subdivided near and far as illustrated below.

_images/dock_ir_fields.png

Three receivers are also positioned left, right and centre on the docking station and will receive a mix of the signals hitherto sent from the emitters and bounced off any object in range of the docking station (i.e. an incoming Kobuki). If for example, Kobuki was still far from the docking station and in the left region, then both left and centre signals will receive the FAR_LEFT state and the right receiver will record null. This is sufficient to enable a simple docking algorithm to work robustly, even if the solution may not always be elegant due to a lack of resolution on the range axis (merely bifurcates, near and far).

Software

Todo

In need of someone owning a docking station to assist with usage of kobuki_dock_drive library and example demo

Embedded Boards

Cross Compiling

Getting Started

Kobuki is c++ and built using CMake, so to cross-compile, these instructions will take advantage of CMake Toolchains, configuration which of is dependent on the c++ toolchain being used to compile the libraries.

For a primer on CMake and how to define CMake toolchains, refer to the cmake manual - cmake toolchains.

Use Case - arm-linux-gnueabihf

Preparation

Let’s get hands on and use one of the c++ toolchains provided by Ubuntu 20.04 as an example.

Note

You are not limited by what your linux distro provides, pretty much any downloadable gcc toolchain can be enabled this one, just merely point your cmake toolchain configuration to wherever you have installed your toolchain.

Download a toolchain:

sudo apt install g++-arm-linux-gnueabihf

This is the generic toolchain for arm cores with hard-float capabilities (usually the more powerful variety of arm cores). You will find the toolchain installed in /usr/arm-linux-gnueabihf/. Next, create a cmake toolchain file that will instruct cmake on where to find your toolchain, your your staging area and set appropriate CXX Flags for your target:

set(TOOLCHAIN_TUPLE "arm-linux-gnueabihf" CACHE STRING "Toolchain signature identifying cpu-vendor-platform-clibrary.")
set(TOOLCHAIN_ROOT "/usr/${TOOLCHAIN_TUPLE}" CACHE STRING "Root of the target development environment (libraries, headers etc).")

# Target information
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR "arm")
unset(CMAKE_Fortran_COMPILER)  # This toolchain doesn't have a fortran compiler
set(CMAKE_C_COMPILER   ${TOOLCHAIN_TUPLE}-gcc) # Make sure these are in your PATH
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_TUPLE}-g++)

# Search paths - only dig around in the toolchain root and staging area
set(CMAKE_FIND_ROOT_PATH "${TOOLCHAIN_SYSROOT};${CMAKE_CURRENT_LIST_DIR}/install" CACHE STRING "Cmake search variable for finding libraries/headers.")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Don't search for programs outside of CMAKE_FIND_ROOT_PATH and CMAKE_SYSROOT
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)  # ... libraries
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)  # ... headers
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)  # ... cmake modules

# CXX Flags specific to the target platform (typical raspberry pi platform)
#
#  - benchmark yourself, mileage will vary considerably, large speedups to be gained
#  - a good starting point is https://wiki.gentoo.org/wiki/Safe_CFLAGS#ARMv6.2FARM1176JZF-S
#
# Also, -Wno-psabi avoids irritating gcc 7.1 warnings about not mixing binaries with gcc 6 binaries
#
set(CMAKE_CXX_FLAGS "-march=armv7 -mtune=arm1176jzf-s -pipe -mfloat-abi=hard -mfpu=vfp -Wno-psabi" CACHE STRING "flags specific for an armv7/arm1176jzf-s platform")

# Hide from cache's front page
MARK_AS_ADVANCED(CMAKE_GENERATOR CMAKE_FIND_ROOT_PATH CMAKE_TOOLCHAIN_FILE TOOLCHAIN_FAMILY TOOLCHAIN_TUPLE)

It can be named whatever you please, here we’ll refer to it as arm-linux-gnueabihf.cmake. In other circumstances, toolchain, staging area and cxx flags would be handled separately for maximum flexibility, but one file here keeps things simple to get started.

Building

Follow the instructions for setting up the sources as in Software - Preparation, but stop short of building, we’ll do that a little differently here. Namely:

  1. Configure your PATH so that your toolchain can be found
  2. Point cmake at your toolchain file

The modified instructions for building:

$ export PATH=${PATH}:/usr/arm-linux-gnueabihf/bin
$ export CMAKE_ARGS="-DBUILD_TESTING=OFF --no-warn-unused-cli"
$ export CROSS_COMPILE_ARGS=-DCMAKE_TOOLCHAIN_FILE=`pwd`/arm-linux-gnueabihf.cmake
$ colcon build --merge-install --cmake-args ${CMAKE_ARGS} ${CROSS_COMPILE_ARGS}

Other variations on the build step still hold as per the instructions in Software - Build.

These instructions are continuously vetted with a github action (yaml, results/logs).

Using The Serial Port (!USB)

If your embedded board has a serial port rather than a USB, you’re in luck, Kobuki has that too via it’s expansion port. You most likely will have to wire your own cable to make the correct pin-to-pin connections, as outlined in the section on the Expansion Port.

Reproducing here for convenience:

_images/serial_port.jpg

The minimum number of required pins for serial communication is three; TX, RX, and GND. Additionally EX3.3 or EX5 can be used for powering external devices, such as line transceiver.

Once connected, you should find your kobuki on one of the /dev/ttySN ports (N = 1, 2, …). Simply pass that string as the serial port identifier in the initialisation phase of your software applications.

Non-C++ Kobuki

_images/languages.png

The only requirement to programming with Kobuki in a language other than C++ is the ability to communicate with a serial port. To do so, you’ll need to implement the Serial Protocol in the language of your choice. The c++ library can be a useful guide in how to do so.

To date there have been several experimental java/android implementations that have made this journey.

Changelog

Note

This is a curated list of changes for all repositories in the kobuki ecosystem (to which this documentation pertains). See also:

[kobuki_core/CHANGELOG.rst] [kobuki_firmware/CHANGELOG.rst]

September ‘20

  • [kobuki_core-1.3.1]
    • configurable stdout logging
    • custom_logging and raw_data_stream demos added
    • dual version firmware compatibility (1.1.4, 1.2.0)
  • [kobuki_core-1.3.0] LegacyPose2D -> Eigen::Vector3d
  • [kobuki_core-0.7.10] dual version firmware compatibility (1.1.4, 1.2.0)
  • [kobuki_documentation-1.0.2] debugging tutorials (logging and raw data streams)

August ‘20

  • [kobuki_core-1.2.0] kobuki_driver & kobuki_dock_driver merged into kobuki_core
  • [kobuki_core-1.1.1] (bugfix) restore low latency usb reads via the udev rule and doxygen revamp
  • [kobuki_core-0.7.9] (bugfix) restore low latency usb reads via the udev rule
  • [kobuki_documentation-1.0.1] cross-compiling instructions
  • [kobuki_documentation-1.0.0] new guide on readthedocs, everything in one place now!

Mar ‘20

  • [kobuki_firmware-1.2.0] new github repo for the kobuki firmware binaries, with license

Jan ‘20

  • [kobuki_core-1.0.0]
    • moved to the kobuki-base github org
    • ported to the colcon build system

Glossary

kobuki
kobuki: n. korean for turtle
fsm
flying spaghetti monster

Whilst a serious religous entity in his own right (see pastafarianism), it’s also very easy to imagine your code become a spiritual flying spaghetti monster if left unchecked:

  _  _(o)_(o)_  _
._\`:_ F S M _:' \_,
    / (`---'\ `-.
 ,-`  _)    (_,