Motors¶
A DC motor is named by the string it has in the robot configuration on the
Driver Hub, such as "left" or "flywheel". There is nothing to look up
first: every motor/* function takes that name.
| Function | Does |
|---|---|
(motor/set-power name p) |
Runs the motor at power p, from -1.0 to 1.0. 0 stops it. |
(motor/power name) |
The power it was last given. |
(motor/set-velocity name v) |
Holds a speed of v encoder ticks per second. |
(motor/velocity name) |
The speed the encoder measures, in ticks per second. |
(motor/set-direction name d) |
:forward or :reverse: which way a positive power turns. |
(motor/direction name) |
:forward or :reverse. |
A typo in a name, or a device that isn't a DC motor, is reported with the name:
user=> (motor/set-power "moter" 0.1)
Execution error: No motor named "moter" in the robot configuration
Each motor/set-* function returns the value it was given, so you can see at
the prompt what was sent to the motor.
Power¶
user=> (motor/set-power "motor" 0.1)
0.1
user=> (motor/power "motor")
0.1
user=> (motor/set-power "motor" 0)
0.0
The power is a fraction of the battery voltage, just like setPower in Java. A
negative power turns the motor the other way. A power outside -1.0 to 1.0 is an
error instead of being silently clipped, so a mistake in a formula shows up at
once:
user=> (motor/set-power "motor" 1.5)
Execution error: motor/set-power expects a power from -1.0 to 1.0, got: 1.5
To limit a computed power on purpose, use clamp, for example
(motor/set-power "motor" (clamp p 0 0.2)).
motor/set-power switches the motor to RUN_WITHOUT_ENCODER, so the power
means the same thing whether or not an encoder is plugged in.
Velocity¶
A flywheel should spin at the same speed no matter how charged the battery is.
With an encoder plugged in, motor/set-velocity asks the hub to hold a speed in encoder
ticks per second, and motor/velocity reads back what the encoder measures:
user=> (motor/set-velocity "flywheel" 1200)
1200.0
user=> (motor/velocity "flywheel")
1187.0
user=> (motor/set-velocity "flywheel" 0)
0.0
The measured speed settles close to the target after a moment. A negative velocity turns the motor the other way. The number of ticks in one turn depends on the motor; check its data sheet.
motor/set-velocity switches the motor to RUN_USING_ENCODER, and a later
motor/set-power switches it back, so the two can be mixed on one motor.
Check the encoder cable first
The hub keeps raising the power until the encoder reports the target
speed. If the encoder isn't plugged in, that never happens, and the motor
runs at full power. Try a small velocity first and watch (motor/velocity name).
Direction¶
On a drivetrain, the motors on one side are mounted the other way round. Reverse them once, and the same positive power then drives every wheel forward:
user=> (motor/set-direction "left" :reverse)
:reverse
user=> (motor/direction "left")
:reverse
Testing¶
This loop tests one motor, named "motor". A and B pick the direction, D-pad
up and down change the power in steps of 0.01 between 0 and 0.2, and the motor
runs while you hold the right trigger:
(def motor-name "motor")
(defonce power (atom 0.1)) ; change it from the REPL too: (reset! power 0.15)
(defn pressed?
"True when button k is down now but was up in the previous snapshot."
[prev pad k]
(and (k pad) (not (k prev))))
(defn step [prev pad]
(when (pressed? prev pad :a) (motor/set-direction motor-name :forward))
(when (pressed? prev pad :b) (motor/set-direction motor-name :reverse))
(when (pressed? prev pad :dpad-up) (swap! power (fn [p] (clamp (+ p 0.01) 0 0.2))))
(when (pressed? prev pad :dpad-down) (swap! power (fn [p] (clamp (- p 0.01) 0 0.2))))
(motor/set-power motor-name (if (> (:right-trigger pad) 0) @power 0))
(telemetry/clear)
(telemetry/add-line "Motor" motor-name)
(telemetry/add-line "Direction" (motor/direction motor-name))
(telemetry/add-line (format "Power %6.2f" @power)))
(defn control-loop []
(try
(loop [prev (gamepad 1)]
(let [pad (gamepad 1)]
(step prev pad)
(time/sleep 0.02)
(recur pad)))
(finally
(motor/set-power motor-name 0))))
Send it, then start the loop with
(control/start control-loop) and stop it with (control/stop) (see
Control Loops).
- A press, not a hold. A snapshot from
(gamepad 1)only says whether a button is down. The loop keeps the previous snapshot inprev, andpressed?is true only in the round where the button goes down. Holding D-pad up therefore raises the power once, not fifty times a second. - Tune it live.
poweris an atom, so(reset! power 0.15)from another connection changes it while the loop runs; see Atom. - Stop safely. The
finallysets the power back to 0 when the loop ends, whether bycontrol/stop, STOP or an error. Pressing STOP on the Driver Hub also stops every motor.