Local locomotion¶
Move to a measured planar pose using local odometry feedback. Finite locomotion does not plan a path or avoid obstacles, so use it only where the swept path is known to be clear.
move accepts X and Y displacement in metres and signed yaw in radians, all
relative to the base frame captured when the command starts. Yaw is unwrapped:
pass the total rotation you want, not an angle limited to one turn. For
example, 3π radians rotates the base about 1.5 full turns before the
command succeeds. Commands stop when odometry reaches the target and report
progress through on_feedback / onFeedback.
Note
The endpoint is controlled without guaranteeing a particular path or that translation and rotation finish simultaneously.
Every requested axis is commanded regardless of motion model; unsupported axes stall when odometry stops making progress on them.
For direct teleoperation, or applying your own control logic rather than measured motion based on odometry feedback, see Velocity streaming.
import math
import time
from olo import Client
from olo.locomotion import LocomotionFeedback, PlanarMotion
last_feedback_time = 0.0
def on_feedback(feedback: LocomotionFeedback, rate_limit: float = 3) -> None:
global last_feedback_time
now = time.monotonic()
if now - last_feedback_time < 1 / rate_limit:
return
last_feedback_time = now
print(f"Remaining: {feedback.remaining}")
with Client() as client:
# Drive forward
client.robot().locomotion.move(
PlanarMotion(x=0.5),
on_feedback=on_feedback,
)
time.sleep(1)
# Turn_left
client.robot().locomotion.move(
PlanarMotion(yaw=math.pi / 2),
on_feedback=on_feedback,
)
time.sleep(1)
# Combined motion at a slower speed. Note that combined motions are much less
# accurate, and separate motions are recommended.
client.robot().locomotion.move(
PlanarMotion(x=-1.0, yaw=-math.pi / 2),
max_linear_speed=0.2,
max_angular_speed=0.3,
on_feedback=on_feedback,
)
time.sleep(1)
import { connect } from "olo/web";
import type { LocomotionFeedback } from "olo/locomotion";
const delay = (milliseconds: number) =>
new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
let lastFeedbackTime = 0;
function onFeedback(feedback: LocomotionFeedback, rateLimit = 3): void {
const now = performance.now() / 1000;
if (now - lastFeedbackTime < 1 / rateLimit) {
return;
}
lastFeedbackTime = now;
const remaining = feedback.remaining;
console.log(
`Remaining: PlanarMotion(x=${remaining.x.toFixed(3)}, y=${remaining.y.toFixed(3)}, yaw=${remaining.yaw.toFixed(3)})`,
);
}
const robot = await connect().robot();
// Drive forward
await robot.locomotion.move({ x: 0.5, y: 0, yaw: 0 }, { onFeedback });
await delay(1_000);
// Turn_left
await robot.locomotion.move({ x: 0, y: 0, yaw: Math.PI / 2 }, { onFeedback });
await delay(1_000);
// Combined motion at a slower speed. Note that combined motions are much less
// accurate, and separate motions are recommended.
await robot.locomotion.move(
{ x: -1.0, y: 0, yaw: -Math.PI / 2 },
{
maxLinearSpeed: 0.2,
maxAngularSpeed: 0.3,
onFeedback,
},
);
await delay(1_000);
The conventional topics are /{namespace}/odom and
/{namespace}/cmd_vel. Pass odom_topic / odomTopic or
cmd_vel_topic / cmdVelTopic for a nonstandard robot. Cancellation and
all terminal outcomes publish zero velocity.