Skip to content

Commit d701476

Browse files
committed
42114-volvo-articulated-hauler: Copy-edit project.
Applies small fixes to description and formats the code.
1 parent 453dd16 commit d701476

File tree

5 files changed

+108
-58
lines changed

5 files changed

+108
-58
lines changed
-8.09 KB
Loading

sets/technic/42114-volvo-articulated-hauler/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "6x6 Volvo Articulated Hauler"
33
number: 42114
44
image:
55
local: "42114-volvo-articulated-hauler.jpg"
6-
credit: "Repkovsky"
6+
credit: "LEGO"
77
layout: set
88
description: "Add set description"
99
---

sets/technic/42114-volvo-articulated-hauler/powered-up-remote/index.md

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ maintainer:
55
name: "Repkovsky"
66
image:
77
local: "42114_88010.jpg"
8+
credit: "LEGO"
89
description:
910
"Control the Volvo articulated hauler with the Powered Up Remote!"
1011
video:
@@ -15,24 +16,70 @@ code: "#program"
1516
---
1617

1718

18-
# Program
19+
# Manual and automatic switching mode
1920

20-
The program for controlling Technic Volvo Articulated Hauler with remote control is more complicated than the other remote control programs because of the 3-gear gearbox, which works both in manual and automatic mode. Manual switching gears requires not only detection whether remote button is pressed or not, but rather detection of button pressing (or releasing) moment itself. This is realized using class '''Key'''. Automatic gearbox needs to detect proper time for switching gear up or down. This is possible by measuring speed of XL motor, when it is running. If the speed is systematically very low, the gear is decreased. If the speed is systematically close to maximum, the gear is increased. To make speed measurement robust to random variations, values obtained from speed sensor are filtered using simple [exponential smoothing|https://en.wikipedia.org/wiki/Exponential_smoothing]. Threshold values of speed, measurement time and smoothing constant are defined by constants `HI_SPEED, LO_SPEED`, `STABLE_SPEED_TIME` and `SMOOTHING`. If the automatic gearbox does not change gears even if Hauler reaches full speed, `HI_SPEED` should be decreased. Speed tracking, keeping the current state of gearbox and handling the remote/hub LEDs was implemented in the class '''Gearbox'''.
21+
The program for controlling Technic Volvo Articulated Hauler with remote
22+
control is a bit more complicated than the other remote control programs
23+
because of the 3-gear gearbox, which works both in manual and automatic mode.
24+
25+
Manual gear switching requires not only detection whether the remote button is
26+
pressed or not, but also detection of the button press (or release) moment
27+
itself. This is realized using the ``Key`` class.
28+
29+
The automatic gearbox needs to detect the proper time for switching the gear
30+
up or down. This is possible by measuring the speed of XL motor, when it is
31+
running. If the speed is systematically very low, the gear is decreased.
32+
If the speed is systematically close to maximum, the gear is increased.
33+
34+
To make speed measurement robust to random variations, values obtained from
35+
the speed sensor are filtered using simple
36+
[exponential smoothing](https://en.wikipedia.org/wiki/Exponential_smoothing).
37+
Threshold values of speed, measurement time and smoothing constant are defined
38+
by the constants `HI_SPEED, LO_SPEED`, `STABLE_SPEED_TIME` and `SMOOTHING`.
39+
If the automatic gearbox does not change gears even if Hauler reaches full
40+
speed, `HI_SPEED` should be decreased. Speed tracking, keeping the current
41+
state of gearbox and handling the remote/hub LEDs was implemented in the
42+
``Gearbox`` class.
2143

2244
# Driving and switching gears
2345

24-
By default left controller controlls left/right steering, and right controller determines direction of driving. This can be changed easily by setting constant `LEFT_STEER_RIGHT_DRIVE` to False.
46+
By default, the left controller controls the left/right steering, and the right
47+
controller determines the direction of driving. This can be changed easily by
48+
setting the constant `LEFT_STEER_RIGHT_DRIVE` to False.
2549

26-
Gearbox can be used in two modes, as in the original LEGO smartphone app: *automatic* and *manual*. You can switch between the modes by pushing the green button on the remote. *Automatic* is default starting mode, but this can be easily changed by modifying constant `INIT_GEARBOX_AUTO`.
50+
The gearbox can be used in two modes, as in the original LEGO smartphone app:
51+
*automatic* and *manual*. You can switch between the modes by pushing the green
52+
button on the remote. *Automatic* is default starting mode, but this can be
53+
easily changed by modifying constant `INIT_GEARBOX_AUTO`.
2754

28-
In the *automatic* mode, gears are changed when program detects that motor's speed is too slow or close to maximum speed. Current gear is indicated by the color of the remote's LED - 1: cyan, 2: blue, 3: magenta. To enable dumper (remote's LED: green), press right red button on the remote. To go back to driving mode, press left red button. If drive is idle for time longer than defined in constant `GEAR_RESET_TIMEOUT`, the gear is set to 1.
55+
In the *automatic* mode, gears are changed when program detects that motor's
56+
speed is too slow or close to maximum speed. The current gear is indicated by
57+
the color of the remote's LED:
58+
1. Cyan
59+
2. Blue
60+
3. magenta
61+
62+
To enable dumper (remote LED: green), press the right red button on the remote.
63+
To go back to driving mode, press left red button. If drive is idle for time
64+
longer than defined in constant `GEAR_RESET_TIMEOUT`, the gear is set to 1.
2965

30-
In the *manual* mode gear is decreased by pressing left red button, and increased with right red button. Gearbox positions are indicated by the color of LED: 1: yellow, 2: orange, 3: red, Dumper: green.
66+
In the *manual* mode, the gear is decreased by pressing left red button, and
67+
increased with right red button. Gearbox positions are indicated by the color
68+
of LED:
3169

32-
Sometimes gearbox tends to jam. If the target angle of gear selector is not reached within the time defined by `GEAR_SWITCH_TIMEOUT` (1.5sec by default), the automatic gearbox reset is performed. Hub's LED changes to red, while gearbox is recalibrated, setting gear to 1.
70+
1. Yellow
71+
2. Orange
72+
3. Red
73+
74+
Sometimes gearbox tends to jam. If the target angle of gear selector is not
75+
reached within the time defined by `GEAR_SWITCH_TIMEOUT` (1.5 sec by default),
76+
the automatic gearbox reset is performed. The hub LED changes to red, while
77+
the gearbox is recalibrated, which sets the gear to 1.
3378

3479
![](./remote_description.png)
3580

81+
# Program
82+
3683
{% include copy-code.html %}
3784
```python
3885
{% include_relative main.py %}
Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
# Controlling LEGO Volvo Articulated Hauler (42114) with Bluetooth Remote (88010)
2-
# Version 1.2, works with PyBricks 3.1
3-
41
from pybricks.hubs import TechnicHub
52
from pybricks.pupdevices import Motor, Remote
63
from pybricks.parameters import Port, Button, Color
74
from pybricks.tools import wait
8-
5+
96
# control buttons swapping
107
LEFT_STEER_RIGHT_DRIVE = True
11-
8+
129
# set gearbox to AUTO mode at startup?
1310
INIT_GEARBOX_AUTO = True
14-
11+
1512
# steering settings
1613
STEER_ANGLE = 65
1714
STEER_SPEED = 1000
1815
STEER_HARDNESS = 4
19-
16+
17+
2018
class Gearbox:
2119
# if speed is stable above, automatic gearbox will increase gear
2220
HI_SPEED = 1400
@@ -32,9 +30,11 @@ class Gearbox:
3230
SMOOTHING = 0.05
3331
# colors of gearbox state indicator LED for automatic (True) and manual (False)
3432
# [gear 1, gear 2, gear 3, dumper]
35-
POS_COLOR = {True: [Color.CYAN, Color.BLUE, Color.MAGENTA, Color.GREEN],
36-
False: [Color.ORANGE, Color(h=15, s=100, v=100),
37-
Color(h=5, s=100, v=100), Color.GREEN]}
33+
POS_COLOR = {
34+
True: [Color.CYAN, Color.BLUE, Color.MAGENTA, Color.GREEN],
35+
False: [Color.ORANGE, Color(h=15, s=100, v=100), Color(h=5, s=100, v=100), Color.GREEN],
36+
}
37+
3838
def __init__(self, remote: Remote, hub: TechnicHub, drive: Motor):
3939
# assign external objects to properties of the class
4040
self.remote = remote
@@ -50,21 +50,21 @@ def __init__(self, remote: Remote, hub: TechnicHub, drive: Motor):
5050
# set defaults
5151
self.last_auto_pos = 0
5252
self.set_auto(INIT_GEARBOX_AUTO)
53-
53+
5454
def calibrate(self):
55-
# calibrate gearbox motor by finding its physical rotation limit;
55+
# calibrate gearbox motor by finding its physical rotation limit;
5656
# first, move left at full power to handle possible jam in gearbox
5757
self.gearbox.run_until_stalled(360)
5858
# second, correct the position
5959
self.gearbox.run_angle(360, -90)
6060
# finally move left with small power to avoid twisting 12-axle and measurement error
6161
stalled_angle = self.gearbox.run_until_stalled(360, duty_limit=10)
6262
# round to multiple of 90 degrees and subtract angle of physical block (90deg)
63-
base_angle = 90*round(stalled_angle/90)-90
63+
base_angle = 90 * round(stalled_angle / 90) - 90
6464
# adjust settings of possible motor positions
65-
self.pos_angle = [p+base_angle for p in [90, 0, -90, -180]]
65+
self.pos_angle = [p + base_angle for p in [90, 0, -90, -180]]
6666
self.pos = 0
67-
67+
6868
def set_position(self, pos):
6969
# limit positions to range 0,1,2,3
7070
pos = min(3, max(pos, 0))
@@ -97,22 +97,22 @@ def set_position(self, pos):
9797
self.last_auto_pos = self.pos if pos == 3 else pos
9898
# update gear state variable
9999
self.pos = pos
100-
100+
101101
def dumper(self):
102102
# return whether gearbox is set to drive dumper
103103
return self.pos == 3
104-
104+
105105
def set_auto(self, auto):
106106
# set AUTO/MANUAL mode and update control light
107107
self.auto = auto
108108
self.remote.light.on(self.POS_COLOR[self.auto][self.pos])
109-
109+
110110
def update_auto_gear(self):
111111
# in AUTO mode changes gear if speed is stable below/above LO_SPEED/HI_SPEED threshold
112112
if self.auto and not self.dumper():
113113
speed = self.drive.speed()
114114
# basic low-pass filtering (exponential smoothing)
115-
self.speed += self.SMOOTHING*(abs(speed)-self.speed)
115+
self.speed += self.SMOOTHING * (abs(speed) - self.speed)
116116
wait(10)
117117
if self.LO_SPEED < self.speed < self.HI_SPEED:
118118
# speed in medium range, reset timer
@@ -124,12 +124,12 @@ def update_auto_gear(self):
124124
# speed is stable - reset timer
125125
self.speed_timer = 0
126126
# depending on speed and current position,
127-
# return lower, higher or None gear (no change)
127+
# return lower, higher or None gear (no change)
128128
if self.pos > 0 and self.speed < self.LO_SPEED:
129129
self.set_position(self.pos - 1)
130130
elif self.pos < 2 and self.speed > self.HI_SPEED:
131131
self.set_position(self.pos + 1)
132-
132+
133133
def idle(self, persists):
134134
if persists:
135135
# increase idle time
@@ -141,56 +141,59 @@ def idle(self, persists):
141141
gearbox.set_position(0)
142142
else:
143143
self.idle_timer = 0
144-
144+
145+
145146
class Key:
146147
def __init__(self):
147148
# variables to store current and previous state of buttons
148149
self.now_pressed = []
149150
self.prev_pressed = []
150-
151+
151152
def update(self, remote):
152153
# copy list of keys pressed during last update
153154
self.prev_pressed = list(self.now_pressed)
154155
# update list of pressed keys
155156
self.now_pressed = remote.buttons.pressed()
156-
157+
157158
def pressed(self, key):
158159
# return whether key is now pressed
159160
return key in self.now_pressed
160-
161+
161162
def released(self, key):
162163
# return keys which were released after last update
163164
return key in self.prev_pressed and key not in self.now_pressed
164-
165+
166+
165167
def direction(positive, negative):
166168
# return resultant value of two boolean directions
167169
return int(bool(positive)) - int(bool(negative))
168-
169-
if __name__ == '__main__':
170-
CONNECT_FLASHING_TIME = [75]*5 + [1000]
170+
171+
172+
if __name__ == "__main__":
173+
CONNECT_FLASHING_TIME = [75] * 5 + [1000]
171174
hub = TechnicHub()
172175
# Flashing led while waiting connection as remote do
173176
hub.light.blink(Color.WHITE, CONNECT_FLASHING_TIME)
174-
177+
175178
# Connect to the remote.
176179
remote = Remote()
177-
print('Remote connected.')
178-
180+
print("Remote connected.")
181+
179182
# Wait for calibration
180183
hub.light.on(Color.YELLOW)
181-
184+
182185
# initialize driving motor
183186
drive = Motor(Port.A)
184-
187+
185188
# initialize steering motor
186189
steer = Motor(Port.D)
187190
kp, ki, _, _, _ = steer.control.pid()
188191
steer.control.limits(speed=STEER_SPEED)
189-
steer.control.pid(kp=kp*STEER_HARDNESS, ki=ki*STEER_HARDNESS)
190-
192+
steer.control.pid(kp=kp * STEER_HARDNESS, ki=ki * STEER_HARDNESS)
193+
191194
# initialize gearbox
192195
gearbox = Gearbox(remote, hub, drive)
193-
196+
194197
# initialize remote keys
195198
key = Key()
196199
if LEFT_STEER_RIGHT_DRIVE:
@@ -199,44 +202,44 @@ def direction(positive, negative):
199202
else:
200203
BUTTON_DRIVE_FWD, BUTTON_DRIVE_BACK = Button.LEFT_PLUS, Button.LEFT_MINUS
201204
BUTTON_STEER_LEFT, BUTTON_STEER_RIGHT = Button.RIGHT_PLUS, Button.RIGHT_MINUS
202-
205+
203206
# Calibration completed, start the FUN!
204207
hub.light.on(Color.GREEN)
205-
208+
206209
# main loop
207210
while True:
208211
key.update(remote)
209212

210213
# gearbox control
211214
if key.released(Button.LEFT):
212215
# manual - change to lower gear; auto - switch to driving
213-
new_pos = gearbox.last_auto_pos if gearbox.auto else gearbox.pos-1
216+
new_pos = gearbox.last_auto_pos if gearbox.auto else gearbox.pos - 1
214217
gearbox.set_position(new_pos)
215218
elif key.released(Button.RIGHT):
216219
# manual - change to higher gear/dumper; auto - switch to dumper
217-
new_pos = 3 if gearbox.auto else gearbox.pos+1
220+
new_pos = 3 if gearbox.auto else gearbox.pos + 1
218221
gearbox.set_position(new_pos)
219222
elif key.released(Button.CENTER):
220223
# switch gearbox mode to the other one
221224
gearbox.set_auto(not gearbox.auto)
222-
225+
223226
# drive control
224-
drive_direction = direction(key.pressed(BUTTON_DRIVE_FWD),
225-
key.pressed(BUTTON_DRIVE_BACK))
226-
if drive_direction in [-1,1]:
227+
drive_direction = direction(key.pressed(BUTTON_DRIVE_FWD), key.pressed(BUTTON_DRIVE_BACK))
228+
if drive_direction in [-1, 1]:
227229
# change gear automatically, if gearbox is in AUTO mode
228230
gearbox.update_auto_gear()
229231
# for dumper, direction of rotation must be inverted
230232
invert = 1 if gearbox.dumper() else -1
231-
drive.dc(invert*drive_direction*100.0)
233+
drive.dc(invert * drive_direction * 100.0)
232234
# report active drive
233235
gearbox.idle(False)
234236
else:
235237
drive.stop()
236238
# report idle drive, if not set to dumper
237239
gearbox.idle(not gearbox.dumper())
238-
240+
239241
# steering control
240-
steer_direction = direction(key.pressed(BUTTON_STEER_RIGHT),
241-
key.pressed(BUTTON_STEER_LEFT))
242-
steer.run_target(STEER_SPEED, steer_direction*STEER_ANGLE, wait=False)
242+
steer_direction = direction(
243+
key.pressed(BUTTON_STEER_RIGHT), key.pressed(BUTTON_STEER_LEFT)
244+
)
245+
steer.run_target(STEER_SPEED, steer_direction * STEER_ANGLE, wait=False)

sets/technic/42114_88010.jpg

198 KB
Loading

0 commit comments

Comments
 (0)