Is "Twist" (still) a good velocity command interface

Of course you can, but I think the question is, why should you?

In my experience, the standard use case is to follow a trajectory using a controller. This could be done direct, but in most cases, we have at least a two-level controller hierarchy: One controller has (traditionally) a fairly general notion of the vehicle, e.g., that an omni-directional vehicle can drive forwards and sideways. This controller is usually an optimizer. The lower-level controller knows the exact configuration and maps vehicle speeds to wheel speeds. In ROS, this is often a fixed mapping plus an underlying PID controller.

So, what we’re are talking about is the interface between those two controllers, would you agree?

Now, this simple approach breaks down very rapidly. For example, the classical DWA formulation can switch instantly between a left turn and a right turn. Even for a differential drive this can be hard on the gears, and once you add a physical steering it becomes a recipe for rapid hardware failure. One way to avoid this is to use things like jerk filters, but these degrade performance.

Because the upper-level controller is usually an optimizer, it is possible to integrate vehicle specific information there, and this is what I see control engineers doing for both simple and complex algorithms. The various cost-function components in the move_base DWA implementation are an example of this, and it usually leads to much better performance.

It can often be beneficial for the optimizer not to work in vehicle-velocity space, but in some other space. For example, for Ackermann kinematics, this could be forward-velocity and steering angle, since this makes it very easy to take into account the sideways force, which is relevant to avoid slippage…

If we have a smart optimizer like that, and we force its output through the “twist” needle-hole, then we have two transformations in play: one forward and one reverse in the lower-level controller, and these have to match. This violates the information hiding principle, and when the transformations change at run-time it can also require additional parameters, which splits the interface.

Moreover, there is also double-Ackermann steering and other things, where it really breaks down. This is rare, but not as rare as you might think (just in the past two years I worked on two vehicles like that, for urban and industrial applications).

This is all my rationale for looking at other interfaces that are more expressive and/or more direct.

Now, in addition to all that, static typing prevents more errors than dynamic typing and thus, while it’s very general, I don’t think an array together with a type field is ideal.

I like the type field idea in general, though. It could be a good compromise between expressiveness and interoperability. Since most people agreed that we don’t need to skimp on the fields, we could have something similar to the twist (e.g., with separate fields for the various velocities) with an added type-field. Rather than change the interpretation of the fields, this would only specify how many of them are actually used (e.g, a diff-drive would only use x and theta, omni would use x, y and theta).

So this would become

Header header
uint8 DIFF_DRIVE = 0
uint8 OMNI = 1
uint8 SINGLE_ACKERMANN = 2
uint8 type
float32 vx
float32 vy
float32 rx

I would still prefer to use something more capable for Ackermann and for aerial/underwater vehicles, but in principle the above could accommodate a simple Ackermann steering in the same way that the TEB driver currently does it.

2 Likes