Skip to content

fix!: waypoint rates are optional, and survive a transmission - #226

Merged
jimsynz merged 1 commit into
mainfrom
optional-waypoint-rates
Aug 9, 2026
Merged

fix!: waypoint rates are optional, and survive a transmission#226
jimsynz merged 1 commit into
mainfrom
optional-waypoint-rates

Conversation

@jimsynz

@jimsynz jimsynz commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Two fixes, found together because the first exposed the second.

Waypoint rates are optional

Command.Trajectory required velocity and acceleration on every waypoint. Positions and times is the ordinary way to describe a trajectory — it's what ROS-style planners emit — and a caller with only that had to invent rates to satisfy the schema.

Inventing is worse than omitting: a driver can't tell a made-up 0.0 from a deliberate standstill. Both fields are now optional, and absent means you decide — which a driver can act on by working out the speed needed to arrive by time_from_start.

follow_trajectory/4 also stopped multiplying a nil rate by 1.0.

Trajectories were broken on any joint with a transmission

Found while testing the above. Transmission.apply_to_waypoint/2 pattern-matched a waypoint as a map:

defp apply_to_waypoint(%{position: p, velocity: v, acceleration: a} = wp, t) do

but the schema has always declared them as keyword lists ({:list, {:keyword_list, @waypoint_schema}}). So every trajectory sent to a joint with a transmission raised FunctionClauseError and took the actuator down:

with no transmission:  ok
with a transmission:   RAISED: no function clause matching in BB.Transmission.apply_to_waypoint/2

On an SO-101 that's five joints of six. Only the gripper works, because it has no transmission — which is exactly why this survived unnoticed, and why I only caught it when I went looking for something else.

It now handles keyword lists, and leaves an omitted rate omitted rather than turning it into a zero. There was already a maybe_apply_rate/2 for precisely that; it just wasn't being used here.

Breaking

velocity and acceleration on a waypoint may now be nil. Anything reading them straight out of a waypoint needs to cope — drivers treating them as always-present want a fallback. bb_servo_feetech already has one: an absent velocity falls back to the leg's time budget, then to the joint's velocity limit.

Testing

mix check --no-retry passes; 1317 tests. Four new ones cover the keyword-list shape, an omitted rate surviving the transmission as nil rather than 0.0, time_from_start being untouched, and the nil-transmission path.

Came out of hardware work on an SO-101 — bb_servo_feetech#94 unified its position and trajectory code paths, at which point the fallback for an absent velocity turned out to be unreachable through the public API.

Two things, found together because the first is what exposed the second.

`Command.Trajectory` required a velocity and an acceleration on every waypoint.
Positions and times is the ordinary way to describe a trajectory — it is what
ROS-style planners emit — and a caller with only that had to invent rates to
satisfy the schema, which is worse than saying nothing: a driver cannot tell an
invented zero from a deliberate standstill. Both are now optional, and an
absent one means "you decide", which a driver can act on sensibly by working
out the speed needed to arrive by `time_from_start`.

Separately, and already broken: `Transmission.apply_to_waypoint/2` pattern
matched a waypoint as a map, while the schema has always declared keyword
lists. Every trajectory sent to a joint with a transmission raised
`FunctionClauseError` and took the actuator down with it. On an SO-101 that is
five joints of six; only the gripper works, because it has no transmission,
which is why this survived. It now handles keyword lists, and leaves an omitted
rate omitted rather than turning it into a zero.

`follow_trajectory/4` no longer multiplies a nil rate by 1.0.

BREAKING CHANGE: `velocity` and `acceleration` on a trajectory waypoint may now
be `nil`. Anything reading them straight out of a waypoint has to cope with
that; drivers that treated them as always-present need a fallback.
@jimsynz
jimsynz merged commit e6e3c65 into main Aug 9, 2026
38 of 40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant