Based on my past experience working on a FRC team, my main advice would be to be very cautious about pushing students to use more advanced abstractions, design patterns, and whatnot.
The issue is that if you spend a lot of up-front effort designing interfaces and abstraction layers, you can end up painting yourself into a corner, especially if you're new to programming and don't have a good sense of how to anticipate these sorts of things.
For example, take the forklift example you mentioned -- what happens if the students decide half-way through the design season that they don't want to use the forklift design after all, and want to create some sort of arm? Or what if they want to create some kind of kicker thing to try punt a ball of some sort?
Or even with the forklift example -- what if the team switches from using some sort of conveyor-belt design (where the user can control the height of the lift) to some sort of pneumatic based system, where the height ends up being basically binary (low or high)? How do you design an interface that can accommodate both (without stuffing the interface full of different methods that are inconsistently implemented across different subclasses)?
And for all of these, how do you map of these to the user interface, which again the team might want to wildly change over time?
None of these issues are insurmountable of course -- you can work around it by having a clever and flexible set of abstractions, or by having a more solid design phase (and pruning down deviations from the initial plan).
That said, no plan is perfect and the danger is that too-brittle and overly restrictive abstractions can potentially end up crippling the team down the line.
This means you should be careful when pushing for more abstraction -- the danger is that if the abstractions you propose end up hampering the team down the line, they might end up resenting the idea of them and take away entirely the wrong lesson: abstractions are bad, and are not worth the trouble.
So, for that reason, I recommend taking a more "organic" approach. Let students write code, and push them to write it in the simplest way possible. The code initially is probably going to be messy, but that's fine -- if your FRC team is anything like mine, there's probably a fair bit of prototyping in the first week or two (or even up to the last week...).
As you see redundancy, point it out, and push students to refactor it. Try and emphasize this sort of procedural refactoring first.
Next, encourage students to create objects to encapsulate behavior and state. Somebody introduces a new subsystem? Create an object for it. Somebody wants to play with a new UI? Another object. Don't worry about having these objects conform to any kind of interface/extend any object -- getting them to think about subsystems as distinct units/distinct objects would be a good step.
Finally, near the endgame, as the design as settled and people are iterating, start introducing the idea of interfaces. The overall design of your subsystems are probably fixed at this point, and the team is likely focused on iterating. In that case, you can probably now create an interface per each subsystem and create a new subclass per each distinct variation or something.
That way, if the team wants to trial-run one particular variation, it's easy to swap out the corresponding code.
You can maybe facilitate this whole process by having code reviews, perhaps on a weekly basis or something. This lets students still retain ownership of the code while still giving you a mechanism to propose changes or give advice.
This does mean, however, that your advice will (at least initially) be reactive, rather then proactive. However, I don't think that's necessarily a bad thing -- it's good for the students to have ownership of their code, make mistakes, learn from them, etc...
And if the team never really does get to the point where using interfaces and such makes sense, don't bother bringing it up. Realistically, the code they're writing is likely going to be simple, in the grand scheme of things. Don't over-complicate it.
Edit: A little more specifically, I'm fond of the "rule of three" (link 1, link 2).
I would try and drill in this principle into your students: when you see something repeated three or more times, that's a good point to think about refactoring and abstracting. Basically, my opinion is that the hard part is learning when it's appropriate to refactor or abstract. If you can teach them to identify when to abstract, it's comparatively easier to teach them how.