Skip to content

EV3-G API: motors#360

Merged
dwalton76 merged 8 commits intoev3dev:developfrom
dwalton76:ev3g-api-motors
Sep 22, 2017
Merged

EV3-G API: motors#360
dwalton76 merged 8 commits intoev3dev:developfrom
dwalton76:ev3g-api-motors

Conversation

@dwalton76
Copy link
Copy Markdown
Collaborator

No description provided.

@dwalton76
Copy link
Copy Markdown
Collaborator Author

For issue #353
I'm still testing everything (so far so good) but figured I would go ahead and post it so everyone can start giving feedback before I spend too much time testing.

Comment thread ev3dev/core.py Outdated
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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This codepath will read from count_per_rot twice. Can we restructure this to avoid that?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed issued #365 to track this

Comment thread ev3dev/core.py Outdated
def set_power(self, power):
self.speed_sp = power

def set_position_rotations(self, power, rotations):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about making them private (i.e. leading underscore)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep can make them private

Comment thread ev3dev/core.py Outdated
"""
Rotate the motor at 'power' for 'rotations'
"""
self.set_power(power)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These powers will be in tacho counts per second. Do we not want to take input on a [-100,100] scale as LEGO does?

Copy link
Copy Markdown
Collaborator Author

@dwalton76 dwalton76 Sep 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the scale be [-1 * self.max_speed, self.max_speed]?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stock Lego interface measures speed/power in percentage of max_speed, and the max_speed is never exposed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see now...will clean this up

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

ev3dev/ev3dev#668 (comment)

So, in this case, our speed parameter should be in degrees per second or RPM.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread ev3dev/core.py Outdated
def get_power(self):
return self.speed

def rotations_greater_than(self, threshold):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should leave the users to do these comparisons themselves. I feel like this obfuscates the logic without any real gain.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread ev3dev/core.py Outdated
else:
self.stop_action = self.STOP_ACTION_COAST

def set_time(self, ms):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack will chop set_power() and set_time()

Comment thread ev3dev/core.py Outdated

def set_brake(self, brake):
if brake:
self.stop_action = self.STOP_ACTION_BRAKE
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned elsewhere, brake in EV3-G == hold in ev3dev

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack will change this

Comment thread ev3dev/core.py Outdated
self.set_brake(brake)
self.run_to_abs_pos()

def on_for_seconds(self, power, seconds, brake):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we wait until 'running' not in self.state? Does the driver consider itself "running" while holding?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok lets have it block by default but have an optional arg that can be passed to not block

Comment thread ev3dev/core.py Outdated
self.set_brake(brake)
self.stop()

def get_rotations(self):
Copy link
Copy Markdown
Member

@dlech dlech Sep 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using python properties for these getters?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate decorators :) Since we do use @property elsewhere though I guess it does make sense to use them here....will fix

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def rotations(self):
    pass

rotations = property(rotations)

No decorator. 😆

Comment thread ev3dev/core.py Outdated
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):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 rotation, 360 degrees....rotation is the smaller unit :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread ev3dev/core.py Outdated
self.time_sp = ms

def on_for_rotations(self, power, rotations, brake):
def on_for_rotations(self, power, rotations, brake=True, block=True):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do others think about calling it "speed" instead of "power"? This would only apply if we switched to using scientific units.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment thread ev3dev/core.py Outdated
"""
return self.wait(lambda state: s not in state, timeout)

def _set_position_rotations(self, max_speed_pct, rotations):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about 'speed_pct'?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that.

@WasabiFan
Copy link
Copy Markdown
Member

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.

Comment thread ev3dev/core.py Outdated
import array
import mmap
import ctypes
import logging
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread ev3dev/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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need it? Should not it be solved by #241 and the related changes in kernel drivers (ev3dev/lego-linux-drivers#57)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@ddemidov
Copy link
Copy Markdown
Member

ddemidov commented Sep 22, 2017

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_FACTOR

and then same thing for rotations_sp, power_sp (percentage of max_speed_sp), etc.

@dwalton76
Copy link
Copy Markdown
Collaborator Author

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.

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.

4 participants