Skip to content

Conversation

@dwalton76
Copy link
Collaborator

The MotorSet class can be used to start/stop more than one motor at once as close together as we possibly can. You can set different params for different motors in the set. Here is a small script I used to try a few things.

#!/usr/bin/env python3
from ev3dev.auto import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D, Motor
from ev3dev.helper import LargeMotorPair
import argparse
import logging
import time
import sys

# logging
logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s %(levelname)5s: %(message)s")
log = logging.getLogger(__name__)

log.info("start")
tank = LargeMotorPair(OUTPUT_A, OUTPUT_C)
tank.reset()
#tank.run_to_abs_pos(speed_sp=240, position_sp=360, stop_action='brake')
#tank.wait_until_not_moving()

motor_A = tank.motors[OUTPUT_A]
motor_C = tank.motors[OUTPUT_C]
tank.set_args(motors=[motor_A], speed_sp='120')
tank.set_args(motors=[motor_C], speed_sp='240')

tank.run_forever(stop_action='brake')
time.sleep(2)
tank.stop()
log.info("end")

@dwalton76
Copy link
Collaborator Author

For issue #315

}
"""

def __init__(self, motor_specs, desc=None):
Copy link
Member

Choose a reason for hiding this comment

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

Each motor object can tell you what port it's on... why does this need to take a dictionary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The motor objects haven't been created yet though, we need to know what type of objects to make and which ports each of those objects is associated with.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I read through the rest of the file then forgot to come back and delete this. I've got it now.

log.info("%s: %s set %s to %s" % (self, motor, key, kwargs[key]))
setattr(motor, key, kwargs[key])

for motor in motors:
Copy link
Member

Choose a reason for hiding this comment

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

I like this process (setting the properties then sending commands).

ev3dev/helper.py Outdated
motor.wait_while(s, timeout)


class LargeMotorPair(MotorSet):
Copy link
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 figure out exactly what our goal is. In my mind, the most common scenario will be a pair of large motors which you want to "steer": even if internally we're using a generic MotorSet, I'd like to see if we can make the "pair" classes more specialized so they legitimately feel easy to use and work "out of the box".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah....to me part of it depends on what the user input is to control the steering. If it is the IR remote we have that implemented in the RemoteControlledTank class in helper.py...we could pull some of that code into a Mixin class that could be inherited here in LargeMotorPair. I also did a web interface for a tank, that could be pulled in too but loading the www modules makes the program take a while to load on an ev3. What other user input methods might we see for a MotorPair?

Copy link
Member

Choose a reason for hiding this comment

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

The scenarios I had in mind were:

  • "Run the left motor at 50% speed forward and the right motor at 10% speed backward"
  • "Run the two motors in a proportion of 3:1" (ratio, differential, etc.)

The second is a special case of the first. Note that the units are nonspecific and just an illustration. The idea is that this class is what's used whenever you have a driving robot... if you want to go straight, turn, spin, etc., you use this.

self.motors = OrderedDict()
for motor_port in sorted(motor_specs.keys()):
motor_class = motor_specs[motor_port]
self.motors[motor_port] = motor_class(motor_port)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The motor objects are created here


def run_forever(self, **kwargs):
kwargs['command'] = ev3dev.auto.LargeMotor.COMMAND_RUN_FOREVER
self._run_command(**kwargs)
Copy link
Member

Choose a reason for hiding this comment

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

One thing I am not sure about is the logic of these run_* functions. Here you are pushing command into kwargs, and then filter it out in _run_command function. This cost a string comparison per key in kwargs, which could be negligible with respect to file acesses, but still is not free.

Why not create _set_args(self, **kwargs) function, and replace the two lines above with

self._set_args(**kwargs)
self._run_command(ev3dev.auto.LargeMotor.COMMAND_RUN_FOREVER)

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There is a set_args() (see line 187) and the user can do it this way if they want but they don't have to.

The price of the string compare feels worth it to me, it let me clean up this code a good bit by moving almost all of the logic to _run_command().

Copy link
Member

Choose a reason for hiding this comment

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

Well, I don't have strong feelings about this, so feel free to merge.

ev3dev/helper.py Outdated
"""

# Set the right_motor speed relative to the left_motor_speed
if set_right:
Copy link
Member

Choose a reason for hiding this comment

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

Huh, this isn't the behavior I had in mind 😆 I was thinking you would specify a speed and ratio, and it would run the fastest motor at that speed and the other one at a speed defined by the supplied ratio. My thinking was that we'd mimic the "steering" behavior of EV3-G.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I haven't touched EV3-G since the day I found ev3dev :) Does its steering do what ddemidov points to or something else?

Copy link
Member

Choose a reason for hiding this comment

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

Looking at the "synchronized steer" code here, I think I can say that they don't match but @ddemidov's implementation would be preferred. LEGO seems to designate one motor as a "master", drive that one at the requested speed, and then run the other at a rate defined by the steering. I am no expert though so I may be mis-reading it.

ev3dev/helper.py Outdated

def set_speed_percentage(self, left_motor_percentage, right_motor_percentage):
"""
Set the speeds of the left_motor vs right_motor by percentage of their maximum speed
Copy link
Member

Choose a reason for hiding this comment

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

We should specify that the units are -100 to 100. The term "percentage" is often used to refer to [-1,1] or [0,1] by some programmers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ack...will also add some asserts to verify the inputs are within the range

ev3dev/helper.py Outdated
* 0 means drive in a straight line, and
* 100 means turn right as fast as possible.
power: the power that should be applied to the outmost motor (the one
Copy link
Member

Choose a reason for hiding this comment

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

power? I think this should probably be "speed"

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Caught that in the initial cut-n-paste....will change all of the power references to speed

ev3dev/helper.py Outdated
log.debug("%s: direction %d, power %d, speed %d, left_power %d, right_power %d" %
(self, direction, power, speed, left_power, right_power))

self.left_motor.speed_sp = int(left_power)
Copy link
Member

Choose a reason for hiding this comment

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

We don't actually run a command here; is the expectation that you call set_speed_steering and then set the command in a loop? Note that updating the setpoint won't have an effect until a command is sent AFAIK.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yep it will be up to the user to call run_forever, run_timed, etc after this....all this function does is set them up with the desired speed_sp setting. Same goes for set_speed_percentage().

Copy link
Member

Choose a reason for hiding this comment

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

Hmm... In that case, we need to make sure we document the behavior!

Copy link
Member

@WasabiFan WasabiFan left a comment

Choose a reason for hiding this comment

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

Looks good; we need to document the behavior of the new helper functions. I'm approving but I think we want to hold off on merging until we've finalized the v1.0.0 release.

@dwalton76
Copy link
Collaborator Author

Is 1.0.0 out...shall we release the merging floodgates?

@WasabiFan
Copy link
Member

Can you add a line to the documentation for set_speed_steering and the other related function explaining you need to call a run_* function afterward?

Also, is there anything we want to do here in response to #348 (e.g. renaming or changing the behavior to match EV3-G)?

@dwalton76
Copy link
Collaborator Author

yep will add a line about needing to call run_XYZ

For #348 lets tackle any of that via a separate pull request. I need to take a look at what blocks EV3-G has, I'm not sure if the MotorSet class would cover one of them or not.

@dwalton76 dwalton76 merged commit b93b22a into ev3dev:develop Sep 7, 2017
@dwalton76 dwalton76 deleted the MotorPair branch September 7, 2017 03:19
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.

3 participants