Skip to content

Robot Locomotion

MIT roboticists program robots in Julia to climb stairs and walk on hazardous, difficult and uneven terrain

Walking may feel like something that just comes naturally, but take a moment to think about all the complicated movements that are involved: lifting each of your legs and feet at the right time and in the right way, setting them back down again at the proper angle, distance and moment in time, making sure not to put a foot in the wrong place or move it at the wrong time, avoiding obstacles and dealing with uneven terrain.

Now imagine programming a humanoid robot to do all of those tasks.

The MIT Robot Locomotion Group programs legged robots to walk so that they can explore remote regions, assist in disaster response or travel places that are unsafe for humans, such as minefields and contaminated sites.

JuliaHub-Modeling-Platform-Case-Study-MIT-Robot-marnl2Controlling a robot with dozens of joints requires algorithms from optimization, control theory and machine learning, and implementing those algorithms requires efficient software.

Which is why roboticists Robin Deits and Twan Koolen chose Julia.

Robin explains:

I have more than a decade of experience in Matlab and Python, and Julia has completely replaced both languages for my research. … I firmly believe that the fundamental scientific goals of reproducibility and openness compel us as scientists to use freely available tools whenever possible. The fact that Julia is performant and expressive while also remaining entirely free and open source makes it an excellent tool for science. Compared to Python, Julia has clear benefits in terms of performance, but, for me, Julia's support for generic programming is just as important. In Julia, I can (and often do) write functions that may operate on a variety of data types, such as scalars or symbolic variables, and it is easy to handle both with the same body of code. Generic programming of this type in Python would require run-time type checking, which adds substantial computational overhead. Julia, on the other hand, makes generic code clear and efficient.

Twan concurs:

My interest and background is mainly in programming online controllers for robots. Of course, one requirement for such controllers is good performance: making the most of the typically limited computing power available on the robot. This results in the prevalence of languages like C, C++, and (to a much lesser but still significant extent) Java for this kind of work. For serious online control tasks, languages like Matlab and Python are pretty much non-starters in my opinion. Another typical workflow used by some roboticists is to prototype in a 'productive' language like Python or Matlab, before moving to C++. I personally have a fair amount of experience with C++, Java, and Matlab.

According to Robin and Twan:

"One area in which we see significant advantages using Julia is in developing online controllers (that is, controllers which run in real time on the robot, typically with control rates of 100-1000 Hz). Modern controllers for walking robots typically involve much more complicated computation than a simple linear feedback controller, and most humanoid robots are controlled by solving mathematical optimizations at these high rates. Even setting up these optimization problems can be complex, so it is extremely useful to have a language like Julia that combines excellent support for mathematical programming, useful optimization libraries such as JuMP.jl, and highly performant code when developing new robot controllers.

In addition to raw performance, a notable requirement for online controllers is a limit on the amount of jitter that can be tolerated (i.e., hard real time constraints). If you're writing a controller for a walking robot and it doesn't provide new motor commands at a near-constant rate, the robot may literally fall over. This typically means avoiding dynamic memory allocation as best as possible, as memory allocation algorithms themselves typically don't have hard real time guarantees. In languages like Java and Julia, the garbage collector is an additional source of jitter. The garbage collector typically 'stops the world' for up to several milliseconds while it does its job, possibly resulting in a robot lying on the floor. Whether or not the language employs a garbage collector, the conclusion is the same: dynamic memory allocation in the control loop should be avoided. Finally, JIT compilation should also be avoided during online control.

Given the constraint of avoiding jitter due to dynamic allocation and online JIT compilation, we find Julia to be more productive than Java. First, Julia provides immutable, stack-allocated user data types, while Java (at least as of now) only stack-allocates predefined primitive types like integers and doubles (note that sometimes Java can avoid dynamic allocation of non-primitive types as an optimization, but that's not guaranteed). In contrast, in Julia you can simply create, for example, a fixed-size 6-vector (provided by the wonderful StaticArrays package, which provides functionality and performance similar to the fixed-size matrices from the Eigen C++ library) in a function and return it by value, while the (tedious) pattern in Java is to pre-allocate everything and have methods mutate their inputs in place. Second, JIT compilation is also better than in Java. Julia's JIT compiler is actually much closer to an ahead-of-time compiler than Java's hotspot JIT, which may choose to optimize code after running it several times online (possibly stopping the world in the process). In Julia, the compiler is guaranteed to run the first time a certain method is called. Support for true static ahead-of-time compilation as an option is also rapidly improving, further improving Julia's position relative to Java. It should be noted that various real time implementations of Java exists, but they are either woefully out of date, or slow, or prohibitively expensive, and they can only somewhat alleviate the problems due to dynamic memory allocation and JIT compilation.

Compared to C++, we found that Julia is much more productive, while retaining similar performance. We perceive the main reasons for this increased productivity to be as follows:

  • C++ projects typically have a lot of overhead due to setting up and maintaining a build system, as there is no one standard package management system in C++. This problem is exacerbated by working with external dependencies, which each have their own, custom build system. So the choice often becomes: do I really want to spend time working on a build system that integrates a potential dependency into my project, or do I reinvent the wheel? Julia is able to avoid this due to its standardized package system, which … is much better than traversing the wild west of C++ build systems. The standardized package system also makes it easy to set up basic continuous integration, which is more and more recognized as a necessity in the field of robotics. In addition, we strongly believe that robotics researchers should publish their code with their papers to improve reproducibility and perhaps code reuse, and again Julia's package system makes such code distribution easy.
  • Julia avoids many subtle bugs by having less undefined and/or implementation-defined behavior than C++. Such bugs tend to take a long time to hunt down. Another class of bugs (and bad design patterns) is eliminated by Julia's choice of not allowing null pointers (or references).
  • Julia doesn't require you to spend as much time thinking about object lifetimes. There is no need to worry about C++ staples like the 'rule of three' or 'rule of five', or to worry about whether other collaborators adhered to those rules. Rvalue and lvalue references of various flavors and things like move semantics tend to confuse, frustrate, and become time sinks for people whose main interest is robot control.
  • Julia makes it easy to support multiple platforms, allowing users to, for example, simulate the control algorithm on their OS of choice before running the code on a real robot.

Online control is not the only problem that roboticists are interested in solving. Problems such as parameter estimation, dynamical simulation, and (nonlinear) trajectory optimization are generally not done in an online control loop, but their solutions typically involve a lot of the same code that is needed for online model-based control. For example, all of these applications require a (preferably fast) rigid body dynamics library, and so it is preferable to reuse this code for all of these applications. Supporting all of these applications was one of our main requirements in designing our RigidBodyDynamics.jl package, and Julia made this easy, partly due to the ease of writing generic code. Generic code allows, for example, automatic differentiation using operator overloading, which is valuable for gradient-based algorithms used in trajectory optimization and parameter estimation. Java's type-erasure-based generics don't cut it due to their significant overhead, while C++ templates tend to be cumbersome to work with. Working with parameterized types combined with the type dispatch system in Julia is a joy, and has resulted in the adoption of a type-generic coding style by many package authors. In our opinion, this has resulted in much more reusable code than what is typically permitted by C++ or Java object inheritance.

Julia's Python-like productivity and C++-like speed allow us to avoid having to make decisions regarding when to move to the fast language, and tediously reimplementing the same functionality. This has been a main goal of Julia since the start, and we believe the designers have succeeded to a great extent in this respect. When initially evaluating the language, I remember trying to ensure that Julia could match the speed of C++ when multiplying two 4x4 matrices (a building block of computing robot kinematics). Using StaticArrays.jl and Eigen in C++, I found that they were both equally fast (and produced almost identical native code, SIMD instructions and all), which helped convince me that Julia's raw performance would not be a bottleneck. The main algorithms in RigidBodyDynamics.jl are currently within a factor 1.5 to 2.5 of a well-optimized, non-generic C++ implementation, with further optimization opportunities still available, and also a bit of an additional speed boost on the master branch of Julia. Our new RigidBodySim.jl package, which provides a simulation and visualization environment built on top of RigidBodyDynamics.jl and DifferentialEquations.jl, can simulate a balancing humanoid robot at a near-realtime rate.

One final advantage of Julia for our work is its excellent interoperability with existing C and Python code. There is a wealth of software for robotics and optimization written in C and Python, and Julia's built-in ccall functionality and PyCall.jl package make it easy to interact with existing tools. For example, we rely on the LCM (Lightweight Communications and Marshalling) library for the low-level communication between our robots and their controllers. Rather than reinventing all of that code, we were able to easily expose the existing C library to Julia with little or no overhead. Leveraging existing C tools like LCM gives us more time to focus on the important parts of our research.”