EV3-G API: motors#360
Conversation
|
For issue #353 |
| self.position_sp = self.position - int(rotations * self.count_per_rot) | ||
|
|
||
| def set_position_degrees(self, power, degrees): | ||
| rotations = float(degrees / self.count_per_rot) |
There was a problem hiding this comment.
This codepath will read from count_per_rot twice. Can we restructure this to avoid that?
There was a problem hiding this comment.
I could cache it here or we could do something in the Device class (or maybe the Motor class) where it is aware of what properties will never change and cache it there. If we want to do the latter I'll work on that as a separate PR.
There was a problem hiding this comment.
| def set_power(self, power): | ||
| self.speed_sp = power | ||
|
|
||
| def set_position_rotations(self, power, rotations): |
There was a problem hiding this comment.
What are these methods (setting the position_sp, time_sp, etc.) for? They don't seem to match up with the EV3-G blocks in the spec image.
There was a problem hiding this comment.
Originally in on_for_degrees, etc I just had a run_to_abs_pos() call where I passed in all of needed kwargs. This worked fine for a single motor but for MoveTank and MoveSteering if I called
self.left_motor.on_for_degrees(...)
self.right_motor.on_for_degrees(...)
it added delay between when the two motors start. We want to follow the model of "set as many parameters as you can for both motors, then start the motors" so that they start as close together as possible. So that is why set_power(), etc are broken out like this, it is so I can set all of the parameters first when working with pairs of motors.
There was a problem hiding this comment.
How about making them private (i.e. leading underscore)?
There was a problem hiding this comment.
yep can make them private
| """ | ||
| Rotate the motor at 'power' for 'rotations' | ||
| """ | ||
| self.set_power(power) |
There was a problem hiding this comment.
These powers will be in tacho counts per second. Do we not want to take input on a [-100,100] scale as LEGO does?
There was a problem hiding this comment.
Shouldn't the scale be [-1 * self.max_speed, self.max_speed]?
There was a problem hiding this comment.
The stock Lego interface measures speed/power in percentage of max_speed, and the max_speed is never exposed.
There was a problem hiding this comment.
ah I see now...will clean this up
There was a problem hiding this comment.
I share the same sentiments as @laurensvalk that we should be using scientific units where possible in places where LEGO uses 0..100 or -100..100
So, in this case, our speed parameter should be in degrees per second or RPM.
There was a problem hiding this comment.
Also, I wouldn't mined a default value for the brake parameter here as True since this is what gets used most of the time.
There was a problem hiding this comment.
wait if the EV3-G on_for_rotations block only takes power as a percentage of max_speed and we take power as the scientific unit then our attempt at a 1:1 API isn't going to match
| def get_power(self): | ||
| return self.speed | ||
|
|
||
| def rotations_greater_than(self, threshold): |
There was a problem hiding this comment.
I think we should leave the users to do these comparisons themselves. I feel like this obfuscates the logic without any real gain.
There was a problem hiding this comment.
Yeah they seem pretty silly to me too but David had screenshots for these blocks so I coded them up. I am fine with chopping them.
| else: | ||
| self.stop_action = self.STOP_ACTION_COAST | ||
|
|
||
| def set_time(self, ms): |
There was a problem hiding this comment.
Function calls in python are expensive. Here and in several other places, your are just setting an attribute. It would be more efficient to just set the attribute directly.
There was a problem hiding this comment.
ack will chop set_power() and set_time()
|
|
||
| def set_brake(self, brake): | ||
| if brake: | ||
| self.stop_action = self.STOP_ACTION_BRAKE |
There was a problem hiding this comment.
As mentioned elsewhere, brake in EV3-G == hold in ev3dev
There was a problem hiding this comment.
ack will change this
| self.set_brake(brake) | ||
| self.run_to_abs_pos() | ||
|
|
||
| def on_for_seconds(self, power, seconds, brake): |
There was a problem hiding this comment.
All of the on_for_* methods should wait for completion before returning.
e.g., this method should not use self.run_time(), it should
self.run_forever()
time.sleep(seconds)
self.stop()
There was a problem hiding this comment.
On second thought, my suggestion would not be a good implementation when there are multiple threads.
Unfortunately, we don't currently have a way to be notified by the motor driver that the current operation was interrupted.
There was a problem hiding this comment.
We could have a wait_until_stopped arg and if it is True we call self.wait_until_not_moving(). But then our API would no longer match the EV3-G API exactly. We could just document how to use self.wait_until_not_moving()
There was a problem hiding this comment.
Why can't we wait until 'running' not in self.state? Does the driver consider itself "running" while holding?
There was a problem hiding this comment.
In EV3-G if you tell a motor to run for 3 rotations does it start the motor and move on to the next block or does it wait for the three rotations to finish? If it moves on then I think we are good with what we have now, it if blocks though then we need to add something like
self.wait_until('running', timeout=100)
self.wait_until_not_moving()
There was a problem hiding this comment.
It's a blocking call. It starts the motors and only returns to the caller once the motor has turned to the specified position. EV3-G programs are very commonly just a single sequence of motor blocks with some branching (drive forward, turn, drive forward, check sensor, etc.).
There was a problem hiding this comment.
ok lets have it block by default but have an optional arg that can be passed to not block
| self.set_brake(brake) | ||
| self.stop() | ||
|
|
||
| def get_rotations(self): |
There was a problem hiding this comment.
How about using python properties for these getters?
There was a problem hiding this comment.
I hate decorators :) Since we do use @property elsewhere though I guess it does make sense to use them here....will fix
There was a problem hiding this comment.
def rotations(self):
pass
rotations = property(rotations)No decorator. 😆
| self.position_sp = self.position - int(rotations * self.count_per_rot) | ||
|
|
||
| def set_position_degrees(self, power, degrees): | ||
| def _set_position_degrees(self, power, degrees): |
There was a problem hiding this comment.
Can we have rotations call degrees instead? It makes more sense to me to do everything in terms of the "smaller" units and convert from the "larger" ones as needed. Or, it might be optimal to just make them operate independently so we avoid unnecessary nesting for execution speed.
There was a problem hiding this comment.
1 rotation, 360 degrees....rotation is the smaller unit :)
There was a problem hiding this comment.
If you need more degrees than rotations to represent the same quantity, the quantity represented by one degree is smaller than the quantity represented by one rotation.
| self.time_sp = ms | ||
|
|
||
| def on_for_rotations(self, power, rotations, brake): | ||
| def on_for_rotations(self, power, rotations, brake=True, block=True): |
There was a problem hiding this comment.
What do others think about calling it "speed" instead of "power"? This would only apply if we switched to using scientific units.
There was a problem hiding this comment.
yeah the use of power is some places and speed in other is already making my head spin :) I think the EV3-G blocks use power?
| """ | ||
| return self.wait(lambda state: s not in state, timeout) | ||
|
|
||
| def _set_position_rotations(self, max_speed_pct, rotations): |
There was a problem hiding this comment.
Why max_speed_pct? I read that to mean "the maximum speed (percentage)" as opposed to "percentage of the max speed". TBH, I think it's an unnecessary distinction as most people would just consider it "speed percentage".
There was a problem hiding this comment.
How about 'speed_pct'?
|
We also need to consider when we're updating docs+demos for these changes. I want to make sure that we don't lose track of that work. P.S. I'm hoping I can find time to take on a few of the pending features; we'll see if I can make that work over the weekend. I don't want to take something then delay it if I get busy. |
| import array | ||
| import mmap | ||
| import ctypes | ||
| import logging |
There was a problem hiding this comment.
I think you should put logging into a separate PR. Every import makes startup time a bit longer, so we should only do this if necessary.
There was a problem hiding this comment.
Fair point, I'll back out the logging parts that are in here and file an issue to chat about what logging we want in core.py.
|
|
||
| # The number of milliseconds we wait for the state of a motor to | ||
| # update to 'running' in the "on_for_XYZ" methods of the Motor class | ||
| WAIT_RUNNING_TIMEOUT = 100 |
There was a problem hiding this comment.
Do we really need it? Should not it be solved by #241 and the related changes in kernel drivers (ev3dev/lego-linux-drivers#57)?
There was a problem hiding this comment.
It is, what I found is that if you do
m.run_to_abs_pos(...)
m.wait_until_not_moving()
The run_to_abs_pos will update the command file but there is some finite amount of time before the kernel updates the state file to running. So you really have to wait until the motor starts moving before you can wait for it to stop moving.
|
I wonder if introducing properties for various units would make a lot of these functions easier (and may be even unnecessary). For example, we could have @property
def degrees_sp(self):
return self.position_sp * self.POS2DEG_FACTOR
@degrees_sp.setter
def degrees_sp(self, value):
self.position_sp = value * self.DEG2POS_FACTORand then same thing for |
|
I think the only thing left in this PR to address is how to specify speed_pct vs speed_dps vs speed_rps so I'm going to go ahead and merge this one in and open a new issue for the speed thing. |
No description provided.