Disclaimer: My perspective on this is that of an FLL Coach. While I'll admit to a bit of tinkering with an EV3, 99% of my effort in this space is through the work of the 30+ FLL teams I have coached from 3rd graders to middle school world champions. So apologies in advance for the skewed perspective.
First off, let me say a big THANK YOU for doing this. This is exactly what I have been looking for for years. We start kids in FLL as early as 3rd grade and by the time they have been doing it for 3+ years, they often outgrow the MindStorms/LabView environment. Its not that the next lessons can't be taught there, its just not optimal. So having this level of control over things like acceleration (both straight and angular!) is really great. The kids will be able to dip their toes in the water of actual physics computations and, for their efforts, have a much improved level of control over their robot's odometry. In addition, Python is a great choice to transition them into the world of text-based programming, and get them ready to move to Java in FTC after that. So please keep up the great work. I have at least one team that is learning Python as I write this and counting on competing with this library next season.
There are 3 typical use cases I see in FLL for DriveBase:
-
Basic Movement: Driving a robot around a board using a series of discrete movements where the robot comes to a stop between each movement. This is the starting point for most teams and most of what you see in FLL. The kids think: "I need to move my robot forward this much, then I need to turn it that much, then I need to move forward again". In MindStorms, they code that as 3 blocks and behind the scenes, a PID controller uses some very aggressive accel/decel settings to try and keep the wheels from slipping too much, but success is heavily dependent on inertia.
-
Sensor Controlled Movement: Driving a robot using real time feedback from sensors. This is "line follow", "closing to a certain distance from an object" using an ultrasonic sensor, controlling heading using a gyro sensor, driving to a line, etc. In MindStorms, anything involving proportional control gets out of hand very quickly as they kids must use the looping constructs and "data wires" to make it work. Only the teams with devoted programmers who have a few years under their belt pull this off.
-
Smooth Movement: Sequencing a series of movements with smooth accel/decel curves between movements as needed. Currently, this is world-class level functionality in FLL. While it can be done in the the MindStorms environment, it is either highly imprecise (sequencing blocks without the "brake" value set) or highly advanced (teams that essentially write their own PID level controllers and master acceleration curves).
Given those use cases, here are my initial thoughts on DriveBase:
---I LOVE how the robot can be "configured" with its wheel_diameter and axle_track. This is a great opportunity to teach some basic geometry to the kids and then let them move on to higher order problems.
---Use Case 1 : Basic movement
------straight(), turn(), and settings() seems squarely aimed at this. I love that both straight_acceleration and turn_acceleration can be specified. This is a great opportunity to teach some basic physics and have the kids experiment with robot designs to discover what sort of acceleration their robot can tolerate before wheel slippage becomes a factor.
------I typically talk to the kids about straight movement, turns (radius turns) and spins (spinning in place). As I understand it, radius turns are missing in this part of the API. Perhaps those could be added? I get that such a turn can be specified with a combination of forward and angular velocity, but this is pretty advanced for FLL teams. We could try to coach them through the relationship between forward velocity on an arc, angular velocity, and radius, but I'd much prefer to see this math encapsulated in this class. Perhaps a method in this section could take radius measurement as an input?
------It's typical for teams to vary velocity from one movement to another depending on the level of consistency they need out of the specific movement. I wonder if the straight_speed and turn_rate parameters could be included as inputs on the movement methods themselves (e.g. straight(distance, speed))
---Use Case 2: Sensor Controlled Movement
------drive(), stop(), distance(), angle(), state(), and reset() seem to be aimed at this use case. It wasn't initially apparent to me that subsequent calls to drive() would "automatically" enact the specified acceleration curve to handle differences in velocity, but once that came clear, I do like it. Perhaps some notes in the doc.
------I haven't tested this yet, but I am wondering about how the acceleration curve will play with a higher level PID (or just P) controller calling drive(). For example, to follow a line (or go straight with a gyro), we will typically teach the kids the idea of real-time proportional control based on a sensor reading. Using this API, I presume they would code up a tight loop that will call drive() and adjust the turn_rate parameter proportionally to the amount of deviation from their intended course. In MindStorms, there is no software controlled angular acceleration curve to my knowledge, so the motors will simply be instantly commanded to the new rate of angular velocity. Here, depending on the turn_acceleration set, it might not. Not sure if that would be a problem. Similar issue for approaching an object and slowing down proportionally based on distance from the object. Some options:
---------Provide an explicit way to disable the acceleration curves. Perhaps this is just setting the acceleration numbers to arbitrarily large values?
---------Find a way for the kids to "plug in" their sensor reading directly to the drive control. Not sure exactly what this would look like, but imagine if they could provide a sensor as a parameter to a drive() function in way that would control velocity and/or angular velocity with a "built in" PID. Essentially the same thing you are now doing with the motor encoders.
---Use Case 3: Smooth Movement
------I've tried some experiments using sequential calls to drive() while polling distance() and angle() in between to try to generate a series of precise movements that do not including stopping between them. The first "oops" here was realizing that if I want to roll the robot forward at a certain velocity (e.g. drive(250,0)) and then make a radius turn while maintaining velocity, I have an acceleration problem. It appears that the next call to drive (perhaps drive(250,50)) detects that my "forward velocity" is already 250 so no acceleration curve there, but my current angular velocity is 0, so it must "ramp up" the angular velocity, resulting in an arc that looks more elliptical than circular. My workaround is to set the angular acceleration limit to a very high number.
------I'm not sure how to stop. I haven't tried calling straight() after a call to drive(speed,0). Perhaps that is the plan? My workaround is to call drive() with a very slow speed, let it decelerate for a specific set of encoder clicks, and then call stop() and brake().
------I'm not sure how far you want to go with this, but it occurs to me that you could potentially expand the straight() and turn() API to enable "optional" stops. My FTC teams tend to code something like drive(max_velocity, end_velocity, distance). In this implementation, the initial velocity is sensed, the end_velocity can be zero or non-zero and the distance is used to compute an initial acceleration phase (if needed), a cruise phase (maximized, but none if there is no room for it) and a deceleration phase (if needed). For example:
drive(250, 250, 1000) #accelerate from stand still, cruise at 250, don't decelerate
drive(100,100,1000) #slow down, cruise at 100, don't decelerate at end
drive(250, 0, 1000) #speed up, cruise at 250, come to a smooth stop
If you added an ability to specify a radius, I think it would be relatively complete. Given something like drive_radius(max_velocity, end_velocity, degrees, radius), I could see code like:
drive(250, 150, 1000, None) #accelerate from stand still, cruise at 250, decelerate before the turn
drive(150,150, 90, 100) #Make a 90 degree 100mm radius turn to the right at speed 150
drive(250, 0, 1000) #speed up after the turn, cruise at 250, come to a smooth stop
---Misc
------It wasn't clear if the acceleration parameters passed to settings() are the same as the acceleration parameters for distance_control.limits() and heading_control.limits(). Does setting one change the other?
------I was a bit mystified by the EPERM runtime errors until I clued in to the "You can only change the settings while the robot is stopped. This is either before you begin driving or after you call stop()." statement in the docs. I presume that was what that was about.
------I have not played around the PIDF coefficients yet, but in my initial tests, the defaults seem good so far. Most importantly, a balanced robot drives straight. :)
------I think I have seen some intermittent situations where even after calling stop() one of the motors continued to trundle along. This could easily be explained by bus issues (poor connections on the EV3 are a perpetual issue) but I'll keep an eye out and see if I can reproduce