ev3dev-lang-rust icon indicating copy to clipboard operation
ev3dev-lang-rust copied to clipboard

Any way to pair motors?

Open karlmdavis opened this issue 4 years ago • 3 comments
trafficstars

Apologies if this is a dumb question, but as someone new to Mindstorms and this library, I suspect there ought to be a way to pair motors together? Like, if I want to start driving, shouldn't there be a way to start both motors/wheels at the same time?

I see that the Python libraries have a MotorSet. Is that what I'm thinking it is and is there an equivalent for this library?

karlmdavis avatar Feb 03 '21 14:02 karlmdavis

No, there is currently no such functionality. This library is based on version 1 of the python library wich does not have such utility functions.

As it seems, MotorSet is primarily used to set a command or get the current state for all assigned motor. But it is not possible to set the speed or the duty_cycle for each connected motor. So you either have to use a subclass like MoveTank which is limited to two motors or keep track of the motors manually to set the individual speeds. It is currently not planed to add higher level functions such as pid controllers, odometry or other such functionality provided by MoveTank and comparable classes.

But it would be possible to implement a slightly more advanced version of MotorSet that supports setters for individual speeds of motors. Such an implementation dependents on a subset of RFC-2000 (to check the parameter count for a set_speed function at compile time) wich is scheduled to for release in stable at 2021-03-25. Here is a proposed usage example:

let motor1 = LargeMotor::get(MotorPort::OutA)?;
let motor2 = LargeMotor::get(MotorPort::OutB)?;
let motor3 = MediumMotor::get(MotorPort::OutC)?;

let motor_set = MotorSet::create<3>([motor1, motor2, motor3]);
motor_set.run_direct()?;
motor_set.set_duty_cycle_sp([50, 70, 30])?;
motor_set.stop()?;

This would achieve the same as:

let motor1 = LargeMotor::get(MotorPort::OutA)?;
let motor2 = LargeMotor::get(MotorPort::OutB)?;
let motor3 = MediumMotor::get(MotorPort::OutC)?;

motor1.run_direct()?;
motor2.run_direct()?;
motor3.run_direct()?;

motor1.set_duty_cycle_sp(50)?;
motor2.set_duty_cycle_sp(70)?;
motor3.set_duty_cycle_sp(30)?;

motor1.stop()?;
motor2.stop()?;
motor3.stop()?;

What would be your intended use case? Would such a MotorSet struct be sufficient for it?

pixix4 avatar Feb 03 '21 16:02 pixix4

So, again, I'm pretty new to all this, but I'm seeing some driving behavior that I suspect is a timing issue. When I use this Python program, I notice that the robot "kicks" a bit to one side when it starts driving: https://github.com/ev3dev/ev3dev-lang-python-demo/blob/stretch/robots/EXPLOR3R/auto-drive.py

My initial guess here is that it's a timing issue: one wheel starts moving slightly before another, and that acts as a bit of accidental steering. I further guess/hope that if I update that code to use something like a MotorSet, instead, that the problem will go away. Lot of assumptions here, I know -- I only get a tiny bit of time every so often to play with this stuff.

My current mini project is rewriting that little program in Rust, so I was hoping to use a MotorSet or somesuch and avoid the question altogether.

karlmdavis avatar Feb 03 '21 21:02 karlmdavis

Yes, such small "kicks" are a typical problem on ev3 robot and it is indeed a timing problem. Unfortunately a MotorSet cannot solve this problem. It is mostly used for convenience to reduce code size. Here is an example of the set_polarity function:

for motor in motors:
    motor.polarity = polarity

As you can see, this is basically the same implementation as the example code:

for m in motors:
    m.duty_cycle_sp = dc

However, it is possible to reduce these "kicks". First you should initialise all properties used, in particular duty_cycle_sp in your example. Both this and the python library use caching to improve the communication delay with the kernel drivers. At the first call to each property of a motor or sensor, a file handler is opened and stored. All subsequent calls to this property use the already saved file handler. To achieve this, you can add the following code between line 114 and 115 of your linked example.

for m in motors:
    m.duty_cycle_sp = 0

And a rust implementation of this example would reduce this "kick" too. Compared to python, rust is a compiled language and generally has better performance (this also applies to I/O operations).

pixix4 avatar Feb 04 '21 08:02 pixix4