Machinekit is an open source motion control software project. The aim of the project is to simplify the creation of motion control systems, be it CNCs, special machines or robots.
ROS, the robot operation system, is an open source software project that aims to make developing robots easier. Besides being a networking middleware, ROS comes with a huge amount of tools and packages provided by the community. In short, ROS is the de-facto standard for open source robotics.
The connecting point between ROS and Machinekit is where the software meets the hardware. ROS is an excellent tool for creating complex
high-level robotic applications, however, it lacks a standard interface to low-level hardware. I argue that this might always be the case since it's simply not the aim of the project to provide these capabilities.
This is where Machinekit comes into play. Hidden under the covers of the Machinekit software project is a true gem, the HAL module.
HAL is short for Hardware Abstraction Layer, which is, in my opinion, a big understatement since it's much more than just hardware abstraction.
HAL provides a clear and concise structure for creating real-time software components and hardware drivers. The most amazing thing about this concept is that Machinekit comes with an abstraction to the underlying real-time API called RTAPI. This means that you can run the components on a development machine without RT kernel or on the target system with PREEMPT_RT or Xenomai RT kernels without requiring any modifications.
Additionally, HAL provides a configuration layer that enables you to interconnect the software components with each other. The configuration
layer features pins, signals, threads, functions and, components. In short, HAL components can provide functions, which are called by
threads, threads are executed periodically, signals connect pins of HAL components which each other. In a more abstract way, it can be also be thought of like an electrical circuit.
You may wonder how ROS users previously controlled robot hardware. From my research, I found that there are following options to control your robots motors, sensors, and other electronics via ROS.
ROS node and OS drivers
This is probably the most convenient form of controlling hardware with ROS. If device drivers for your OS of choice already exist for your hardware, it's just a matter of writing a Python or C++ ROS node to use the OS APIs to access the driver.
The big downside of this approach is that this requires either generic or special purpose drivers for your hardware. For custom electronics, this could mean writing a device driver for the OS, for Linux this means digging deep into system and kernel development.
On common hardware platforms such as the Raspberry Pi or the BeagleBone Black, you are often even able to access GPIO pins or device peripherals such as PWM generators via device tree drivers. Although, this approach is easy and allows scripting, be aware that this comes with major performance drawbacks, depending on the application and interface.
Generally speaking, the closer you get to the hardware, the harder it is to implement the functionality on OS level. And this leads me to the next very common approach for interfacing hardware with ROS.
rosserial and external hardware
Since it's difficult to implement functionality such as step generation for stepper motors or PID loops on a high-level operating system, it sounds like a good idea to outsource this function to external hardware. This may include microcontroller based platforms such as the Arduino or Mbed.
rosserial is a protocol that serializes ROS messages into a binary format suitable for transfer over a serial interface. This can be for example a serial connection via USB, RS-232 or with the right hardware RS-485. Since we have serialized byte stream, we could send it over almost every protocol or interface that can handle generic data, including for example a ZigBee or a Bluetooth connection.
The advantages of this approach include that that microcontroller hardware is extremely cheap and also approachable (e.g. Arduino).
A huge downside of this approach is that microcontroller based platforms are usually less suitable for highly computation intensive
tasks. This includes CV or kinematics for example. Of course, these tasks can be easily done on an embedded computer and result can be sent back to the microcontroller hardware. However, this comes with the big drawback of communication overheat and most importantly the introduction of unnecessary jitter. For motion control applications this could be the difference between a well-performing application or one with poor performance
In some cases, the serial connection might also be a bottleneck. Size of packets sent over the serial connection is limited and most importantly we have an upper limit to baudrate of the connection.
Additional drawbacks include that you require a computer and a microcontroller platform for the applications, which drives up the device and development cost.
External motion controller / Black Boxes
As roboticists and a ROS developer, you don't care about how the low-level hardware interface is implemented. At least that's what manufacturers selling proprietary motion controller will tell you.
I call this the external motion controller approach of integrating ROS with its environment. In this scenario, the ROS integrator accesses
the motion control hardware via high-level interfaces and commands. In some cases, manufacturers even offer ROS packages implementing common interfaces.
All the low-level details of how to control the motors, PID loops and sometimes even kinematics are handled by the external black box. This black box could be PLC or a robot controller provided by the industrial robot manufacturer.
Especially for professional applications, this setup is very attractive, since you don't need to deal with the integration of drivers, electronics and, interfaces between hardware and software.
The drawbacks of this approach include a very high price, unsuitable for low-cost products, vendor lock-in, since every motion or robot controller works differently, high risks of discontinuing support, especially when you are dealing with a startup, and limited possibilities tuning the parameters and performance.
This setup is probably the most common for ROS applications with industrial robots. Since you are mostly dealing with existing robot hardware that already features a robot motion controller.
ros_control is a generic robot controller interface for ROS. It provides a common interface to drive
a robot in terms of ROS topics and services. Moreover, it also aims to provide a streamlined hardware interface for ROS. This means if you want to write a new driver for your robot, the project provides some structure to do so.
Using ros_control it's possible to abstract the underlying robot hardware. If you want to support two robot hardware systems for example, you could write two ros_controllers implementing the drivers for each robot.
Besides providing a common interface to the robot hardware, it also provides support for transmission, enforcing joint limit and probably a lot more. Most importantly, the project is also real-time capable, which makes it suitable for high-performance (in terms of timing accuracy) systems.
* Baxter SSH
Why do we need yet another solution?
In the previous section, I explained the different approaches to interface hardware with ROS. Let's recapture really quick with a focus on controlling motors as the application.
Option 1, interfacing with the hardware via a ROS node and the OS device drivers is a good option for common hardware such as joysticks for example. On some platforms, such as the Raspberry Pi, for example, it even might give us access to the device peripherals.
In terms of controlling motors, the closest you will get to a reasonable solution is using the PWM peripherals of a single board
computer. Using PWM, you can control RC servos, but it won't help you controlling stepper motors to build closed-loop systems. In general, this option is most reasonable for educational and toy applications.
Option 2, includes offloading the low-level motor control commands to external hardware such as Arduino via rosserial. On the Arduino, we can implement step generators, closed-loop control loops and almost anything we can dream of in terms of motor control.
However, we have two potential downsides, which might relevant for your application or not. First of all, the Arduino and most other microcontroller platforms are less computationally powerful than their bigger counterparts. This can be a problem if your motion control application includes non-trivial kinematics for example.
The other downside of outsourcing every bit of the motor control hardware leads to a bottleneck in the command pipeline. Everything depends on the how much data you can get through the serial connection. Moreover, oftentimes the used serial bus is USB or RS-232 which is very easy to disturb.
Option 3, outsources everything to external motion control hardware. In comparison to the rosserial solution, the motion control hardware usually has a bigger understanding of the controlled environment, meaning we can work on a higher level with the black box. A robot motion controller, for example, might be able to handle commands such as a move to Cartesian position x,y,z with a velocity v and acceleration a.
The advantage of such a setup is that we don't need to worry about the motion control part of the application. Moreover, the external motion control hardware might provide additional features such as motion planning, that might handy in some situations.
The biggest downside of this scenario is the significant cost of such hardware and the potential danger of vendor lock-in. Moreover, the performance of the systems you can't control might be unsuitable for your application. E.g. some robot controllers allow a tight feedback loop of a few milliseconds, whereas others only operate on very high-level commands.
Option 4, using ros_control, is, in my opinion, the most interesting setup. It separates the hardware and low-level motor control from the
ROS application with a common interface, which allows you to break free of any kind of vendor lock-in. On the other hand, very few ready-to-use ros_controllers exist at the moment, so you might end up implementing your own controller.
Moreover, the performance of the system really depends on how you implement the controller. For example, you could combine ros_control with Option 3, a motion control box or you could access hardware directly.
So although ros_control provides a common interface to ROS, the hardware side still strongly depends on the implementation of the controller.
So do we need another solution? I argue yes. It's still very complex and hard thing to interface motor control hardware in a standardized and performant way at the moment.
Do we need a real-time system for good motion control? Well, not necessarily. In my opinion, the focus much too often on using a real-time system where none is necessary. However, the closer we get to the actual motor control hardware, the more interesting a real-time can be.
Let's say we want to implement a PID loop for our closed-loop motor control with encoder feedback. In this case, if we don't calculate and update the control value at a very exact point in time, we end up with bad motion control performance.
However, if the controlled hardware operates on a higher level, understands time and is synchronized with the control computer, then we don't necessarily need a real-time system to achieve good performance.
This is where Machinekit comes into play.
Machinekit and ROS
Machinekit HAL is designed to provide a generic solution to build motion control systems. It was originally intended to be used to wire together the motion planning features of LinuxCNC with the hardware.
Since homegrown CNC hardware can be very different, it was necessary to abstract, the way machine integrators configure control loops, hardware interface and, control logic.
The Machinekit project extended the concepts of LinuxCNC even further to single board computer platforms such as the BeagleBone Black or the
Raspberry Pi. This enables machine integrators to target both, a desktop PC with an external FPGA motion control card and also a BeagleBone with PRU-based controls independently.
I argue the same is true for robot hardware. We use many different hardware platforms, motors types and all sorts of custom hardware to control for example grippers.
This is exactly where Machinekit and ROS could work together.
ROS is really good at providing a common platform for high-level robot software components. However, for the most part, it lacks a common infrastructure to interface with hardware.
Machinekit, on the other hand, is really good for low-level motion control applications and it lacks the high-level applications.
ROS is great in motion planning, computer vision, managing the state of a robot and many more things.
Machinekit makes real-time motion control in hardware independent way a commodity.
Combining Machinekit and ROS could be the start of a new era in open source robotics down to motion control and hardware.
Integrating Machinekit and ROS
Now that we have talked about why Machinekit and ROS would be a good fit, it's time to elaborate on how they could be integrated with each other.
ROS node interacting with HAL
The probably most approachable and easiest way to interface with Machinekit and ROS is to create a ROS node that uses the Machinekit API.
As the Machinekit environment executes completely independent of ROS it's even possible to implement real-time motion control applications inside Machinekit HAL and to send input data for example via a Python ROS node.
This approach is excellent for simple applications, where you just want to control a motor position without timing for example.
jplan, ring buffers and, trajectory playout
This option is more interesting for robots that require a movement over time. This would be for example an industrial robot arm or the actuators of a service robot.
Motion planners, such as MoveIt! or Descartes play out a so-called
trajectory. A trajectory is nothing else than a set of waypoints over time. Meaning that the actuator needs to move between the waypoints in the specified time. A timestamp specifies when the actuator should be in which position.
In short, the playout system requires a way to interpolate between the target waypoints, moreover, it needs to be capable of some basic planning to reach the waypoints at the correct time.
For this purpose, we almost certainly need a real-time system to execute the command. This makes Machinekit very suitable for this application.
Machinekit comes with a component called jplan, short for joint planner. This component plays out commands from a HAL ringbuffer, this includes acceleration and velocity limits.
Using this component one can build a playout for ROS JointTrajectories. Create a ROS node, subscribe to JointTrajctory messages and feed the joint position commands into the HAL ringbuffer.
Bas uses this method to control the Matilda robot
ros_control has been mentioned previously in the section discussing the different approaches to control hardware ROS features at the moment.
As state earlier, ros_control is great, because it provides a standardized interface for robot controllers. However, one still needs to implement the controllers for each robot as there is no generic hardware abstraction.
Therefore, it seems only logical to combine Machinekit with ros_control to create a standardized and generic hardware interface. In this case, a new controller would be created to write and read from HAL during each cycle.
ros_control manages everything necessary to play out joint trajectories from ROS. Therefore, on the Machinekit side, we only need to connect the target joint positions and feedback values to the ros_control HAL component.
This way, the Machinekit interface would be generic for all sorts of robot hardware. The only thing that needs to be changed is the HAL configuration.
Since Machinekit comes with drivers for all sorts of different motion control hardware and platforms, this enables ROS integrators to design robot systems more quickly.
This approach is currently tested in a private project. If everything goes as planned, efforts may be made to publish the results.
ROS lacks a standardized way to interface with hardware. Machinekit does provide a so-called hardware abstraction layer, that could be used to fill this gap.
Several approaches for how to integrate Machinekit with ROS have been presented, whereas integration with ros_control looks like the most promising solution.
In the future, attempts may be made to upstream any work on integration Machinekit with ROS.
I hope I was able to spark some interest in Machinekit and ROS.
If you have any questions or application ideas, feel free to reach out to me via the comments below or send me some private feedback.