moteus.move_to and moteus ruckig python example
For some time now the moteus python library has had a convenience function to repeatedly execute a command until moteus determines that the trajectory has completed. This helps a lot with simple applications with a single device, but as soon as multiple controllers are involved in the same machine numerous problems appear:
Watchdog timeout: moteus has a watchdog timeout and unless all devices are sent commands on a regular basis, they will fault. This is easy to do with set_position_wait_complete, which by definition only sends commands to a single device.
Synchronized motion: Often a user wants multiple devices to execute a motion in some kind of synchronized way. There are many possible semantics for this, ranging from just completing at the same time, to avoiding self collisions in a multi-link robot arm. Using just set_position() doesn’t really support any of those mechanisms, as the joints will just move to their target independently using their configured or commanded acceleration and velocity limits.
It has always been possible to send arbitrary piecewise linear trajectories to any number of devices using the position mode command to achieve motion and have no issues, however that approach can be relatively advanced for those beginning with motion control.
To make this process a bit easier, there are now a new python API and a new example which make achieving this simpler for those who are getting started.
moteus.move_to
The simpler method is a new API in the python moteus library named ‘move_to’ (API documentation). This method enables two straightforward means of synchronizing multiple devices.
Simultaneous commands
The first is if you want to send multiple devices a command, want to wait for them all to complete, and have no care at all when they do complete it. In that case, you use it like:
c1 = moteus.Controller(id=1)
c2 = moteus.Controller(id=2)
results = await moteus.move_to(
[(c1, moteus.Setpoint(position=0.5)),
(c2, moteus.Setpoint(position=2.0))])
This form will simultaneously send commands to both devices on a regular basis until they both complete. It is roughly equivalent to:
qr = moteus.QueryResolution()
qr.trajectory_complete = moteus.INT8
c1 = moteus.Controller(id=1, query_resolution=qr)
c2 = moteus.Controller(id=2, query_resolution=qr)
while True:
results = await transport.cycle([
c1.make_position(position=0.5, query=True),
c2.make_position(position=2.0, query=True))]
if all([x.values[moteus.Register.TRAJECTORY_COMPLETE] for x in results]):
break
Rough duration matching
The second form is basically the same as the first, except that you can pass an optional duration argument. That looks like:
c1 = moteus.Controller(id=1)
c2 = moteus.Controller(id=2)
results = await moteus.move_to(
[(c1, moteus.Setpoint(position=0.5)),
(c2, moteus.Setpoint(position=5.0))],
duration=5.0)
In this mode, move_to will assume infinite acceleration and calculate an appropriate velocity_limit to send to each device. That will result in all devices completing the trajectory at roughly the same time, with the error depending largely upon how relevant the acceleration phase is to the total length of movement.
ruckig example
moteus.move_to is simple and gets close to what many people need. However, there is now also an example showing how to use the ruckig library to construct jerk and acceleration limited trajectories for multiple devices and send them piecewise to controllers on the fly.
Using ruckig achieves several properties that can’t be easily achieved otherwise:
- Jerk limited trajectory: Internally moteus implements a limited acceleration trajectory planner that results in infinite jerk. By using ruckig, the rate of change in acceleration can also be limited.
- Exact synchronization:
moteus.move_to(duration=X)only roughly synchronizes joint movement, since it has no knowledge of acceleration. Ruckig considers both acceleration and jerk in its planning, allowing multiple joints to achieve a target state simultaneously.
There are several downsides:
- Implementation complexity: Because of its increased capabilities, ruckig has a more complex interfaces.
- Runtime performance: It also takes longer to execute
- Licensing: Several features, like a limited position envelope, are gated behind a commercial license.
That said, for many people ruckig itself is all that is necessary and if not, the example still shows how any alternate trajectory solution could be integrated with moteus.