Skip to content

Add before end markers #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

kevinthegreat1
Copy link
Contributor

Adds the beforeEndTime and beforeEndDisp markers, which executes the given action a given dt or ds before the end of the current / next trajectory segment. I believe this is more correct and functional compared to #123, but I did have to change the signature of MarkerFactory#make. This code has been tested in production on my team's auto.

@rbrott
Copy link
Member

rbrott commented Mar 9, 2025

Is there a reason that afterTime()/afterDisp() with negative values don't suit your needs?

@kevinthegreat1
Copy link
Contributor Author

Is there a reason that afterTime()/afterDisp() with negative values don't suit your needs?

They don’t allow negative values. Also it would work since it would involve sleep actions with negative times.

@rbrott
Copy link
Member

rbrott commented Mar 9, 2025

They don’t allow negative values.

Ah I forgot I disallowed that after all. I'd rather open that up in the API though than add another method.

Also it would work since it would involve sleep actions with negative times.

I would propose clamping the sleep duration to zero / eliding the sleep if the duration would be negative.

@kevinthegreat1
Copy link
Contributor Author

I think you're misunderstanding the point of these methods. The point is that the new methods offset from the end of the current / next trajectory segment. It is not the same as the start of the current / next trajectory segment. It is useful for when you're not sure how long a trajectory segment will take.

@rbrott
Copy link
Member

rbrott commented Mar 17, 2025

I think you're misunderstanding the point of these methods. The point is that the new methods offset from the end of the current / next trajectory segment. It is not the same as the start of the current / next trajectory segment. It is useful for when you're not sure how long a trajectory segment will take.

Understood. Concretely, my counterproposal is to replace

.beforeEndTime(2.0) { ... }
.forward(20.0)

with

.forward(20.0)
.afterTime(-2.0) { ... }

@kevinthegreat1
Copy link
Contributor Author

I misunderstood you the first time because negative values would need extra processing. All done now. The new code is not tested on a real robot, but unit tests show that it produces identical trajectory actions to the old implementation.

One question: negative values get treated as 0 in SleepAction. Should it fail hard?

@rbrott
Copy link
Member

rbrott commented Apr 6, 2025

One question: negative values get treated as 0 in SleepAction. Should it fail hard?

Clipping is fine. I don't really want TAB to crash unless it really needs to.

Thanks for tweaking the implementation. It doesn't quite do what I was hoping for though. The proper replacement for

.beforeEndTime(2.0) { ... }
.forward(20.0)

should be

.forward(20.0)
.afterTime(-2.0) { ... }

not

.afterTime(-2.0) { ... }
.forward(20.0)

This should further simplify the implementation.

@kevinthegreat1
Copy link
Contributor Author

I believe I have done what you commented. Can you check 7b137ae again?

@rbrott
Copy link
Member

rbrott commented Apr 18, 2025

I believe I have done what you commented. Can you check 7b137ae again?

Sorry, on closer inspection I see that the implementation is in the right direction. I think it can be made a little more general though. The current implementation works for markers that end up in the previous trajectory but not any of them beyond that.

I think that the main problem with the naive strategy of passing along negative offsets is that the code here doesn't put them in the right place:

val (aNew, msRem) = ts.zip(ts.scan(0) { acc, t -> acc + t.offsets.size - 1 }).foldRight(
Pair(tail, ms)
) { (traj, offset), (acc, ms) ->
val timeTraj = TimeTrajectory(traj)
val actions = mutableListOf(seqCons(trajectoryActionFactory.make(timeTraj), acc))
val msRem = mutableListOf<MarkerFactory>()
for (m in ms) {
val i = m.segmentIndex - offset
if (i >= 0) {
actions.add(m.make(timeTraj, traj.offsets[i]))
} else {
msRem.add(m)
}
}
if (actions.size == 1) {
Pair(actions.first(), msRem)
} else {
Pair(ParallelAction(actions), msRem)
}
}
. That code should be updated to compute the "global" time offsets of each marker and then compare those offsets to the trajectory offsets to assign them to the immediately preceding trajectory. Hopefully that makes sense. I can also give illustrative test cases if that would help.

@kevinthegreat1
Copy link
Contributor Author

I think I understand what you're describing. However, it would be helpful if you could provide some tests. Also, ActionRegressionTest got refactored, and I'm a bit confused. If you can migrate my test here, it would also help a lot. Thanks.

@rbrott
Copy link
Member

rbrott commented May 2, 2025

I converted your new tests to fit the new regression test, added some edge cases, fixed an unrelated bug 😬, and added a slightly different implementation in before-end-action-updated. I didn't touch the comments -- they may need to be updated.

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.

2 participants