Replies: 3 comments
-
|
Okay, answering my own question a little bit. It seems like the key thing here is being able to check whether the user is actually holding the control is occasionally sending a perturbing signal to the motor to overcome the static braking force. This script is able to detect when the motor is held or not with a very quick feedback. Note that the average duty cycle here is 10%, which is not ordinarily enough to even overcome the motor's static friction. I could make the program even better by switching to a fixed duty if I detect that the motor is moving in the driven direction. Meanwhile the motor is still backdrivable at this duty cycle, with significant effort. See here for demo: https://photos.app.goo.gl/RtH7X9DqfcRcbeDi9 from pybricks.hubs import ThisHub
from pybricks.parameters import Color, Port
from pybricks.ev3devices import Motor
from pybricks.tools import wait, StopWatch
from micropython import const
from umath import sin, pi
hub = ThisHub()
m = Motor(Port.A)
OSCILLATE_PERIOD = const(25) # ms
STATIC_FRICTION_DUTY = const(20)
def compute_duty(base_duty, t) -> int:
added_duty = sin(t / OSCILLATE_PERIOD * 2 * pi) * STATIC_FRICTION_DUTY
return int(base_duty + added_duty)
TEST_TIME = const(20000) # ms
w = StopWatch()
report = StopWatch()
speed_pos_count = 0
while True:
t = w.time()
if t > TEST_TIME:
break
base_duty = 10
duty = compute_duty(base_duty, t)
m.dc(duty)
if report.time() > 200:
print(
"Time:",
t,
"ms, Base Duty:",
base_duty,
" Speed:",
m.speed(),
" Held?",
m.speed() == 0,
)
report.reset()
if m.speed() > 0:
speed_pos_count += 1
if speed_pos_count > 5:
hub.light.on(Color.GREEN)
else:
speed_pos_count = 0
hub.light.on(Color.RED)
wait(1) |
Beta Was this translation helpful? Give feedback.
-
This is in the nature of DC Motors. 0% is shorting the motor, so already applying a passive brake. Any duty signal makes the resistance you feel even stronger. If you want a "loose" control input, you could instead use a negative D gain (see
this is indeed very similar to the above 😄 |
Beta Was this translation helpful? Give feedback.
-
|
I obtained a serviceable result. Demo video: https://photos.app.goo.gl/YoXWD41TQHNDVNVo9 Code: from pybricks.hubs import ThisHub
from pybricks.parameters import Color, Port, Stop
from pybricks.ev3devices import Motor
from pybricks.tools import wait, StopWatch
from micropython import const
from umath import sin, pi
hub = ThisHub()
m = Motor(Port.A)
# These variables are used to compute a duty cycle that resists the user's
# attempt to turn the motor in a direction with a low duty cycle, while still
# allowing us to tell when the user stops turning the motor in that direction.
OSCILLATE_PERIOD = 22 # ms
STATIC_FRICTION_DUTY = 20
T_COEFF = 2 * pi / OSCILLATE_PERIOD
RESIST_MAX_DUTY = 30
def resist_duty(base_duty, t) -> int:
added_duty = sin(t * T_COEFF) * STATIC_FRICTION_DUTY
return int(base_duty + added_duty)
w = StopWatch()
lw = StopWatch()
LOG_PERIOD = const(100)
def maybe_log(x):
if lw.time() > LOG_PERIOD:
print(x)
while True:
t = w.time()
angle = m.angle()
speed = m.speed()
maybe_log(f"Angle: {angle}, Speed: {speed}")
if abs(angle) < 10:
hub.light.on(Color.BLUE)
maybe_log("Near zero angle")
if abs(angle) < 1:
m.stop()
elif (angle > 0) != (speed > 0) and speed != 0:
# If the motor is currently turned in one direction and turning back
# toward zero, encourage that motion by running the motor toward zero.
maybe_log("Turning toward zero")
m.run_angle(speed=1400, rotation_angle=-angle, wait=False, then=Stop.COAST)
hub.light.on(Color.GREEN)
else:
# If the user is currently turning the motor away from zero, resist
# that movement with a duty cycle that increases the further we get
# from zero. Duty cycle of resistance maxes out at 15%.
base_duty = 0
if abs(angle) > 50:
base_duty = min(RESIST_MAX_DUTY, RESIST_MAX_DUTY * (abs(angle) - 50) / 130)
duty = resist_duty(base_duty, t)
duty = duty if angle < 0 else -duty
maybe_log(f"Resisting with base_duty: {base_duty} duty: {duty}")
m.dc(duty)
if duty < 8:
hub.light.on(Color.ORANGE)
else:
hub.light.on(Color.RED)
if lw.time() > LOG_PERIOD:
lw.reset()
wait(1) |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
All,
I'm working on testing the EV3 bluetooth code by building the lego tank and EV3 remote controller. One of the features of this project is that the tank's turn rate is set by backdriving a motor and sensing its angle. This is the "steering wheel" of the remote control.
I want to make the steering wheel have "force feedback". When I am reaching the limit of the turn input (i.e. I'm turning 100% to the right or 100% to the left), I want the motor to resist further turning of the wheel. I'd like the resistance force to ramp up as the user gets closer to the input limit.
I wrote a little test program to try to see what range of duty cycles I should use for force feedback:
What I found was interesting. It becomes almost impossible to backdrive the motor even at a duty cycle of about 15%. However, another test program showed that even 100% duty was unable to overcome a relatively modest resisting force.
So here is the desired behavior:
The second part of the challenge seems very tricky. For example, if we're at 75% input, I want to use a duty of, say, 4%, to resist the movement of the wheel further in the input direction. However, if the user lets go of the wheel, I want to use a much higher duty cycle to, first, overcome static friction, and, second, to quickly return the wheel to neutral. It seems really tricky to detect when the user has let go of the wheel, though.
I'm curious if anyone has solved a problem like this and what pointers anyone might have to offer on how to implement something like this.
Beta Was this translation helpful? Give feedback.
All reactions