Skip to content

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.

@kevinthegreat1
Copy link
Contributor Author

Hi, I can’t find where you pushed your updates. Do you mind sending a link? Also, you can push to my branch.

@rbrott
Copy link
Member

rbrott commented Aug 29, 2025

@kevinthegreat1
Copy link
Contributor Author

It looks like the branch is done. The negative time marker tests seem fine as well. I do have some problems with the negative disp marker tests, though.

For the second and third negative disp marker test, the sleep should be 2.8284271247 + 1.2928932188 and 2.0000000000 + 2.0000000000 + 1.2928932188 respectively, according to my original test, but it is currently 1.2928932188 and 1.2928932188 respectively.

Although I don't quite understand Actions#endTrajectory, the negative time markers seem to work fine.

@rbrott
Copy link
Member

rbrott commented Sep 4, 2025

I do have some problems with the negative disp marker tests, though.

I missed adding back the global segment time offset for disp markers. Should be fixed on the branch now.

@kevinthegreat1
Copy link
Contributor Author

I just have one more question. Before, the actions were based on the current traj segment, but now they are timed from the start. Does that make it more inaccurate when the robot takes longer than the traj time? Can we still time it from the start of the current traj segment?

@rbrott
Copy link
Member

rbrott commented Sep 15, 2025

Can we still time it from the start of the current traj segment?

Right, yes, done

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