From deeafb71d1b9bd616ba9db2899648dd0c10b6483 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 26 Aug 2023 15:36:22 +0100 Subject: [PATCH 01/81] slight tweak to power board --- kit/power_board.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kit/power_board.md b/kit/power_board.md index c8c593c2..ba0e8c4a 100644 --- a/kit/power_board.md +++ b/kit/power_board.md @@ -24,13 +24,13 @@ Connectors ---------- There are six power output connectors on the board, labelled L0–L3, H0, and H1. -These are enabled when your robot code is started, and supply around 11.1V -(±15%). These may be referred to as 12V ports elsewhere in the docs. They should -be used to connect to the motor and servo board power inputs. The "H" connectors -will supply more current than the "L" connectors. +These are enabled when your robot code is started, supply around 11.1V (±15%) and will turn off when your robot code finishes. +These may be referred to as 12V ports elsewhere in the docs. +They should be used to connect to the motor and servo board power inputs. +The "H" connectors will supply more current than the "L" connectors.
- When connecting the brain board, the "L2" port should be used. + When connecting the brain board, the "L2" port must be used. This ensures the Brain Board can turn on before the rest of the power outputs. Your Brain Board will not power on unless it's connected to "L2".
From 37547764c2723a3d8de9ce710932594800b67002 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 26 Aug 2023 16:30:01 +0100 Subject: [PATCH 02/81] Update servo numbers for new FW, rewrite for ease of understanding --- kit/servo_board.md | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/kit/servo_board.md b/kit/servo_board.md index 47197dfb..4eb3b337 100644 --- a/kit/servo_board.md +++ b/kit/servo_board.md @@ -10,7 +10,7 @@ Servo Board A photo of a servo board The Servo Board can be used to control up to 12 RC servos. -Many devices are available that can be controlled as servos, such as RC motor speed controllers, and these can also be used with the board. +Many devices are available that can be controlled as servos, such as RC motor speed controllers, so these can also be used with this board. Board Diagram ------------- @@ -24,12 +24,12 @@ Indicators | Power | The board is powered over USB. | On | | 5.5V On | There is 5.5V power on the board. This usually indicates that the 12V connector rail is powered. | Off | | Aux On | There is auxiliary power on the board. | On | -| STATUS\|ERROR | Blue when the board has successfully booted
Solid pink if an error has occurred (often 12V power being lost) | Blue | +| STATUS\|ERROR | Blue when the board has successfully booted
Solid pink if 12V power is lost | Blue | Connectors ---------- -There are 8 servo connections on the left-side of the board (numbers 0-7), and 4 on the right (numbers 8-11). +There are 8 servo connections on the left-side of the board (numbers 0-7), and 4 auxiliary outputs on the right (numbers 8-11). See the labels on the board (also visible in the photo above) for how these numbers map to the outputs. Servo cables are connected vertically, with 0V (the black or brown wire) at the bottom of the board. @@ -38,6 +38,16 @@ For the servo board to operate correctly, you must connect it to the 12V power rail from the power board. A green LED will light next to the servo board 12V connector when it is correctly powered. +Auxiliary outputs +----------------- + +Servo outputs 8-11 are supplied from the separate auxiliary power input. +If you want to use these ports the power has to be connected to either the a 5V or 12V output of the power board. + +
+Only connect 12V to the auxiliary power input if the servos connected to port 8-11 support 12V. +
+ Case Dimensions --------------- @@ -46,27 +56,15 @@ The case measures 68×68×21mm. Don't forget that the cables will stick out. Servo Control ------------- -Control of servos is often referred to as using -pulse-width modulation (PWM) -and while that is a useful way to convey the general idea it is unfortunately -not a technically precise description of either how the control protocol -operates nor how the Servo Board implements the control. - -Instead RC servos typically to react to the duration of the pulse sent to them -and essentially ignore the gaps between the pulses. However this is not -universally the case and some servos do care about the time between pulses as -well. +For an RC servo the angle of rotation is determined by the width of an electrical pulse on the control wire. +This is a form of +pulse-width modulation. (PWM) +A typical servo expects to see a pulse every 20ms, however this can vary from servo to servo. The Servo Board is able to precisely control the duration of the pulses sent to servos (based on the values you configure using the Python API), within the ranges in the table below. -Conversely the time between pulses sent to a given servo is allowed a much wider -variation and in fact can vary even when that servo is not changing position. -For example this can happen if another servo's position changes from being a -longer pulse than the first to a shorter pulse. The range of possible gaps -quoted below represents the worst possible case that a servo may experience. - Specification ------------- @@ -76,10 +74,9 @@ Specification | Nominal input voltage | 11.1V ± 15% | | Output voltage | 5.5V | | Maximum total output current [^1] | 10A | -| Pulse range | 1ms — 2ms | +| Default pulse range | 1ms — 2ms | +| Maximum pulse range | 0.5ms — 4ms | | Pulse precision | 5µs | -| Min time between pulses | 16.075ms (62.208Hz) | -| Max time between pulses | 18.575ms (53.836Hz) | [^1]: If the auxiliary input is connected, outputs 8-11 have an independent maximum current. From 10d110ccbeb065a825b0e4d87ef5a4d2674f7a7f Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 26 Aug 2023 16:31:15 +0100 Subject: [PATCH 03/81] Specify L2 is reserved --- kit/power_board.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kit/power_board.md b/kit/power_board.md index ba0e8c4a..b105dc51 100644 --- a/kit/power_board.md +++ b/kit/power_board.md @@ -9,9 +9,8 @@ Power Board A photo of a power board -The Power Board distributes power to the SR kit from the battery. It provides -eight individual general-purpose power outputs, with one reserved for powering -the Brain Board. +The Power Board distributes power to the SR kit from the battery. +It provides eight individual general-purpose power outputs, with port L2 reserved for powering the Brain Board. It also holds the internal On|Off switch for the whole robot as well as the Start button which is used to start your robot code running. From 187f0b8c2df087ae6d477a86e92c32c9d3e47bb8 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 26 Aug 2023 16:45:35 +0100 Subject: [PATCH 04/81] Update power board image to label brain port (L2) --- images/content/kit/power_board_v4_diagram.png | Bin 18635 -> 50509 bytes images/content/kit/power_board_v4_diagram.svg | 176 ++++++++++++------ 2 files changed, 118 insertions(+), 58 deletions(-) diff --git a/images/content/kit/power_board_v4_diagram.png b/images/content/kit/power_board_v4_diagram.png index a7414e95778162cb623b241fe8984687b338d459..500f67ac7f025699a4c4d7ec702eed61791ce7aa 100644 GIT binary patch literal 50509 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 B;zR%d literal 18635 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 AM*si- diff --git a/images/content/kit/power_board_v4_diagram.svg b/images/content/kit/power_board_v4_diagram.svg index 8cd937f5..4213220e 100644 --- a/images/content/kit/power_board_v4_diagram.svg +++ b/images/content/kit/power_board_v4_diagram.svg @@ -2,15 +2,41 @@ + id="svg2" + sodipodi:docname="power_board_v4_diagram.svg" + inkscape:export-filename="power_board_v4_diagram.png" + inkscape:export-xdpi="36.917416" + inkscape:export-ydpi="36.917416" + inkscape:version="1.2.2 (732a01da63, 2022-12-09)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + + + + @@ -149,7 +187,7 @@ image/svg+xml - + @@ -394,7 +432,8 @@ y="61.217251" id="rect6902" style="font-size:18px" /> + + H0 H0 + Terminals + BrainPower Port Button On / OffOn / OffButton + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;text-align:start;writing-mode:lr-tb;text-anchor:start">Button Switch Plug External On / OffExternal On / OffSwitch Plug + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;text-align:start;writing-mode:lr-tb;text-anchor:start">Switch Plug LED 5V On LED + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;text-align:start;writing-mode:lr-tb;text-anchor:start">5V On LED + d="m 1112.1354,849.43385 -72.8453,-36.89486" + id="path9344-7" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8814-0)" + sodipodi:nodetypes="cc" /> + d="m 1171.809,679.73191 -95.1631,-32.23303 h -65.6599" + id="path8944-3" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8814)" + sodipodi:nodetypes="ccc" /> + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker8814)" + sodipodi:nodetypes="cc" /> From 7f8508001c10ae4d7ca0f1d4bd0c4203210f093c Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 26 Aug 2023 17:08:11 +0100 Subject: [PATCH 05/81] Update diagram to label status leds --- images/content/kit/mcv4b_board_diagram.png | Bin 31739 -> 32312 bytes images/content/kit/mcv4b_board_diagram.svg | 118 ++++++++++++++------- 2 files changed, 82 insertions(+), 36 deletions(-) diff --git a/images/content/kit/mcv4b_board_diagram.png b/images/content/kit/mcv4b_board_diagram.png index c98d9fe66bcf3924195517147f11c4b9f3f558d7..6dcce1d779bdbeb4fa9a181e6f5c1db21894171b 100644 GIT binary patch literal 32312 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

- Indicators ---------- @@ -33,6 +28,7 @@ Indicators |------------------------|-------------------------|------------------------------ | Power | Green when powered correctly
Red when polarity is wrong for Power In | Green | M{0,1} Speed/Direction | Brightness indicates speed, colour indicates direction | Off +| M{0,1} Output Status | Blue indicates more than 50% load (5A)
Red indicates a motor driver fault | Off | USB Power | The USB interface is powered | On | USB Data | Data is being transferred to/from the board | Off From 6eaf212686f1909e76b17d9bca7e5999347413bf Mon Sep 17 00:00:00 2001 From: Josh Perriman Date: Sun, 27 Aug 2023 17:04:11 +0100 Subject: [PATCH 07/81] Remove power board note stating individual outputs Co-authored-by: Jake Howard --- kit/power_board.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kit/power_board.md b/kit/power_board.md index b105dc51..18252b76 100644 --- a/kit/power_board.md +++ b/kit/power_board.md @@ -10,7 +10,7 @@ Power Board A photo of a power board
The Power Board distributes power to the SR kit from the battery. -It provides eight individual general-purpose power outputs, with port L2 reserved for powering the Brain Board. +It provides eight general-purpose power outputs, with port L2 reserved for powering the Brain Board. It also holds the internal On|Off switch for the whole robot as well as the Start button which is used to start your robot code running. From f8090ce9865ea9f5e9c2719ec7025d5e7615252c Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 17:13:20 +0100 Subject: [PATCH 08/81] Add notes about the fan --- kit/power_board.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kit/power_board.md b/kit/power_board.md index 18252b76..4ec7f7e9 100644 --- a/kit/power_board.md +++ b/kit/power_board.md @@ -73,6 +73,11 @@ Controls | ON\|OFF | Turns the power board on, when used in conjunction with an external switch | START | Starts your program +Fan +--- + +The power board has a fan to keep the board cool when it is under high load. +The fan will only begin to spin when the board gets hot. Case Dimensions --------------- From 7802bb75baf74cab0b622a122ed146978c5d7b95 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 17:52:12 +0100 Subject: [PATCH 09/81] sidebar rules --- _data/sidebar_tree.yaml | 6 ++++-- rules/code_of_conduct.md | 9 +++++++++ kit/safety-regulations.md => rules/safety_regulations.md | 0 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 rules/code_of_conduct.md rename kit/safety-regulations.md => rules/safety_regulations.md (100%) diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index c8caf71e..828e60de 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -30,8 +30,6 @@ tree: title: Ruggeduino - url: /kit/servo_board title: Servo Board - - url: /kit/safety-regulations - title: Safety Regulations - url: /kit/wifi title: WiFi - url: /programming/ @@ -79,8 +77,12 @@ tree: - url: /rules/ title: Rules tree: + - url: /rules/code_of_conduct + title: Code Of Conduct - url: /rules/archive title: Game Rules Archive + - url: /rules/safety_regulations + title: Safety Regulations - url: /simulator/ title: Simulator tree: diff --git a/rules/code_of_conduct.md b/rules/code_of_conduct.md new file mode 100644 index 00000000..b9b1e837 --- /dev/null +++ b/rules/code_of_conduct.md @@ -0,0 +1,9 @@ +--- +layout: page +title: Code Of Conduct +--- + +Code Of Conduct +=============== + +TODO \ No newline at end of file diff --git a/kit/safety-regulations.md b/rules/safety_regulations.md similarity index 100% rename from kit/safety-regulations.md rename to rules/safety_regulations.md From 00e40234ca373e384024139eeb40cff94dae1017 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 18:04:40 +0100 Subject: [PATCH 10/81] sidebar kit section --- _data/sidebar_tree.yaml | 20 +++++++++---------- kit/{ruggeduino.md => arduino.md} | 2 +- .../brain_board/python_libraries.md | 6 +++--- kit/brain_board/{updates.md => robot_os.md} | 8 ++++++-- kit/{ => brain_board}/wifi.md | 0 {kit => tutorials}/assembly.md | 0 6 files changed, 20 insertions(+), 16 deletions(-) rename kit/{ruggeduino.md => arduino.md} (99%) rename programming/python/libraries.md => kit/brain_board/python_libraries.md (97%) rename kit/brain_board/{updates.md => robot_os.md} (93%) rename kit/{ => brain_board}/wifi.md (100%) rename {kit => tutorials}/assembly.md (100%) diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index 828e60de..1b8b33c2 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -6,8 +6,6 @@ tree: - url: /kit/ title: Kits tree: - - url: /kit/assembly - title: Assembly - url: /kit/batteries/ title: Batteries tree: @@ -18,20 +16,22 @@ tree: - url: /kit/brain_board/ title: Brain Board tree: - - url: /kit/brain_board/updates - title: Updates + - url: /kit/brain_board/robot_os + title: Robot OS + - url: /kit/brain_board/python_libraries + title: Python Libraries + - url: /kit/brain_board/wifi + title: WiFi - url: /kit/brain_board/advanced title: Advanced - url: /kit/motor_board title: Motor Board - url: /kit/power_board title: Power Board - - url: /kit/ruggeduino - title: Ruggeduino + - url: /kit/arduino + title: Arduino - url: /kit/servo_board title: Servo Board - - url: /kit/wifi - title: WiFi - url: /programming/ title: Programming tree: @@ -42,8 +42,6 @@ tree: tree: - url: /programming/python/functions title: Functions - - url: /programming/python/libraries - title: Libraries - url: /programming/sr/ title: sr tree: @@ -98,6 +96,8 @@ tree: - url: /tutorials/ title: Tutorials tree: + - url: /tutorials/assembly + title: Assembly - url: /tutorials/basic_motor_control title: Basic Motor Control - url: /tutorials/microgames diff --git a/kit/ruggeduino.md b/kit/arduino.md similarity index 99% rename from kit/ruggeduino.md rename to kit/arduino.md index d421eb5a..a4ca06ca 100644 --- a/kit/ruggeduino.md +++ b/kit/arduino.md @@ -1,6 +1,6 @@ --- layout: page -title: Ruggeduino +title: Arduino --- # Ruggeduino diff --git a/programming/python/libraries.md b/kit/brain_board/python_libraries.md similarity index 97% rename from programming/python/libraries.md rename to kit/brain_board/python_libraries.md index e926a6cd..4c8231f6 100644 --- a/programming/python/libraries.md +++ b/kit/brain_board/python_libraries.md @@ -1,10 +1,10 @@ --- layout: page -title: Included Python Libraries +title: Available Python Libraries --- -Included Libraries -================== +Available Python Libraries +========================== Robot Kit --------- diff --git a/kit/brain_board/updates.md b/kit/brain_board/robot_os.md similarity index 93% rename from kit/brain_board/updates.md rename to kit/brain_board/robot_os.md index cc9f8070..b13b7c12 100644 --- a/kit/brain_board/updates.md +++ b/kit/brain_board/robot_os.md @@ -1,9 +1,13 @@ --- layout: page -title: Updates -extra_js: updates +title: Robot OS --- +Robot OS +======== + +TODO + # Updates Keeping your kit up to date is very important. It enables us to deploy new features, as well as fix bugs. diff --git a/kit/wifi.md b/kit/brain_board/wifi.md similarity index 100% rename from kit/wifi.md rename to kit/brain_board/wifi.md diff --git a/kit/assembly.md b/tutorials/assembly.md similarity index 100% rename from kit/assembly.md rename to tutorials/assembly.md From e7bcdd6db07896a2245bd076729765115a905c87 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 18:42:39 +0100 Subject: [PATCH 11/81] sidebar programming section --- _data/sidebar_tree.yaml | 73 ++++++++++--------- .../custom_firmware.md | 6 +- .../{sr/ruggeduinos => arduino}/index.md | 6 +- programming/{sr => }/cheat_sheet.md | 0 .../{sr/compass/index.md => compass.md} | 0 programming/index.md | 22 ++++++ programming/{sr/leds/index.md => leds.md} | 6 +- programming/{sr/motors/index.md => motors.md} | 6 +- programming/{sr/power/index.md => power.md} | 6 +- programming/python/functions.md | 38 ---------- programming/python/index.md | 25 ------- programming/{sr/radio/index.md => radio.md} | 0 programming/{sr => robot_api}/index.md | 4 +- programming/robot_api/metadata.md | 10 +++ programming/{sr/servos/index.md => servos.md} | 6 +- programming/{sr => }/vision/index.md | 0 programming/{sr => }/vision/markers.md | 0 programming/vision/orientation.md | 9 +++ programming/vision/position.md | 10 +++ {programming => tutorials}/editors/index.md | 0 {programming => tutorials}/editors/pycharm.md | 0 {programming => tutorials}/editors/vscode.md | 0 .../getting_code_on_the_robot.md | 0 23 files changed, 108 insertions(+), 119 deletions(-) rename programming/{sr/ruggeduinos => arduino}/custom_firmware.md (98%) rename programming/{sr/ruggeduinos => arduino}/index.md (98%) rename programming/{sr => }/cheat_sheet.md (100%) rename programming/{sr/compass/index.md => compass.md} (100%) rename programming/{sr/leds/index.md => leds.md} (95%) rename programming/{sr/motors/index.md => motors.md} (98%) rename programming/{sr/power/index.md => power.md} (98%) delete mode 100644 programming/python/functions.md delete mode 100644 programming/python/index.md rename programming/{sr/radio/index.md => radio.md} (100%) rename programming/{sr => robot_api}/index.md (99%) create mode 100644 programming/robot_api/metadata.md rename programming/{sr/servos/index.md => servos.md} (97%) rename programming/{sr => }/vision/index.md (100%) rename programming/{sr => }/vision/markers.md (100%) create mode 100644 programming/vision/orientation.md create mode 100644 programming/vision/position.md rename {programming => tutorials}/editors/index.md (100%) rename {programming => tutorials}/editors/pycharm.md (100%) rename {programming => tutorials}/editors/vscode.md (100%) rename {programming => tutorials}/getting_code_on_the_robot.md (100%) diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index 1b8b33c2..39e77733 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -28,50 +28,42 @@ tree: title: Motor Board - url: /kit/power_board title: Power Board - - url: /kit/arduino - title: Arduino - url: /kit/servo_board title: Servo Board + - url: /kit/arduino + title: Arduino - url: /programming/ title: Programming tree: - - url: /programming/getting_code_on_the_robot - title: Getting Code on the Robot - - url: /programming/python/ - title: Python + - url: /programming/robot_api/ + title: Robot API tree: - - url: /programming/python/functions - title: Functions - - url: /programming/sr/ - title: sr + - url: /programming/robot_api/metadata + title: Metadata + - url: /programming/leds + title: Brain Board LED API + - url: /programming/motors + title: Motor Board API + - url: /programming/power + title: Power Board API + - url: /programming/servos + title: Servo Board API + - url: /programming/arduino/ + title: Arduino API tree: - - url: /programming/sr/cheat_sheet - title: API Quick Reference - - url: /programming/sr/leds/ - title: LEDs - - url: /programming/sr/motors/ - title: Motors - - url: /programming/sr/power/ - title: Power - - url: /programming/sr/ruggeduinos/ - title: Ruggeduinos - tree: - - url: /programming/sr/ruggeduinos/custom_firmware - title: Custom Firmware - - url: /programming/sr/servos/ - title: Servos - - url: /programming/sr/vision/ - title: Vision - tree: - - url: /programming/sr/vision/markers - title: Markers - - url: /programming/editors/ - title: Code Editors + - url: /programming/arduino/custom_firmware + title: Custom Firmware + - url: /programming/vision/ + title: Vision tree: - - url: /programming/editors/pycharm - title: PyCharm - - url: /programming/editors/vscode - title: Visual Studio Code + - url: /programming/vision/markers + title: Markers + - url: /programming/vision/position + title: Position + - url: /programming/vision/orientation + title: Orientation + - url: /programming/cheat_sheet + title: API Quick Reference - url: /rules/ title: Rules tree: @@ -98,12 +90,21 @@ tree: tree: - url: /tutorials/assembly title: Assembly + - url: /tutorials/getting_code_on_the_robot + title: Getting Code on the Robot - url: /tutorials/basic_motor_control title: Basic Motor Control - url: /tutorials/microgames title: Microgames - url: /tutorials/python title: Python + - url: /tutorials/editors/ + title: Code Editors + tree: + - url: /tutorials/editors/pycharm + title: PyCharm + - url: /tutorials/editors/vscode + title: Visual Studio Code - url: /team_admin/ title: Team Admin tree: diff --git a/programming/sr/ruggeduinos/custom_firmware.md b/programming/arduino/custom_firmware.md similarity index 98% rename from programming/sr/ruggeduinos/custom_firmware.md rename to programming/arduino/custom_firmware.md index 68c0682e..31fe90a1 100644 --- a/programming/sr/ruggeduinos/custom_firmware.md +++ b/programming/arduino/custom_firmware.md @@ -1,10 +1,10 @@ --- layout: page -title: Ruggeduino custom firmware +title: Arduino custom firmware --- -Custom firmware -=============== +Arduino custom firmware +=======================
This documentation refers to a feature which is only available on the physical robot kits. diff --git a/programming/sr/ruggeduinos/index.md b/programming/arduino/index.md similarity index 98% rename from programming/sr/ruggeduinos/index.md rename to programming/arduino/index.md index 8aa0880c..98d08284 100644 --- a/programming/sr/ruggeduinos/index.md +++ b/programming/arduino/index.md @@ -1,10 +1,10 @@ --- layout: page -title: IO (Ruggeduino) +title: Arduino API --- -IO (Ruggeduino) -=============== +Arduino API +=========== The [Ruggeduino](https://web.archive.org/web/20170317171649/https://www.rugged-circuits.com/ruggeduino) provides a total of 18 pins for either digital input or output (labelled 2 to 13 and A0 to A5), diff --git a/programming/sr/cheat_sheet.md b/programming/cheat_sheet.md similarity index 100% rename from programming/sr/cheat_sheet.md rename to programming/cheat_sheet.md diff --git a/programming/sr/compass/index.md b/programming/compass.md similarity index 100% rename from programming/sr/compass/index.md rename to programming/compass.md diff --git a/programming/index.md b/programming/index.md index 05416ed3..72c8d6a8 100644 --- a/programming/index.md +++ b/programming/index.md @@ -15,3 +15,25 @@ Robots that are made using the SR kit are programmed with [Python](/docs/program * Under the [Python](/docs/programming/python/) section, some of the more intricate features of Python are explained in sufficient detail to program a robot. Links to tutorials are also provided to introduce a new programmer to Python. * Under the [sr](/docs/programming/sr/) section is an API reference for the Student Robotics module, used to interface with the hardware. + + + +TODO - below is from python page + +Student Robotics robots are all programmed in [Python 3.10](https://www.python.org); +to program a robot, it is important that you have a basic understanding of Python. + +There are a number of tutorials out there which might help you to learn to program in Python: + +* [Our tutorial](/docs/tutorials/python), called _Python: A whirlwind tour_. + This was written especially for Student Robotics competitors, + and explains the basics while trying not to overwhelm you. + +* [The Official Tutorial](https://docs.python.org/3.10/tutorial/) -- the good stuff starts at chapter 3, + but you should at least skim the stuff before it; + there is a lot there and it may be a little overwhelming. + +* A number of tutorials for beginners are linked to from [here](https://wiki.python.org/moin/BeginnersGuide/NonProgrammers). + [Wikibooks Tutorial](https://en.wikibooks.org/wiki/Non-Programmer%27s_Tutorial_for_Python_3) seems like a good one. + +* [The Official Docs](https://docs.python.org/3.10/), for the version of python on the [Brain Board](/docs/kit/brain_board). diff --git a/programming/sr/leds/index.md b/programming/leds.md similarity index 95% rename from programming/sr/leds/index.md rename to programming/leds.md index 3b4f2c85..eb036be7 100644 --- a/programming/sr/leds/index.md +++ b/programming/leds.md @@ -1,10 +1,10 @@ --- layout: page -title: LEDs +title: Brain Board LED API --- -LEDs -==== +Brain Board LED API +===================
This documentation refers to a feature which is only available on the physical robot kits. diff --git a/programming/sr/motors/index.md b/programming/motors.md similarity index 98% rename from programming/sr/motors/index.md rename to programming/motors.md index e1967edf..f1c8f022 100644 --- a/programming/sr/motors/index.md +++ b/programming/motors.md @@ -1,10 +1,10 @@ --- layout: page -title: Motors +title: Motor Board API --- -Motors -====== +Motor Board API +=============== The `motor_board` object is used to control a collection of Motor Boards. diff --git a/programming/sr/power/index.md b/programming/power.md similarity index 98% rename from programming/sr/power/index.md rename to programming/power.md index 64a5c917..e42c02e1 100644 --- a/programming/sr/power/index.md +++ b/programming/power.md @@ -1,10 +1,10 @@ --- layout: page -title: Power +title: Power Board API --- -Power -===== +Power Board API +=============== There are a few things that can be done with the power board, namely current and voltage sensing, and beeping. As there is only one power board, it is not accessed like a list like `motors` and is instead accessed directly, for example: diff --git a/programming/python/functions.md b/programming/python/functions.md deleted file mode 100644 index 5ca939c0..00000000 --- a/programming/python/functions.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: page -title: Writing your own functions ---- - -Writing your own functions -========================== - -Python allows you to group useful code that you're going to use over and over again into what's called a *function*. -You might want to write a function that handles all the behaviour of your robot once it's got the info back from the vision system, - or perhaps something much smaller like a precise left turn. - - -A simple function that calculates the square of a number: - -~~~~~ python -def square(num): - return num ** 2 -~~~~~ - -This would be used by some other code as follows: - -~~~~~ python -sq = square(5) -print(sq) # prints: 25 -~~~~~ - -Another advantage of grouping your code like this is that if you decide you change the way that it works slightly, - (but still having it return the same answer) you can do this easily, - and the changed code will be used throughout your code, without having to change it in many, many places: - -The same function modified & printing a log message: - -~~~~~ python -def square(num): - print(f"found the square of {num}") - return num * num -~~~~~ diff --git a/programming/python/index.md b/programming/python/index.md deleted file mode 100644 index 883ab90d..00000000 --- a/programming/python/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -layout: page -title: Python ---- - -Python -====== - -Student Robotics robots are all programmed in [Python 3.10](https://www.python.org); -to program a robot, it is important that you have a basic understanding of Python. - -There are a number of tutorials out there which might help you to learn to program in Python: - -* [Our tutorial](/docs/tutorials/python), called _Python: A whirlwind tour_. - This was written especially for Student Robotics competitors, - and explains the basics while trying not to overwhelm you. - -* [The Official Tutorial](https://docs.python.org/3.10/tutorial/) -- the good stuff starts at chapter 3, - but you should at least skim the stuff before it; - there is a lot there and it may be a little overwhelming. - -* A number of tutorials for beginners are linked to from [here](https://wiki.python.org/moin/BeginnersGuide/NonProgrammers). - [Wikibooks Tutorial](https://en.wikibooks.org/wiki/Non-Programmer%27s_Tutorial_for_Python_3) seems like a good one. - -* [The Official Docs](https://docs.python.org/3.10/), for the version of python on the [Brain Board](/docs/kit/brain_board). diff --git a/programming/sr/radio/index.md b/programming/radio.md similarity index 100% rename from programming/sr/radio/index.md rename to programming/radio.md diff --git a/programming/sr/index.md b/programming/robot_api/index.md similarity index 99% rename from programming/sr/index.md rename to programming/robot_api/index.md index 633a0a93..4c9d0049 100644 --- a/programming/sr/index.md +++ b/programming/robot_api/index.md @@ -1,9 +1,9 @@ --- layout: page -title: SR Module +title: Robot API --- -SR Module +Robot API ========= Student Robotics has written a module — `sr.robot3` — which is used to interface with the hardware. diff --git a/programming/robot_api/metadata.md b/programming/robot_api/metadata.md new file mode 100644 index 00000000..ecb17963 --- /dev/null +++ b/programming/robot_api/metadata.md @@ -0,0 +1,10 @@ +--- +layout: page +title: Metadata +--- + +Metadata +======== + +TODO - pick better name +TODO \ No newline at end of file diff --git a/programming/sr/servos/index.md b/programming/servos.md similarity index 97% rename from programming/sr/servos/index.md rename to programming/servos.md index 4360b365..a719ab06 100644 --- a/programming/sr/servos/index.md +++ b/programming/servos.md @@ -1,10 +1,10 @@ --- layout: page -title: Servos +title: Servos Board API --- -Servos -====== +Servos Board API +================ The `servo_board` object is used to control a collection of Servo Boards. diff --git a/programming/sr/vision/index.md b/programming/vision/index.md similarity index 100% rename from programming/sr/vision/index.md rename to programming/vision/index.md diff --git a/programming/sr/vision/markers.md b/programming/vision/markers.md similarity index 100% rename from programming/sr/vision/markers.md rename to programming/vision/markers.md diff --git a/programming/vision/orientation.md b/programming/vision/orientation.md new file mode 100644 index 00000000..17774f64 --- /dev/null +++ b/programming/vision/orientation.md @@ -0,0 +1,9 @@ +--- +layout: page +title: Orientation +--- + +Orientation +=========== + +TODO \ No newline at end of file diff --git a/programming/vision/position.md b/programming/vision/position.md new file mode 100644 index 00000000..5fee2655 --- /dev/null +++ b/programming/vision/position.md @@ -0,0 +1,10 @@ +--- +layout: page +title: Position +--- + +Position +======== + +TODO + diff --git a/programming/editors/index.md b/tutorials/editors/index.md similarity index 100% rename from programming/editors/index.md rename to tutorials/editors/index.md diff --git a/programming/editors/pycharm.md b/tutorials/editors/pycharm.md similarity index 100% rename from programming/editors/pycharm.md rename to tutorials/editors/pycharm.md diff --git a/programming/editors/vscode.md b/tutorials/editors/vscode.md similarity index 100% rename from programming/editors/vscode.md rename to tutorials/editors/vscode.md diff --git a/programming/getting_code_on_the_robot.md b/tutorials/getting_code_on_the_robot.md similarity index 100% rename from programming/getting_code_on_the_robot.md rename to tutorials/getting_code_on_the_robot.md From 2e6431df4b395ccd4d84ff3fdfdb5e851496091b Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 19:06:07 +0100 Subject: [PATCH 12/81] sidebar - simulator and resources --- _data/sidebar_tree.yaml | 36 ++++---- competitor_resources/index.md | 9 ++ .../microgames.md | 0 .../simulator_programming.md | 5 +- tutorials/setting_up_simulator.md | 90 +++++++++++++++++++ tutorials/using_the_simulator.md | 45 ++++++++++ 6 files changed, 166 insertions(+), 19 deletions(-) create mode 100644 competitor_resources/index.md rename {tutorials => competitor_resources}/microgames.md (100%) rename simulator/programming/index.md => programming/simulator_programming.md (98%) create mode 100644 tutorials/setting_up_simulator.md create mode 100644 tutorials/using_the_simulator.md diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index 39e77733..ed6bfe8d 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -48,11 +48,6 @@ tree: title: Power Board API - url: /programming/servos title: Servo Board API - - url: /programming/arduino/ - title: Arduino API - tree: - - url: /programming/arduino/custom_firmware - title: Custom Firmware - url: /programming/vision/ title: Vision tree: @@ -62,22 +57,24 @@ tree: title: Position - url: /programming/vision/orientation title: Orientation + - url: /programming/arduino/ + title: Arduino API + tree: + - url: /programming/arduino/custom_firmware + title: Custom Firmware - url: /programming/cheat_sheet title: API Quick Reference + - url: /programming/simulator_programming + title: Simulator Programming - url: /rules/ title: Rules tree: - url: /rules/code_of_conduct title: Code Of Conduct - - url: /rules/archive - title: Game Rules Archive - url: /rules/safety_regulations title: Safety Regulations - - url: /simulator/ - title: Simulator - tree: - - url: /simulator/programming/ - title: Programming + - url: /rules/archive + title: Game Rules Archive - url: /troubleshooting/ title: Troubleshooting tree: @@ -90,14 +87,12 @@ tree: tree: - url: /tutorials/assembly title: Assembly + - url: /tutorials/python + title: Python - url: /tutorials/getting_code_on_the_robot title: Getting Code on the Robot - url: /tutorials/basic_motor_control title: Basic Motor Control - - url: /tutorials/microgames - title: Microgames - - url: /tutorials/python - title: Python - url: /tutorials/editors/ title: Code Editors tree: @@ -105,6 +100,15 @@ tree: title: PyCharm - url: /tutorials/editors/vscode title: Visual Studio Code + - url: /tutorials/setting_up_simulator + title: Setting up the simulator + - url: /tutorials/using_the_simulator + title: Using the simulator + - url: /competitor_resources/ + title: Resources + tree: + - url: /competitor_resources/microgames + title: Microgames - url: /team_admin/ title: Team Admin tree: diff --git a/competitor_resources/index.md b/competitor_resources/index.md new file mode 100644 index 00000000..af0b344b --- /dev/null +++ b/competitor_resources/index.md @@ -0,0 +1,9 @@ +--- +layout: page +title: Resources +--- + +Resources +========= + +TODO \ No newline at end of file diff --git a/tutorials/microgames.md b/competitor_resources/microgames.md similarity index 100% rename from tutorials/microgames.md rename to competitor_resources/microgames.md diff --git a/simulator/programming/index.md b/programming/simulator_programming.md similarity index 98% rename from simulator/programming/index.md rename to programming/simulator_programming.md index 9930915b..86170364 100644 --- a/simulator/programming/index.md +++ b/programming/simulator_programming.md @@ -1,11 +1,10 @@ --- -redirect_from: - - /competition-simulator/programming layout: page title: Simulator Programming --- -# Simulator Programming +Simulator Programming +===================== ## Developing your code diff --git a/tutorials/setting_up_simulator.md b/tutorials/setting_up_simulator.md new file mode 100644 index 00000000..23fb191d --- /dev/null +++ b/tutorials/setting_up_simulator.md @@ -0,0 +1,90 @@ +--- +layout: page +title: Setting up the simulator +--- + +Setting up the simulator +======================== + +### Prerequisites + +You need to download and install [Webots](https://cyberbotics.com/#download) (the download is around 300MB). +This is the platform we run our simulation in. + +Versions "R2022b" and "R2023a" of Webots are supported. +*Note*: versions of the simulation prior to sr2023.3 did not support Webots "R2023a". + +#### Python Version + +You will also need Python installed. + +| Platform | Supported Python Version | +|----------|--------------------------| +| Windows | 3.8-3.10 (64-bit) | +| macOS | 3.8-3.10 | +| Linux | 3.8-3.10 | + +There are a small number of [external libraries]({{ site.baseurl }}/programming/python/libraries#simulator) +which will be available to your robot code during the competition. Note that for +local development you will need to install these yourself. + +### Installing the simulation + +1. Create a directory, perhaps called `simulation` where you will store your robot code. +2. [Download the simulation](https://github.com/srobo/competition-simulator/releases/download/sr2023.5/competition-simulator-sr2023.5.zip), the latest version is sr2023.5, released on 2023-02-22. +3. Unzip the simulation as a subdirectory of the directory you created in the first step: + ``` + simulation + ├── competition-simulator- + │ ├── ... + │ └─ worlds + │ └── Arena.wbt + └── robot.py + ``` + If there is not an existing `robot.py` an example one will be created when the simulator first runs. + +4. Using the Webots IDE, open the `worlds/Arena.wbt` file. + +You may receive a warning about your computer's GPU not being good enough, which can be ignored. + +
+ On recent versions of macOS you may need to give Webots permission to access the directory where you have extracted the simulation files. +
+ +#### Changing your version of Python + +If webots is not picking any version of Python or is picking up the wrong one then you'll need to change it. +When this happens Webots will print errors to its console and your robot will not move. + +You will need the full path to the version of Python that you want to use. +This will vary based on the system you have. +One way to find the path is by launching the version of Python that you want to +use and running the following code: + +~~~~~ python +import sys +print(sys.executable) +~~~~~ + +On Windows you can set the path to the Python version to use in Webots UI via +**Tools** → **Preferences** → **General** → **Python command**. +Your Python path is likely similar to `C:\Users\\AppData\Local\Programs\Python\Python39\python.exe` when using Python 3.9, where `` is your login. + +On Mac you can set the path to the Python version to use via **Webots** → **Preferences** ,. +Your Python path is likely similar to `/Library/Frameworks/Python.framework/Versions/3.9/bin/python3` when using Python 3.9. + +You'll need to ensure a matching version of Python is installed. If you're still +having problems, ask for help in [`#simulator-help`][simulator-help] in +[Discord][discord]. + +### Updates + +Occasionally, we may release an update to the simulation. To update, you will need to delete the `competition-simulator-` directory, and re-download it using the above link. + +If you need a specific version of the simulator, or want to see what changes +have been made with each version, please see the +[list of releases](https://github.com/srobo/competition-simulator/releases). + +[discord]: {{ site.baseurl }}/team_admin/discord +[programming-help]: https://discord.com/channels/900501415548579842/900501416269971457 +[simulator-help]: https://discord.com/channels/900501415548579842/900501416269971458 diff --git a/tutorials/using_the_simulator.md b/tutorials/using_the_simulator.md new file mode 100644 index 00000000..e487a05a --- /dev/null +++ b/tutorials/using_the_simulator.md @@ -0,0 +1,45 @@ +--- +layout: page +title: Using the simulator +--- + +Using the simulator +=================== + +## Overview + +Within the Webots IDE, there are a few different panels: + +- In the centre of your screen is the 3D simulated view of the arena +- On the left is a tree hierarchy of all elements in this "world" +- At the bottom is the console, where output from your robot code will be displayed +- At the top are your general controls which include the time controls. Press the centre play button to run the simulation at normal speed. + +### Useful links + +- [Camera Controls](https://www.cyberbotics.com/doc/guide/the-3d-window#navigation-in-the-scene) +- [Graphics settings](https://www.cyberbotics.com/doc/guide/preferences#opengl) (Useful if Webots is running slowly) + +## Time + +In the simulated environment, time advances only at the pace that the simulator +is run. The relation between this time and the real passage of time depends on a +couple of factors: the speed the simulation is configured to run at and the +ability of the computer running the simulation to process it fast enough. + +You can configure and observe the speed the simulator is running at from the toolbar in webots: + +![]({{ site.baseurl }}/images/content/simulator/speed-toolbar.png) + +Here the simulation has run for 13.28 seconds, but is currently paused (the +speed multiplier shows 0.00×). You could choose to step a single time increment, +run the simulator at real speed (▶), or run the simulator at various faster +speeds (▶▶ and ▶▶▶). + +These differences mean that your code will need to use a different mechanism to +find the current time or to sleep within the simulation. Find out more by +heading over to the [simulator programming docs](./programming). + +## Programming + +Once you have the simulator installed you can begin [programming your robot](./programming) in the simulator. From 40d9846077f415a8b7c40a415bc9893b6d017f4e Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 19:14:38 +0100 Subject: [PATCH 13/81] Fix links in interactive troubleshooter --- resources/troubleshooter/data.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/troubleshooter/data.json b/resources/troubleshooter/data.json index cb31ff87..e3209dae 100644 --- a/resources/troubleshooter/data.json +++ b/resources/troubleshooter/data.json @@ -62,7 +62,7 @@ }, { "answer": "No", - "message": "There is an external On|Off switch connector. If you are not connecting a switch, you must connect a loop of wire between the two terminals. See the preparation section in kit assembly for more details." + "message": "There is an external On|Off switch connector. If you are not connecting a switch, you must connect a loop of wire between the two terminals. See the preparation section in kit assembly for more details." } ] }, @@ -157,7 +157,7 @@ }, { "answer": "No", - "message": "Connect the Ruggeduino to the Power Board as described in the Docs.", + "message": "Connect the Ruggeduino to the Power Board as described in the Docs.", "ask_if_fixed": true, "next_question": "io-board-dead" } @@ -196,7 +196,7 @@ "answers": [ { "answer": "My peripherals are fine", - "message": "You may well have mixed up an IO pin index in your code, remember they are zero-indexed — double check your pin indexing.
Make sure you are using the right pins for your type of output (digital or analouge) as per the docs.

If you continue to have difficulties then consider posting in the Discord." + "message": "You may well have mixed up an IO pin index in your code, remember they are zero-indexed — double check your pin indexing.
Make sure you are using the right pins for your type of output (digital or analouge) as per the docs.

If you continue to have difficulties then consider posting in the Discord." }, { "answer": "I need help with my peripherals", @@ -281,7 +281,7 @@ }, { "answer": "Red", - "message": "The Motor Board's power connection is most likely back to front. Adjust the connection to the Power Board as described in the docs.", + "message": "The Motor Board's power connection is most likely back to front. Adjust the connection to the Power Board as described in the docs.", "ask_if_fixed": true, "next_question": "motor-board-usb" }, From 1da26c01fc02fdb8fdf188edbe7efd684d18cc44 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 27 Aug 2023 19:34:20 +0100 Subject: [PATCH 14/81] Fix broken links --- kit/brain_board/advanced.md | 2 +- kit/brain_board/index.md | 2 +- kit/index.md | 2 +- programming/arduino/custom_firmware.md | 6 +- programming/index.md | 8 +- programming/radio.md | 2 +- programming/robot_api/index.md | 10 +- programming/simulator_programming.md | 2 +- simulator/index.md | 132 ------------------------- tutorials/assembly.md | 4 +- tutorials/basic_motor_control.md | 2 +- tutorials/editors/vscode.md | 2 +- tutorials/setting_up_simulator.md | 2 +- tutorials/using_the_simulator.md | 4 +- 14 files changed, 24 insertions(+), 156 deletions(-) delete mode 100644 simulator/index.md diff --git a/kit/brain_board/advanced.md b/kit/brain_board/advanced.md index a8270235..d3e3e126 100644 --- a/kit/brain_board/advanced.md +++ b/kit/brain_board/advanced.md @@ -5,7 +5,7 @@ title: Brain Board - Advanced # Brain Board - Advanced -When connected to your [Brain Board WiFi]({{ site.baseurl }}/kit/wifi), it is possible to access the Brain Board via a terminal interface. The Brain Board is running Student Robotics OS, a purpose-built Linux distribution that does not have all of the conveniences of a usual Linux system. +When connected to your [Brain Board WiFi]({{ site.baseurl }}/kit/brain_board/wifi), it is possible to access the Brain Board via a terminal interface. The Brain Board is running Student Robotics OS, a purpose-built Linux distribution that does not have all of the conveniences of a usual Linux system.
Proceed at your own risk. We only provide limited support for these advanced features. diff --git a/kit/brain_board/index.md b/kit/brain_board/index.md index b0836414..541cab15 100644 --- a/kit/brain_board/index.md +++ b/kit/brain_board/index.md @@ -61,7 +61,7 @@ The SD card is located on the underside of the board underneath the green power To fully update your Brain Board's software, or refresh it if you think it's not working correctly, you can flash our SD card image onto the microSD card in your Brain Board. -To update the SD card, you'll need to download our image from the [updates page]({{ site.baseurl }}/kit/brain_board/updates). The latest version is `{{ latest_version }}`. +To update the SD card, you'll need to download our image from the [updates page]({{ site.baseurl }}/kit/brain_board/robot_os). The latest version is `{{ latest_version }}`. The flashing procedure is identical to flashing Raspberry Pi images. ### Etcher diff --git a/kit/index.md b/kit/index.md index d2f4d5ab..0620d276 100644 --- a/kit/index.md +++ b/kit/index.md @@ -18,7 +18,7 @@ The boards provided are as follows; more details can be found on their individua * [Brain Board](/docs/kit/brain_board) * [Power Board](/docs/kit/power_board) * [Motor Board x2](/docs/kit/motor_board) - * [Ruggeduino (including two screw shields)](/docs/kit/ruggeduino) + * [Ruggeduino (including two screw shields)](/docs/kit/arduino) * [Servo Board](/docs/kit/servo_board) Ancillary Parts diff --git a/programming/arduino/custom_firmware.md b/programming/arduino/custom_firmware.md index 31fe90a1..4fd43193 100644 --- a/programming/arduino/custom_firmware.md +++ b/programming/arduino/custom_firmware.md @@ -10,7 +10,7 @@ Arduino custom firmware This documentation refers to a feature which is only available on the physical robot kits.
-The Ruggeduino that came as part of your kit was shipped with a firmware that provides the functionality outlined in the [Ruggeduino](/docs/programming/sr/ruggeduinos) page. +The Ruggeduino that came as part of your kit was shipped with a firmware that provides the functionality outlined in the [Ruggeduino](/docs/programming/arduino) page. You may wish to extend the functionality of this firmware, or completely replace it. The `sr.robot3` library provides support for three Ruggeduino firmware scenarios: @@ -18,7 +18,7 @@ The `sr.robot3` library provides support for three Ruggeduino firmware scenarios 2. [Extended SR firmware](#extension): Firmwares that add commands to the default SR firmware. 3. [Completely custom](#completely): Any firmware not derived from the SR firmware. -By default, the [`sr.robot3`](/docs/programming/sr/) library assumes that all connected Ruggeduinos are running the SR firmware +By default, the [`sr.robot3`](/docs/programming/robot_api/) library assumes that all connected Ruggeduinos are running the SR firmware or firmware which is compatible with the SR Ruggeduino firmware. If you're using completely custom firmware, you'll need to tell the kit to ignore the ruggeduino so that you're able to define your own setup logic. @@ -27,7 +27,7 @@ If you're using completely custom firmware, you'll need to tell the kit to ignor
Because the API sets all pins to inputs when the robot is initialised, you cannot set pin modes in the setup function as these will be overridden. -In order to setup pins you can either create a function on the ruggeduino that you call after your robot is initialised or use the [Python functions](/docs/programming/sr/ruggeduinos/#pin-modes) to setup the pins. +In order to setup pins you can either create a function on the ruggeduino that you call after your robot is initialised or use the [Python functions](/docs/programming/arduino/#pin-modes) to setup the pins.
You may wish to extend the SR firmware with additional functionality. diff --git a/programming/index.md b/programming/index.md index 72c8d6a8..3978fc0c 100644 --- a/programming/index.md +++ b/programming/index.md @@ -6,7 +6,7 @@ title: Programming Programming Your Robot ====================== -Robots that are made using the SR kit are programmed with [Python](/docs/programming/python/) which is made possible with a custom-built Python library, `sr.robot3`; see [here](/docs/programming/sr/) for more details. The pages under this section should contain all of the information you need to know to successfully code for your robot. + + diff --git a/programming/radio.md b/programming/radio.md index d5c5cc6a..de729aec 100644 --- a/programming/radio.md +++ b/programming/radio.md @@ -129,4 +129,4 @@ station_code owned_by : The zone id of the robot that currently owns the stations territory. A `None` value indicates an unclaimed territory.
- Remember that you can find out which zone your robot is in using R.zone. + Remember that you can find out which zone your robot is in using R.zone. diff --git a/programming/robot_api/index.md b/programming/robot_api/index.md index 4c9d0049..c099b96f 100644 --- a/programming/robot_api/index.md +++ b/programming/robot_api/index.md @@ -34,11 +34,11 @@ R = Robot() Within your `Robot` (`R` in this case), you then have access to the following attributes: -* [motors](/docs/programming/sr/motors/) -* [power](/docs/programming/sr/power/) -* [servos](/docs/programming/sr/servos/) -* [ruggeduinos](/docs/programming/sr/ruggeduinos/) -* [vision](/docs/programming/sr/vision/) +* [motors](/docs/programming/motors) +* [power](/docs/programming/power) +* [servos](/docs/programming/servos) +* [ruggeduinos](/docs/programming/arduino/) +* [vision](/docs/programming/vision/) They can be used in your code just like the example above. Note that `motors`, `ruggeduinos`, and `servos` are Python lists, and so should be accessed as such. diff --git a/programming/simulator_programming.md b/programming/simulator_programming.md index 86170364..92440e61 100644 --- a/programming/simulator_programming.md +++ b/programming/simulator_programming.md @@ -126,7 +126,7 @@ information about other objects within the simulation. This simulates the system of fiducial markers which the physical robot's camera can detect. The information returned by the simulated vision API is similar to the physical -robot's [vision API](/docs/programming/sr/vision/), however there are a number +robot's [vision API](/docs/programming/vision/), however there are a number of differences as are noted in the vision docs. ### Pressure sensing diff --git a/simulator/index.md b/simulator/index.md deleted file mode 100644 index d6f82ff7..00000000 --- a/simulator/index.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -redirect_from: - - /competition-simulator - - /programming/simulator -layout: page -title: Simulator ---- - -# Simulator - -## Installation - -### Prerequisites - -You need to download and install [Webots](https://cyberbotics.com/#download) (the download is around 300MB). -This is the platform we run our simulation in. - -Versions "R2022b" and "R2023a" of Webots are supported. -*Note*: versions of the simulation prior to sr2023.3 did not support Webots "R2023a". - -#### Python Version - -You will also need Python installed. - -| Platform | Supported Python Version | -|----------|--------------------------| -| Windows | 3.8-3.10 (64-bit) | -| macOS | 3.8-3.10 | -| Linux | 3.8-3.10 | - -There are a small number of [external libraries]({{ site.baseurl }}/programming/python/libraries#simulator) -which will be available to your robot code during the competition. Note that for -local development you will need to install these yourself. - -### Installing the simulation - -1. Create a directory, perhaps called `simulation` where you will store your robot code. -2. [Download the simulation](https://github.com/srobo/competition-simulator/releases/download/sr2023.5/competition-simulator-sr2023.5.zip), the latest version is sr2023.5, released on 2023-02-22. -3. Unzip the simulation as a subdirectory of the directory you created in the first step: - ``` - simulation - ├── competition-simulator- - │ ├── ... - │ └─ worlds - │ └── Arena.wbt - └── robot.py - ``` - If there is not an existing `robot.py` an example one will be created when the simulator first runs. - -4. Using the Webots IDE, open the `worlds/Arena.wbt` file. - -You may receive a warning about your computer's GPU not being good enough, which can be ignored. - -
- On recent versions of macOS you may need to give Webots permission to access the directory where you have extracted the simulation files. -
- -#### Changing your version of Python - -If webots is not picking any version of Python or is picking up the wrong one then you'll need to change it. -When this happens Webots will print errors to its console and your robot will not move. - -You will need the full path to the version of Python that you want to use. -This will vary based on the system you have. -One way to find the path is by launching the version of Python that you want to -use and running the following code: - -~~~~~ python -import sys -print(sys.executable) -~~~~~ - -On Windows you can set the path to the Python version to use in Webots UI via -**Tools** → **Preferences** → **General** → **Python command**. -Your Python path is likely similar to `C:\Users\\AppData\Local\Programs\Python\Python39\python.exe` when using Python 3.9, where `` is your login. - -On Mac you can set the path to the Python version to use via **Webots** → **Preferences** ,. -Your Python path is likely similar to `/Library/Frameworks/Python.framework/Versions/3.9/bin/python3` when using Python 3.9. - -You'll need to ensure a matching version of Python is installed. If you're still -having problems, ask for help in [`#simulator-help`][simulator-help] in -[Discord][discord]. - -### Updates - -Occasionally, we may release an update to the simulation. To update, you will need to delete the `competition-simulator-` directory, and re-download it using the above link. - -If you need a specific version of the simulator, or want to see what changes -have been made with each version, please see the -[list of releases](https://github.com/srobo/competition-simulator/releases). - -[discord]: {{ site.baseurl }}/team_admin/discord -[programming-help]: https://discord.com/channels/900501415548579842/900501416269971457 -[simulator-help]: https://discord.com/channels/900501415548579842/900501416269971458 - -## Overview - -Within the Webots IDE, there are a few different panels: - -- In the centre of your screen is the 3D simulated view of the arena -- On the left is a tree hierarchy of all elements in this "world" -- At the bottom is the console, where output from your robot code will be displayed -- At the top are your general controls which include the time controls. Press the centre play button to run the simulation at normal speed. - -### Useful links - -- [Camera Controls](https://www.cyberbotics.com/doc/guide/the-3d-window#navigation-in-the-scene) -- [Graphics settings](https://www.cyberbotics.com/doc/guide/preferences#opengl) (Useful if Webots is running slowly) - -## Time - -In the simulated environment, time advances only at the pace that the simulator -is run. The relation between this time and the real passage of time depends on a -couple of factors: the speed the simulation is configured to run at and the -ability of the computer running the simulation to process it fast enough. - -You can configure and observe the speed the simulator is running at from the toolbar in webots: - -![]({{ site.baseurl }}/images/content/simulator/speed-toolbar.png) - -Here the simulation has run for 13.28 seconds, but is currently paused (the -speed multiplier shows 0.00×). You could choose to step a single time increment, -run the simulator at real speed (▶), or run the simulator at various faster -speeds (▶▶ and ▶▶▶). - -These differences mean that your code will need to use a different mechanism to -find the current time or to sleep within the simulation. Find out more by -heading over to the [simulator programming docs](./programming). - -## Programming - -Once you have the simulator installed you can begin [programming your robot](./programming) in the simulator. diff --git a/tutorials/assembly.md b/tutorials/assembly.md index de3cb088..7e5a8a03 100644 --- a/tutorials/assembly.md +++ b/tutorials/assembly.md @@ -25,7 +25,7 @@ Each of the modules in the kit needs to be provided with both a control signal a All the boards use USB to connect to the [Brain Board](/docs/kit/brain_board) so it can tell them what to do. If you run out of USB ports on the Brain Board itself, then you can use the provided USB hubs to provide more ports. -Most of the boards (with the exception of the [Ruggeduino](/docs/kit/ruggeduino)) also need an additional power connection. +Most of the boards (with the exception of the [Ruggeduino](/docs/kit/arduino)) also need an additional power connection. This should be provided from the [Power Board](/docs/kit/power_board). The following table summarises the connections which need to be made for each board. @@ -35,7 +35,7 @@ Board | Power [Brain Board](/docs/kit/brain_board) | 12V, must be connected to L2 [Power Board](/docs/kit/power_board) | 12V, via the yellow XT60 to the [battery](/docs/kit/batteries) [Motor Board](/docs/kit/motor_board) | 12V -[Ruggeduino](/docs/kit/ruggeduino) | via USB +[Ruggeduino](/docs/kit/arduino) | via USB [Servo Board](/docs/kit/servo_board) | 12V In order to connect the Brain, Motor and Servo Boards to the Power Board, you will need to create some power cables. diff --git a/tutorials/basic_motor_control.md b/tutorials/basic_motor_control.md index d3179b00..0e456a56 100644 --- a/tutorials/basic_motor_control.md +++ b/tutorials/basic_motor_control.md @@ -127,7 +127,7 @@ while True: ~~~~~ You're familiar with the first few lines; in fact, the only lines you may not be familiar with are the `R.motor_board...` lines. -For a comprehensive reference to the `motor` object, see the `sr.robot3` module's [Motors](/docs/programming/sr/motors/) page. +For a comprehensive reference to the `motor` object, see the `sr.robot3` module's [Motors](/docs/programming/motors) page. But, to summarise:
diff --git a/tutorials/editors/vscode.md b/tutorials/editors/vscode.md index 6faa7c79..75a37f38 100644 --- a/tutorials/editors/vscode.md +++ b/tutorials/editors/vscode.md @@ -42,7 +42,7 @@ you'll need to make the library available within the Python environment that you This documentation refers to a feature which is only available from software version 2023.1.0 and later.
-When connected to the [robot's WiFi hotspot]({{ site.baseurl }}/kit/wifi), it is possible to attach VS Code's +When connected to the [robot's WiFi hotspot]({{ site.baseurl }}/kit/brain_board/wifi), it is possible to attach VS Code's debugger to the robot by performing the following steps: 1. Ensure the [Python extension](#python-extension) is installed. diff --git a/tutorials/setting_up_simulator.md b/tutorials/setting_up_simulator.md index 23fb191d..8421cb06 100644 --- a/tutorials/setting_up_simulator.md +++ b/tutorials/setting_up_simulator.md @@ -24,7 +24,7 @@ You will also need Python installed. | macOS | 3.8-3.10 | | Linux | 3.8-3.10 | -There are a small number of [external libraries]({{ site.baseurl }}/programming/python/libraries#simulator) +There are a small number of [external libraries]({{ site.baseurl }}/kit/brain_board/python_libraries#simulator) which will be available to your robot code during the competition. Note that for local development you will need to install these yourself. diff --git a/tutorials/using_the_simulator.md b/tutorials/using_the_simulator.md index e487a05a..748c34fb 100644 --- a/tutorials/using_the_simulator.md +++ b/tutorials/using_the_simulator.md @@ -38,8 +38,8 @@ speeds (▶▶ and ▶▶▶). These differences mean that your code will need to use a different mechanism to find the current time or to sleep within the simulation. Find out more by -heading over to the [simulator programming docs](./programming). +heading over to the [simulator programming docs](/docs/programming/simulator_programming). ## Programming -Once you have the simulator installed you can begin [programming your robot](./programming) in the simulator. +Once you have the simulator installed you can begin [programming your robot](/docs/programming/simulator_programming) in the simulator. From 5807727d0f0743d03987d6abc6e8f65ea3a735d8 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 28 Aug 2023 09:42:41 +0100 Subject: [PATCH 15/81] Update content for ease of reading and clarity --- kit/arduino.md | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/kit/arduino.md b/kit/arduino.md index a4ca06ca..34747e6c 100644 --- a/kit/arduino.md +++ b/kit/arduino.md @@ -1,26 +1,41 @@ --- +redirect_from: + - /kit/ruggeduino layout: page title: Arduino --- -# Ruggeduino +Arduino +======= -The Ruggeduino board allows you to connect the SR kit to your own electronics. -This board is very similar to an Arduino which you may have used before. +The Arduino board allows you to connect the SR kit to your own electronics. +For the SR kit we use a special Arduino called a Ruggeduino. +This board acts the same as an Arduino Uno but comes with extra protection to stop it from being damaged. -It has 14 digital I/O pins, these pins can either input or output a digital signal. -5V logic is used to interface with the Ruggeduino. +We also provide some screw terminal headers that convert the pin headers on the Arduino into screw terminals. +This makes for easier connection of wires to the Arduino. -The board also has 6 analogue input pins, these pins can read an analogue signal from 0 to 5V. -The board has a 3.3V pin, a 5V pin and three 0V/ground pins which can be used to power external devices. -If for whatever reason the board does not seem to be working pressing the reset button will reset the Ruggeduino and may solve the problem. - -The Ruggeduino needs only to be connected to the kit over USB as it uses this for both power and communication. +The Arduino only needs to be connected to the kit over USB as it uses this for both power and communication. ## Board Diagram ![Ruggeduino diagram]({{ site.baseurl }}/images/content/kit/ruggeduino_diagram.png "The Ruggeduino") +## Digital Pins + +The Arduino has 14 digital I/O pins, these pins can either input or output a digital signal. +These pins operate at a 5V logic level, so any signals connected need to be 5V. + +## Analog Pins + +The board has 6 analog input pins, these pins can read an analog signal from 0 to 5V. +These pins also support the standard digital functions. + +## Power Pins + +The board has a 3.3V pin, a 5V pin and three 0V/ground pins which can be used to power external devices. +The power available on these pins is limited when compared to the power board, so only connect low power devices (sensors, LEDs, buttons). + ## Indicators | LED | Meaning | Initial power-up state @@ -32,7 +47,7 @@ The Ruggeduino needs only to be connected to the kit over USB as it uses this fo ## Case Dimensions The case alone measures 86✕68✕23mm(L✕W✕H) without the extra pin headers. -When the Ruggeduino is fitted the whole unit measures 86✕84✕29mm; some screw heads may protrude from the bottom of the case by up to 2mm. +When the Ruggeduino is fitted with the screw terminal headers the whole unit measures 86✕84✕29mm; some screw heads may protrude from the bottom of the case by up to 2mm. Don’t forget that the cables will also stick out. ## Specification From b6777d9e6f96d37e2f2f46984ea6be571c6e7c1c Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 28 Aug 2023 20:27:29 +0100 Subject: [PATCH 16/81] KCH board API page update --- programming/leds.md | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/programming/leds.md b/programming/leds.md index eb036be7..82f9701e 100644 --- a/programming/leds.md +++ b/programming/leds.md @@ -6,37 +6,34 @@ title: Brain Board LED API Brain Board LED API =================== -
-This documentation refers to a feature which is only available on the physical robot kits. -
+The KCH board provides diagnostic information about the state of your robot through a collection of preconfigured and user-controllable LEDs. +It is part of the [Brain Board](/docs/kit/brain_board) assembly and will be found attached to the top of the Raspberry Pi. -The KCH HAT provides diagnostic information about the state of your robot -through a collection of preconfigured and user-controllable LEDs. It is part of -the [Brain Board](/docs/kit/brain_board) assembly, however control of its LEDs -is provided through a distinct API. +A number of the LEDs are controlled by the Robot's Operating system, the description of these is provided on the [Brain Board](/docs/kit/brain_board) page. +There are also three LEDs that you can control via the API. The LEDs on the KCH can be accessed via the `kch` object: ~~~~~ python -R.kch.something... +robot.kch ~~~~~ -The KCH HAT has three RGB LEDs: A, B and C. The LEDs default to off, however -once set they will hold their value even if your code exits. This is potentially -useful to understand the current state of your code as it runs or the final -state of the code afterwards. +The KCH board has three RGB (red/green/blue) LEDs: A, B and C. + +The LEDs default to off, however once set they will hold their value even if your code exits. +This is potentially useful to understand the current state of your code as it runs or the final state of the code afterwards. Each of the LEDs can be set to one of 8 colours: ~~~~~ python # Set LED A to red -R.kch.leds[UserLED.A] = Colour.RED +R.kch.leds[LED_A].colour = Colour.RED # Set LED B to cyan -R.kch.leds[UserLED.B] = Colour.CYAN +R.kch.leds[LED_B].colour = Colour.CYAN # Turn LED C off -R.kch.leds[UserLED.C] = Colour.OFF +R.kch.leds[LED_C].colour = Colour.OFF ~~~~~ The available colours are: @@ -56,11 +53,11 @@ Alternatively you can set the red, green and blue channels for a given LED separ ~~~~~ python # Turn on the blue segment of LED A -R.kch.leds[UserLED.A].b = True +R.kch.leds[LED_A].b = True # Turn off the red segment of LED B -R.kch.leds[UserLED.B].r = False +R.kch.leds[LED_B].r = False -# Turn on green segment of LED B -R.kch.leds[UserLED.B].g = True +# Turn on the green segment of LED B +R.kch.leds[LED_B].g = True ~~~~~ From 0442894311aa0155ed155de33951e4732e6294ff Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 28 Aug 2023 20:30:15 +0100 Subject: [PATCH 17/81] standardize on robot. rather than R. --- programming/leds.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/programming/leds.md b/programming/leds.md index 82f9701e..282bfb79 100644 --- a/programming/leds.md +++ b/programming/leds.md @@ -27,13 +27,13 @@ Each of the LEDs can be set to one of 8 colours: ~~~~~ python # Set LED A to red -R.kch.leds[LED_A].colour = Colour.RED +robot.kch.leds[LED_A].colour = Colour.RED # Set LED B to cyan -R.kch.leds[LED_B].colour = Colour.CYAN +robot.kch.leds[LED_B].colour = Colour.CYAN # Turn LED C off -R.kch.leds[LED_C].colour = Colour.OFF +robot.kch.leds[LED_C].colour = Colour.OFF ~~~~~ The available colours are: @@ -53,11 +53,11 @@ Alternatively you can set the red, green and blue channels for a given LED separ ~~~~~ python # Turn on the blue segment of LED A -R.kch.leds[LED_A].b = True +robot.kch.leds[LED_A].b = True # Turn off the red segment of LED B -R.kch.leds[LED_B].r = False +robot.kch.leds[LED_B].r = False # Turn on the green segment of LED B -R.kch.leds[LED_B].g = True +robot.kch.leds[LED_B].g = True ~~~~~ From b3a0a472acc56f967747cb2c04c54c08122af7e3 Mon Sep 17 00:00:00 2001 From: JoshP Date: Wed, 30 Aug 2023 20:11:46 +0100 Subject: [PATCH 18/81] First parse at top level vision page --- programming/vision/index.md | 299 ++++++++++-------------------------- 1 file changed, 83 insertions(+), 216 deletions(-) diff --git a/programming/vision/index.md b/programming/vision/index.md index 21713eda..d42b1472 100644 --- a/programming/vision/index.md +++ b/programming/vision/index.md @@ -6,17 +6,16 @@ title: Vision Vision ====== -The `sr.robot3` library contains support for detecting fiducial markers with the provided webcam. -Markers are attached to various items in the Student Robotics arena. -Each marker encodes a number in a machine-readable way, which means that robots can identify these objects. -For information on which markers codes are which, see the [markers page](./markers). +Your robot is able to use a webcam to detect [fiducial markers](https://en.wikipedia.org/wiki/Fiducial_marker). +Specifically it can detect [AprilTags](https://april.eecs.umich.edu/software/apriltag), using the `36H11` marker set. -Using knowledge of the physical size of the different markers and the characteristics of the webcam, -your robot can calculate the position of markers in 3D space relative to the camera. -Therefore, if the robot can see a marker that is at a fixed location in the arena, - a robot can calculate its exact position in the arena. +Each marker is unique and encodes a number in a machine-readable way, which means that robots can identify these objects. -Under the hood, the vision system is based on [AprilTag](https://april.eecs.umich.edu/software/apriltag/) and [OpenCV](https://opencv.org), using the `36H11` marker set. +Using [Pose Estimation](https://en.wikipedia.org/wiki/3D_pose_estimation), it can calculate the orientation and position of the marker relative to the webcam. +Markers are attached to various items in the Student Robotics arena, in known locations. +Using the marker poses and their locations, we can either calculate the location of object relative to the robot or the position of the robot relative to the arena. + +For information on markers, see the [markers page](./markers). [Camera](#camera) {#camera} =========================== @@ -24,40 +23,25 @@ Under the hood, the vision system is based on [AprilTag](https://april.eecs.umic The interface to the vision system is through the camera, accessible through `R.camera`. see -: Take a photo through the webcam, and return a list of [`Marker`](#Marker) instances, each of which describes one of the markers that were found in the image. +: Take a photo through the webcam, and return a list of [`Marker`](#marker) instances, each of which describes one of the markers that were found in the image. -Here's an example that will repeatedly print out the distance to each arena marker that the robot can see: +Here's an example that will repeatedly print out the distance, in meters, to each marker that the robot can see: ~~~~~ python from sr.robot3 import * -R = Robot() +robot = Robot() while True: - markers = R.camera.see() + markers = robot.camera.see() print("I can see", len(markers), "markers:") - for m in markers: - print(" - Marker #{0} is {1} metres away".format(m.id, m.distance / 1000)) -~~~~~ - -see_ids -: Take a photo through the webcam, and return a list of marker ids (**not** full `Marker` objects). This doesn't do the same [pose estimation](https://en.wikipedia.org/wiki/3D_pose_estimation) calculations as `see`, and so is much faster to run. - -~~~~~ python -from sr.robot3 import * -R = Robot() - -while True: - marker_ids = R.camera.see_ids() - - if 0 in marker_ids: - print("I can see marker 0!") - else: - print("I cannot see marker 0!") + for marker in markers: + print(" - Marker #{0} is {1} metres away".format(marker.id, marker.distance / 1000)) ~~~~~ -
-The simulated camera does not have `capture` or `save` methods. +
+Taking images while moving will cause them to be blurry, which will cause marker detection to fail. +Try pausing movement while taking an image.
save @@ -65,10 +49,10 @@ save ~~~~~ python from sr.robot3 import * -R = Robot() +robot = Robot() -# `R.usbkey` is the path to your USB drive -R.camera.save(R.usbkey / "initial-view.png") +# `robot.usbkey` is the path to your USB drive +robot.camera.save(robot.usbkey / "initial-view.png") ~~~~~ capture @@ -77,57 +61,52 @@ capture ~~~~~ python import cv2 from sr.robot3 import * -R = Robot() +robot = Robot() -frame = R.camera.capture() +frame = robot.camera.capture() # Flip the image with OpenCV flipped = cv2.flip(frame, 0) ~~~~~ -[Field of View](#fov) {#fov} -------------------- +[Frame argument](#frame_args) {#frame_args} +------------------------------------------- -The Logitech C500 has a [field of view][fov] of 72° and the C270 has a field of view of 60°. +The slowest part of vision is capturing the image. +You can use a frame with the other vision commands to avoid recapturing. +This may be useful if you wish to use both your own vision algorithms and our marker detection on the same frames. -[fov]: https://en.wikipedia.org/wiki/Field_of_view +~~~~~ python +from sr.robot3 import * +robot = Robot() -[Definition of Axes](#axes) {#axes} -=================================== +# Capture an OpenCV frame +frame = robot.camera.capture() -The vision system describes the markers it can see using three coordinate -systems. These are intended to be complementary to each other and contain -the same information in different forms. +# Run marker detection on the captured frame +markers = robot.camera.see(frame=frame) -The axis definitions match those in common use, as follows: +# Save the frame with marker annotation +robot.camera.save("photo.jpg", frame=frame) -x-axis -: The horizontal axis running left-to-right in front of the camera. - Rotation about this axis is equivalent to leaning towards or away from - the camera. +# Do some other vision algorithm with the OpenCV frame here +~~~~~ -y-axis -: The vertical axis running top-to-bottom in front of the camera. - Rotation about this axis is equivalent to turning on the spot, - to the left or right. +[Field of View](#fov) {#fov} +---------------------------- -z-axis -: The axis leading away from the camera to infinity. - Rotation about this axis is equivalent to being rolled sideways. +The Logitech C500 has a [field of view][fov] of 72° and the C270 has a field of view of 60°. + +[fov]: https://en.wikipedia.org/wiki/Field_of_view
-Note that the axes are all defined relative to the camera. Since we have -no way to know how you've mounted your camera, you may need to account -for that in your usage of the vision system's data. +Note that the axes are all defined relative to the camera. +Since we have no way to know how you've mounted your camera, you may need to account for that in your usage of the vision system's data.
-[Objects of the Vision System](#vision_objects) {#vision_objects} -============================== - -The vision system is made up of a number of objects, the primary of which is the `Marker`: +[Marker](#marker) {#marker} +=========================== -[`Marker`](#Marker) {#Marker} ----------- A `Marker` object contains information about a *detected* marker. It has the following attributes: @@ -137,171 +116,59 @@ id size : The physical size of the marker, as the vision system expects it. -
-Pixel coordinate information is not available in the simulator. -
- pixel_centre -: A [`PixelCoordinates`](#PixelCoordinates) describing the position of the centre of the marker. +: A [`PixelCoordinates`](#PixelCoordinates) describing the position of the centre of the marker in the image. pixel_corners -: A list of 4 [`PixelCoordinates`](#PixelCoordinates) instances, each representing the position of the corners of the marker. +: A list of 4 [`PixelCoordinates`](#PixelCoordinates) instances, each representing the position of a corner of the marker in the image. -distance -: The distance between the camera and the centre of the marker, in millimetres. +position +: A `Position` instance describing the position of the marker. + See the [Position page](./position) for detailed definitions and diagrams. + + distance + : The distance between the camera and the centre of the marker, in millimetres. + + horizontal_angle + : Horizontal angle from the camera to the marker, in radians. + Ranges -π to π. + Positive to the right. + Directly in front is 0. + + vertical_angle + : Vertical angle from the camera to the marker, in radians. + Ranges -π to π. + Positive values upwards. + Directly in front is 0. orientation -: An [`Orientation`](#Orientation) instance describing the orientation of the marker. +: An `Orientation` instance describing the orientation of the marker. + See the [Orientation page](./orientation) for detailed definitions and diagrams. + + yaw + : The Yaw of the marker, a rotation about the vertical axis, in radians. + Positive values indicate a rotation clockwise from the perspective of the marker. + Zero values have the marker facing the camera square-on. -spherical -: A [`SphericalCoordinate`](#SphericalCoordinate) instance describing the position of the marker relative to the camera. + pitch + : The Pitch of the marker, a rotation about the transverse axis, in radians. + Positive values indicate a rotation upwards from the perspective of the marker. + Zero values have the marker facing the camera square-on. + + roll + : The Roll of the marker, a rotation about the longitudinal axis, in radians. + Positive values indicate a rotation clockwise from the perspective of the marker. + Zero values have the marker facing the camera square-on. -cartesian -: A [`CartesianCoordinates`](#CartesianCoordinates) instance describing the position of the marker relative to the camera. [`PixelCoordinates`](#PixelCoordinates) {#PixelCoordinates} --------- -
-Pixel coordinate information is not available in the simulator. -
- A named tuple of `x` and `y` coordinates for the point, in pixels relative to the top left of the image. ~~~~~ python # Print the x and y coordinates of the pixel location print(marker.pixel_centre.x, marker.pixel_centre.y) ~~~~~ - -
- -[`CartesianCoordinates`](#CartesianCoordinates) {#CartesianCoordinates} ---------- - -A named tuple of `x`, `y` and `z` coordinates for the point, in millimeters relative to the camera. -Increasing values are to the right, below and away from the camera respectively. - -~~~~~ python -# Print the x, y and z coordinates of the marker's location -print(marker.cartesian.x, marker.cartesian.y, marker.cartesian.z) -~~~~~ - -[`Orientation`](#Orientation) {#Orientation} ---------------- - -
-Orientation information is returned in different formats between the simulator and the physical robot kits. -One (possibly both) of them may change to resolve this. - -In the simulator the `roll`, `pitch` and `yaw` properties are strict aliases for the `rot_x`, `rot_y` and `rot_z` properties. -Additionally the `rotation_matrix` and `quaternion` properties are not present. -
-
-There is a bug in the simulator such that the `rot_x` and `rot_z` values are not reliable -- specifically they depend on the orientation of the robot itself within the simulated environment in addition to the orientation of the marker. This is believed to be a bug in Webots simulation software. -
- -An `Orientation` object describes the orientation of a marker. - -rot_x -: Rotation of the marker about the cartesian x-axis, in radians. This is a - pitch-like rotation. - - Leaning a marker towards the camera increases the value of `rot_x`, while - leaning it away from the camera decreases it. A value of either π or -π - indicates that the marker is upright (there is a discontinuity in the value - at π and -π, as both values represent the same position). - -rot_y -: Rotation of the marker about the cartesian y-axis, in radians. This is a - yaw-like rotation. - - Turning a marker clockwise (as viewed from above) decreases the value of - `rot_y`, while turning it anticlockwise increases it. A value of 0 means - that the marker is perpendicular to the line of sight of the camera. - -rot_z -: Rotation of the marker about the cartesian z-axis, in radians. This is a - roll-like rotation. - - Turning a marker anticlockwise (as viewed from the camera) increases the - value of `rot_z`, while turning it clockwise decreases it. A value of 0 - indicates that the marker is upright. - -There are additional attributes for the [principal axis rotations](https://en.wikipedia.org/wiki/Aircraft_principal_axes) of the marker. - -yaw -: A rotation about the about the vertical axis, in radians (an axis top to - bottom through the token). Turning a marker clockwise (as viewed from above) - increases the value of `yaw`, while turning it anticlockwise decreases it. A - value of 0 means that the marker is perpendicular to the line of sight of - the camera. - - This differs from `rot_y` in the direction that increases the value. - -pitch -: A rotation about the transverse axis, in radians (an axis right to left - across the token). Tilting the marker backward increases the value of - `pitch`, while tilting it forwards decreases it. A value of 0 indicates that - the marker is facing the camera square-on. - - This differs from `rot_x` in the zero point and direction to increase the value. - -roll -: A rotation about the longitudinal axis, in radians (an axis normal from the - apparent front to the back of the token, normal to the marker). Rotating the - marker anti-clockwise (from the perspective of the camera) increases the - value of `roll`, while rotating it clockwise decreases it. A value of 0 - indicates that the marker is upright. - - This differs from `rot_x` in the zero point and direction to increase the value. - -Finally there are attributes which express the orientation in other forms: - -rotation_matrix -: The [rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix) represented by this orientation. - This 3×3 matrix is represented by a list of 3 lists, each with 3 values, in an arrangement compatible with tools such as `numpy`. - -quaternion -: The [quaternion](https://en.wikipedia.org/wiki/Quaternion) represented by this orientation. - On the physical kits this is implemented as a [`pyquaternion.Quaternion`](https://kieranwynn.github.io/pyquaternion/#quaternion-features) instance. - -
- -[`SphericalCoordinate`](#SphericalCoordinate) {#SphericalCoordinate} ---------------- - -
-Spherical coordinate information is returned in different formats between the simulator and the physical robot kits. - -The simulator does not provide the `theta` or `phi` values and the values for `rot_x` and `rot_y` may be slightly different for equivalent positions. -
- -The spherical coordinates system has three values to specify a specific point in space. - -distance -: The radial distance, the distance from the origin to the point, in millimetres. - -theta -: This is the angle from directly in front of the camera to the vector which - points to the location in the horizontal plane, in radians. A positive value - indicates a counter-clockwise rotation. Zero is at the centre of the image. - -phi -: The polar angle from the y-axis of the camera "down" to the vector which - points to the location, in radians. Zero is directly upward. - -Also available are two computed angles which express the same location slightly differently: - -rot_x -: Approximate rotation around the X-axis, in radians. - This is the angle from the camera's horizontal plane to the vector which - points to the location. Zero is at the centre of the image. Values increase - towards the bottom of the image. - -rot_y -: Rotation around the Y-axis, in radians. This is similar to `theta`, however - values increase towards the right of the image. Zero is at the centre of the image. - -The camera is located at the origin, where the coordinates are (0, 0, 0). From 2623a65166930d2a9304d47b05a5d49d3befd255 Mon Sep 17 00:00:00 2001 From: JoshP Date: Wed, 30 Aug 2023 21:04:51 +0100 Subject: [PATCH 19/81] Add images to orientation page --- images/content/vision/orientation/all0.png | Bin 0 -> 19128 bytes .../content/vision/orientation/pitch-45.png | Bin 0 -> 19723 bytes images/content/vision/orientation/pitch45.png | Bin 0 -> 19354 bytes images/content/vision/orientation/roll-45.png | Bin 0 -> 26560 bytes images/content/vision/orientation/roll45.png | Bin 0 -> 25589 bytes images/content/vision/orientation/yaw-45.png | Bin 0 -> 27622 bytes images/content/vision/orientation/yaw45.png | Bin 0 -> 28141 bytes programming/vision/orientation.md | 18 +++++++++++++++++- 8 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 images/content/vision/orientation/all0.png create mode 100644 images/content/vision/orientation/pitch-45.png create mode 100644 images/content/vision/orientation/pitch45.png create mode 100644 images/content/vision/orientation/roll-45.png create mode 100644 images/content/vision/orientation/roll45.png create mode 100644 images/content/vision/orientation/yaw-45.png create mode 100644 images/content/vision/orientation/yaw45.png diff --git a/images/content/vision/orientation/all0.png b/images/content/vision/orientation/all0.png new file mode 100644 index 0000000000000000000000000000000000000000..f66084cdd3e84f5bef1bcf105fdb63ed05e74be9 GIT binary patch literal 19128 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 Sz=>2w+c)oaE7=%u>OTRh*+;to literal 0 HcmV?d00001 diff --git a/images/content/vision/orientation/pitch-45.png b/images/content/vision/orientation/pitch-45.png new file mode 100644 index 0000000000000000000000000000000000000000..3034d941e56db881ff2bd17558a7b3f638835c17 GIT binary patch literal 19723 zcmeHv_dC~b|9(kDl#vi+7a~+5BYT#Wik4A?kR39!B9U1ltCW#SB`eukS!E>ISxHve z^K-slukQO#`2O%cZpU#S_xpIibv>`^daUzto{y_LI$EmRx3O&7uwlb?bu}g34I4Jj z5dKF&hJT~x296EtO7dr1H~uc8In(OmCA>1vq_bt8B+o1VPit2`K>OWvo4)aNmaIKt+4RX3-y&Fy-D@9GFdSkTq1;@^xOaYhyPu||EHcH zeJfKxYi&SwZJ?%xf|>BcklHe8cys9fNRl@jjT|ile>tRSB=f}! zr(=7hoPXCeHErF5AHOrbs}nAnnVBW4=X)WgkANc!|OHlT zHW1I#JN`YV&RVR!wbi=g?PeNvTvBsssod48LkkP}l~>d?H8&G~k9~H2USCg7OH1qP z*RQEhpZ0MQcGy|uo>y!7JTtTN?Nut$cP-Y}R`*9`eE9I;`SWcw+E_2KZs_@o7a<`b zFJ8XXR8u2;*NA%i_Op6=h6N7KaFqV?09RKWMJ?C1*U-qwNMAqiTFZW7GFzzplA>By zSJ(dXqWPP1<>lp5Q{;605%IF##UDOMb8AC>YX_=Gc(iL+WO+$ z8?rDZeE$yYn4WHN{rbzy%!}D(+ll*RYsomBrm??eW$~|-l~vA#gKT#=xTK$-PSd&{ z6C-T+As@>gK1@$qHU$QQX|Yk7o2u!#Ig;?_ z&!2^bh40_LfBSY$`T%a7S~ZOAps=t}o{hov>jz0+KY!!Kjc#G<@9!wXsaZKd zr{rIq>pnkmy)(D1t*vNnY3$!`2GX#Lv4n2%yIg&5ud6lA{OyvEzOr(FR$AuEm!Ws> z65L`_#U>Ukm7bpOG^*?Ax%x!;?$kGOx-xdyu?J^@9lciFh<21t-pQ*Kjk~>=TW^_j zJ0PI{4JDVaEbQ20Aqs~&_fqer9@Q57+!h}EyLjNh0j)&o^H{)P=){5_2iJmM(};k8 zfRy8~20j$LOMr0WrBeZoNwP+UhKbS9XD(i3-Me?YH?0!6ti+99(O)d;Yil!JzT8el zMcV2}XR&8tgIQ))mQjh<@fEnn%Ez(U2lwy8wcFd;4qkXiK{R*L01SHd>eVk_zUXBh z6%yLv#T7)c=D0A`57Ykm@nf%)^DRPr_d6nJt4$BLwYS^$eAvCljo&&VEqzH(Z+P1E zg`U-k6WfT^nHc+YUjXYxMMddl-st&obBbMFX)1Gdd7d@2DlRVW;Kf|hdc`~TN}7tY zAVGb7d9h#LH>gh^JI9}739Yissj99fW~=_wDyph=pFVZj4HoWM!0&rful zWfv3|d$pu$5-qq8ZBl)2cW704xx(E_GVh&Ay}e%M*?&o@zkNS1FIIbB zSU5H|cI3#Bep}xl4*9EBpS6s>z)k%pUc6prXScvmX=xW<>V>LoBCJP=ySEi#Zf)iT!4Wc)!gwzcoXWb18 z%e(Lr7afkgJ$TIihd-=VR|f|pW}S+r-*D`a#)R3~SynOYTYIpz!1l3&2M^vT?8aTL ztt=9bO5tpiSNQJTnUpW*UVdFzaIN7CqG;YD)(ZrI| z$-gxybVjIkSm)+gwG+3-oN(OztA(f#F*FnoOL??q|U>zjXv*B8Rpkzo~SABkQ6K3_b4T$=Lu@#8op zY72G-6B7TRl=s?)GUJ+0pWa@0=}OPS!gAuo34{~v#}Jj2Hp|MW*H-PX-oJm3%0jTB z40hB6D?WHIH!}k(22e5mP^VRZ*K!6K6njW;a&8P}WoG6(crf(dy`DVVB&{?*58t5V zAcQR!6k)z`<7=ueCSl}8P^$oG5-v~&#pvu-C( zo(#Nwdv3flCOVqD5{_ePPAJrzeCGujqiV@3x6{zXJa}*~EUa^U4(2BOv1kW`Ct@=} z%EboO)%K+&LoCZ*yK+S#<%^s>Ap;8u^&n-k+J4yh3?|jR9T<4>#0d|Xq;8wAyLbJ5 z%(xY`jFRK#!cp_a$HxPHAmFf*Zku`7PFGhqnL%Dj=QAnMva+y z&9OcA5NUi9tr@4`6|Z00A#Lh}b8zul7#k!_CW^hIAI}K9b-?;fjf8ZAQ%-t|NQmqgtm6z4-{xtugjIeyL294L$97Z`7u}< zwSD{cyNm~Yf6(vT*=-|EFv=!6dHdDXRX7V$gmd9>)W1*kO0a;lGb0mI)`gtsxw(wN zRu&d~{QQLVY59bNa`DA3;H`SI>;|)o$g2#zK@{2w>gspuhTe9LqiX8JlWfuVXQ+;n zl2Yd@s@$nl5h^KfI>#jwb_DNK^5^aUcxPv2Wu;1r{n*!+I+y}? z>^&eP^eQW>_ruL2iR7rN^r*o2O7#GiAuOw>s;cVjEQWKOX|NX4KYLdGJOUe+%@$T| zlgG=$)2B|0FAaWvtcWimp&+(y|L~_pWa;F*d2ZUY_~YtMcay zT_#^;WJtJ9^`Tx(D*@j{>76=dKU^PMRaFJxtau)NWs}DWU;L$@r1KT^{nV*bPEJlH zCMKq)Jc)cbe6ysKl(LpQBm#Pu{HWeU+G4_vd@Tt%N1LsulWXvY-ZTCYdbZysF)a1Ak8OwgoaQ|IM2M-QjOn&kE_wRf6c8H{xy?g!IwjuWKr%=|W zu7aIjBlq%MXp)pTIoY+7ypBq*1oro@|D5&mSQcY$Rmp6-7aA&m9#P(zZ$HqOAZ6se zdP8F)Ve=tjVPmsP=qd0;fIz%5fsRC}VnQLg& z~$tPus^5w0N1x&N`V6s)PFLNbwKjQ*=jrI^0G^B~L=GJ>AJ%&Gz-~N_qugY{?(2)M;o)`dujuHQ$(ZTj!Or8T zg<5HVF*{F#1ku=s?5UOZ7g0h;b-w?igsk>-OIlX!@#Qhu)r@?1rZDv&8nY&wV)uDJ zR`F8WbO4&d_wSRF1@vG2oLxeN39RtR09;I^kEwgsKIZ7)Ajn9?FgWXmzMzy=B(d4T z!MsUp@h5lx-!fqZg|2M#yq;pu*OQ{zvM`yPlB?@leXOuS3EPjqW!`+EPIis)B?Zpo!yn0+$!O^06=#^}7$5B3HG442<$HouxN%>H z`Nda8C1Mhkx8)*ZV%od9s5kFD?&dU56-vm5V%N(Vkc&=-8D_Dwus9ssgDRjCj^uqh zj`qjJQ>U`DH0mEdytT(ARdc!jcZWG(v^_$BOa%>_t}Z>nfl#*L5|=LRNkvoq(Cyw{ zDLteDBcl>Rb?4*ePAM#8r9Q2HMMLAu>{yFUkH5xzs`k2iAvfu6FrqFaAj9DlZc4k?b}yJ#@g>tgz=D~j{-G; z5D+?gBoxJP#=v0QNiuAY#9{9>4-IvA#M$wV?6lO>)Qk*%#KH;t!fxblol6%lUewas z!NBW(>xahvsLCs4et#G3mPEa#HZ6F#0^e?@IT`e(rY3dwk+tz(XQd<2Nxym}L>`C& z78FFm*Jzo;5~{*iIqr=779Ab!xjc9IIlYp9hNZEwTvmHil5DSKj)MGuc4ug4=;(Na z_%%|dXA5r%q++rgrnc#=8hC@_@w6x1Or35_SpQ(}7c>=4`Ym6-meJO+S)Dy_b~L6( zA2q;OsB)EO_Q5;90T*7paN*t_iO9IPZO_i=>pyBRoDYTp@x&)^{CH7jCKb)k zr1jmrTE^DYVGU;4{So1`lTC3q>PMFQhBc*mvO6xFJsZ#$o{-S}<;%va&r(z8=H?Xm z5nG!#Zw4OQ5y&klNPYS~-0!kbOlESjargThXV0G12`1J}-ycZr?=BpSK70QB`O~L2 zU4`wo@7VD}J|v{idrdnetb*NIjFs)YTF`Qbd4h?Fxa=A;O@&P!6B85nF$&IPunWYr zFf`R)5fBhSaK`pml??lFzKhqcWz-ExIt;2QD3C=*K79Dox$v=IW}Brb%i|aK?%n(S z+Zv~x3b-Bkr7L8&H2a9%J7lZl;-4W{TS^FcoF?IWd~Prt^-0Qi014r4!}qU2XYLqo6STLl?u1*=<_Vw$7s3^bQ*{GyGsot`NN8zRTmiKEqxa55v=zQ56-HbFFP{ zIq%*HUdpE>e(EEEt{oeD6c@)RbcsSazkA~2Mn47+BbQ^XAQlh6Wv}w3dwavB!ewB?-KZ2=j#t7xZ47^VtCQ!N&x>Hm`P~ zwT(?85FEoP1B2^gj|ji}_VD4uu=3+%@DM#chAWXxf%kSeIV&7~)UZY3mCSrsZ`X&# zsA+qT$Xe64=LB^MN#K+ZP$(lSizfIX7zIAQ4WGNZ_AoNuCVs18$YE$?1gWKDx{6f? zmu;$~L>_%qOf0p{@^SK~PoLuASoqY4jZ~j`(3JMN|4v@Jfi1{^*5Lz&YBeQ|iJl%; zexyZ3EgW<%EXvPkwnTw=3!nb`=TCAFC{ZAnqoSfOj7r#i6yX~uu3fw4?d|R8=xA*% z$Z!a(M;^Lz1_oI33Eo;;5>w(Jh$KQWu)af9*m+M&L-W4Ns*hrl^ zIcN|H97hz+E0I4VVE*mfkBW<XC>M*hTp9-oTqfD*5W{uNUa zFLC&-Q*9)Zq3gzNz%e?Xsv{yokC^3W83sJR9~~_!Dk>-M=jVr<9d7IDdQ4VUcHh2A z%N(u^lw1lM0F@e>nzYfnqEYoX%yN-T(4hqwu*s7;bcil8;I{wMhPYYYJR8kIPxmt4 z&B8@9^bDri9m&a`g}n=&rfoG^+|~->{L|R10zXTr*_A6~!N=_SZq=GvUcYYe&Vr5% zybO72ccF{8sOTP9FX?^zHvcIgrJZzA+S!J*^R__y`0NFX^si%^r~cFlr#*WnFAs}s zL&D9>y=dSaD9@2xEuU?5gY9L3()XFFeu0=fq)nQhmETr!V#aPY$YC}rMDp`{pd=wua5)^dH_a7e8B_=w~vUi_9 z`+oa2xW1yj@?iH7vk!CMt#ra0%=~;A1@r!AZVy;8bR}-I(6OcxOJlU)hGu9oAxe z5O(;arT3H^rYEz2apb~N#}&VzP_8AB*7DGu3taywc-jONfrx_2PJcF z7b7FErw}yGKUYmnom^ZL`8_>70WWkubrm?Gf9vD@a>RV?rmJiHH|8KaSujTZ{rx(@ z1lv(>+2j?YcQ?l>BK+0{lnCD-ia&_@mxe`d(?2R1?{40xg*@5?_3Kw#CRha&UQQzy z7Z(+kEzyw)39IJbbLKiHjEs7hCrfC&DGEHLJWU4$Qc@PIdsl|VliYss^F7PX-ns2l zef>vIWhLW^Egs`mv}8URq#o%6zn~!0*l=6)(n#mkVeHzPn##8TWlLSaAZ2%WtSn3u zcq$oA@-xq0zy92B*EdbqpWtHmi@`%iWaZvSqT)4vvnCZ|C=UQ-fx26^ZXNjXgQEjE z+Q!C)NWQ7|5!Mrzmi~ZPo|$oibh1c7KP>++{vo=@CG6hwE)V0$E6iQjZvLG8JW6{D z3gHJs;b;Y=@X4QFQh*YH+e{JDWJw%c>E&Pq6SU0HeZdqwAdlmuT!hX92TeWPvUNJo z`|g)hA|7I#Q>xznzGU?eedM_PZ|T;XIfAMgtb_23Vo!Y*vesGfd*|RXmoN7dXzWFl z0)7a6`SzRK=YRYAW zQJPZuBeB&{C`MUD@kY}NHHfggoS&nkwG9m~QLSHJ-ZZ#VK`xYZ1~#9jz(%cjslY;) zvMCWpVHLCe$R%VzYCr6bq2=T3cy=-dt3&r1Zg}*V5yAl%+zvVnaxuEF$Of?b78VrG zqY@K)+S&pFmC0kMUcSutWTmFz7o--@>$}*iphl4@metO>YuDyy&_$s1xdTPu!huj) zp%0uP2<#FP$aV+8>iC;#rJc@dR}2lJpc(U{G0yJ)dQe0<5?P`)35)sCJ}d_yg({XD zL<)U1XDIbuU0p!lkdqXYq2d_mWf{KwVu3!Cj9>_Eu5!iQyL~ErBIrb5=%mp7IPh936+wlvRE_r}ug{q?Pmej@zgGOQd zyl^ZoZYqV{r~i$z%9G>`cka-VosOg>JHg4NHazQwq<>mh7wYh1A>5h}5K&!>aFV!f zVw+`vDJ+ZGnSU@xuVj-9&ww#smm*q>)W@)8hY){)HfN{(Ac$f3D?xN1v-k;N79|;m%c6R1^pse1@v=K-kD8 zyW{IC1FBkh921GdSpM}}$z5uqXS1J&0tfe|x)^RjYVBy8^EHuC3a=e^7B(vGG8Xv& zXrT~LBmX?;bwqc>60Vi-$H^`RGuqAFJHa0fAcGT(DtXn!2UH@rZzV~18 zeCO@dpMrzrY=oMdr~HBWKpDw_mVa7YEK?&YDf!-G@z0AFFP=X?kdD!{^b7y>6&@rH zgCL5M@d_IIpR`^pCL+?5{r%_ib_f@dgFFF3jj7wb_`S0;`|VqfmdA>L^=5xZThgKc z^bO$^mLzdDO*=_TQ!_vV-HK2Q)q%sujxjSa1sc7-xzn~Nz1ZHnGywzaiM`$;zD z#PpvQ*57V{>$kwqL{~w$FiEUCoFIAQfUs~@N{R_)Ty&1&`U@d>wzs$E<>ggZS1ayE zJUu1G(=Ceb?u7}LGLfV8iJ`&U>JzvBBe!Z?Nc>9886BMs1g1kr3d=}^RYxTx6hW!N z{D55Q%F4=v2aM^M&S2fWyKF1F@uZ>0$|2^|kSx#b-qTDJ+XJ{rku=E7RnywifAO8!=be)HZBrX z;s6c687S{io8&luC z)hl?8BYGmz@{Nhd3xP$^88#pnHy=yTDl!baH5+(gd?`#e@z5b3BN1k5V^<1{1h9o| zjXZenb>FiX!*bz<_&YgP(?9M`i4)-~=XnK%a-?I9)*7g55zb} z4UfN^@lsJ*;(_-rwsQ@S7D|BhnAyrN+7{gy`+86<*LnOq0O!gDCGxK?%IbtGvPdKb z=XnyJ{8YmJ$Hyv&W(iU;k&(X1AVA6nfr8K@V0HsKE8A1`lP58JLLmwPXYHnv(#%gj zuR5!&A~BOHY7uEZwC#)dq|821hOu^TeoBQ>ZFERsK^!drlHg>=ouzy+o<$x7hzlbS zcq*u5%|NdRNRDLbU390U9f$Q*RHxM;Z)#!2tilgnXLysbNT18KoQ1lQkMR;yTf?g1 zahqx}KnL&OY>=A#07S7i*J&$^(VsF1Jihgi_i5OqT_?IEK#N@is@}t?FsQ2hY;7iO zVXx8ZFMUW$WmC!ME_fiGT_|)uIAK*r_QG&G=hQcny2OJq8UoKKie?yF2TD~9M|;kJ zJd0bS-cms*Fq&RoUMEjdktwaTXs-{=j+Kv8Og=nhQiUtoa(nC5><*5Olfp&sjLXQn z+TUFhiJkhL?P`s#gtpK1+EkI zfIOap-cJU7bMJaCXO&c5g?HCvt z6>dM~{M{Q?tDf%LU{-j{yfNa-DK6hVO60Ma7CCcBzL} zg^L&0og3rtvU^cT?B9eS(oo2-(M9yN*g?Vq)K6lhdOpc77I<+n-wgsj?{EsBu59?tNwWq+* z+}76C$w`#7W?Gs#F67^(D^mE#nGyBgm5H9?#}a;yDfTV*(|G@U?`q#vA3GTqrnmzG zdI}UZrecpkmvUfE$QeKu?KLyHVQSeJ(59Gn)Dsv4Z423p;vD!8up(1ZT+jhZ*4@dA z|Bf`R58M}+y?mKnOGD0=q&5)m6P`*a)CdtMQc%G}0EL*nCUC#FY3vYVAmOD%WK4b- z76QtiaG_fuT;<~ToXG``yooL!a>l!rribO@qIk8z7%Q;6)RPj!HEi3q?cIfOfYH(^ zN8g~*DFKu|b#3il=ud-~zUyyUb8U$%k=n0gq6G7%;ibex@6*52YdzNdgl+e>gon3?dpc-z zPj=0O5Kg6#oX}MJkwd2fBHv2Gh9-&s-)bgJiN`@Zez5B&Bc5z$;@x034Aop47$EJ% zm&{{;HN13S8f_Pc@$B3jLRG;RpcSAzNy=phbqPQRbqNX|N(yHG^M#6>;gl!o zWn?WiH9;7oBu5=Wo+HlJ%V1QTS?1-_ryocDLf%pEC22b%E5T+=zpqqpawRp2nDqvJV-VdygBeAnqGT4|dqcnSMSLxB`L|90?42x)d?v<=rp$(yg3t(CR5}cfGTc$n%GYT?_c*G&NyQ%@uAh8`}q~Zw* z3W7LAUXk^}bFQN`X@#6{*J`s}j(oy_m=^~8gV`xGFg8qRWd+YWfHMRO;?v(kxNaG( z1j(x+%tD4A(pyIRY>E7gU%&p_k@}mn6*R;fI^#XH_1lNt3Z=ayq(v64k`jx76mlRf z=-Y4J&{5A5ykI^iBH|5Zd$J3)`veQG&LKru`)YvLIQ}zj8)5*FZO_^Y8?I> zu*?ywua6Wo06`8DE<4Ab+t7`24a5*8lW1>|D2IMqOfSPh3lLn)GEuOa$A0vV@$tW% zB#HChi;Ih(#f(dT#~#VE675GlibeTY{}IAPjbKJaxOm|T(mb@L6-|nbTl}>jlCGCuYqQZ%h`o&em zS`=dKOk11l(zS(0sBu9-eYTSy{^DIrW8q{m7;CEktb!VYzI(EG86#SP#Qprou?f4W zIbyP9Z7J)J<9_@nA2}0IByE8eyo4Zq+P9yI5NAwv4Go&=>K(|~D+ft}v%Ii-W4ylX z3N3M!zbk*!)8)=FuRJGl+&;IlADg`8N+zQZuG3clsF{n=!-{ykA-zl~S)M3F&+Iis z%w0S|cki<9*`t)4@347!uD|CvK}3e_1{W)2mDDN80*UVC2mIrWN%1{wY%*w80jIH` zbogc}MnX4>^gSicNj?Zr5gaC?pt*fN?{zWOL9~|PBaeEOyky8!ZEPlLqj))e+AJ~h zK9z}1n90cF4Mq^YJW)1YxI*wuIY{76m`{bIy>QXN!NJ>GmWO%Io{+nD73DzGwzcJZ zb=&0i7P&LgLoccM`zy1iQKgSoK@OF&&`P+n>L#=Dy0FmQ)%77ggR=b1wTVd!o8cdH zbkS(rZ7eK;TG1Qeam~$k1DDGl{G5~nb>H7zYw_8%)p?>z-I7jTg2(>U!ir;}M?uOu7wI;1Umrb7hGO83%O2+m>p>R9 zhrXfD(G>4i(nZ^kmM>b4o++Ru?dH<8du*$+>+JjZf_?u;FK}d=*+*c0^!M0U3VEd3 zcy6c7#dj885G#j@UxfSHZ(+MR7y(1#&0OMByeGj`^OZrRU=lo68ZdcRcwxkQIIm>y zo;?_5q#>@kvQS+gU#rZ;#U~FvQd^rweRWv8qyX`OS4zSqD-I#T0z8-F2@hkSo*_6P zb4Q#}((!%p;6Y*{n~(?n`cl@OVpjSMn0y`9bMpd;ML<7GMkI**T;@T{)8%r~zm{2>R!?1bi zeBQmxS3j=223t|h?j29B#+5OTUJMVSeoszjWoA|!+k|VZ|J_^ST^qR%9RP+yd3Mne zZxD1K7~b?V2cnbtww-XO-1Xvo#j_ZPLk|yM{q*e{a|R}&TyETmXjP;+j-lkQP$BHi z6zHpV7p{UnD4P8Wh#dZu%aWl}k^hgVBAstlM zYx*uG0Q$%CdaI7HVq1cEDpdkO7MO!O0fPkCWz+?CcX!C4O!-Z+<}LfR;?Y^Mv9ayX zL1?I7Wc6NczeLUD+lq=0CS1)D1P2B(=g}@0hGf%4iZnK-YCgh@2U;u@UBXxGeWX0& zyq9>_R(t1vcN{r(OwCgHluqPOUF?xSDnp2oR#pNT2Ab{4<#0 z0cN5>-(W{sMROOL&m#rDGALBjq-Q#ICuuc=TeZh?z8fh{S0LCoa%Hvmv{BP$;Ep$0 zSz)a(Eci7DC2U>`mqW4{u(uv~3pqR@vJnqXqxny(5guuw?d((7KP|joUglS_+&$^F zI%X7g0ZuZ=D|@+$TF8}7-V*bG=p3FpBB6HL6kfNmNPqvHWk4{1CnQ@Oko4QPW1^yX z?sifVzl+ihw&qfw*Q`K~hoh#o#K83P=QPz-YO3@Z zfctM!Qv7h%#!2t>W!(GP+8W~ga(oePPith6dCup>tpakvjprEgOcB0d|>3c(A!l&?C3Bc}-w`fGifcV^f7`x=ifI0^>MEWHHLzb83&>C=&rs*e0Q+mxTa133 zav>88@~Q9(&vYM?2c~Lu|M{XCsfK}+K~9Se8>rtC{$7AI%#j2V#g>r(u8Z;0crND=yPA8!7I$~(O@mKCL?s8~^2x?yMSeNd2bwNdL=oSdM$ z#9VTqR3~ixbxH1A46j!BJsw?HJ4#6s?)x1hkhs3RA><15DlA9L8{zXK+r_)&xJYS3 zgD1woZS=dqt8^ANL&#MqR%*`)W-@AuFIfI|s5wFjRm(C`Z3G$42(@E%zBepFPOV(c zWOZp43XA93;)psg*F&|nD}M%gQ?-hIf4oCrjNwl_XOZ~I<}gJ(KRxi2`_5Cscr?rz z*-ChFA!KlZ3lEXcy)~_UgL%GY3U%gd)HF0rAVBTxUcY*EPl*vv0j%1E_J?FCIbCwp z9KW-(7ZWNNNK+^7kSR@Te0fTd9-sii6=w!|W+apFXrAV4R#uPjVD0(YOyaV&(o?0I zFyyj$dpoO|skOEBr)Yi+9@!7Wabs&!fmz=q21{EMNg_7RlyzgbMD!4DS;P3Kkcp2Ownf(9>z~+y$jtQnbE?@LMdJ^bC7S z@X&MK9ani}mP3^8vCA4FV3DG9(H_2s6sP_anvk{i3k*DPqXb0g;u$iy7e5*qH7*Eh zm>@7MUwCw_<-rb`O>n6*&vZF+gt_!I<7R3zPV;cxd79|8G#bd}2KF|h24x&kI|Bwn z!g1J`XJ@2{?)N-fBP22aF+%WEc|(ZmP-W*tlGeR{BA`_!QTpcWNYY6zI(442r%xY| zlsqAcHxl0q9C`0ZFaOR)&(NEDomJ%ooc+y3n}TOWMXRq0C%7VljLY*=Q-^tzJpSB2 zofiCrBLfx1vSj_2{(8H4k~&ua$^o>L$Y6>{Nz5RCWVr+7PkO$~5wGx%kk-Qs+A|04 z0~OGZVq*LH^{ZDN65}T!&gqZoDF%Z3sEz088_fQ$W!p^w%sb zFnM8oCEvR$WGsx`DA&4UU(6*1Ny0tm8hXwaPL^nMMXDX(B5tj>WPPjbf;=u>#f_6P zg#jY2H^}Re`dGzn|3Z&C2JG;9_WPwzVKP6;b0>dg8hNgJ?x$DhY5xA*twmG(pi%K2 zZsgQc!N`h8%pj~H)s#dcJc?3>z2jlYv?MNj&Ql)?b8@mitgnrvX&;F|$2(NKJi#5n zt$MG^bs!W&!OYJPThEx;*b<>FKrtAKG1A%wg`_9Pis`n!p&D0&D%;Jc&!7Lg!<$&j zr>s`aSGS2qdMYQ&bNq`c`@V>vQjsbnQf}qPoFo2n1pHkHc(i_vxbRiT5cxw7F2c99 zxafQ?hm^qoQd9TqE{tk>yW!0Rj27}18JPYcsD@6k;YIU3C%2KD`=dxzEDt?KTD>uQ zPN=q|-vq5`i@|2Z2s{m6ZvlM@M69_Dz3?Zf7VjNLv_j}n2ptGRA(%r0F~=J~{7tkd zAotWB7ZltC{|Jat!FLKLYR4lKV2}Vz`$Z{`>i_LSHarpK;MKqqb#!*#J@pDV{T>87 z9^0-W-;x%r#v5~RlIoYiQ@9|LcDwY3l77=X$#V{9>te1oX8>woifY&u<^ef+et;tk zHQQlSCB=!8(nR`_edR6;IRatDXX8CuRh0_0$sRSXfU9*60B*e!ns++}UTrNo^O8O%FB-e@gj*Izr{kT;;gbq98 zh2!Ke4GDWMLJ~#2-dj=A2^zQ^0kAN2Im8|{u<9b>h4@!2?7bcF23-JJ3H%C^$t5_z z^j!Usrai~q-XVcj)U;vj5)u{uN{}j7z}As6JaEOdlEk%U9%}BBxZiVU&w^yh#F}d5 z$7m!aC4nf}J!ekgzn)Ij0Lhl|{CPREEkLR?;f`?(0_%Nw&>W5gwAs_-z2+G5m(Y%| zU%!4GH#A1@hs&mBpk#1Z{M8Z_$7pb*#X}+@uT5(sF-K#ps zKY(275A&KyA-qRo?sy1JKT2DsqqWuF-V2fP*9_0S;nGI++!zHe-WH&tD5ITKezJRs^rzlkCUpp4en$ zSi=;fdMowhU4@Hx0!R-68a`(M$Hl31G-&0PFR~W37NUh8- zCbp!ek-V#J?aIDzaD^u4@y1E}{K4jJatmXzNu6agQ+tGc4kjKK^C$~sWb!+hNL?o) zEXKv;S1KPG`1m+}>fHUm2mU9A|JlR;{NjJ{;r}#F2DkrgeQjPHC1v{BY!#EA+U%&> zf|I$qxwbZQaC=1{V{rR+6&26%fv>XGbsCA(u>PhLwMnv5RaKRk9UJ1WT|{_z_{*0s zn^N?Wj?=4du3Y0h(oyI#V#1XZz`?^skF4T;9w>$ zrs9E-k%+Lc--Cnd6gh0WU?V?&duuBXC#Ua0iuu&kRBCFfcBI4s$`1TedHhec)JS`d zJx(IVHShehCOS4YmN+5xKF$<>^vIFa=g$x8>S9lYV`F2brKO5aKOa-W@yiZH=b_eb zZ*5{?V)VuQeMPjwuYP;e|En=ctiT+ba*>gh<(4v;{go8)d{gZ*2_B%!HXD zcsLGF?m(DsqdyDhrw0`hA3t8^SE_R8&>_>g0fRhCslu@nCr>t|ozFk+`T-d*f{Tmm zSiG{^)Sq635*{2Qo=T+_6{*JVzF2w-o7FmWD3R)eEt&ip87W?vpOG>t2;{{3Cr3IX z4pMeU#lsd3!(;`evg-{%z1*~VT7J&;H;vb5~Yw5-mxjFLPb z92m&M!^6nLbda)-5a4c{v&Ga~W#`Udqvq!3$ou!9;N@6!)v8qv4h}EQyjptDc(OZ0 z3fs*5_g^N_4;2;8*RLlYcipc+I;f?kb>qejYim)a7G9kF-I06Se}B)HmzL%b|Mq#dD8DXE&^XUbDK80^m8Wme))g|q?WP%rk1qxO4fA(i|3(!hOrzz%&2?#$Pu@n(biUVu|cJWaPHh+ z@Bu$RzZBiL(k&G{TnVRVZ>^OrEgc>l5n^JRUZ@D4%XjH;8!TPLl~7O4a~i~(OXF4C zCw{jZ7#IXBtsy2ZK6pn<@NA~VT3xXnJ9bD(Y0-8J3n?|f^YW~j8F3Qf77WZ+a-Dxb zv1|Y52Z>v_64IVM+oq_%8kCJt2|3fOJUEC`RCR8wN#O&JZ@0M) zznT|CX@nYmev{ml%GTFv-jnCK5`?#JPtMBX6lGy$)zr`^nEDxW=gu85HmZ2`E##52+T{{ra`5uimuSYg4%>2Zz9)FDLc< zZr$>jY46meFku_UuP>JQ&(F`p?*z<34>+t`n2UE89QiHRlJ1n179m*ZF!syT2%lFh9%Q-!hVEBL>%grMhcv0-G^d1 zH*MM^AW$PJbDNuwZ#2Y|%3Cnm;qLJyc`fI>i+y?^we7=)4v?c+aB1nm*rui?2s#Wq?;Hy;&A7~|uI}N5 z&ASPovv+jbAH1!ys6e8QyAIjr+`RFuK#)8%oy zgoU3J7Yp@SSXg}d`jv8<+th}@&pFgh`5{f95MtD$2PD&Elw(S`VWb0Oj03LJFDZCLGGF%2ne)#ZVU7hay z_qHsgEp>G=Uwd}mWf9Fc;X(`Y_Qktq&mBJe@%{T{F3K>Keb)z0A=tAbJn#w#ynOzA zKZOS_Baj8p&B(~e%#;z3QBdIF;|oqgbnB~&3rPxaXT3TVNtgO?*{G7s34bmeh1Imkfb-voEo>r%%@FCmQE;>}e?bmSr!o+u%DQ}Hi zN+uAhjg2@|6?-osL5Q0>$Euc(s}9vxDKC*i3=CQj2?@ofK(SXYT{^6%SE-+m(0i?a zy}G(WWwx!rpJ6(Uzterh_l3bBtf;Q8e&fczaQI(UU0ubNSFc{3J$u%^Xlgr?HIWBH zXGV&P)6(n)zN9KVef4VBjvZ^Yb{S>A%*i2gW0zw59|LAQ$W_a%rA1`lE z($)3~#r0fcEf+6}PERv3RPe-Z7gn(DC~$-2N2pBjFs&gL{>QdGd(*yazq(3%6-2je zNlZ#wsde4ix#GizuIGV)f%EfTvDEU6j8dDHgKJhZF)?X|3oI^u`ON7Jd3m7%RdKaE zzD*hoeF6%ku4wCcMTc&Nzbij|`qVn!?I$Hbs6uh+?X^5*yJRblB`7$QNO@gMGhHK5 zU3xj&w&%?@?a0u$-8b*9uL$d5jZ8=|J%2td>8Q4LOS)-lVYJND%n~SU2r+rEU&3veaNGe|1z(&otvgiFt&I zzZ0zO)srVrGBVh;5H!jwDjb=Gi>ZFyFARLsjVZ+$63i)W*E3bP+`LB| zq)ST3cko3W4bqpn$O<{x z60OI&ct%p#ffwJ~|F9K}pbPcm*xc}FV+!2{mrPeCojTPOTT)We&~O|pP~GNw)G}@Ph3Oq5^O7Ol<}5C& z%~^D{b#?77UoKG;)r*f+ad)=1wsv(@2#!IZE7uwL{W~ly%=Yr-Xk|BiZK56 zZ{Mz5xk9=x?Y~UgWtcoYeM2=Vr^9U2N|pW(#p(m2Rh>nLawsMH-=$1*c`1b`^nJV| z0-*H!_ir&fBCyNv*t^%cGJF%NgAa=EVkm(j@YAPHmerB8v$H=$XKOo2zxm3}oZ=Ec zLuxrsJ_FC#C@9#`(h`tF7!kcELh5WLB#dCvZB;P#SsMnf6(Lxzzm%030*8l(pFbB< zjb_YuhOb@u00bHs(Kmr4wSuEWUkQ7EZLa0yBpVz908?HIX!lyert!%kBt`Wub`I4x z_Yr-Qy7CaQ`Ld@+3^$5sl9Ukizce>@zsbzZ?Cv%bkh}bOYkPKIO|);f6H!(;DUrI~ z1H$VisD>r|`{Kpu=;&Gj;Zjk0m0s&x=5l);#k=;NA?cp$>ao{t+qO}NjquCcDZ_j_ z3f=WR?2t1&>_&gq#>m+;e|jM904cxc;N)y-YpYs!zPK*!e5`U%caP_98)X`Uh-TL= za~4T<4uP*tP4{l!CXM}SOg)+MgzkZyPxlBHQj34^;A=;RprBwlk^w_FkPx7iSJN|X zk3OZb@kII_m(DOtjtzbRf4v2nwR)$xxWg%Z{l>1Y3znAKg6k_`g7uMJ zi`Ikt_wPqVaVqff^PfCjXc?b_C@TMLjg&cPPHVga{KHV7Mh5ffcEE_lacV`h{G7HY3bNKM{7cXM=+VX{e zs;UA?+V(h*abfsvOY7^Eew^ENo;?0XKZ$hm#EBC|M*NU@>|XbYAq3Cf>Zm08OJYYa z_u7$LzkNFgSlL7*bRgW=GepTQ@#FE~d%}hgZFasvXrIJxDbE{bfWi zt%i?xsw=`hFVf7o;_1F>n?+UvKP;hrhXbfMFJ9ad4spiGSbiG*o^Ophsk;d^8m*sI zKq2GC_yEewfLph^o(~KR%+7kO(5N}@va8s6*N?f$UDXF^;Xstqpgu9uZ(us%vUu0gfKY*^tg%y7a57Ycudt z%^QcFjboD$xz0drmQa4uN&cla+-6siK-2o?3weKjvuJvLSIEbX#;}^>JR#`$KIXz;b@8%jogpC6< z@$|xpa|k8F84@82W93qr9*QpC{W!6362RG$Co58RoqxASLgFQUEKeaK)=7!~_k)xr zmvXYsw;yWVAZ(g@Eok9(bhKNi=VT)N0n9^C)q}^yFtVK3^Vzer^CRXgIgkf3kAO=c zqpp(kkfEk#1vG^`AJ~15*!Yos`}QRti~juivkZV^tW_wbE>hNOcBBwlbCx^OGLilk zdAY}uV=&umTYeYF+8X@KY;x7Tspg#B{zexoWVqM`!uB6b3V1Q?6G^72V^LwFdu75R9{>z*76A)Q-tRhOp?U0q>Gn$g-( zxp&THS(R5-Cehj3vmL@L&ypL?yU>c~xka=bnPD9ws;WhWg_}~wCnweDUA};75vtQ- z!Tdxrsc;@9=OG)OOwpzI;7HlodxsBKJPhlp449a36zQD(DPI}N>)Wjk{3;h|>7bsP zYGJY4acd5I*#=U$+M1DwyqcEAS{?@ZI+&wSi%|aa5!sw{Q`%qYE2Ft!Na*Z{eQ|A= zVWeMoQqg4Lh$ppVO|FQNl9G(f-G!a|9+8c=>we&|(}8Z()mILKF)+@)%B$<%6DAK` z0SxXjuVCSlzW`5M2Vna?^ChRKuEwE5ls8KbK72tW)a}|Vy|d6&`o|wj)tXERE}_$a z{mzuq&Re&Pxl1mK8>D&ssS=g;*H2<(GW#MH+tko-J3c;K>EOYGt5>fk9XN0Ru%`ia zfza&ss}ZLYs_p-k;6)4I9dnUNkObYjVq#>U3P z{uXgUH&D&-r6o@vKfZg70+td#c#VkJ~08SE!yMd(XKSo<+D_2lN-N&-N4TuV)HIPKk3O6(t&g5zKKLA zXI}Zb6W#!Q2D~)N8HN`lU*$edNfF(*@6kV?BkV2(*2XBhgztC${p~GIR-8B45jooD za6m^#$Ju#r8sOEVp`oEikNAwKpS7jhvOT*$1U-JdnX4GoYI*wj*cfb?U^W}IgvwW; zA^{X)NQU>`yt!P!Wqc>UyyLv`^1AHbLS&cDM$_39-0)@aJ(NeSt*vQkl8fs4{7f5o z@r6OU2{^N0Q^H+Z_99MuFUMss z!V0?pX7cm-TduZcK70P0vE|L1H(NzTdwi@DJhPD}2-4N77NRvpE(T}oHcP8aaP3}e z-0g?}+%$Op_8U&$@P1iAL{P>tbPLHQZU8us?E< z-SSf+EZ)Ae2z)2}%U@nZn8jX2MTJ;D-}(35ZQ3L)U0q#U+g-u@x$?r@{fUYC+)~{T zH}yTI-flOU;nsK8Z4Mk*zFb#>RGH`0M^sfcvh@<@-UL!${AKN<^=id9BGw0;38z91 zDVFmp7%y3KVCBk1L1mD(G$m8Q(JN_fHC$n=t*!0oC_{)9xWA^vcMWd`P8hn5l{NX< zvt5Q67ry>`IyWgbb;qt<>XJ5E|IGrdD-Y8N3#}<6UHtI%#gYOV)SssG@R_qLmz0xx z`}#G9%bT}vbrMuPJBvKA@z9Cb-^^BFGri2%;+%75yVeokLs zR0O@SQ$%!43tYoP*vBSP^8CI%dtm$JyR9^CU6g>pq@`b@u)zbz$q%KiD7V%YFEksu zmckBpTQWtcRD8UXE3!!V_%1UzdGcOdT$ob0fa2XdcN$w;AI8SkkSsNBQ3k#mnAX}ZYpO$|Vd`G7z_tFx<^iKvPoh2N9Gd(?BN?BRJ1&y+&&z^1AxKS05jzvvV zgU1UsX2X{+*HH1o^vR7&WOMgDGCv0`r6n!QYeYr);@OQIhDEV-SL1up(RtnrUMo8P zmbbO5_>of)k+XV@Z-=+g;@hPntny{&Y z*B6${T6H~IqSZVoe}i)J8L*n zK^9RU=#Y?*7tf!+M{eUvFgtbp_&l9P*tIz9I&5L0)9~H>t_#NCAqW^Q1-jkR!lLUD zx&^(oN+GoyW4~}z-o3ENt3-BT-YB7f&)!^aBK@t1gY`IMZoHARBS*R4pavuA^@NyO zlry25qystv9R{i&olc*aa3=3T=?$VI^1%b?tB`u@$NG99K0eeF+;+j?;lus?kb3Ds zb?wr!k%8fg#r?fDKl?VA*5go%u9U2-ER7jC8gynm-rty{Wo~B1BZ!?T3&2~@w4NOP zKH67biIbA$ckgCjzy9EUe!gz0g}ZfiHZ5tV&zxC|qBRL~yp4?vs^%=ikc*gv#0NRl zu0U@l2=74IOH0DjuV1_NrKP1Oc^&`W`;n2|svKJy&KG|ASv%X!jUEgRQ{o93MyJGg zYnuvFd;9vpRQ6;pK_c?lP*%O*zH86XpH)_ly@=j-*?v^2P${CVko%A{p?*(L)xc>C z@6fZUb5Zb~H=XY_^`iO;brIpd=ANF+<@Gny7X$CQ$#pC&w@`u;CJ5*d6&a~I(Q3}J z5|*+$-gBZ&J%IHqNVb50o<~b@os8{7-MMq%@!&{be&@6F5r9H~y~RMCb(ObxuAS~9 zy?PSx(jX2%ZtK~O|`dThg-9AwlEH2DAbe3yFA+QmS6orU!75O`r~dU`}i zNEx9d?j>H3EmpO*F3z~fgHEMD4NFQYaGRwFbu;tty(+eASGgznM;eWmyF*@{yA=lG z1rEWKUqIj(wyCR2V0)ZQ2hlG#eW`F4qN^c2vadF=p8g`>5@<0YH%7zbvbn{)qwjJm)c_~AY3GPWqNbfNxg?f>X?;kK& zrbQmSlV>CStdn&u8-+ptt}4m-oD*130>C5DRIk#3etY|?AaPN!DC2J z#i4;+rs+k~1AzRWv12s4$*0brk4s31m|4%siNf5E%{aee;`eNi+6q+LYY8(doXj*^ z1rC<}FzRCD?NP4|wxm}suB{y<`5P!J-$hC?WYH%}JOKbf z0Mp+$uuB!ZZ_7eMWiiRhTCu!1gA_#flU}VC$SB#K>(B%02#ATGGxT^lN>Y|s80GeC zZVZD|hf2GeUAVA$>sAJ#+#3%@yrP`?FxqhD?Aah9pJ|tulU;Hf@oKErCu;Ja^s~w_ooWFfX zHg2iR%bX)caP_vb0JcSGe*V+@p|Xb?v;-KJ{|+SoHanY;WK*&ZW=cvTpd~Hz$%cwN zXIKT56&$)X$Ox=N*(Qqm8cY^7K)%yr@JxsM$lDeZ8#}uS0vbH46zUr^t<-{`o`5t3 zxQPP&^d=`K2l-nKjOPdrSQ8^ZLJS=gd#OlP*Vm_~rB!eW``FLU_MI}2-Q7SY&wjH> zrz{Gf)bCh~e}smX*5ue&*IJGe%fE)>_^Tn2z`vcS8Ab}=mEbS_+LvVok21%SSE>az zd@h|Vu77J2z$T>XJ|Pj0UXYZ^7<+#|9pu>&H#f!jOCX3NA}TGTqoXlVK)@as99;@% znc%{e-5d`cS|y3DJG4~B2_u3j(R~@U!l?r<_M=x}Z!fjDs7`|JnT-`)+)y~00ItGH zO8LpjtD}CC^RhBBFfic@xt5s{D?<}!(N0QK53s6vc(WQX^3kJ5)DOriuzp&=V9V6} zUtb8Xn7NwI$A8PZc3 zVQpoF!bAIVhBe2Ji-aNXVvzBOf!zNu1X^=Yah-l#ppeMN{jRU2iA#M=@rkD9YOg<|30Y2}LLOAO5mMZu_6@yMi$|5{Xm_Ur2&9=73r_a__^}LJDjnli zD>_kxfic3YR`sV(BvUUhuj|+4An;j@TM4tbM!XZm4IahBn7aR#TReF6#|uA?d5A8R z)aikY!|h0VJrQa}U_nrSvC~5X+3CPIXU?5_2V{8}l^U>$2^fcw!ND>;4F$(rWdR(D z$d-Tsi>JQYEb+6x(gh(*gX zj9ZT=_6n#O8Fw)ybxt=?_#^wB)c{@*^FAfhnPDy(Z7UN39AI?s#Wcquo<6Hx%bWGBq(VLATrFFP*P0 z>>~=b0{`d16yQ=TK#Z?A1H$^undX77FNg`485A8T{q)Iqr6Twzf4sFxk}~GOAd-CX zln$|ce7k48$^y07C|7EUwq`?+JC0+RmVJV&uGVDyzj!?^tfv&D%&%)fuS`X3v+i?uEeZQU}fMUx9x^5ciWP49R{0ULA`PN zuJ0Tsy7z|jmfEj`rOQK*K}abc$(d{j22MI0Upj=XH~{aY{KwMS^U9$7I<4PPY_+2!Q7t zi`)11$rGPtIL3Y_I3JFIDTVX-&XgeJg0Zn{Tsc>U9!h&Vww&j5zwr<9&Bu?27tO|f zZaCkCmBl#Man&TEF|q4JkdF87b1A=pnWMy1e(lgxu}R4pE`ZlO*pH%o|0rSrCK%uj zlE%60dLRH`Mv1wnDHktp1DD!Ayc$#vpMXGU0Qw(JeMMBiIAIkGNQ!DO*uyR(1G|Ej zB0h-VbkpMa#YOMM#I$#IB3^N#`70VKEhfP3Hu)U`iUQNkM*kr0R|7{++L1`(zZ&B} zmIW;h*eTy%s%it!Nd@&t`*2cE{FC*>>O0 z9Y6k0>GDZU4P`kq4XV<}G6b1juoyC?j>j=kIWkzri6gbOwYffDnjvBPs{uWXqe=j~ z1Fac=JK?(G5wK7LG?nCK1y4j?Mk z#HA{wV4JY3CDewfh?fFnF|cCUaOeSw#G^z~bTxy)?%BiTCz~89l;GX_>3Ik@T1=z0 z=%U!SW!L_uI-dFxQJ=ugL5^A?w+k?tWAyhB-S2O%#9@dq2vYtgrpIoYV<;G2jKY(c zI*CW<1hKROQ%~kz_OtX=BgH~{cC&BT@b*H37Bbw*adj( zDkd#W19z~vJCtqDQ&ZP6F$wSI;n=dDU|{6sG0Bk!?{M9=XU{bfiQR9dk7jtRiXEBE z%&lphCv*ApQ>4Q#$UbjVc!x`8iRt_a4JyF?tECQ5q7JnZV)MxmH0c?%xa z(TRiDIx=lz+9@Ayt-Xn?;%z}yJWvJ3Ej zH<)(bAgw@FAy5?P5YxicGfUv1V<)=fV3))ZecOw@7f{gwiRbn3a!|qvIDjDpS4;~L zRgw5#I3U2e=)3FJ-%S6cWwHy5pUNhH}Z(^$wC80q>v4 zX?S0f?;a?>ck0L&tGkoC+%vo-v|H5nnCcYWP!a5u@b3evFK)@4#3jub^Os5oQ1Y3`)e&N$mh}h7kydUkzJ6#mLHjG7{tZ)HjOE zap~&S58^;)%*@P;HvMY0-}&gH3B}`;YAZ@Eu-89py4+mV={e%QFvI5kUYtTwh(6Z+ z3KbmYYn837T%Q?A6wUsrhLhwOrzI!*%lxbfm5HqHGxeJ4otv3tTept#;9z2gW%b5e zZ-2zj)T382H#-_!ujW17pJM9PyFochH|4CTz1nDvePmQEiynd`f(~=NVjq?KK%9WE zv`h z1c}nIwza7eRw9?Gm=Q%wFLX`Mum>guV zapOjmugYgIVZ)g=!F!4UBAP z27qca%_Suk7RF`%s0qy^AQ47ULb#=Hzk{-+PvGDYWzhT!a{+3zjcT)Kf!XZ6+o?7; z*6BHZnvzw|ezX@gPTH$i<>FHws_cwIh;P@f-6z)ilZ8mi$Pip^CK&}3ULg*q7h#)m z8zpuoqI0UQ^O2{w^@845T4S+jv{ne+oFaomh3{2yay zrmI8^5^fA+*?Z4$8qPK)cr8rzEx24Czi0NE{?&j{(5?Sj6zYw2%804Y;^X5_?YvIy zI`AnJesgT=`aG|>X+R%Lbqivo?wq0Fe)e1T+hxntktmrNR)Cp-{l9zn$gvytf49DG z<<0%p)_u4zCAgD|3AF72JpK|h9~kWGPQ+xU(vRFY6dq02kyLhZFC}lvY#gY;SM>{P}K`1+^J;5$I|T z21-m#4Ye6JWSaUxc8%4=Ded5|s}bW*0ldbP2P)plp*G(DG!KcU8^Zy*f3<}S*m&1p zc=65s7m#bJEbeAfU{x7!-h63qH#&b_MNSUYj^AHDz^=;V_U`S-b6R6xZuZ)x(^EdG z8cwAz7#poGh!?grH93LsTe*g{YgeCu&t`flBHD`=FK7)zLsy|UJ-zyd2KL^PXkJTV z)RLVMEfO1ME$jC2-9`OF zAT&iSV?#@wdOY#?uiw8>GeYNd+GO7z0=F$mZEKvuT- zD+-IfF_>K&Lc*ms|7RogI%Zt|o5v8&D|AnBP+;3*+slZzOh+dqjEs!fU%K?sLI$j` zs||Qzzk|yUu+M(1p_040x?=2$+@LEk4q0BY(XYMgfGmevUtisukCagW{sxezehZ_I zUaw=`@UUIM^`43_$QI>+t8(46gH^@8v**rX?(x;jmo*kLumV?cc1A=x5q#zCn>QYG zN`Pw`#^n;~t!1Jy$<-W$gH(a6B{}^iwg>3t1K{lOKYyFIi{tx26pugnE{s6aqjHderV9dq|QPH!`?5~5Z?mv zHaR}-S4KhmuR9N&ndoR)IEu>+f#Y2@q2qW%l?A5rXtA=g%1B7WEFsv5iXI2$s9LL;nHdg-iz|N( z4#K5ehcU5HR#w)3Z5kc&a;Lf)sY^TPY>1WZP;6l?0|8}S4?;t0wk>}=KQC|9 zd=^qd&o0E_LCjU8p4u5xJKUDFrXIP6g^f*wvDCk@tK9-DHyB;yU zzyC7Bfz}InCumi_K8)N$(dZg|_@IFxYvKU7fg2s2EiEn>q(b)~OEA`n(I;bzC#{^D zu;pP4LqSLgSV~9(#Pg!~8wX-s=ZuYGA3nT<3jt1l^B}$ivq1Z=D-S%(aWRasBW41! zQ*S|!4!90_VRvg&lkC2IpKkp6_3L4DG$Gs6qN4ev2vswcW4gNjWt40CJdq8Q5g0#< zZwx`qK*WV|r}`4CRTTOlsg?OoeYzO6j*m%5VDmc&K*Y((36HoRFDrZ09wTBrEJf5( zW<=NV+ts~Z<1b7J3j>?J0M_HtI0;m+hVY$QtNeHG#8eQoI^doFIf&!v3UNSGhISnV zyi9)QjEqFLZ%2K#LmE#RsuC{)7QxF9q#0pD3a<@^e&=-RrZiz3=E(;{WivEvkiz<} z-2jD0JfZ<)YoZ|F1yKT_Z3Fl`@dzz24Ph*Sh0s!6dJK`P`62Q|FsYpY|`H%r`n!V-; z(m<;C+)rdh2$X&Ph$?Yr zU7w0nx&0n?4E-=Jd_wRMzVi|2RVEsD4XkyLx^#3F%@zD1d^3Bx5%D{?WqnXhZSA)8LC7PEuUwU5-x;nU3?IM?ii+ap09fQ4aVMUn zKyGW}ji?mgxs#pHKqeX}DNPXM_`r+wOiiSqjexOR*Q3I9Ihp!p$@1Cf&u3+2HC}py zYrKLHm~PD2NIYm|ZCyqofy$MMewmi0iS_6%8$!wrB_?p^b?f>dWXh!ni5nrcR-8c} zUZfWV6WaPm(zHk*OcR$P%PnNi>g$)7Lks5BeWR}pUm{829W5=yIg}rxx&g)kcad@; z6^9#bjg6+Y;?BZ#BuNcn8hRY?fz8VIF*XrUM!B$C0WpMFtOuDlqTH<9OjExtxpR6uH_f$R(G3_V91u8m25#F`xr0ouF(=`<+-v&`wTW3p%#4|%<>`i6aL4CeiNo6jFAc}doWC{ zycL^!N?hLO3qvkwY}8-eoC{($aO#q;jo!vdcW>n8#{E0)jT-^pi24+o&-h6M?vQc- zhsufyKo~b32#*K7Bt9OVRu~1|3mlC19^8Q&=2ls4IDgms<%0e=s^Ox_aLrLjNT@T* znvHlWh`6|oxQQYaB1KG6-i_^LU@RCX!-x|r8{0zH8nyipv)COy)hUZhmo9;$_3$Wo z@gigmg8iD45*x8gQBhIviL8XVUs`H}DTt+Z&JDZ3Iy`zG7=YDBkJ5EFcDlQGs@5Kt zVF66=6uWX02GC&srWoT<=D~{}!aWuya*7ed4^R!YwX0FfaSEbr!ov)mBrAW&pbJF^ z@nsK$YaGGHUO|?y0_r+zD?)oklo}R1Tp}MEP52WXC;#7<|0l5jJ0n?O%MnSd$=j=% Sg}=GA?8qTqjkJRoeEuJYgRe{g literal 0 HcmV?d00001 diff --git a/images/content/vision/orientation/roll-45.png b/images/content/vision/orientation/roll-45.png new file mode 100644 index 0000000000000000000000000000000000000000..84cdb27413a729ca3f4fde34deda5747b6d8b783 GIT binary patch literal 26560 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 literal 0 HcmV?d00001 diff --git a/images/content/vision/orientation/yaw45.png b/images/content/vision/orientation/yaw45.png new file mode 100644 index 0000000000000000000000000000000000000000..34bb07d949817128db2c8680856a3472d978b6c9 GIT binary patch literal 28141 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 PW%9?Bj>Sk{y!n3s3I!NN literal 0 HcmV?d00001 diff --git a/programming/vision/orientation.md b/programming/vision/orientation.md index 17774f64..9001581a 100644 --- a/programming/vision/orientation.md +++ b/programming/vision/orientation.md @@ -6,4 +6,20 @@ title: Orientation Orientation =========== -TODO \ No newline at end of file +TODO + + +# Examples + +The following table visually explains what positive and negative rotations represent. +The red arrow is not normally present on the marker but is used in these examples to indicate which way is up. + +Zero on all axis: + +![Orientation zero all axis]({{ site.baseurl }}/images/content/vision/orientation/all0.png "All axis zero") + +| |π/4 |-π/4| +|-------|-------|-------| +|yaw |![Yaw 45]({{ site.baseurl }}/images/content/vision/orientation/yaw45.png "Yaw 45")|![Yaw -45]({{ site.baseurl }}/images/content/vision/orientation/yaw-45.png "Yaw -45")| +|pitch |![Pitch 45]({{ site.baseurl }}/images/content/vision/orientation/pitch45.png "Pitch 45")|![Yaw -45]({{ site.baseurl }}/images/content/vision/orientation/pitch-45.png "Pitch -45")| +|roll |![Roll 45]({{ site.baseurl }}/images/content/vision/orientation/roll45.png "Yaw 45")|![Yaw -45]({{ site.baseurl }}/images/content/vision/orientation/roll-45.png "Roll -45")| From 860d1dc0e5c6e7111a1aff56319b8cfb23d92953 Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 31 Aug 2023 19:23:57 +0100 Subject: [PATCH 20/81] Update power board API --- programming/power.md | 101 ++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/programming/power.md b/programming/power.md index e42c02e1..aa640951 100644 --- a/programming/power.md +++ b/programming/power.md @@ -1,4 +1,6 @@ --- +redirect_from: + - /programming/sr/power layout: page title: Power Board API --- @@ -7,121 +9,120 @@ Power Board API =============== There are a few things that can be done with the power board, namely current and voltage sensing, and beeping. -As there is only one power board, it is not accessed like a list like `motors` and is instead accessed directly, for example: +The power board can be accessed using the `power_board` property of the `Robot` object. ~~~~~ python -R.power_board.something... +from sr.robot3 import * +robot = Robot() + +my_power_board = robot.power_board ~~~~~ + [Power Outputs](#outputs) {#outputs} ------- -Each of the power board's controllable outputs has a constant whose name closely - matches the name of the output: +Each of the power board's controllable outputs has a constant whose name closely matches the name of the output: * H0 : `OUT_H0` * H1 : `OUT_H1` * L0 : `OUT_L0` * L1 : `OUT_L1` -* L2 : N/A (Not Controllable) +* L2 : N/A (Not Controllable - Brain power) * L3 : `OUT_L3` * 5V : `OUT_FIVE_VOLT` -Both of the 5V outputs are controlled simultaneously. +Both of the 5V outputs are controlled together. -While they are all turned on when your code starts running, - you can control whether each output is turned on or off like so: +All the ports are turned on when your code starts running, you can then control whether each output is turned on or off like so: ~~~~~ python -from sr.robot3 import * - # Turn output H0 off -R.power_board.outputs[OUT_H0].is_enabled = False +robot.power_board.outputs[OUT_H0].is_enabled = False # Turn output L0 on -R.power_board.outputs[OUT_L0].is_enabled = True +robot.power_board.outputs[OUT_L0].is_enabled = True # Find out whether L3 is enabled -print(R.power_board.outputs[OUT_L3].is_enabled) +print(robot.power_board.outputs[OUT_L3].is_enabled) # Find the current (in Amps) being used by L3 -print(R.power_board.outputs[OUT_L3].current) +print(robot.power_board.outputs[OUT_L3].current) ~~~~~ -An exception is raised if you try to set an output index which doesn't exist. - You can also control all the outputs together: ~~~~~ python -R.power_board.outputs.power_off() -R.power_board.outputs.power_on() +robot.power_board.outputs.power_off() +robot.power_board.outputs.power_on() ~~~~~
- If you turn off the power output which is powering another of your boards, - then they will appear to be missing and your code will break if you try to - control them. +If you turn off the power output which is powering another one of your boards, +they will appear to be missing and your code will break if you try to control them.
[Battery Status](#battery) {#battery} -------- +------------------------------------- The power board can report both the battery voltage, in Volts, and the current being drawn from it, in Amps. You can access these values like so: ~~~~~ python -# Print the battery voltage and current to the log -print( - R.power_board.battery_sensor.voltage, - R.power_board.battery_sensor.current, -) +# Print the battery voltage in volts +print(robot.power_board.battery_sensor.voltage) + +# Print the battery current in amps +print(robot.power_board.battery_sensor.current) ~~~~~ -A fully charged battery will measure 12.6V. -The power board will turn off and signal a low battery at 10.2V. -The discharge curve is roughly linear between 11.4V and 10.4V. +- A fully charged battery will measure 12.6V. +- The power board will turn off and signal a low battery at 10.2V. +- The discharge curve is roughly linear between 11.4V and 10.4V. [Beeping](#beeping) {#beeping} -------- +------------------------------ The power board has a piezo buzzer which can beep. -The `buzz` function accepts multiple parameters, depending on what you -want to play. The first argument is the duration of the beep, in -seconds. The later arguments are either the note you want to play, or -the frequency of the buzzer (in Hertz). You have to specify which of -note or frequency you're passing using a keyword argument, your code -will fail otherwise. +The `buzz` function accepts multiple parameters, depending on what you want to play. +- The first argument is either the note you want to play, or the frequency of the buzzer in Hertz. +- The second argument is the duration of the beep, in seconds. -Theoretically, the piezo buzzer will buzz at any provided frequency, however -humans can only hear between [20Hz and 20000Hz][pitch-range]. +The `Note` enum provides notes in [scientific pitch notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation) between `C6` and `C8`. +You can play other tones by providing a frequency. -The `Note` enum provides notes in [scientific pitch notation][pitch-notation] -between `C6` and `C8`. You can play other tones by providing a frequency. +The frequency on the buzzer is limited from 8Hz to 10,000Hz
- Calling `buzz` is non-blocking, which means it doesn't actually wait - for the piezo to stop buzzing before continuing with your code. If you - want to wait for the buzzing to stop, use the blocking argument! + Calling `buzz` is non-blocking, which means it doesn't actually wait for the piezo to stop buzzing before continuing with your code. + If you want to wait for the buzzing to stop, use the `blocking` argument!
~~~~~ python from sr.robot3 import Note # Beep for 0.5s in D. -R.power_board.piezo.buzz(0.5, Note.D6) +R.power_board.piezo.buzz(Note.D6, 0.5) # Beep for 2s at 400Hz -R.power_board.piezo.buzz(2, 400) +R.power_board.piezo.buzz(400, 2) # Beep for 3s at 250Hz and wait for it to finish -R.power_board.piezo.buzz(3, 250, blocking=True) +R.power_board.piezo.buzz(250, 3, blocking=True) ~~~~~ -`ValueError` is raised if the note is not recognised or the frequency is not an integer. +[Start Button](#start_button) {#start_button} +--------------------------------------------- + +You can manually wait for the start button to be pressed, not only at the start. + +~~~~~ python +robot.wait_start() +~~~~~ -[pitch-range]: https://en.wikipedia.org/wiki/Hearing_range#Humans -[pitch-notation]: https://en.wikipedia.org/wiki/Scientific_pitch_notation +This may be useful for testing, but be sure to remove it in the competition, +as you won't be allowed to touch the start button after a match has begun! From eac0ae960c43145f82eac616d6a52068ae268582 Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 31 Aug 2023 20:00:16 +0100 Subject: [PATCH 21/81] Update servo board content --- programming/servos.md | 66 ++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/programming/servos.md b/programming/servos.md index a719ab06..54072053 100644 --- a/programming/servos.md +++ b/programming/servos.md @@ -6,64 +6,60 @@ title: Servos Board API Servos Board API ================ -The `servo_board` object is used to control a collection of Servo Boards. +The kit can control multiple servos. +One servo board can control up to 12 servos. +See the [Servo Board](/docs/kit/servo_board) hardware page for more details about this board. -When a single Servo Board is connected to your robot, you can control it -using the `servo_board` object. -~~~~~ python -R.servo_board.something... -~~~~~ - -The serial number of each detected Servo Board is printed to the log when your robot starts. -It will look something like this: - -~~~~~ not-code -sr.robot3.robot INFO - Found Student Robotics Servo Board v4 - srABC1 -~~~~~ +Accessing the Servo Board +------------------------- -If you have more than one Servo Board attached, you need to specify which one you want to control. This is done using the serial number of the board. For example: if you had a board that was labelled "srABC1", +The servo board can be accessed using the `servo_board` property of the `Robot` object. ~~~~~ python -R.servo_boards["srABC1"].something... +from sr.robot3 import * +robot = Robot() + +my_servo_board = robot.servo_board ~~~~~ -
- When you have more than one servo board connected to your kit, - you must use `R.servo_boards` and index by serial number. This is so - that the kit knows which servo board you want to control. -
Setting servo positions ----------------------- -Each of the twelve servo outputs can be controlled separately. The servo outputs -are numbered 0-11, see the [Servo Board](/docs/kit/servo_board#connectors) docs -for details of which output is which. +Each of the twelve servo outputs can be controlled separately. +The servo outputs are numbered 0-11, see the [Servo Board](/docs/kit/servo_board#connectors) docs for details of which output is which. + +This board object has an array containing the servos connected to it, which can be accessed as servos[0], servos[1], servos[2], etc. +The servo board is labelled so you know which servo is which. The position of servos can range from `-1` to `1` inclusive: ~~~~~ python -# R.servo_board.servos[SERVO_NUMBER].position = POS +# Set servo 0 position to 0.2 +robot.servo_board.servos[0].position = 0.2 -# set servo 1's position to 0.2 -R.servo_board.servos[1].position = 0.2 - -# Set servo 2's position (on the Servo Board with serial number srABC) to -0.55 -R.servo_boards["srABC"].servos[2].position = -0.55 +# Set servo 2 position to -0.55 +robot.servo_board.servos[2].position = -0.55 ~~~~~ You can read the last value a servo was set to using similar code: ~~~~~ python -# get the last setting of the second servo on the first Servo Board -last_setting = R.servo_board.servos[1].position +# Print the last setting of servo number 1 +print(robot.servo_board.servos[1].position)) ~~~~~ -
-While it is possible to retrieve the last position a servo was set to, -this does not guarantee that the servo is currently in that position. -
+Disabling servo outputs +----------------------- + +Setting a position to `None` will disable an output. +This is the state all the servo outputs are in when the board turns on, where no servo pulses are being sent to the outputs. + +~~~~~ python +# disable servo output 5 +robot.servo_board.servos[5].position = None +~~~~~ [How the set position relates to the servo angle](#ServoAngle) {#ServoAngle} ----------------------------------------------- From ea963707d3bf6259b7f1a6fda28f21a2e3fccd13 Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 31 Aug 2023 20:02:06 +0100 Subject: [PATCH 22/81] fix servo board code typo --- programming/servos.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programming/servos.md b/programming/servos.md index 54072053..7dcfb4fb 100644 --- a/programming/servos.md +++ b/programming/servos.md @@ -47,7 +47,7 @@ You can read the last value a servo was set to using similar code: ~~~~~ python # Print the last setting of servo number 1 -print(robot.servo_board.servos[1].position)) +print(robot.servo_board.servos[1].position) ~~~~~ Disabling servo outputs From d3deba00c6cb71819239678981ed162dc8b72e99 Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 31 Aug 2023 20:29:49 +0100 Subject: [PATCH 23/81] Motor board page update - simplify for clarity --- programming/motors.md | 128 ++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/programming/motors.md b/programming/motors.md index f1c8f022..9b0b492c 100644 --- a/programming/motors.md +++ b/programming/motors.md @@ -6,41 +6,54 @@ title: Motor Board API Motor Board API =============== -The `motor_board` object is used to control a collection of Motor Boards. +The kit can control multiple motors simultaneously +Each Motor Board can control two motors. +See the [Motor Board](/docs/kit/motor_board) hardware page for more details. -When a single Motor Board is connected to your robot, you can control it -using the `motor_board` object. + +Accessing the Motor Board +------------------------- + +If there is exactly one Motor Board connected to your robot, it can be accessed using the `motor_board` property of the `Robot` object. ~~~~~ python -R.motor_board.something... +from sr.robot3 import * +robot = Robot() + +my_motor_board = robot.motor_board ~~~~~ -The serial number of each detected Motor Board is printed to the log when your robot starts. +If you have more than one Motor Board attached, you need to specify which one you want to control. +This is done using the serial number of the board. + +The serial number is physically written on each board and the serial of any connected board will be printed to your log when the robot starts. It will look something like this: ~~~~~ not-code -sr.robot3.robot INFO - Found Student Robotics Motor Board v4 - srABC1 +sr.robot3.robot - INFO - Found MotorBoard, serial: srABC1 ~~~~~ -If you have more than one Motor Board attached, you need to specify which one you want to control. This is done using the serial number of the board. For example: if you had a board that was detected as "srABC1", -you could do this instead: +You can then access the boards like this: ~~~~~ python -R.motor_boards["srABC1"].something... +from sr.robot3 import * +robot = Robot() + +my_motor_board = robot.motor_boards["srABC1"] +my_other_motor_board = robot.motor_boards["srXYZ1"] ~~~~~
- When you have more than one Motor board connected to your kit, - you must use `R.motor_boards` and index by serial number. This is so - that the kit knows which Motor Board you want to control. + When you have more than one Motor Board connected to your kit, you must use `robot.motor_boards` and index by serial number. + This is because the kit needs to know which Motor Board you want to control.
Setting motor power ------------------- -Control of your motors is achieved by setting a power output from one of the -channels on your motor boards. Valid values are between -1 and 1 inclusive. +Control of your motors is achieved by setting a power output from one of the channels on your Motor Boards. +Valid values are between -1 and 1 inclusive. Fractional values (such as 0.42) can be used to specify less than 100% power. Negative values run the motor in the opposite direction. @@ -48,35 +61,27 @@ The field to change the output power is `power`. As each Motor Board has two outputs you will need to specify which output you want to control: ~~~~~ python -from sr.robot3 import * -import time - -R = Robot() - # motor board srABC1, channel 0 to full power forward -R.motor_boards["srABC1"].motors[0].power = 1 +robot.motor_boards["srABC1"].motors[0].power = 1 # motor board srXYZ1, channel 0 to full power reverse -R.motor_boards["srXYZ1"].motors[0].power = -1 +robot.motor_boards["srXYZ1"].motors[0].power = -1 # motor board srABC1, channel 1 to half power forward -R.motor_boards["srABC1"].motors[1].power = 0.5 +robot.motor_boards["srABC1"].motors[1].power = 0.5 ~~~~~ -The motor board will continue to output the requested power until it is told -otherwise or until power to the board is removed (usually when the robot turns +The Motor Board will continue to output the requested power until it is told +otherwise or until power to the board is removed (usually when your code ends and the robot turns off). Therefore to stop your motors you must explicitly set the power output to zero: ~~~~~ python -# motor board srXYZ1, channel 0 stopped -R.motor_boards["srXYZ1"].motors[0].power = 0 - # Put motor board srABC1, channel 1 at 25% power for 2.5 seconds: -R.motor_boards["srABC1"].motors[1].power = 0.25 +robot.motor_boards["srABC1"].motors[1].power = 0.25 time.sleep(2.5) # wait for 2.5 seconds -R.motor_boards["srABC1"].motors[1].power = 0 +robot.motor_boards["srABC1"].motors[1].power = 0 ~~~~~ Since each output channel can be controlled separately, you can control several @@ -85,21 +90,20 @@ motors at once. ~~~~~ python # Set one motor to full power in one direction and # another to full power in the other: -R.motor_boards["srABC1"].motors[0].power = 1 -R.motor_boards["srABC1"].motors[1].power = -1 +robot.motor_boards["srABC1"].motors[0].power = 1 +robot.motor_boards["srABC1"].motors[1].power = -1 -# Wait a while (perhaps for the robot to move) +# Wait a while for the robot to move time.sleep(3) # Stop both motors -R.motor_boards["srABC1"].motors[0].power = 0 -R.motor_boards["srABC1"].motors[1].power = 0 +robot.motor_boards["srABC1"].motors[0].power = 0 +robot.motor_boards["srABC1"].motors[1].power = 0 ~~~~~
- You will need to work out for your robot which values (positive or negative) - result in it moving in each direction. This will depend on how you have - positioned your motors as well as how they have been wired to the motor board. + You will need to work out for your robot which values (positive or negative) result in it moving in each direction. + This will depend on how you have positioned your motors as well as how they have been wired to the Motor Board.
Getting the current motor power @@ -108,40 +112,44 @@ Getting the current motor power You can read the current power value for a motor using the same field: ~~~~~ python -# get the current output power of Motor Board srABC1, channel 0 -target = R.motor_boards["srABC1"].motors[0].power +# Print the output power of Motor Board srABC1, channel 0 +print(robot.motor_boards["srABC1"].motors[0].power) ~~~~~ -Stopping the motors -------------------- +Special Values +-------------- -When you set the motor power to 0, this signals the Motor Board to actively stop that motor from turning. +In addition to the numeric values, there are two special constants that can be used: +- `BRAKE` +- `COAST` + +`BRAKE` will stop the motors from turning, and thus stop your robot as quick as possible. + +
+ `BRAKE` does the same thing as setting the power to `0`. +
~~~~~ python -# store the motor in a local variable because -# typing it out gets really boring -molly = R.motor_boards["srABC1"].motors[1] - -# set the power to 100% for a second, then stop immediately -molly.power = 1 -time.sleep(1) -molly.power = 0 +# Stop the motor as quick as possible +robot.motor_boards["srABC1"].motors[0].power = BRAKE ~~~~~ -However, you may also want to allow the motors to gently coast to a halt. -This can be achieved by using the special `COAST` value. +`COAST` will stop applying power to the motors. +This will mean they continue moving under the momentum they had before and slowly come to a stop. ~~~~~ python -# set the power to 100% for a second, then coast to a stop -molly.power = 1 -time.sleep(1) -molly.power = COAST +# Slowly coast to a stop +robot.motor_boards["srABC1"].motors[0].power = COAST ~~~~~ -When the power of the motor is set to `0`, it is equivalent to setting -the power to `BRAKE`. + +Motor currents +-------------- + +The Motor Board can also measure the current being drawn by each of the ports on the board. +This value is measured in amps. ~~~~~ python -# set the motor to brake -molly.power = BRAKE +# Print the current in amps of motor 0 on board srABC1 +print(robot.motor_boards["srABC1"].motors[0].current) ~~~~~ From 0d216dbefceab2af9cde21b3ffd3979fde758e38 Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 31 Aug 2023 20:48:06 +0100 Subject: [PATCH 24/81] Updates for style consistency --- programming/power.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/programming/power.md b/programming/power.md index aa640951..78713af1 100644 --- a/programming/power.md +++ b/programming/power.md @@ -9,6 +9,13 @@ Power Board API =============== There are a few things that can be done with the power board, namely current and voltage sensing, and beeping. +See the [Power Board](/docs/kit/power_board) hardware page for more details. + + + +[Accessing the Power Board](#access_power_board) {#access_power_board} +------------------------- + The power board can be accessed using the `power_board` property of the `Robot` object. ~~~~~ python @@ -20,7 +27,7 @@ my_power_board = robot.power_board [Power Outputs](#outputs) {#outputs} -------- +------------------------------------ Each of the power board's controllable outputs has a constant whose name closely matches the name of the output: From 6c0ee29dc7f3e13c4f2182209cec2807dbed025a Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 31 Aug 2023 20:55:14 +0100 Subject: [PATCH 25/81] Style consistency --- programming/motors.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/programming/motors.md b/programming/motors.md index 9b0b492c..47fe2ef7 100644 --- a/programming/motors.md +++ b/programming/motors.md @@ -11,8 +11,8 @@ Each Motor Board can control two motors. See the [Motor Board](/docs/kit/motor_board) hardware page for more details. -Accessing the Motor Board -------------------------- +[Accessing the Motor Board](#access_motor_power) {#access_motor_power} +---------------------------------------------------------------------- If there is exactly one Motor Board connected to your robot, it can be accessed using the `motor_board` property of the `Robot` object. @@ -49,8 +49,8 @@ my_other_motor_board = robot.motor_boards["srXYZ1"]
-Setting motor power -------------------- +[Setting motor power](#setting_motor_power) {#setting_motor_power} +------------------------------------------------------------------ Control of your motors is achieved by setting a power output from one of the channels on your Motor Boards. Valid values are between -1 and 1 inclusive. @@ -106,8 +106,9 @@ robot.motor_boards["srABC1"].motors[1].power = 0 This will depend on how you have positioned your motors as well as how they have been wired to the Motor Board.
-Getting the current motor power -------------------------------- + +[Getting the current motor power](#current_motor_power) {#current_motor_power} +------------------------------------------------------------------------------ You can read the current power value for a motor using the same field: @@ -116,8 +117,9 @@ You can read the current power value for a motor using the same field: print(robot.motor_boards["srABC1"].motors[0].power) ~~~~~ -Special Values --------------- + +[Special Values](#special_values) {#special_values} +--------------------------------------------------- In addition to the numeric values, there are two special constants that can be used: - `BRAKE` @@ -143,8 +145,8 @@ robot.motor_boards["srABC1"].motors[0].power = COAST ~~~~~ -Motor currents --------------- +[Motor currents](#motor_currents) {#motor_currents} +--------------------------------------------------- The Motor Board can also measure the current being drawn by each of the ports on the board. This value is measured in amps. From a40db43fa131a75954bdeee6566b0ca0e862ca5d Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 17:51:35 +0100 Subject: [PATCH 26/81] Review comments --- programming/motors.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programming/motors.md b/programming/motors.md index 47fe2ef7..c64e909b 100644 --- a/programming/motors.md +++ b/programming/motors.md @@ -6,7 +6,7 @@ title: Motor Board API Motor Board API =============== -The kit can control multiple motors simultaneously +The kit can control multiple motors simultaneously. Each Motor Board can control two motors. See the [Motor Board](/docs/kit/motor_board) hardware page for more details. @@ -44,7 +44,7 @@ my_other_motor_board = robot.motor_boards["srXYZ1"] ~~~~~
- When you have more than one Motor Board connected to your kit, you must use `robot.motor_boards` and index by serial number. + When you have more than one Motor Board connected to your kit, you can't use `robot.motor_board`. This is because the kit needs to know which Motor Board you want to control.
@@ -103,7 +103,7 @@ robot.motor_boards["srABC1"].motors[1].power = 0
You will need to work out for your robot which values (positive or negative) result in it moving in each direction. - This will depend on how you have positioned your motors as well as how they have been wired to the Motor Board. + If you want to swap the direction of a motor you can swap the wires connecting the motor to the Motor Board.
From b3e9be8924e2c9fd160d260aa25daadac183f55a Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 17:57:34 +0100 Subject: [PATCH 27/81] Review fixes --- programming/power.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/programming/power.md b/programming/power.md index 78713af1..39d3cfee 100644 --- a/programming/power.md +++ b/programming/power.md @@ -12,7 +12,6 @@ There are a few things that can be done with the power board, namely current and See the [Power Board](/docs/kit/power_board) hardware page for more details. - [Accessing the Power Board](#access_power_board) {#access_power_board} ------------------------- @@ -35,7 +34,7 @@ Each of the power board's controllable outputs has a constant whose name closely * H1 : `OUT_H1` * L0 : `OUT_L0` * L1 : `OUT_L1` -* L2 : N/A (Not Controllable - Brain power) +* L2 : N/A (Not Controllable - This port is used to power the Brain Board) * L3 : `OUT_L3` * 5V : `OUT_FIVE_VOLT` @@ -131,5 +130,5 @@ You can manually wait for the start button to be pressed, not only at the start. robot.wait_start() ~~~~~ -This may be useful for testing, but be sure to remove it in the competition, -as you won't be allowed to touch the start button after a match has begun! +This method will block until the start button is pressed. +This may be useful for testing, but be sure to remove it in the competition, as you won't be allowed to touch the start button after a match has begun! From 6657f597c2218bcfcea90ba226ff43e5eeb5d39d Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 19:39:46 +0100 Subject: [PATCH 28/81] Remove explicit Note import --- programming/power.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/programming/power.md b/programming/power.md index 39d3cfee..34dfa2d3 100644 --- a/programming/power.md +++ b/programming/power.md @@ -108,8 +108,6 @@ The frequency on the buzzer is limited from 8Hz to 10,000Hz
~~~~~ python -from sr.robot3 import Note - # Beep for 0.5s in D. R.power_board.piezo.buzz(Note.D6, 0.5) From a98f278778fc3e16155efb3ec78aec4777fdf46f Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 19:45:48 +0100 Subject: [PATCH 29/81] Jekyll auto creates internal links to headers Header all lower case, space replaced with hyphen --- programming/power.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/programming/power.md b/programming/power.md index 34dfa2d3..c4d4ffc8 100644 --- a/programming/power.md +++ b/programming/power.md @@ -12,7 +12,7 @@ There are a few things that can be done with the power board, namely current and See the [Power Board](/docs/kit/power_board) hardware page for more details. -[Accessing the Power Board](#access_power_board) {#access_power_board} +Accessing the Power Board ------------------------- The power board can be accessed using the `power_board` property of the `Robot` object. @@ -25,8 +25,8 @@ my_power_board = robot.power_board ~~~~~ -[Power Outputs](#outputs) {#outputs} ------------------------------------- +Power Outputs +------------- Each of the power board's controllable outputs has a constant whose name closely matches the name of the output: @@ -69,8 +69,8 @@ they will appear to be missing and your code will break if you try to control th -[Battery Status](#battery) {#battery} -------------------------------------- +Battery Status +-------------- The power board can report both the battery voltage, in Volts, and the current being drawn from it, in Amps. You can access these values like so: @@ -88,8 +88,8 @@ print(robot.power_board.battery_sensor.current) - The discharge curve is roughly linear between 11.4V and 10.4V. -[Beeping](#beeping) {#beeping} ------------------------------- +Beeping +------- The power board has a piezo buzzer which can beep. @@ -119,8 +119,8 @@ R.power_board.piezo.buzz(250, 3, blocking=True) ~~~~~ -[Start Button](#start_button) {#start_button} ---------------------------------------------- +Start Button +------------ You can manually wait for the start button to be pressed, not only at the start. From 9f2bb461c7f0d5059de4dd88abffe1969accd08b Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 19:48:09 +0100 Subject: [PATCH 30/81] Jekyll auto creates internal links to headers Header all lower case, space replaced with hyphen --- programming/motors.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/programming/motors.md b/programming/motors.md index c64e909b..4f69e52c 100644 --- a/programming/motors.md +++ b/programming/motors.md @@ -11,8 +11,8 @@ Each Motor Board can control two motors. See the [Motor Board](/docs/kit/motor_board) hardware page for more details. -[Accessing the Motor Board](#access_motor_power) {#access_motor_power} ----------------------------------------------------------------------- +Accessing the Motor Board +------------------------- If there is exactly one Motor Board connected to your robot, it can be accessed using the `motor_board` property of the `Robot` object. @@ -49,8 +49,8 @@ my_other_motor_board = robot.motor_boards["srXYZ1"] -[Setting motor power](#setting_motor_power) {#setting_motor_power} ------------------------------------------------------------------- +Setting motor power +------------------- Control of your motors is achieved by setting a power output from one of the channels on your Motor Boards. Valid values are between -1 and 1 inclusive. @@ -107,8 +107,8 @@ robot.motor_boards["srABC1"].motors[1].power = 0 -[Getting the current motor power](#current_motor_power) {#current_motor_power} ------------------------------------------------------------------------------- +Getting the current motor power +------------------------------- You can read the current power value for a motor using the same field: @@ -118,8 +118,8 @@ print(robot.motor_boards["srABC1"].motors[0].power) ~~~~~ -[Special Values](#special_values) {#special_values} ---------------------------------------------------- +Special Values +-------------- In addition to the numeric values, there are two special constants that can be used: - `BRAKE` @@ -145,8 +145,8 @@ robot.motor_boards["srABC1"].motors[0].power = COAST ~~~~~ -[Motor currents](#motor_currents) {#motor_currents} ---------------------------------------------------- +Motor currents +-------------- The Motor Board can also measure the current being drawn by each of the ports on the board. This value is measured in amps. From 21bf83d6b093bbf4f8120959d478246cb0505a70 Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 20:17:23 +0100 Subject: [PATCH 31/81] Servo extended servo range --- programming/servos.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/programming/servos.md b/programming/servos.md index 7dcfb4fb..4738e281 100644 --- a/programming/servos.md +++ b/programming/servos.md @@ -50,6 +50,7 @@ You can read the last value a servo was set to using similar code: print(robot.servo_board.servos[1].position) ~~~~~ + Disabling servo outputs ----------------------- @@ -61,15 +62,34 @@ This is the state all the servo outputs are in when the board turns on, where no robot.servo_board.servos[5].position = None ~~~~~ -[How the set position relates to the servo angle](#ServoAngle) {#ServoAngle} ------------------------------------------------ + +Extended servo range +-------------------- + +For an RC servo the angle of rotation is determined by the width of an electrical pulse on the control wire. +A typical servo expects to see a pulse every 20ms, with a pulse width between 1ms and 2ms. +Our API will take the position provided (between -1 and 1) and map it to the correct pulse width. + +However there is no standard for the width of this pulse and there are differences between manufacturers as to what angle the servo will turn to for a given pulse width. +The API can be used to change what the limits of pulse width are for each servo. +You should experiment and find what the actual limit of your servos are but be careful not drive them past that. + +| Parameter | Min value | Max value | +|-------------------|-------------|-------------| +|Pulse width default|1000 µs|2000 µs| +|Pulse width limit |500 µs |4000 µs| + +This code to set the pulse width limits should be done before setting the position of the servo. + +~~~~~ python +# set the range of servo output 7 between 500us and 2500us +robot.servo_board.servos[7].set_duty_limits(500, 2500) + +# Then move the position of the servo to the upper position +# (pulse width = 2500us) +robot.servo_board.servos[7].position = 1 +~~~~~
-You should be careful about forcing a servo to drive past its end stops. -Some servos are very strong and it could damage the internal gears. +You should be careful about forcing a servo to drive past its end stops as this could damage the internal gears.
- -The angle of an RC servo is controlled by the width of a pulse supplied to it periodically. -There is no standard for the width of this pulse and there are differences between manufacturers as to what angle the servo will turn to for a given pulse width. -To be able to handle the widest range of all servos our hardware outputs a very wide range of pulse widths which in some cases will force the servo to try and turn past its internal end-stops. -You should experiment and find what the actual limit of your servos are (it almost certainly won't be -1 and 1) and not drive them past that. From e8b0ee983b1726428f808238a6708aaccdf2a23c Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 22:41:08 +0100 Subject: [PATCH 32/81] remove ! --- programming/power.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programming/power.md b/programming/power.md index c4d4ffc8..8db75176 100644 --- a/programming/power.md +++ b/programming/power.md @@ -104,7 +104,7 @@ The frequency on the buzzer is limited from 8Hz to 10,000Hz
Calling `buzz` is non-blocking, which means it doesn't actually wait for the piezo to stop buzzing before continuing with your code. - If you want to wait for the buzzing to stop, use the `blocking` argument! + If you want to wait for the buzzing to stop, use the `blocking` argument.
~~~~~ python From 61ebb2c0a289388bb52628f3615d7794765d4641 Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 1 Sep 2023 22:42:57 +0100 Subject: [PATCH 33/81] Duplicate log line for consistency --- programming/motors.md | 1 + 1 file changed, 1 insertion(+) diff --git a/programming/motors.md b/programming/motors.md index 4f69e52c..16242e16 100644 --- a/programming/motors.md +++ b/programming/motors.md @@ -31,6 +31,7 @@ It will look something like this: ~~~~~ not-code sr.robot3.robot - INFO - Found MotorBoard, serial: srABC1 +sr.robot3.robot - INFO - Found MotorBoard, serial: srXYZ1 ~~~~~ You can then access the boards like this: From 82988bd28e468b42b85956977188545b06e85ed8 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 2 Sep 2023 12:10:22 +0100 Subject: [PATCH 34/81] Update brain board pages --- _data/kit_versions.yml | 28 ++--------- _data/sidebar_tree.yaml | 4 +- kit/brain_board/advanced.md | 12 +++-- kit/brain_board/index.md | 76 ++++++++++------------------- kit/brain_board/python_libraries.md | 14 +++--- kit/brain_board/robot_os.md | 25 ++++++---- kit/brain_board/wifi.md | 42 +++++++--------- tutorials/update_brain.md | 48 ++++++++++++++++++ 8 files changed, 130 insertions(+), 119 deletions(-) create mode 100644 tutorials/update_brain.md diff --git a/_data/kit_versions.yml b/_data/kit_versions.yml index 38918d4c..af7a845d 100644 --- a/_data/kit_versions.yml +++ b/_data/kit_versions.yml @@ -5,29 +5,7 @@ # changelog: # - Changed something # - Changed another thing too -- version: 2023.2.1 - released: 2023-01-07 +- version: 2024.0.0 + released: 2023-10-21 changelog: - - Fixed issue in camera optimisation that caused JPEG corruption. -- version: 2023.2.0 - released: 2023-01-05 - yanked: true - changelog: - - Reworked underlying vision library to improve marker detection and distance calculation. - - Added a display of last captured image in web UI. -- version: 2023.1.0 - released: 2022-12-01 - changelog: - - Added dark theme and WiFi details to web interface. - - Added terminal interface. - - Updated axes for orientations. - - Added frame argument to camera operations. - - Added robot settings error file. - - Added competitor port range (7000-8999) to firewall. - - Updated C270 and B500 camera calibrations. - - Fixed bug where stopping running code didn't work. - - Python packages can now be installed onto the brain board using pip. -- version: 2023.0.0 - released: 2022-10-21 - changelog: - - Initial release for SR2023. + - Initial release for SR2024. diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index ed6bfe8d..a05f7bb2 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -17,7 +17,7 @@ tree: title: Brain Board tree: - url: /kit/brain_board/robot_os - title: Robot OS + title: Student Robotics OS - url: /kit/brain_board/python_libraries title: Python Libraries - url: /kit/brain_board/wifi @@ -104,6 +104,8 @@ tree: title: Setting up the simulator - url: /tutorials/using_the_simulator title: Using the simulator + - url: /tutorials/update_brain + title: How to update your robot - url: /competitor_resources/ title: Resources tree: diff --git a/kit/brain_board/advanced.md b/kit/brain_board/advanced.md index d3e3e126..4fa37c33 100644 --- a/kit/brain_board/advanced.md +++ b/kit/brain_board/advanced.md @@ -3,9 +3,11 @@ layout: page title: Brain Board - Advanced --- + # Brain Board - Advanced -When connected to your [Brain Board WiFi]({{ site.baseurl }}/kit/brain_board/wifi), it is possible to access the Brain Board via a terminal interface. The Brain Board is running Student Robotics OS, a purpose-built Linux distribution that does not have all of the conveniences of a usual Linux system. +When connected to your [Brain Board WiFi]({{ site.baseurl }}/kit/brain_board/wifi), it is possible to access the Brain Board via a terminal interface. +The Brain Board is running Student Robotics OS, a purpose-built Linux distribution.
-[Marker](#marker) {#marker} -=========================== + +## Marker A `Marker` object contains information about a *detected* marker. It has the following attributes: @@ -117,10 +116,10 @@ size : The physical size of the marker, as the vision system expects it. pixel_centre -: A [`PixelCoordinates`](#PixelCoordinates) describing the position of the centre of the marker in the image. +: A [`PixelCoordinates`](#pixelcoordinates) describing the position of the centre of the marker in the image. pixel_corners -: A list of 4 [`PixelCoordinates`](#PixelCoordinates) instances, each representing the position of a corner of the marker in the image. +: A list of 4 [`PixelCoordinates`](#pixelcoordinates) instances, each representing the position of a corner of the marker in the image. position : A `Position` instance describing the position of the marker. @@ -161,10 +160,7 @@ orientation Zero values have the marker facing the camera square-on. - - -[`PixelCoordinates`](#PixelCoordinates) {#PixelCoordinates} ---------- +### PixelCoordinates A named tuple of `x` and `y` coordinates for the point, in pixels relative to the top left of the image. diff --git a/programming/vision/orientation.md b/programming/vision/orientation.md index 9001581a..5bde483a 100644 --- a/programming/vision/orientation.md +++ b/programming/vision/orientation.md @@ -3,20 +3,48 @@ layout: page title: Orientation --- -Orientation -=========== +# Orientation -TODO +Orientation represents the rotation of a marker around its center. +The axis and rotations follow the [aircraft principal axis](https://en.wikipedia.org/wiki/Aircraft_principal_axes) as shown in the diagram below. +![Yaw Pitch Roll axis](https://upload.wikimedia.org/wikipedia/commons/c/c1/Yaw_Axis_Corrected.svg "Yaw Pitch Roll axis") -# Examples +These properties can be accessed as follows: -The following table visually explains what positive and negative rotations represent. -The red arrow is not normally present on the marker but is used in these examples to indicate which way is up. +yaw +: A rotation about the vertical axis, in radians. + Positive values indicate a rotation clockwise from the perspective of the marker. + Zero values have the marker facing the camera square-on. + +pitch +: A rotation about the transverse axis, in radians. + Positive values indicate a rotation upwards from the perspective of the marker. + Zero values have the marker facing the camera square-on. + +roll +: A rotation about the longitudinal axis, in radians. + Positive values indicate a rotation clockwise from the perspective of the marker. + Zero values have the marker facing the camera square-on. + +~~~~~ python +markers = robot.camera.see() -Zero on all axis: +for marker in markers: + print(marker.orientation.yaw) + print(marker.orientation.pitch) + print(marker.orientation.roll) +~~~~~ + + +## Examples + +The following images visually explains what positive and negative rotations represent. +The red arrow is not normally present on the marker but is used in these examples to indicate which way is up. -![Orientation zero all axis]({{ site.baseurl }}/images/content/vision/orientation/all0.png "All axis zero") +|Zero on all axis| +|---| +|![Orientation zero all axis]({{ site.baseurl }}/images/content/vision/orientation/all0.png "All axis zero")| | |π/4 |-π/4| |-------|-------|-------| From a4aa897998217141dd0a01f85aff8918ef4a0fbd Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 4 Sep 2023 19:01:11 +0100 Subject: [PATCH 37/81] review comments - brain board --- _data/sidebar_tree.yaml | 6 +++--- kit/brain_board/robot_os.md | 1 + kit/brain_board/wifi.md | 7 ++++--- programming/robot_api/comp_mode.md | 8 ++++++++ programming/robot_api/metadata.md | 10 ---------- tutorials/update_brain.md | 5 ++--- 6 files changed, 18 insertions(+), 19 deletions(-) create mode 100644 programming/robot_api/comp_mode.md delete mode 100644 programming/robot_api/metadata.md diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index a05f7bb2..36ff8430 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -38,8 +38,8 @@ tree: - url: /programming/robot_api/ title: Robot API tree: - - url: /programming/robot_api/metadata - title: Metadata + - url: /programming/robot_api/comp_mode + title: Competition Mode - url: /programming/leds title: Brain Board LED API - url: /programming/motors @@ -105,7 +105,7 @@ tree: - url: /tutorials/using_the_simulator title: Using the simulator - url: /tutorials/update_brain - title: How to update your robot + title: Updating your brain board - url: /competitor_resources/ title: Resources tree: diff --git a/kit/brain_board/robot_os.md b/kit/brain_board/robot_os.md index 41bc2ef5..177aa1a3 100644 --- a/kit/brain_board/robot_os.md +++ b/kit/brain_board/robot_os.md @@ -7,6 +7,7 @@ title: Student Robotics OS # Student Robotics OS The Raspberry Pi runs a custom operating system that contains the Student Robotics software. +It's based on [Raspberry Pi OS](https://www.raspberrypi.com/documentation/computers/os.html) (`2023-05-03`). The OS contains a set of pre-installed python libraries that you can use, the list of which can be found [here](./python_libraries). diff --git a/kit/brain_board/wifi.md b/kit/brain_board/wifi.md index 2fc28c89..6ef169ec 100644 --- a/kit/brain_board/wifi.md +++ b/kit/brain_board/wifi.md @@ -19,9 +19,9 @@ It will initially have a name starting with `robot-ZZZ` followed by some random You can now connect to your robot in the same way you normally connect to a WiFi network. You will need a WiFi key to be able to connect and you can find this inside `robot-settings.toml` on the USB drive containing your code. -
- If there is no robot-settings.toml on your USB drive, you can generate one by first running any code on your robot. - For more information see our docs on Robot Settings. +
+If there is no `robot-settings.toml` on your USB drive, you can generate one by first running any code on your robot. +For more information see our docs on [Robot Settings]({{ site.baseurl }}/kit/brain_board#robot-settings).
These details can also be printed using: @@ -57,3 +57,4 @@ Changing the starting zone allows you to test how your robot handles being start Changing mode applies from the next time your code runs. The WiFi interface will only be fully accessible when your robot is started in dev mode and will be restricted in competition mode. +See the [competition mode]({{ site.baseurl }}/programming/robot_api/comp_mode) page for details of what competition mode is and what limitations it imposes. diff --git a/programming/robot_api/comp_mode.md b/programming/robot_api/comp_mode.md new file mode 100644 index 00000000..fed92fb6 --- /dev/null +++ b/programming/robot_api/comp_mode.md @@ -0,0 +1,8 @@ +--- +layout: page +title: Competition Mode +--- + +# Competition Mode + +TODO \ No newline at end of file diff --git a/programming/robot_api/metadata.md b/programming/robot_api/metadata.md deleted file mode 100644 index ecb17963..00000000 --- a/programming/robot_api/metadata.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: page -title: Metadata ---- - -Metadata -======== - -TODO - pick better name -TODO \ No newline at end of file diff --git a/tutorials/update_brain.md b/tutorials/update_brain.md index 3a741e58..c52063b8 100644 --- a/tutorials/update_brain.md +++ b/tutorials/update_brain.md @@ -1,6 +1,6 @@ --- layout: page -title: How to update your robot +title: Updating your brain board --- {% comment %} @@ -11,8 +11,7 @@ The below will automatically calculate the latest version number, to be used whe {% assign latest_version = latest_version.version %} -How to update your robot -======================== +# Updating your brain board The SD card is located on the underside of the board underneath the green power connector. Grab the SD card with your fingers and simply pull it out of the slot. From 613f9095e5004f1862e92ffe1a0e9c2f1bb0b585 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 4 Sep 2023 19:44:25 +0100 Subject: [PATCH 38/81] Review tweaks --- _data/kit_versions.yml | 5 +---- kit/brain_board/robot_os.md | 9 ++++----- kit/brain_board/wifi.md | 14 ++++++-------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/_data/kit_versions.yml b/_data/kit_versions.yml index af7a845d..feba8730 100644 --- a/_data/kit_versions.yml +++ b/_data/kit_versions.yml @@ -5,7 +5,4 @@ # changelog: # - Changed something # - Changed another thing too -- version: 2024.0.0 - released: 2023-10-21 - changelog: - - Initial release for SR2024. +[] \ No newline at end of file diff --git a/kit/brain_board/robot_os.md b/kit/brain_board/robot_os.md index 177aa1a3..0598c017 100644 --- a/kit/brain_board/robot_os.md +++ b/kit/brain_board/robot_os.md @@ -13,13 +13,12 @@ The OS contains a set of pre-installed python libraries that you can use, the li ## Updates -Keeping your kit up to date is very important, it enables us to deploy new features, as well as fix bugs. +Keeping your kit up to date is very important, it enables us to fix bugs, as well as deploy new features. -Below is a list of the versions released, once you have downloaded the file you need, refer to the steps on the [tutorial page](/tutorials/update_brain) to apply the update. +Below is a list of the versions released, once you have downloaded the file you need, refer to the steps on the [tutorial page]({{ site.baseurl }}/tutorials/update_brain) to apply the update. -Each update file is a complete upgrade. -Each file contains the changes of those before it. -If you need to jump up multiple versions, you can do so by using the latest file. +Each update file is a complete upgrade and contains all the changes of those before it. +If you need to jump up multiple versions, you can do so by just using the latest version. ## OS Versions diff --git a/kit/brain_board/wifi.md b/kit/brain_board/wifi.md index 6ef169ec..df5f840e 100644 --- a/kit/brain_board/wifi.md +++ b/kit/brain_board/wifi.md @@ -6,9 +6,8 @@ title: WiFi # WiFi -The Raspberry Pi that makes up your kit's brain board contains a WiFi radio which allows you to interface with and debug your robot. -You can connect to your robot using any WiFi capable device (laptop, tablet, phone, etc.) - +The Raspberry Pi that makes up your kit's brain board has WiFi which allows you to connect to and debug your robot, using any WiFi capable device (laptop, tablet, phone, etc.). +The WiFi is only available during development and will be disabled during competition matches. ## Connecting to Your Robot @@ -49,12 +48,11 @@ It will also show messages from the initialisation of the robot's hardware, as w You can also see your robot's logs on the USB stick, in a file called `log.txt`. -### Setting up the Robot's Environment + +### Setting the robot zone The toolbar present at the top of the page, or bottom of the page on mobile, allows you to change the robot's starting zone and mode. Changing the starting zone allows you to test how your robot handles being started in a different zone. +Changing mode only applies from the next time your code runs. -Changing mode applies from the next time your code runs. - -The WiFi interface will only be fully accessible when your robot is started in dev mode and will be restricted in competition mode. -See the [competition mode]({{ site.baseurl }}/programming/robot_api/comp_mode) page for details of what competition mode is and what limitations it imposes. +See the [competition mode]({{ site.baseurl }}/programming/robot_api/comp_mode) page for details of what competition mode is. From 4843684825d793800cea6957ffdd6b46373376c1 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 4 Sep 2023 20:02:25 +0100 Subject: [PATCH 39/81] Review tweaks - servo API --- programming/servos.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/programming/servos.md b/programming/servos.md index 4738e281..c0a1c17d 100644 --- a/programming/servos.md +++ b/programming/servos.md @@ -6,9 +6,8 @@ title: Servos Board API Servos Board API ================ -The kit can control multiple servos. -One servo board can control up to 12 servos. -See the [Servo Board](/docs/kit/servo_board) hardware page for more details about this board. +The servo board provided in the kit can control up to 12 servos. +See the [Servo Board]({{ site.baseurl }}/kit/servo_board) hardware page for more details about this board. Accessing the Servo Board @@ -27,12 +26,15 @@ my_servo_board = robot.servo_board Setting servo positions ----------------------- -Each of the twelve servo outputs can be controlled separately. -The servo outputs are numbered 0-11, see the [Servo Board](/docs/kit/servo_board#connectors) docs for details of which output is which. +
+To use servo outputs 8-11 power must be provided through the auxillary power input at an appropriate voltage for the connected servos. +See the [Servo Board]({{ site.baseurl }}/kit/servo_board#auxiliary-outputs) page for more info. +
-This board object has an array containing the servos connected to it, which can be accessed as servos[0], servos[1], servos[2], etc. -The servo board is labelled so you know which servo is which. +Each of the twelve servo outputs can be controlled separately. +The servo outputs are numbered 0-11, see the [Servo Board]({{ site.baseurl }}/kit/servo_board#connectors) docs for details of which output is which. +This board object has an array called `servos` containing the servos connected to it. The position of servos can range from `-1` to `1` inclusive: ~~~~~ python From cb8f7593c2575bde08d97eb25d9b1e7f186ef7de Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 4 Sep 2023 21:59:47 +0100 Subject: [PATCH 40/81] Update vision pages --- _data/sidebar_tree.yaml | 2 + competitor_resources/markers.md | 7 ++ images/content/vision/marker-0.png | Bin 1683 -> 23866 bytes .../vision/marker_with_size_labeled.png | Bin 0 -> 96444 bytes images/content/vision/position/pos_centre.png | Bin 0 -> 27877 bytes images/content/vision/position/pos_down.png | Bin 0 -> 27584 bytes images/content/vision/position/pos_left.png | Bin 0 -> 27537 bytes images/content/vision/position/pos_right.png | Bin 0 -> 27613 bytes images/content/vision/position/pos_up.png | Bin 0 -> 27638 bytes programming/vision/index.md | 64 +++++++----------- programming/vision/markers.md | 47 ++++++++----- programming/vision/orientation.md | 6 +- programming/vision/position.md | 39 ++++++++++- 13 files changed, 102 insertions(+), 63 deletions(-) create mode 100644 competitor_resources/markers.md create mode 100644 images/content/vision/marker_with_size_labeled.png create mode 100644 images/content/vision/position/pos_centre.png create mode 100644 images/content/vision/position/pos_down.png create mode 100644 images/content/vision/position/pos_left.png create mode 100644 images/content/vision/position/pos_right.png create mode 100644 images/content/vision/position/pos_up.png diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index ed6bfe8d..86c08b0c 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -109,6 +109,8 @@ tree: tree: - url: /competitor_resources/microgames title: Microgames + - url: /competitor_resources/markers + title: Game markers - url: /team_admin/ title: Team Admin tree: diff --git a/competitor_resources/markers.md b/competitor_resources/markers.md new file mode 100644 index 00000000..b0609cc7 --- /dev/null +++ b/competitor_resources/markers.md @@ -0,0 +1,7 @@ +--- +layout: page +title: Game markers +--- + +# Game markers + diff --git a/images/content/vision/marker-0.png b/images/content/vision/marker-0.png index b8d78870a32fec3bc8c975f012b42c433c9ed971..c8454121c7489bfc99e65e4d1dd9bd796f455ef2 100644 GIT binary patch literal 23866 zcmeHv2UL`6lO|WM2?R`tl3G!MpdeZD6^ux-B}d61QF6ws1QVhJ$skvt2?CNct#Cmb z5YQ$_Y)~>yhQ@}bd+Wpb&wqAy&dfbKGrRxpkLMuJbbq1Vs;8cM>Z|vWrutPzdNz7G zIy%Pd*Dj&y==L0eufuzHz$?qHFXrLfFK+0oN_6>c98>ViuQnIeF4EBzMlev!cERua zT(0T6(a{N=LcV_S&QSD(7x$W3XsIHfgfF_1b5pP28=Ljd*Wkx}Z{cjk@Wa3S+c`-` z=bsJ#(b1iBzpUr3>}qA^Zs+WDLd(w4ica*rsF=)oX_@olCqzYLL?mUzF0k*8DWsz_ zvb}!kqL#M_aj@U%NKe(h&q59k?@n+_6Q5s`e)~{KXniv^-T4oW^p6K0Y2LrE+st82 zscj&Vb))#ZrQPdj`KA!E4SsxgPVNtJV~M<;8RTy0=x9^TKUk0-?j8Q~*4FnEDTpoT zeq98^(9xZEh(W&5U6k8;|K4wBkl%hgps{tY_q`EI(jD0T=(d@*U1B?O{_9X8u6t(n zSY?M!ZdV+FFhAzc#1+s@YmUB~EtOt_@&prb5TnpY+f{|a~w z+ik>#T&+iNjpF#FdCYQR8|wa>GJ=^wL660#l3|{Xg-n@|&f$}?TjNSb4J%SLpF^k7&e2_w%=w0O_OludMV2uz&n<>Di)cUQ@ z_LE2?E(L$vC<){(L65wf&u-pSCj@fNSg^CR_Y{~CdV72O`q+dw3qC}kXe)RPi>=f* zaUwQ#h%ZoxP#Au`{1X8@u zBBvN`e~4*qkK)sS%al0!q%oXbYL0*nn(5AqVh*kHU7h}b3ZhJ5QlG6BUX9=)j|3ne zYP&9H6{zlXqSke+*(NoBM4Hi~VfxFRQwM^4U0jAoZX$0=it=u5<>26;P&ek`nuv3A zPEJnGE?2ruwBb0j!M^N=x84TFvNoyl7Rj_IcP4P*&qzv1d3ky^nHPGKm&UfjZCLf& z88NpdCk$eOd}H<}_`w<G#yC%}AhZ7Xg4KPf<)G8}uT z#z!o((cIqNUe(5T!V%Wi0-+T5@L>K{+=@@1GKo-FSP19;$tEUF!lABSy=o!jB^NN^ zIJ4@DkSRJBnZ@XM>39W<9UDIW=i#Zl;U)4s9<#+e-ue8+tSgjkm(cVpa!kx&jrjPe zsbz4mt!vZG|6#)nufI#Z^&$@baBB-GhG`Lx|7>*tIC1KDK=34f4WU@=wo>ELX!g1 zI)CY-7{A4Djd4ex%+5Nl{XDar)@f^N8yg$d4=Hj!qM5f~Ig|Fkk#Zj5`1{)BgXe6n zuNLZ^O;HF~hbSrJkKmF=VyaMYpf14gr}cOjqshI4gPi!n+S*z?S2b;A3J)F}(P(~m zwy!iwaTU35I-3I;@u|!TKZZxg@td2Q^K~0@xF|k8ueds&AFE0%bwh=xs^8bgxhI zSw~05%^6c#-_Q`(j0LQB0o(_89PJzi6|nNc6+!lQrrar_xw zwInAeFEVa@&S}oaSHD68aou+W-aI39&uyOjSJLADRZWiu^Jka+s&arp=ws$U#VW;)cOrltQ-&`*!)a-P-$yitqnJeOFZw zU!NM;=62mt#pnseT6{}&{9kayW|a7nJKgBezb&A>m)8(xSd*(gKd{d!)BkH*>;tDv z4us$A95#205j{2M(`@){3feTk%U{J(Mj z?Pb~)MYctezb1Go;VLGEr`RWrb+Vt;yE^LF# zwtBIxUTi-a`k#A(_FmBQN4;!8A#`-C54UakpR*-U#Q(pO(mz|3{0`e+z2==aeko|U zJ>NaUsLJeGhO6L7m4~jMcs`tyu{BSMdcooL^rZMx+ox$_kuB$w_V2EIdL!~$rJ5Jp zsn|{zrPE{ROP8^$2JK$+gy~*Ft=|`GPhTu;VST8(Z${r9&es0`#Z8WF%|jh98{f_X|EL+TC`}MoF&cS8NpL2{WfLvL4GXNP{r5EP z%D^gAv)XN_p}4v7j8oRBcC~vI#t3u^hBN|~qKv%;SEFyKU#}Yfl*k%dbaJ1Vw4l!G zE8(Gx>`v9QWyvxg(*!JSwwyp+{D2Az&*m<+{xm&Mx!lC=JWy)iBSr8RD=8w)RZgV1 ze192z)}VyHU%SSnW-l5pu$y$WvA^Q&1{ z$I2-*8huEma-fmb)W4BMH@JDXu%Go~EsT2dP_3V-XRlSBK`FhO;zQKpWny3HO#1bx zrLlmu!8P(kYJ04{A*+$gdl+3vK2kR1JF_;mL52YjZQC@)ziSO%_jhHqplbmpa@lk|Me@=GCOwrP7bJfW>fU5p6W zATh2!Him0Ri*E6Nfd`mxNJF37v~?{PP@E;4l>5~0{XM*(daequp(x)8GZBNA&r;1L zY&(Ww*5j-oD=j4BU0N8I3=D;&yZm|Z>QgRx8A4pSdE?{JOyfF#A9USRW@4M%a;v{z zr=fFgSFuey3_pbWW&~1~Q(;b|Mkmi8jR_tAU%%pA^?TJ)vtSEW7ZbR-?$}q->yB5u zp`wD^n6%rt|6(I+T7g#4$YoaJBLXaX*vL01HE)A4)amL5I0(<$m8@u$K>3$v^^06a z>ft0WX*~KCd$mKltDopme({?6V$~ddhPzp;)x|z$5cCQaKj9-S7#kaNSIPbziwN>I!D}{$mRRZtWrTgf8TA zvTtG5S7+c(c+Q0Bbbr}{@_(+Oewb6%vz4{^w4xVTY00oBO*zcJvIcQV@WEzYj4bhO zL!5|(!usTE_&6^%305F*BH3#<6GvRQ$~=}vHFT=v(E>NoBka|Xx{wOb??ZsF3rscq zz$sE`Ye6)xf?86JezCQ!8c$cc$P}BDlMw{nQaA(v5^wb(49nWr9IB==`QXDa(f|fv zP56>NMOkiVW~s|1&Gu^~O3uR5|8e4WU!KX%9W&6sA&jNC@@uoDl-c1kVYSJwK5KBJ zH-u}C2uvL+5wmWc>@Bt-#TH+FaV&+S@nxxwK9j~?S* z-9HE}4UA!&o_bkY!|!%7*G#1NDe=T^fWd`oSH5Wej)vH$;!~+5FiH{XDnL1&bze8WbzMHZ+AA}Of2IGiE z51V4n%FTA9DU-*pw$$SR&?HRxoncnW>Pb6qoAg8yuUbN|N-IyS5fY)A`kP@;?%Z2_ zxZwUk%#TFWY##GnnY>A{S1r=8M5deC6*e5Qe!CJYWLySwRhqc50Y0rgRwH)g$_!-X z)^L2Oqb)J5a>?L=q&!=gWAf+Meg!_hF^>((8g=caLC)0E*ar*WYeNEnT-Fq*H}RWg zQOQQa6usV@{iY)U2}@;rfs&W>dD%>I6NfXVXX^L*oKr{YhNU{<>? z={TWkqHEJ}8_H@y(B^oebN#E*H7Gfl*u-sNNXl-YVr$f8aIR*#4Gh%^ZoNJdh6=sN zvNtS6VB;h1{Zn=+A?deFoHD03RGBnFj88ICt`6!k_uC)UJws>BWtr;i<&E+25c0i!K_Wsdz>H(&2& zWIMf~_+$9N)u+b_n_7;P3Vk@9e<`~oA%|j~H!F|zCohef$V?=;$#~5T6j`;5-r_ST zvAz55mp!LH#2c@>h1YG8`b8|7k^)G*kaO<9aFweeFG`v$z=I;+XAt_)PoN%c()PO% zE0io|qh4o(yN`LKQjed)W5Uov<&8%TwM4I$ww?^*K(fNO#wP@yiR3Mxk}rD9s0vA| zzO;P|0;QtEAUH-)Hzjtl^Oi=)vsQ;U67=lRum})K7fN)_{#=Gum_C!d{qCMheI}>Z zz+JE~?xUtg-r}(#z^zjea-vV4WgC{y0gWni=q-ACQQFK%fHnc<$&e>~JtZD<3mdpO z(hxRLe(u1PBPSi+zN~M`HqTd&A0_Qg%=myZurTo%iHza2|Z zVUnoS1!(9iDo=`mlv$fwP$zTzXqjo0hhp3Lti5Ir(2a!UM>k(zaRl!$v)#ocJ^H-I z#P6JgW+6CY4W;kZE)oR&aWU&)#+-2F7F>QqKVVdcK@L&E2-kkuR)B>RCj*mA%MFK& zt34U>VpQ79zMhDoy+;6GbBXg7OSdUbaic+T48M1(c5R4(bLIpwA31TFgtn@juI2NU z_j^z!_B}%TbM(U5B%B~qYF^h4-0wByW5eV8t!?^CMoT2mje6@`hEr%4%Y9^cw93P#q3(Yk1zP5{g+o4ANb8x z@;MxL8wp_zO|wT=d(QSdRiAUvkq`{7 zUqL8M5N%V|eNt2McNFa_j2!|bi+19P9)M)iKumz1lzlaS(vgrL z=d<+rDk_<%*eSRGj<~sjrELHYTJCyz&XCuyAKb4Q`*8fRU_L+A+WZ10L&<(ROmSAf z_d^szn`%6PMqRT>_Rx9%ZQnVAl4}aoA-{R!^8upd?Da9>AZhP~Va>0#KvVl);?!X6NessjV4Kl`!dilaJpv{;&KQ4E zrdncXcPG%SyUk}Xa**=U>Z(pEhZ=cwX{SZzfDbJ-Ua!-^VTN2=&MDj9K_=Z6QMAS3 z2h5@C5xSngAWSk8ogoR-Pg4c1mOmQFixKGu1=yb><8OF1DaKgEE&#kohxtP1at@FZ zbNrjkk3|buJ6VVnFRYVh*{g0bvWXv)bodHB&QYbCw+Xm@9;=+p#p(((uaT04Xj!A+*sr7)<9IbvIM#L6z@tzu?|v~eTJI_TMaN`PP#cXFb@=+Wqr7St z!%<7ttULgw%Qt=*4Y`|bu0GBd5|HtjFamsnwe0$4JB^|-$zN*w`41woanwzI;oZ)( zf+S#rQpXR3W@6kSVW$b7a*st^if3^QVE15ChC!Lb@!DBz#P_p+1_FxZ_B9j8tg}OY z>_FRMSEl?>Fim|nfnZJEXkU&8DJ{`Y6??jEy^3vF7I$&cC zj3krq{N7h;AHgAAq@FB8o(A-oub!{-ujEz^-A`^54m>E3Yu{Hg^^+cQ?-Z_GL1$^{ zLe=9dNrd6sp);7ILjoC*e)A8Q752s$IZbw?#TbPg){t9-B+VSN63wf7PD_Afyt4P5BN@2g4E`UH?5iyQkG+k%bH zBg)1kB>|%tZMnJYhSZVQ!&08%Gu~i8O-5ionvm(+qBIorsf`0qj-F$ab{&O!uOIx^ z;+gb#e)Sws&H^RVQkeLy8C4EZ5%K~iz=6aZ`gbU4B%%f0LS8+1>PoPRP*1hj`~xN# zel2rg44)xY0wts8l|K3vqD8o3_`c?&GY|9WkGkeLnLs@QN!13Wtw5tJ7n2RRE0djo zL&FDvrX&VP1a%fbN+H@4oGZs(0?#|EWzGa-{+5QkgqFa0fKB41`lZ;ZJr~*pUQiy5 z-e3cM3NUTF@LK?~ip*@{{90ckq|NGsHLnN!7zu-9QR+-kh&E@Fbci$l%?IXp!AzF4 zSY`5pd(+e7xk36y{Jw z41s&F%j6XDrFe78mfH`%`#nvo@V-=4^coE1+n6q+TNNS{sXu_WW~#oo`1D{ z9bWW)3^I1pGfs8^k+GdcO9rKPVvKK_QGP7u>I6ko5p#$@#vpcnerzpbvpV_tR5ZG` z+>fV-fxAHD4NAs+Qc)!)C{17D=Ft>M`|h)3iI&)w=nzcffF#_w!@Xb(dx@H4?I^*qHE$~bzRjp7=cu3*K9AP5X$)oxut86 zp_OYpi`y00Ri++%;Y)H+%F>$t^RPy%b!>04zfG?`?#9cr@$!D2S?M|LcXj4KAYjf| z8B=c@Nd1&3rBOF;1u3(<<3)4b3K3j5mm;x_lQNG*%g)!h+2&a{*W;HKCjnY*J|)Bm zGax;bhlf%k`^}%66RKIL4}yyPFt6$bD4bd}+En~kyL7P|gYzT+6pu$cfO1>d6Y2us zewC~^YoH=Xi*6F+$rUy~)&+`%rV2~Ct3A&K(;t7X7g?Lb8zFJ@BKg?23I42fCP33S zTr6l=5=soBO7bm?I%9^1<^eV;kdM6?TC37+&1#8~=b~LJ<)IJ^$6Fl6sm1scT8`A* z4|;d+m$c}*q-oeZLUFF*ds8?&qAzq9TxJqfI#%Tbw*_!D#$Xlsqz9jDjflJxB9i29(}M+Z(yhGnn#{!A9)=U9SHY{0HtWAhVdo^w*baYMey*!Gt> z@@Eu15j%~njZn3EJuC*R%FJ+wGV?ia5)D)m6}CjEfB`A(0fEP2P4goU&~V(rX*0u{ zYXo80%VQHSU&Ak=j$)1MYHu3A}W zU>3fXkn;$TP(1pvUTev zf6Ns&w7AQk_uxu6TL@afZ+YvQ=VVbfJivfF8yKaI{7Jw$Qef#fmtwI4fL@7`j=oUV zJw+)htY}+k|0v5M2AEj-8@+w*MAk-j(a53(W3;m1fMHy|lRp-}r+R;Tbgq0rv?TMe zTo97WR6HV$BTFwn*qv75B;Yf6>qQR5XCmkk?3eH~?>ABx2{N6}WLlUKk2NlmC#~dV z5C4Wiu?Lp_(S2-6z%2TYbq<@cI;8wp=^=yjcy{aCAcDz*-Hdmj<^gyD#03e*v*Y3( z06Hq{xWSy)^_zE};&-UYK6=GOM^lJ4iVS%f%{uPMia~+40x{0*&29WN2zBLb^sa`Q zPbi=x0jK4xJ|Rd86wlhULEd%rqr;tBYMpt8Qqeg=<<06+xt1g=9(pcj+TwBGFr-#f z_s^HXt<|!kqdpydqWWs7XvF%xUrAl!eXI%8R+=i>so&oiky>?Ub&EjmP!3}Xplz;w zczPU<4Whwj3L(#)>Y4_W!j29QTmlbMo)qTnHM9L(kEb3-a%-Jnuc2)-e`5It;%J#@ zGU*Mmi{dn5N8*F7-M!V2h3G#<31uh&JN|{8HP`(etx#RU-t-%?EeiAjAjt*eUfRC~ z%3-#F%62U49S2>6DaL5-lN6z5wya+%BZ+ux7E~vu3~tNVXW{B$Muxju&K&vGuxsLi zU~H{oam0dd3?!5_V(&6g*+8g$5HShFCl?6JUZ2TFAGrL8A5TRXnGRW#lC28H!%%_8 zRAL15%18E8fJN!DTxp3m;MA7>(ax=#1AVPuE-(11d4BW7$egGdqzqjT7 zQjwI#mLnsDFOnA35=jlJv^V<2;~GKhct`}+tffDeQ)wJ3G#PT(W{V}^sD#hbXotZx zcxk&ALU6>bMyrd87+71AWy|CE)>@7-UOPRJ1Lz5xD}j!x>|JGs%g0KCfT}5=elbvs zr-0O^ZXzg}nsZ4q*PzOM3JMTTff+<9+n-_N1O*77{$3UxDVO1Qa7(;E$B;^%tpJg_ zkP_3hu7(&cA5%@EZbZFLi$rwdbJ5-JUq>jgsZ5D& zKi15VGzlb+`KJM0Se@=dD9Gy3<0;lba))u1^cnQ6sfsityF;A5dcZ`Fbn3eWktjND zlk_5-6={!fi`9WPWL^9gB;hrnIa%L;+1N z1Lg=OLwF#lU1?~6N;gE**S!9qW{ddl3S~y6BzJiOCdV+>7e(5aG!6IzaqQ(Ah!R%f zk6EvaD9#Z=dNu0OW6_$|F?Eo#fyX}IMA7l^@bpTT0pHsP-Uz9!*GNL3dd23wVS3I3 zm2aPsdj-d8i+!?RBg%ON5hn$vvfG3hG)HdZcggfz!+A>j{tbDNI;h93rvatsHp7mvYR&f__If2s?c4(aWG#O93BzD#)@f zirAtfwhziEq=JKzajYeFZJwa`RL}#C7B=@39MS&a98t_r8Nx=!&%c?DK6m<8H zXVW7A~bC1 zx~0G$+dv*gB!LCzb*-fc3hW*P#k(@F)O`v3;dQkHas3JxvttLNjKc~c zND%tRIfgXg5;snRXd(-xRY!6HFt2yN@3imAVYy-%d@vUpf2Nb_j!+aK7lcU-WCJ{9kdDHM3v=L~owT3!f_W=jG?sx2k3bh&05FTnxUek8;HlWxE zqrd9rd|VKJk&M#=Hv|sO<8CPjxybC2K;=$^`?6ZkG4<{vIdFzB52*T(6i!X`-pmjg{e;PxEynE(xZ1Jn~UTQw`2cI`t4tx?~EqBw>i z^S>ZUlAl=N2bziCrwm8W?(L7I56I48)aaQmVLe6U*Y@|8fp*wiwP_%$%$&I`LeC8{ zK^X$D8U$8u2&zefXebjWo1_rm(`#0I9=s3_9#CRym9zt-OA-Irq3xGs^%$$y7>p6r z&2JSFdQtxQ3cJT-$4N354`Gt{t=g^J3iN92thH;0V7E!i@i8xjPt)|+2NPq|}qB;|~A@g`5- z-p%>3Ab#_0&G$ba9ajD@OLftb(YR0LS+JQ z#PG6{zW^u?=)VhUQiEES6%^&1QOBFe#)8O>g~F`lvaB}{AK0A`q$Zvt_m?8&BT$M| zXfp5jK!8#M9u-I}Y1}=DY>x=7Ug!w__;N$|Q?l$DB0B*jgRUSo=#GapR|n7tUNH$h zufFk9NE=t8$7mTefEu7uNQZ5xhMeLzM~=l(c-&*f05=g80n`tGF7;<=0KiCr2gO<> zHV|_CxCI_W=F=NrXc9xU&NYiymRO@S)}U1#2xJUYKUgb$XdXVh@a-Xhr~qmq87|Q3 zDw5@F2$xM>fSW%%F2*a}IU1)3DQfgrr<%nkL&s8WpleLv?xVI0I=eqMXL>YJDoZ)B zWtiGS_8XXD=v%u$oe-)bv<58mNR8WBdr+7npl%Rrms?A{bfx8CE)IqZ}SWsP-a<74+B+ zMil=h!fNk0Plg1o5qJJ1xfA^7x`Icb&o@Bj`4YT3uDk+z?7MCp(=`VH2aw+vRP0F8 z(9xM?z*}C^_)(iQ?OYvPd;N>6T=D)3=3k&D9km=U_?!VP#9ph|G4|z5jntH+34foy z5<8p?F1DweLS9CI9O(ME^IK^C@=W+3jmqvk!zG3&ii>uC6vWSOi+_8rg{vRg<8J%W pGO)ov<+Hc{xyS1G`C>PMj^9^GxBFEF*>jcd`epS?`AWC%{|_#XwgCVD literal 1683 zcmeAS@N?(olHy`uVBq!ia0y~yVEh8Y96$kvcY7Pc7#P^1JY5_^D(1Ysd$aGB2ZKZ4 zb&dc3Pn+_`8Jsm*C~T8pZF~*8&VQ*(b1Ki4vfDQ zwbMWT?pNCX;Okb)SuYnXYVP9GvRd(i9*q1oZ$8Y7i@$#$cHyF#bh8!Y!(SJs%ErYn ze<=HJ+Eryb`V>`hH=KmVogs`9;!%Z|RLb7X&>j-MIyN;wI@|F zIdWR+Zu4W?N@eY~dnLAiETr?4_*|K_*b-tV?O95Zd_zZ+&bJC7BL>71T(ytndS z^*#S`yKR5>=A5^bo2ya3Ieq`-;}Q0M|1REB)7ba*{I0K6^J~}XCGXzbH}mtR(wMhj zQ@Y-!ANj~z)FXR);~MoF&5QTXiCUh#yK3jodGWK3U))|pT|5avc5j= zubbVm7^QQkSAKo5WoGd`pFg$o248MoUjO~^wXLuDKZiW}_|jlqUff*&-`_u9U&udRLlde!A+mrd_p zjsI=@?t1a@y5sla*GuQ$yY#YRTIJVyXIGcMTJ`msRP5*Fd!PT!R^0!C+Q!q>&t;ucLK6UD93R5~ diff --git a/images/content/vision/marker_with_size_labeled.png b/images/content/vision/marker_with_size_labeled.png new file mode 100644 index 0000000000000000000000000000000000000000..c7ead02e5575de7943ca174de7a9313da05bd37c GIT binary patch literal 96444 zcmeFa2~^VC`!7t{$vO>ImRg!Lova+wOq0PnR#r}REH%f{avn2BL@ZC4a~&&lq_VVB zG&5%bO_9=+oIr8FB*h64Cr|`lOtI7dci;8i`>yrgd)IqmEtmN9%l_{D+0XEKp1q$< zk`S&ZX=k)xJ ze_sFq!W<3)#DBf?>2HACuUGa+uJ-=*N|@ogs#U*UQr|0T=odsp7>VTQ7l8nXR77M( zevt@~sTFyVUj#zrX+JQ{z|#P7YaRA+fBgrb z>Zs&@Jm^or=AR#3eD`quD(zpdoIJRu@7F7Uq%ER`et|@Ukw}hy5eSirh|I{Z0&zoR zYJUYpq#_~}`BfrBD$@5WAR-m{zgH3Ozfj-@=2KeKfw?3WHj06^?KH-&SEpkB*pO4; zOLMGr^#x+e3wKov2Q04+Z44L2@5Zm_9dkU}VvPRG7uX;F zdd4}E#MsWd?mO(i;PeN2aivZ0l36Y6joQIthX6(RYsS()0D#nE!botBTZLjYb7aA= z63jodzzdouBj6gg6pAr+!}862H-zljdt-5i56v+uSa;zF20^QKRsefJ9+0NAy5t%R zE0gVF(ooZ!vyPhnQPiNB>Vis0dflgZnhZ**j$sFhaSWcr%s7W|&Q#i-R~0UR83TlU z%qW>r@_$MMokcgkhB1+k&Y-&rrW(=Cxu`4>!z~XIqYJf*IU5Z5y@G{!pc_SX2?({s z7fwx28=^hIq0h|HH0AR%g)4MbY+>l27TntU1aV38{hNT%tH+2f zcnptNd++TBd&(55**HeHmM61M*e877tv2bF_X%6ipesxgL5BPIg)`1E?jy7Q#|*-! zJKq>(4Mdjgh7nq~39)*-f|b%i)uzlV;%*I0$2F!U0b2E09@zGfY4>BMnda%I_bYcL zr^IVy9RwhwA`M)w4z}Dj%)F`VZ!Se`|1lRlQqCMs@T^JDa?@3-R(fSWNe*diDMw#2 z?6Hll`^(boYM0!3KyToJ8j76LQ+n%FM^N`==%MEqE3}`OaV=u`Av5pjZ8L-9nm}%T z`?N63x^_$$D9ObSD_%kxjBZYr)!STEQf#lPoCAreyI{Gw(SHnL(y;lUirgRm^1AI3 zzdwteFKYEOUhY&_g3&UOt~#0&rJ;2s6Ql}>JZrhRX1sO4Inz&TS9}C`Q?LMevf)b} z(&`d6`Dqqzn#BB^&&q(ZzM<7+i^{Bg#z{!(*zI{K;ZWD_Sx#m4br{-~3g*_mw%5#Z zbM<&#u%+MdoY`3Bz(seDr{cpK?9M`zO`Z%l_;}9I*kiasBqlV@+R&wd^R*=17F8vNBPqu}w=9!sk@ExnB`vhl#oE3lc-#8x3TVF?#j239UNbMM30 z^SPl;?@UiG51g%~KSO(Q`jh;H5Ap6_zZ|%0o6U@tv}A`DHNIHWClH#9BqG|`bK_w^ z{_iccL28Upr2&1lLJ{?@%yfGB^eKG_@vn=6oN*pLf2`%!!|c1$?r9x4Uo`jTHHjW`?3~wCw)%+63CphLQvM!W0wu(OLgvz+XSR zRzeY=UBg#EGsxrA?)K^abHxr$ylA$AGJ2t^nsAs8*;>-y9SswUejmV~#89v{Ci$C3 z8EEs$h%y}e_T1<*Lsd%Q73+Af&IU8C#SmL zYu=7FTbJ*~BC$L3#dWq;YG*PSyW_A{Q4CEC!+sa1K7M~B)+(98vzXr#KK|-_84de@ zr2pWgRk8uAe}<;P9BB%d1lyEo+YMvrCjg#TVKRu28QeKSGw<)oxg<6^A3gix1FOYP ze&NPwzs3k>cF_k$bex!;vfPSb_6C;*RFLr=n+D0)RKoRrZ0#{ls!$5FuL`Z-jTfO@ zGfTgB(>DOuJ+8n0PDn}#(z;qY5_nefm9#)9zN}k4VfK*xy96id5T#H)*31xwc(|3_ zJjN@2WR~%6D@=d=d8<|uEnYq>^v#aHi~ASS?$pig>~Ou;hMUuwgmbcdkA2xvs`<8j zM{TH|>Mp&&L)E9)prHu0>6i9*IeXAOieja+1^!=X^wZ;g-z1H~6T zyDMVf?fJSY6wt67x|cN27qUs`l}b}HO)sjdM1IVU@=d&8c<;u5wKET+JY&^)Q0>HU z%q*Tc5tUHJz)_Y&!uzsN`zO`le~kFL^bmuOrORtx>x$Eg*D;AsD^+`9rfhmi5tLpu z5UBBe55)fX2#FVB7)~nn)d=PL10-*EIiKmvH*bk{I2QEH-ZJ(h?%9xoJxp;mmzz^< z%%^`pP&+3sp)5n3+x6#~bo-f0)|8TDgA(e%y8RP%X_tP(<}iaF&~70RF+-JUZE)Rk z*fN7KRuQ_zq-O#LqcLD_A@%) zH0}$AMH~)Gl}eJoMXr=EuY8=Ee8TsZwJKQ zik#vU01*MquXl-DOS?m}e)C7*&ujLMGVxjxYc&S2nyB)uPbGe7(GD0Z>(TyX40oRG zISSv-SK{X7DfP%3O!p(G!n=s;<8f?KUw?sesJBeKw}qkzYx(s?R$E!p)RTA#7^7 z`rRgaKD8YGakG$xcr30nCoQk@5Toa5dmtQBajk5}TsoISy2KciCm-Q;|FDMvk8W|Z z8ns!yVk*Ix{EZx`RODa=^U55g68OQ6KiRi_1`CO-%ZO#gqZcxRia1?vI5m!r zA<%4>(3zYn&L1tti`B2MdU1Ezsh)kS^AvpHwz-r>S1&gsFa;Ol|Sj%DN?4wK~h zvIf=OF*oQht?OV>ef>oQpb{PB25yrnNO8NIT45LMOClU#tL$sd5N`@6GT>cNH~Bkh z<#1J0Sh>;y!s!8P+zMqe7XkArj(4l9nB2p&+1f2HQ_XSCFdIW+r8rrwyl&;ZniK>i zG7=l$6Cbd{$ejO$b(_1>--3@MP%9Z4Wzs)7AEvav>iWA`VD`qPu>_U=U>MgJCH|(V z6$QJ!ix5IY$j1JZG z-Er+HQJpXIvQm*|h0LJ4?3Aqh;8sx%x-HZ5uXYql(BacgeF+04^0iHWSq8L34P!^W zEcxH*NNQ`8bFi8*1p{O+`!_(AaF1SCr6J*QM2uSs_qRSp6CHf~%>q9hb9(nHFG%#+ z&)Yi$%qwFraI9XAP8G26 zOJ{t=9yo89!I~TH8Q1F8eN^l~F-MrQhYL!qJA$14&9)zZ0dp*je~`y4J+LNK5ZZ{v>2=BWZSSx5M(m%hQ&E@y zCu*2cFfq_~P){L&G-$AoHscK5U43Kd&d!DDu{*|#okCIh9JP-fBZQ`JXR-Rok3GZ( z1Shv`?5roPZ5;3HS?gY=#W~_9#_bI+*Af$?RRnw2E2V8LRL>y z_m+r{kQ6hatS!doHk9jz{7^T+H1F8ToOnqg_aHV8z^|ke_JY+MCT9sgc_S|X5FH~< zsKtumKDgp8O}CC+tgH$WpVBw}v1Z|GxBbF^tEI%J z@3_5&jgUM3S~^ABS)_Qt5pgDOzdtY5|X2)@P zUBjFjSLurN?b&fo`)7mKwKOPHZ&q9*hsi!a48z6}9uI$e`-}(G(s(t@*da+K9mnqn z0F$FG7Ju#GR_typ`rz(%+*L0<#(JlU1b99zHXgwaKF6Z}2pNkVGeSM&?5VBq84*FM zOLnemc;9kdx5wZ|l!PA7>UjFw-#z&7flD{NP6ZDi=y-82?8XcGd6;#wKR5o&nL}1X zBN|yF^G%KTr0e2+J0$F70;fT3tjjyv>|!GKA_;f2l{uknSXxU@NR?WF{uo4Zv4e|o z)cZDKq4VvH>nh^K74jPse?2@03c?Rtny;O4n5^2!kzabo(4%E{69zW2i=4N%ENQ=4Y9`gI9I|ufL+)ii zaF21j&EXBC3go%;*Yy*R7gaYKJy>E_+c2e}Hy3O(*|)0vS!&O{>`~A^Uju^6`w?p_ z;s-m{^zB?TBX*4l>^U)+n1ODW?zg==%@4`z=c?O2oGSu~a{GRB@ zk^fdN^M{8gv0t-)S1(CFW90dyL+6~P?orQD88&KC5miqSzK5w=T+QNp7unc`CLPd=NYc6 z<;ORT0->-4J^;jM3FqehA3_0hTM{rdrC0z~9W}zm&jQg}oYns&w)2Ch6D0YMRe_z% zY3Wxr&FzBNs&ydWK^?u;7%FUNQnJDfZp<9tV(-DSfPYf}?5 zPbi-n)h2U2_M>!$?2%?h!Sd&aDvEZ% zTsnJk&$JIqqy}PkRJbH*2q7POB9zeuBa=ivj@Xjm{A%D%yR*{kfxU&7K&}gcGYxOD zZh{*13D5`IN*owLmc>48d(tFt7~Ul&I0raD1nDoE)C)OHNAxIy00PzMoO#pASA#lL z6*-*L5`m~C4}R`YorNqxG_jIUb686A3E=3u(D3yA`cmI1pTSS9URf`X3ygG#%pabR z=sHVF;4Q@w^VWY77$4T49I_VnZc>(%&K8@H%VX?0nBHNd{PdnfEP4zvfzc$Au+^=4Yxat*>NcncgIX)Me?_&E4u_vTfmBJ;K1~q+F zn6?TmwUoc0rN!GG=5>4X9`PNz#*2zSL=LP>HXFd3R&?MlUymb9p;nm;rY)u*JBb_# zjXtr(eC4_q?!$V!ECr#jq1LLvo9`BQPBm4Vj@%%eZ4cc^oXg!j40Bj9Q(KqKl-j`~ zAM0N$Ga1y)VFq$VaVeHtz-L>*$^*QsW8It+v3& zD)tnE)}u*J2Qq)C=6GL^Q+uB#&(G`RUKsSCY8)@*_;|=x#N+$Z=t9O)%ok=Wq)0N7 zSFR5T5)j+5?jE?_YOT$(&I^G*k`+x|=W+W%^7rjMd?zkB;*N1FDx~=?Jq@=f)NBd5 zGTnYx=L*zkQFho=v-X4ejrz){E|?666`@8C8a*|3JEWub&b%&WT>SRuk6sUJ5%RB_^XBj&#l4i22pekM~#1&G=B%d|K$VTO>H@vRtM`hT;yJ3&zXUmsI@~QlzwXc+d8}2oELS^yQsPzfx4>& z34Yy2yn1`-YKD7T{tWN={m)&L%Xp&Lx(=)I4T)a3&MqpaD$=vb$ql=>iV*yMcPywT z9?R-vEs6ihL?JDVc~ZYVJMBwj?G4M####w3APe>_!z^Yx_zA16g$}K0Q8)FOjli|6 zpH$`?9ZGAzvo@tdQKEllc_Ac}=Qy_U`p3C*C`b$mNm0tX#A+TJueYz9AGscU#r|80 zHYkkh}@atc~+mRj?Nd!HeCFM}8MyM$=Aw!**aMC30fwhZ<^ z_*YK@R(hJ4!@bukU?TUogz5iCccb6h$WdG>6`H0EZ$9i~XDg}oLf1uE`3$7VXWlbG z>_(uixPUu@Ep+a+!kH<~hCi+FtZTYbp+TVsL-`}N$9j!2?cxU`*PS9oyG@^|>_)D& z-3GwMW@3Y78mCcBp13lGN|qlmuQ%G)=5zxpj2qwIVRvOW#|p=AG0F{{nWM8aQoMt{ zg{D>{6CHASJ={Df$BI>*1CprE`k2M(yXmu;Um^`XCOdNv3Y@r9w;~4<&4>*yn?5wp zH%zDtc8_)Q{ortjqf^9$?$Z1KfE{sZbE^IEam}_cL2fl-`>DLT(W#P*N49=W$DzC# z8dPCJKu$lnQMoRw!A|0M6eV~|J`_tjnv3e;{sArPabrWHSj`O(P1&M*(TwyDXx75@ zV-yZcFequXq18AwG`(WSjI72btlI8`)(8U4Q*j^9LvfdELqW+0+nqWRUS87~Vad9`=`GP{lIasX2H)chXD`Mmh-5_fXzFW=lUVC zhm)33IU5{%3)WhyAt^`(MSI2^_y=_VqL;ha#Gi!gE(+XQt{~Tiao@)Dj=citCF6In zI`>QmzPkBalQ>$xmE%)mV8m6S)%&K(o8fO zzIOWBb7nvTkJ%ePzb@a);~Ot>lg}t+lTZGaqRcO>c|b(yiq1I+`9U3F`boNd{dI8? zdhM{J^jeuj^*xJPcIq@v+TmsXyZ7ulL9(K(0u3?j`?Q!?)xQ56^=2N|moah&Pw_I! z>Ik^rK?*oNqPv6ft)fAJy;ssDe=!3fAxlau5~c$WhjTnc1KuFKMem`X2oQhU~8QmAb-s7$%Y27 zh|M)z?q@)Dm<;)bmNP2}B zsR^TDhqpYN-*rK2eG75AZa7bVOraj~HaS@yjzE-qp-elj$~n1x94oV9oPogIN~0X+ z@%Ef4(~x{#{9eN|NX#}TE7~6P0+JYXHKq`bE7~6lY_$WP#&Hd$Jd>9hJ6x`J$&U42bxsYee)p#@CN8JW>ATrwB{H^?=Eo`Iu{1L8Id6C(GZa|kbUyK5z7&af2Fv+QFB_F>G_mDmHfrjy zmG}+Mf!~ixaiElyz&}|~5_!(;d2AxhNf68sY~9EC@@rh9FtSg6L)`he(s?MyQ; z{H=CgG|tE7S=8qo9I09t{9Dx2ZLGG5Yy&RH)i=(Pl&h^Z)8CzXIIr{jgO`jW6;?US z!QljFDER@pf93_ugHm0Tb|?<(kJjc5hs5bhb7)LElPIp`*rR^A@yEeD#v%-yg23s%n^8tqsUD3H^pj;M30ASC5 zZgSoxG`hl@!VU-th42dC{C|;Y0N@`XZ(X_Pz<-n;pZm{^f1(bp;I;ThgwQW=07NqM zD;^>(`xOt79r^{2|D!B*Es|&TrudH7!fYbe%u1XyM&XZe=f^q=OM_=zy=21%+aXBd zWH#)CH0_z1`&d8aD%?#exRc$1 zPRIV`vD?sl-B%?6%OvqHW0%+bg>vFuFLLHWEEa#C6w#f3!yzJs|FsHG#87{c4-rHC z1r8BI{g;bfL`K9=f0YjrL;VGh|Dzb{|NmV_pTpPm*eq#P{OFt_BQy#LaV^Q#J$)pv zQ=WEbduD&{*g#-#l}?BbXh(EAa+!(B{3c9prv{-)S;M!LVq(oXUWY^dxvl4!_Bua4 zEGL!a)bH;GmdfU8MfsI}O^(KvD@oMI^183!#Jy@^MoYWIv#|-!B?cC0t#P0hTnx3OmqJcRvuJ~`>+II(#M(Zg zKyMW8d#XCo9;I5B-FRO)rif)f5uMm?Yqd_uBdWd$33sZgq|eRMtJty*f)+J^CQeQc z1@l{ltyK!^T>$%wzb41wrB!h#SD^5}v%=jheaB*osF-!a=p~FL?04TuP(e4nHsKBw zhwM>?w(FjFr@k7QmhCTW;^gxBJs_eZjzP4oYrF$h?I-8-7fymza&v3bP{v9OGk&rT zsw}WAuUKf-wOPXMt6lC1EM=VUnZTPp3OXYY7n{Uz*PV2Fk8>{VV@68w& zQScTZOwya5jgf?neSOu`PE>38pYVS6Ht?9PG${uy;n&|(-wftc53 z&UE+W`NzN7!BT0$(+gZ#@CsQUYP+SL>DI!rC_9Ps zZWk0ijR>_RxT9zoxLxxQ-|oyRlqkSpGt;oH`_V+^_4abxftA>XG^V?L}P#*5GEG-PrmB%l%{k_|mG?5@G{_T87P z3(dNdxfFJ>-?=thxtgNb#u4$Xfi<)jrT1Bxj{W{VA^PvTBE|Vv<13szd+4%t^P^q` z@WU9S3*w#U(~2A)4|<Qe`C&)hvOq)XeC%=PzCnytq%A2LvV!q6>_7GL}Ik*40txzl2m`0uPTD zS+z@~6m_J>>PzR>Qp?IDPnS{)z9uX3cANJPk6l(W(`|emQe7R=t$T>4vICD%J0ZzB zn9bFAy|F9W&%(N9zj)Q_0{?CmVoz&l=OdenZ@Cq4uY7E0sphm>N0b*7qTOrby!(S= z{R6Ca@%}YmNV;j@!7^Y|5dvNiIpu%(CL4Y@{V8jBeLth+PdA-$^U>?2ZaPyxCPgqU z@hQs-xq!3R7ane5#nRbMdaWTQ?z#(5&)Zj>bU&qes;{PO(2v-bhkY&G-(+Vj^_K}Q zBbuApW$q7R>Jc>qo<*v^>fO*~k{#nHi}fEso31U;vkl$gH!qt5FECDveO(Nn~sXa(qplDG)Cl9wWJ`cu4tu!65f zxD_vKEBjV1BBZjrr(T-+w0Yo*w?Z57u!5_E)+_ld&n)U*CH9*Y1?a4W5YqzR&F>pe ze=$snZXKAO%_Q23Z4KO!6F=V@1o^mbfjIW&F6C`tTVI*`I8G9HgvuBsGAn}dnlIOd z+;&^GsZA?;osW6gAIH$UzR_zybN{Z&3Ad8lkjz$ zodj$h?2N`bxa*9wHuy$svY(gxlcL;Md=mYpUyoUT6LF7Q-o*tQ3No-~6BHG!I;o6x zC>lIYjDo$cw3~$nXl2is=QXsq+tdGu48^lnvqE|Nds}c>8#^id@rtg3%O0~DY)D9l zP6rkd|88^gMfCRP*!~kAigsxQkG`h# zw9yi-r8)mazuhJfp0cYR$W0AB?WF~l_n)f59`#>R^0TcgQg=knN)&!g26rACjO=8G zK78AJ)BEb$jI?>G#Yir$(VPG?ZRAAsWfW=K)KUEK!E31uOudEwA1d43COj!_q}T}A za8y~#$M0X(+jp+Yx{_FMD~Lb6k22!qSF9CX#CplvcK6i2!NHdlW+}usu&)<({PEUV zGH=7)D3hJT`=&Fq_a#patHX& zjW%+oJMZQbQIX#&FH-gPTMO$8Hm~^Zz0q_7X1S-%2r=I!qZ>&{X)D()V3Wy?N@?Fg ziAUXRK4=r;ybfm59p0ps+gkKmM!M<$uEKh3+6ddrCSH?FO1Yd@fG>J9hL=Vp;(dLo z>r{b<@;mGIY-J0EH*3iKFwzr?wwQZHdKt35`r1*LX*1(nFw1n#{mrbzU%j9JQoXeP zW!)i%hcNB(oFW7bRF6fpu{`PJL$zs7s+&IFDUQ}G=zZ(I+5f$?tX$8#l?l@qPMAz_ z-3Ed?FOJhXN6fwD&~!>C+EK`KA-6lLo{DN?6xG-Fl*mClj8^7GlJ!}C zhP}ua>Rpyl@4TNynkj9bcQL`Ch7FiQ{Z9fRTGAy}+$?p~0Q30Ws&P%duSPcv)b=+` zgkvHQZHB1bpx$*8U=vdog}zeNp9_dI!0T6pE`q~5v{;+jWgO$ZJPHn}*-G%8tI z;xU=&*Yg%`SFxLv(-^+#@N|#49G>F3RE5%aMk*Tl!7URZO`9r7z)c&ZCHlM=URUX8 zqvXZa`M@b;eT;ME?Ad_&Y>`_mo3QYDnR$6X{mEeIrMghNBf0$+sZsfE$s`5-&X3y%18!5lo!$A_yuwJ4$|y_z#1&u- zYb}hByr|oqi%f@e38wADJ(fO<)@1w23h1<3!3U-r_>jUht2uBQcK2|1gd0vvQ}wG1 zc}_{eNlWSu2^DP|qSa;*tifw$yaqVX*@JR%l8w zHOQzc_=aD?vjDArV8-u7DQZV|#5dV2Ko*8Oi%hHHCnxVjer9JgqJdEiZ0Bj2u2^8X zrfHRIY^VwJ9_QqKD01Myglti3Tl|Z0?ees5sNwn+MjBLAOC~-a>f*XDL~o@FM}<5% z=7O7!Ji#t5tFX`~=kxhoU~~bK{hln>hSFsrYXv?{r{@yzP%f$iB-_Bi|K^Z{V$)3m~Pn8K|`2GS*#iQ~8NH0`X zzsCnWmvJX?wYwKywSDlER9*6%go)`&p9DKwmYjS{TcFa1Oe(K?$KJr(RHR-Spf^g@ z^`JO^nsYS`(;EeoCJ2hcI%;Q)vyP`?)NMUx+oQZJx%_MUUod1-m=2BqcpjKn&TF6? zPcBQu$c-YPDZ!C7go>K9)xCUe9j1fisEz<^tkcM)gIyBY)kRB>1<;a&G{^X}?Yhtt z@5=fz#G+%Tx#vT{{rZ zk6X(JPg~^{QHZ;eU-1hnYp)-{jF*H&xy||(ov_waZI@Y$I2yk>-}=E61zd4%g-Doc z%9ET1sly-EE!5WG9bS;Z7af{!7J4>DVI%TG=NC>U6e=9-pK_f#S&_zm@on_|2h%C( zji7mt>1QK%=QSp`MUedpxIU*__ge@{WNn3JweMtF>0+=ZH(a<)%gnsLY2Fn)G+uja z#%ovY$5-nXUPtJ=A4%8Zf8(2YP9O=$dT8FAXX+RxJmot7Zv6t|WsVusH`@6`UlVkG~e%f7Q3E zi)m04^p08D;RhwSx!Q>3r(il>P184;gj|7~;^K4a`7CoxH3Vj1?27zaxWDs~alYcW>2F;JvdT z-lK83p@;W`;(Ye)l=^tvrr+dK;;>Fp!Ci^{!`;^1o=L8tzr-+Vee+)iN=XQB^^>)p z3ZdGvdTmP$^mPk2Ggdu-2yEfl!dzeuS?8v}I|pa(-q5bHpBS`LtJps~ttJLfFEjAT zA&Ui4ZV60UrDgPMVfNwEN@gOX7`;Q6Zq^lH;cQfAUj%nO=tv#@vi0mEhJdmX1(ek$ zQWi%OEJ~71=*Mo`yHH7i7Yl9ok$n*U+2pnAR=vB%=Cr>MiZq$Rs;WO${B4=%LZQmg z?jNev+8hN#8cC?l!4%TnQFXEE>LD}gS8JqXkc!WO2D~l6i#h>zueu%pN%I!i^-oi z-rgH2jzy(KzNIj<_cNV!4P>!(;XSUji}X^2&+HeVTPsFRHotgtaitZINxYBAu<)e{ za#(+d1TKdMNfwhQa22p`F8KR`3Jso<Fta>;Q;)ReUt ze!l{pXF47LV%D5woOrY0sh~og_SQ9LRn706twVLXCIcbFh-rP(06AcD;qq5`IIkpn z&!QlbC2p%HKA0Apioc#=4t@M?*qvQWD1RL*?UPlE1IBkD8wHvZFuR?C7;kx)-w7yS zRHE76roGky>r*u4_B10Eh;z)Bte%7Cvh&L|sdv}lceJ5u5fTOTX zpf&Op6`Bc^f@0jsp_D#=lW1k8#G6{Id8PgG!5cnPk)TVvX?B z&b3(=weoheR5u^B2loB8(S+BLTRfu7?G;D`=Ng^uUw+4hAX&)tXvEHbMiAF-?3|LB znXA-w^=Yu^5m@~qrS)$w}p)learl4ncAwJ0wj7L^(utpN%#u64T}8 za1yJ_Cd zZdadtcO)`2AMyxjLAJW% zNUuO0M8jjBC9TvB7y7}kkC)<|^d^SUgS-++iBM3_Fa@&gvK@>yLN$~t$X@lcmLXav zIhOYm^HrPVPaL#$w~NXe@QK;C2XZ&UO|huJ#cO_U%f9cXnr3n}-2;Jb@*#U?@;bi& zio63L@0luSvJ_aB%g3~u{Q_;PO2s)dEV94J12T?ndq^NHx?qxCt zjP>m1qImbVqt}J_yS`x!*u#poiHgi}HiJN&YjA?iFV}vguGA4~e?1)nf*qFMJ?DEO z-L_ILpO!zJsYmgQ#s4|ul{hhf420qJdLFB7p+Nn4Z4L)vyWWkwE~x*FBB~upTO;c+@ImjiA1+| z6u+F8!;{HZbakv1#&bQ)y6|dWx6gjG0C`GNxGp^b6!SdvWZ7R36IVZT>L5Tl*gw3t zWgT0a&RS}f=W>6xr;rKAVq~|H@Y#~^Tm_q7w$}guB7;M1b{jOIbyN85+m!v z`Y=3%9pLd7cIu)vzdeoZ?-~3&@sN^$CM0r4k00NPjNinYyw~F0gg=T@tQbg!1x)g2 zxT8wj@!kg%u4;K5D?J`R@`>f`Cm&zAS->quqS@aPY7}yj?(nwM*6{74WgA-%-JNmB zXaWZ#hdd;2BFUVw06~F$XZGVcyh!4Y1kA?gAt&s0Ib3l-V+Uh&Eu9(0Gh8l}4Kpn= zy%&U!#ij`4ktCk;o3!P?cKTePwJ`)SI#rr#VJ(SBY#s>kvLD?$S{WlNX4uQUxD(Dm zWCJ(dP)OHOJIVgE7Cb(xv6vZ5BhFe{TkXD<9d!MA?rxG*JM96JHEA}t`o1gebQH(2 ziouDSNDkYNPt<>BWgOtCQyybM4)po5 zZsAS+L+l)NXIo2MKJ)qU;~sc!^kOFPwcp?C7PRZx9CF0ky&{^%^^#lZN8J}7eT z8@V3fHuvo8v>dr`r&pc4F#pcBh^2DO!{6k3};LQ6+8-T;N1DATkekOFq+9mRNn-JOVE&_kU3To+jAU>MolM#b4 zw>_rKtdI@h7dCT1x)(#^`R-}jywTs<6!EcM(TsO~fw}V&@skziAg{;#FTvp4+50cG zp3DXLK#ZkqRT%nD6r#LRu#OO%U)P3*642h>f(8F%A7`ylwn;(eS`63@9D(nnKMXEgmhecjybFga4dg( zF68<^P|*+ZICfnS5@P0F?KZcRWCq3HKU&PlK2euhxLG2p>*aY!sU~EnU3?LBsz5J~GVZ@{IT!b1sp~)jnSw^V zWcN~GSZ3U0g}x8a13#rJ$e|?AI8M!=c++;Sl`VgS2FsozDW~gUwPq$`_of^oP6ZaE z^TD>cG`;lm5ExzxM;?MW1r$?h#^y1b1hZFF#tPFPb|!FjO75 zcHz@`VGy@C&0~T{DphbIlxR<-FeF_Peo+a@zoldm6H$?kP%@T&i*^kDiifDCaysnP5w>ds{5TT>AkZx#`-m5gwxW z7mD~FUsUuzX4S*^4vaXDUH4ZXit68S&Om3Vl7C?QPif2df1YeMaoW0zs_0`()w*-D zlV88wlanhsMq$_X{6{tImj9@@eef)gQn%L3I?l@h1%o0zNjI;i4+dd80L82!UI;r7 zxFJj#j;V|*8s}9Cj*R42A2V`T^-dazT{Sz9By-I!Jz~`?6)bx)e99fbDQEd%1u-;o zPHZ_xEbPhu%f1Qs0STJ{oEAp5D_8aj(^D%~whALZ;gzs;!j!e}il{q6Bm`L>5jsD? z5XsI@EJT|16AO{u`H95?kyTy+rW^-!!EPgy-I#=EJHg7_i1^M56e7O!6N>*MGbdrHv*QXM zMnMEB8`LGEez5V)4*k9R3dl9vz?!r_&Aabr&3=`~C8*TMIVykuDnI%nVpzWZGend*Rv+OJRbPy%yq^WUU8T-Fl;fWQ&-uAATS5#xRa3! zo4>K9mx_3|{O5Kp(UpHmq6nXVfdLSqAwpvb0g)t#BtawzA}tVUfk+EPS|HMb|IfAH zC5`i&$66`oz&O{#vb&f7#Z1w@f4>nM7uTD#$^y}VhU)swBphG$E)5B1sTQf=CNQS|HK_krs%wK%@mCEf8seNDD+-Aku>W zrCMO-b`54jF3S=mNh?^Er2)auvG6U+b8_2c<RF`^<>_zQ8)97rM)D8s=94q!ndm39aVYd$13ylBqT@?AhMqmI?LE=*bBIP zY-`oom8v;bQg)f7DlxHwmEY*c#wBBXV@Y2k1Kj-kLwYdk?eZJgpEr$VOO$MGJ7?AF zX1ZHWF0WP0WBpQZs>+35CIDbP1NpphY%(lv-&G2}Iu(#ts2MQWSROQO0iWuAfvEka zk)?AgkYjPJX7^nCq#vu=Zd1{6D`7Lj@5%s#-pJ<7QarlXB`Ua$#~N?xjjYDI`G5S} zx8-d~ejR!FaP_MBm7(gYUztkmDs5IlqEhrh3gK7_jX%9R_Hbtmy=0dUet)n>h}cI% zqP4;rU9hdMIayiW8)jdbDZ$8B4WPh`y0px06ho7}sDBO8M(kzH9o(;(3#Li{AZ2mN za^j|oB$mIysc*CJbfJgClby*yo(Lo4tzGQ!CQ^VtK_%Fd6XHbS1!7Rwg7}(<93u%W zO3J;A#mNZa>6sQuVj1mS14UAU6z$8c3QZ(>@meAg zm!jo5%y;}|#+@50zzNL?xeDG+5Q^T=2+vnsq zSUdOg4(Q4qkAeFpiY@AsO5A2I2q6W?OA0aFV@v@WU$IL8dcODMH6LDX7f?k#BkanW zV9Z(y>}{ig1UeEynif*&W|tF?GUt2i*88*i`iw7tT*3FmM=lw) z(<8CO06x>P(Z;jHj9TS9!{woNf!T)$ZrP3!!0eRxNpSh}5bRlj7X$+FARprT-J*H# zW_UDk8WA?CA-pp-?hUZp{nQsIze=6$$cc;l(JTmZ z&PtO>=eH|1Kzr3;1@|)@^N59Z`I8*RX#RdZ>|2WR7$=+e1V!QrCU~N7kSht;`4k4h zbRhumpyRWCJ+pyTEvdrks2$b$O?;XikUKrp6xptzqi_Y7?G*>LukH2;dOsK^ zpRFnpPdU*OeC1$o=RuNk!%{Ei{OG4yh#wpu_XJPbjTZ9@8TGayW8j-i zUXAA`n0N-M@y0Dm113IK4*=b+joz=N?A{-4HF_?kqU0UJ%D?^f?wT2*E0qyf%qInm zXD7hv@=KyvwQI#bmd)}EW_CMI)mZbMd)l~6UFg~L3<)yRA{Z8@xD8Eq3M}PCBcx@} zmke_mi{f1Zk4`gF+i8H)wbh-(+`n$en3-&csu2)iWZ@dTjlR}F*A$RE8{m>3p z^_}(z3TyQ5KG3b%<*$bgtQ78a4!gF$ozF=mFQ_GGvJ$WS{ZW#tiMCxe1z)x@CGCV% zxYd(_J59r_RwI4M>Hnb5|6Q5;LI+am5X<7`85J>_|F8DGJF2O)Uw12xIG+WT5k#7b ziUNXw5~Mn#f}<226p$7KDGG$D^aRIIKnRSZG-)a$NEf9;5*$jDs?rH06alFrKxiS! z*&9;e%(-XXd)9Zpd)7L6|Jj=Cw>`i9Zn7V952^a()5gkzE}tJrCm~byMf>8G(l9O-wN2Kf-DsCiBT0m1Wpf(&@veLD@>A`p z=o2QAcc^ZV7DcFa7?1Uh=+j+TZq`(tV~*Vys1l9?l?jWU*@v_-^caECh**C(GF=7d zow1puZi2&34P<({maE`2y0koeJ$3PAs3gnZ$WB9plM-VNd9F7YC4wjcd6eF4%X0R! zSPUi7>Gt_3da4@M#UEYJYS>@%*9>By)Vw2EyTJXJtvS*NAEUYGe}m+Z)4H`ir_IPj zy95l_^M9GNk0y4S7yQMvVF!c9vjMp04HXBm-58vH7-6mx@6AX^NXYv9d6M+!#)_2d zlhl%#8abeiMC}W7F-vH-4MGo^1OgRZs&M79$m6>R5p6?T$tEaR+WFiUgfy!xS7NMl zW_8xMFUvx0c47FfG8jdXCM4oylQULR(F#_oDqc2E^*rU}{2hG0)@EenYT$hLZtb)f z!`56BVZt`NMKJOsmO49|e=EhyAXZ}Xpb|1tVZp9ZGDnsCkKe6&`l^*28AFWw^eBVZE#kqU(gmL6yKe83RWqmXaAl^R?aR?dW03 z)HPy<&e*)uohWb9-@=EZ%{|9vsU1rVXcdZgvsVH(P-;$J&#S=F-_h+-x$#hxM^4+e zd{kf*tnl*^-OO@O9Au32y%fAqCQ|1uiGn3&_Jo??O7@w@OGb7ko1PGiv9~8}TD7uI zMjXw@iI=4`|1l`3P}KFbr|Y3+rCn*R$2Ze$DttrpJLKd;i0;oRG%+WpMN=|5PNHg5 z_I(NUFTI3>;(fV|4o2R}8d#$KCas;W2t>g>+LdNWg?XmarTMd?MFZNx`NuG^+utix zw~d($_!NcJu~LhnAK6~@Ymu$4A=BNnvJaM0TBJCxxmQ$s>7WW`UgxU+oA3SGB496> zm*dR$g>}KpB3qT+5n)%8AEj)Kcu20w6f8<<{{CjiM(lJ(US@-#;NIkEoV;?jTV`upvuW6{ilDT#pCGDf)PAea zt9`V(54qIp9IO({{10)maZ|*wLKlGDnK{+1<7$Z>DDY^ON<+oUG>ggbQ9KCOMx89O zg<|SD9&I041>wwHwv>aCL?2O`q8|I2+727z#F=GhGco&(j1 z3H<~2lvvr$AvUEA5OnR;+1~Cq?{aZ^kuOJzKlKIZ6l#c)wQ9}3tnMkOg`M>PZ!)}g zBm~Pu-t7rk3O2}Wr}cj*bQlZq$8D?541CRhS8D|}j-V%+0;%BJmF3~N_OCidTV$zng1h68>M=Lbb42Ta`HL)a>w}jt&M2s+b~;FcDhG1 z`t7a&TFwH^$Pn*z&HOzJkSNY8Q5}n6OE>gX1_p=WAs@Y;efIF>b2(kKM@hLES$5}@ z0@A2Px2pxc(DPNVWQ2IxZa6b^WCX+mEqg1_Vx1AGZ_)Y$mtg6E={~j&?JuFkE*b@Y@*z)lT&~GM zON5cFn15EK!HVmmu(GD#93j5WyQv!whqQYLr2xl2zYf_NdaL?DveMK`{ zD{=$&dV@oBd)*+v?^u{i-@7~-=2Pyj={{kDcbdBFODd=;$rEas4DM|}_u|S$)D(9~r#aLPSdNl}n693v5|-V5 z6KAXK?`9JUi7Ez?;iDHs$`t)T?vIY668@3Zy(Y;uy?uN$1UYQ6)UQc2_G^s?z-Qlrz@ET&Glw#Ce@>2ln{X>go7C#;-BgPN*d z#VNh@DTt>eKba8la)6`aSbG!VoL~IuNpAUE?2qlPAF-a5OoICakpD7`Gc(6|qJOK0 z!$bY;#vkYob^B#2?Nl(S>pn|^?Knpe%r&f*bh5MBF4NipugPlcNxWV5UBGJ!i`@nA z0t7TkrY0z28D) zBelZx}Di!nLtWBI}~tS)h~M^yV6biX^4LEUPz$3L~AV-=sv#X zdSOQXV-Fx?w z6sJ2BUCax+kArE8y?U8-nM#>DLS8>k{qWpNr=RV1w@%@wWk~J8Q z_JSIx_#PW<_%2T<<2V_t9Bk{6UmsTRu+gRZ+U9<`OH&P=k3M*>aZW%(97R&PJlPeC z-Gg_sGZ>l`5dX&r!v(E;Z06-v+xBro*>7Qw-@8z*D{o51U)Rpkw(|Ed&!?9NHXCKS zjj63l>~{_sZEUdpH`>K+-}JIf5kC^nDukS@E%tk6gOQ(}ei0W}ql>{jK0aYH!Jr)o z@GP5u;!^m)Bs1?__5bKR0HBsYGGZ#w67q(p(YR;Zuv_>Iad zo$5l==zBBo_mjZF&4j;r(s6(IV28aa82?hMpb&cEA`mBk-u3BHQt zQ_}X{RqZ%)u_0uE;)IF7#qX%<)#b12wxZXE#s)AREHH4eI1=s|p`LsVf%k2PZ`HzD*`48_~%)STRCn)K``B&mVIh<2ec3MVL?O*uohx}|8` zJ6B6qj%R$F6KLy*_Z1gft2<_iqPPx;9wRAfo2koj{w{Ni3g?T7e|>n|QCw7CcOyH2 z;PFg%6Lx0ntAE}sacT51g!xFhPa zMDD?pLq1F3n=)k>NvFi|xUuuw;9*+}l(fDYl>0e_w!dgk>;G2Os;V{Y$(q@eLoqz1 zyyCayOZqyeya#mQRf6r4uFF=FK=BeWjk2K2@4xf1fNlXrE!xXz(c*6Tu6V4PP;2#X zG=J}=G2h%!PxHIp{jIJ!_|G5HtNA~@+c5p=nh;9ZPHO3;&BBu2a!UAd{hp9)!G>OB zs>UZ-Wkt2qyFg{rNf1J@g7T#c>=JDGCn`q4uei@wRmMNQ{rzhRwY{Pwtv&bQfx9B% zWqS=RXkIGI6JX86oT%-93Rg>~@}VN(-6x?1K9g`QkA5$do3cc+J+6`gBMo1;T4!lE z?jyCiNq%aeZJ}cu@&ps`*H0k4)W=IQPwyEJsIG6gykMLl@6A*aPje-O1PThed-!c| z_Xw0RAeKs)`%COM#8Xg2M0tczQ#z&No3`|TMae{HR|8FPd1F)WH*c7_cCwjrF>ar^ z^du7{B-|4-MO$k1q0S`|lh{YzTS4z}RQc0i-c953MotYbKj_=q+A3;vXS)8j@%a;j zt$WL{h%+f`PP0oFz`stSiwTM)*e*3zjR~v$LwZ$9?JTJ71DXX zm2)}cMa}8upJukb6pBep>u*9Un_gY8a!K$eS&#c>Img2H6SsW@p_9tgJjK=Iwc*H4eBv!9H zgx2Hne@C3Ml6TdPGpPrVim9>XR(L1v@EW7uRP zv3lh(Y_7-S|BZ`{-ItdSv5{E4@~mt<9y}{sj|b1n*5kqBv0aY`uTWfv2d_|E2M4cE z+$%f1=p(5gA2vt^A zzEcJ+5)PZQ?f-xl;e`w=dCM&8*P z8;R8`uj*fq2e0a1j|VR?S&s)VFF}8LxN6E{+T&tz=I@33-??Epzb9h% z;~6>|34q7x#D(I2C8yKs_IX9l8a3b*Ib1M!MGhAWUari=;=eD1R(UZ_sl4gW*A6e3 zq%OGo%GXURHO<)Zy}|g;FHVek$^nJ57=KBpME`Bzay0&ECjm{TAQo$23b%mV_`Hv( zi3Q*9;ylpRPA|aoA!hhlzu{&tp6Bb>NPt(Kuj7Kj^L1P-c)pH{#ebi#<5e#llzCp6 z0|n13uYm&Kd1WpdJg>~fg6EaFSn!TVxmfUyN4Z$=jz_sz@DdCz7QC`=4Hmovg9`>P z!Qf)SOE9=t{4K!GZkSf$7lJ?p}tTjf`D61IP?Y$(}L+<9X-thlip=LL}!)wo^2I_fpg5oqFfYNn!N!4 zupVJ74m5$cnlm?4*p*Z7;6YXceEGkBw*}9Ma3T9&Y$jX&_PZV4Av;Ggcr`Ew3f>|6 z8Yp;&>}$Z_RViFBcvT7)3toc3#e$b$aIxSWvU9QEB^X>Rc!%t3u;3lCbHU&x7+frP z2?iGnUV_2Jf|p=$vEU^bTr7AA1{Vung0Th*UV^~|<9}L$(Ty$dYO2`KG%r9Kc~h2# z?;+nU!*n{Ul!*k@(Z-o<5^Hb+74bz3zMwjvv9X8=N>HTlhJXLAzklJcJO7ghE?lU= zC7bacN&MFj-50(?-~woU0$fCq-xXy8z;TeWF-X?m^?>~q7S|x89R-12gi-7sptl`j zFtrFhyqDY0bASPUu>=tUm{?O-96C@Zi;@T50mbs*bAgHn*L}EeR6F6_te;h83IvU< z3>I=&m^;{|0WBe*cN!W@_{7@|zs0e6`kJ@Gv@3d4%53BZishhD<3e%VQDdKc-eLhV zbGnH@J5b)uW-9{6KB1ce%RUHxIs>u9CA|M2ye;i;9GeOrydmh(XPPlc3t8a{(eEMu zFj+IM=^w0dC7^MW_cOS#ScmGkTAc@%stvIVp182Q9UN=5Rs5k}l$(-!j}JIu0DkB8 z^45VnqrcqtTIAvb;JO?)$#3EZ_DX@I_Nwnc?7q0LW`J%qvFZ`H8UtzQ+dtN>_EbE= zRu|645A^y#YckZ^nr0MZkWzP?Jv#ur7DZP)=mh{g#CjTFee>g?{AHhG7W8-;)611>>7$hUXB zc|~fCG6dy(_J>0o-2_eBwszV#dd)rLZ%(}c;0ZAz+ut&)O(=~Ou>`nQ2jCi4F&=_2 zu5bKsP-m@hW={DD$bs7(f;6K3(OL<#-74KG5x89pur~Nlo=e-Ama}&OUQ;yWzH>Y6 z8rYMkDKVv}04mG6_V2=PP<0u7+C_T=v}R7xIfkkcQPi1I>oh zq8&;X_ki7;AX;d9Ls0!WtxnNnOctDytwe85-?^nceeDeA2B6miQVmE)Zj^6c({sja z)RO!`>K9N+R+L_M?XWXSkRO(df{g6XRp-GOv0d4ta%IXDQ)tWYpzLE2^Fp_rM0Za- z!fgmT1;vOk>9zY40`0Vd23dkkxDJP~80w^&Yvk&cW^N7M@a71#)Kfab+MwNVnrh@@ zPzSADpw6QqpwX~fZ)vK(1vIYv%D9l9Vz*v3ok_Q95>W9BRHLBQXVE{U$v@P=YHVTO z0Dypk(5bFCAWg{Uvs=YtqMxVwbcG;Lu^ZAst99cB=s>Px#pjKq*KcG2@fB+f`i&_R zxQifWY_zTw=HOzL;hpdVwmpi?V~v#{dbw@8H@VG|`oH(WY3?Fa>-UR7vCQfoG{P=rx`c#Ebd zsmgHMa4+I@;fLSTpKegQ`e%dQQ+tDqbBTz|(#ML|5oMG@<{#l7jqj9=Jbitm@%#+~ zQLnQ%o_p2qylHazp4(HLKC!z%(>7djYOj{$$LSeHh!_lK5RKCSX6MAOgR(g@56TAM z|FqOQ@?27jI!)Nl71DkwNO+&2_%U0-CXa^J-T3T2QnD~+@Dcz(OwxWw)@V|1);CCS z&tcG(&VD}*4dSh^WEn{A8ekYWle|`&RF6Zp zBCQKr;3wU+QrO({BaYOyv?CiF_c;n0H*kng4w`PYZN8=e?gOAh2C8~ctDft`dr!^; zUI&6wAQ!IslUB>xZ>BY&Z&tDhn{0uifC_GsQE{uUEDr#5FM&&O)07ynqcYe5)OD$^sP>5kJCJ`{!t?fJ1Ey zv8t)Atm)FFON#31W$LRsU-9u)Rjrv6mUc^>amBw`gIr}Z(PPMPIy98A>s_lS<)dy2 z<-DC-s_dX{VE$X<$lPQ^wasZ}`-OTqW50yOK6!`LwORj;A&-@Ug-GM3-Zuguyen<7 z;3c_XVTqQllILBdV}rPTk})^pV?oy4hBc0Sk!C*liMU)4MzMa&Qt6<3hk;8gFRhW{T~{g3fPUo; zwS-fkdaSgdAFq#=#O+XRXwrruIC zA~Mr%{7*mm)H=4uDThS8()Rv9Z781h$`sr2Ty3`})Ay;9u5;?5_k=@^n#GaTTYz3o zHh>jp9INOk7q5~<|3gCxCN_(0)jqUqGwz-TlbFL-asuxhh|W!n^f#gfcVS|a#Ly*5 zMX=qdxF6VYwn7qeVk>QPMQt6tACWDl7VGqY1lO;+f5}$S@|ZATU&P2qO0nl18`u^& zR;7(J(HlkaL1ylrVZQsmyx>8ATvGS55Or~qBx`jE`W$p;3;UT?<4=7Rq}<8i%838K z)kWQpP0AWa@VmNvmrOR6LLx1u)9Y^yJK!FTd0`HcmFKj{XS- z>5QY4P+eZ)qLc8z`7=Go%!mo%{{+8W!?i4t+FR{p9E_<8JzkWM*piRGEYcHc%Y4V;8DOE(cMC z*3Ip&eb>Opps}`j;wZ;t(zoNKTa_m5sO`mYg_&E|P=c52G=vIZ^zXCA%-v~}zDlz8 z?FYTeq#QiGK&OP|I4f>(-cEJFMOBNa07NDh(-cHsyt{rq6Cb5Z^)ZzdUdr($C1WFG z70&mvB$E~j`pT8Izhq1j9hH8+OQ&9gAj5B9Cd)|j?ILB9{I3TbUL^Y;K);;Z)M(LNk<|T0vWd$C~DWi*~*we^2A4;)%=20CHsF;Bpa;=3n?z!`A zC%qSc(!iaxFyOyT3YU#knd@BE+&vS+6hmQ7whf|CQbI}e-7w?%GNvu%Yvx4@1?kl} zRprUFmDi^$Z%`dS3&tC$o!LR#)ap{Nf2}{>5O1?|?x!iKEfq97(uK12-%I2DTLLD` z8`>uale~7t%!}o$Dp19NEfp_tjH^j3S%dPK3_xlP)uxMvWrDk!mC4Z{Jc{xwxgfXv z27XSHND!JRXrK>}jZKM8zr#f$hudpvUtp?*&srdfT9`F&H!bh zM!zj!=n&1cLpaGX+L|{{WEa!DGV3S8t1xXtWRKC#n}?$nBA=dBH>b?Upp{ma2b>7u z2=)F=65!(+mMQ8!pcmXrJKwcyrrj?*zrAqah6o|9Z28CTkvlW#Ol@w z(_`#YxBF0fu+mOgrz+@IO&8?F$`0T6ll)acGbcHHXF%U|jP0M|Mo~T|t`G6us-lzC z9}Hp;dq{Pr%LUB}8(zEMy&VFZA=X~7RRB=#v(mObANhDu19wMjD0A>9h1=$!KTCeF zGay?6W?a|ZW~x!5W<11~;fmM3ZHI8UonMsQFRo`8ad+W6K%Ejyx}Vy z<*8_1b63cTI|`K5thBw3mG1P>bLE(8q5i?!-q8Q9?RRTi8chRyL)wW1w}3CVX#NxWSxGH_GLA&ob{JE;5A=Jit_Wy6$oBLV zw`Swfy_&(j1?YPb6(&}U-QG@vpA4u00iL&t#r&?idhMvs3=W_tt@wA=DT?*;Z5S6B zMKOSvdb+ZOL0qPdPT=P+PWhc_RB_y-D3Gd5vYH zvTuB0-On*iW7lZn$6Tu?T-9BfNo4^ClHmK~(-@{ca~ZZ? z$RAKFA9-^In>z;n5J2r6`+z+twXb-llR?Of8AU4%zMYQNrw-i_3jD6!1_3<|1`R@z=u&oXT6X@~ z!%25e#E!;Os9}j7j;@S%AcixKUvI-(h!8SP*_4E{*70dOTNZ$2d}dOw%7nJ*!VS=n zy4$WEme7k{e0^1CK%rHsh#kNGC6go^;Y0n+2a87pJmClEM1~}e2$wFLq=E}Z^ zNcGL2xv%kB)==nJ z9lLDF;jH5iGsvU1T_!v#Glb)#9Ke7^qF+H3{4|qBb7n?7Ua0%(7H3#%+)W$~Y_w$T zpG>HTo*t8&5KL@W=^u;H6{E01f}6~mF>@xKj)0*ET}*M*1S9DvNoB-%neI4X=}7$8 zl5BV=i;*1_gR22;>zbw{$wKYLg+LFD$!m4x!PUaK1GSHJdbgX`uvEoh16#NiXF5Zx zTm9+-XrsT2z8B4#^3Z%(8j7=(OS0&3sH9%WbggMuKuT6Ii5I$Q0UD`wBMaN{iA_y- zi_;T!u#IGg3R3=CFu|3?J$m5^hcR)cku=@B!xF2q46;?8EljUJBRVG8_%t08njGVu z91|UFbNB9D?^XE)Awgr40lm&yT`5JB?@4Te7&@-j4Zp?j6?PAaKIPPHIq`!LlU*W~ zwuR8ZO0LK3G~)_2K#nx7)nVmZBB7kPaq9LA>A7Ey*wPJE?6Q|ej-=bzB9=U({fs={ z#ZPJf*f^hw(aFjJqp!A05hfDT!!o#UJv3|%K|kLA|lezCjRanRr}hG_`YIB zau74*?38gasT}3%8iDsJVa3Y|Yt9o>__rq$IkwsIj<$gxP4GUZ^yi% zghYgTa-u6189!Cx>%w^VnU8MSo+~jR1VT1yO?Edd+kleYeBNZmrTX4T^Cc1K-{u)^q-XQasktw-RuK{-{s%g> z`V~+?+|>ICj*@9&{hxhFIKh@B`x7364jYNpD~~~EJs#ZC2KMJXgxE-|UU>+u$AiZ) zv>p#0$IyB_{^cA)>!;_@)2+jUN94N>4jz#&_nGesFuZ8LUO#xzemx%lylBrUpW@iZ zPOOEBMz&)|Jy&_SCGa^oPyt6D8IsJb$_#B3^yD9uJ;BU5^LP zpRUK_pXX0G4+Yp0@G@UE5-V4pr(OpKFZ1PPzH1bM=ZDsS0siw}n`?5O$gvl}J2YV< vv3ljjvUPayV%a)4c(LrC6U&aBTh{hF8FDwhUC#yr_~-Km=W>3s{`KDg#2ls0 literal 0 HcmV?d00001 diff --git a/images/content/vision/position/pos_centre.png b/images/content/vision/position/pos_centre.png new file mode 100644 index 0000000000000000000000000000000000000000..4c57adf6e18efeb96ab747f30fe92ebb064aa471 GIT binary patch literal 27877 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 N@$d6Pznm@0`Um@}?d$*m literal 0 HcmV?d00001 diff --git a/images/content/vision/position/pos_down.png b/images/content/vision/position/pos_down.png new file mode 100644 index 0000000000000000000000000000000000000000..76dcfb98b81f184f221c11313e638c6656d1b585 GIT binary patch literal 27584 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 JoLDU0^=~h*Q|1*i$xNGI2Fn#*28 zI3ZjX&iqAa&ycZb7o`j@nw9wr`v{rxSeX0cQ5Bg&5}CPRBr5xA{%G?z(LY$p@S2SE zDc(2Tk{S0$SV^4=R8@YKZ1DOOL!F5=ZyzvR;fdoUu{m>J@(H;TnVAdH6wIvvCfqVV z3Fz!hp-aEAI7?iOSazRS&EG%D>97U3sjL)EqNll(trlHW0p)XEE3H!7z9Qz?1x~0U_gTo1cOg81OsFS$PADf z4C)QaBq)=}6bJ?g21Bw0!2rR~o3BA;fM9@NfMDp|VuN6SV1QtNV0gFX4RZ#VGr*jo z_ft9u1_%ZS1_*{;g#^I>!2rPk!O*un1t!oifrbe*OrSq$0v*M1UBSZLcec8`E6|1X z0+dm8k_(E8obtpb6;&95NcDM?RGDn4SWNiG*Q-Gj(8c-iEG)OfY*v+m;>jOAv(oGJ zNt*61Per~G8VDkV%)^6)@r;J z4{sdES^+*#ioWB3v9`9x$Hc^>f`UHWU45@+OZVHl;uV!z+czAdahYcG>eD>@ak^YKrPctnm$ILMX z-B=D9bV+mO%ppcZL=gAv*%SY|=jq)G#7%B97q5ErVqEl_*BTAwVoeRTwY8P9b=x+j z&)truQ}JnRfnHaedZ)@(^2L_F6edixv9VdZeY;~wSlA^{q+OvkDUzm7GS&blR*4ZN zlZo~8^u#(iI7ACJwaZNQygJ3h%8qFAw9JPu7j7YED&Qp1ljv=J1H zusVJEG;#Ch&BUu$uf|(D11C~E|LrnWr+uxERZVNA2-mH{tvz~lLi8)W_Vp(9gKPid zPArI%0c{{lPia1_1cD%Bvm-nG+;`o6#V<^wAJ@>fv8jhoD4p&%(d_+V z3x($BNR5iBb`L%)=(*V0)7_=2nAle@fVgTEV(4>dpB^HtA|n${DwnQWdvI|=LV~7K zjaAfa@${ZdubOPl%8_5bJbQj)Sy`F4aB@T|$DfbKv2v{B((EJ?Rsj?RTzo2MVM!ct zr+37^N2LAX4JIF#YCs$*n^>3cme(Kgn1@-0CXsm-n?ydv#z~lG8>_B+~LYg;jr~hjCmFDKU)UJ*%%jNdhpK3smuXo<6 zA`I*k`oYQ2X7yqU&1#H9wkt4$5|E+0)cLrfQdK(e)rNnMMohOs6ETN^7N+U+d^a%k zKHOoPk01Lhqdsi*;h_2{*82l}Ngu(4(p=6%;4Kwo&6}6*3ZOozKn^OOO`h=tXk4Ye zAF2prV`K4tettk1a(1+}Y`WXhLT?bbxw+v{eQRr*jmb$5cUq&@m7uy^2Ul4aV*>p_ZSQzeL^l%u?OOFD^f;-r8K zZ4FG8PF=>Jj~*>2j~Z2n>`^||0yd`3KN4Hp2}AFJ(GaqTWCEG*o&8+icy$+Q0aE4@BLvgn+^Ti6(Mm{D|pFY9bj zN0yjzq5~1DfehvN-Zmj|0?{zJKW^iiPffNTzptjA2^>HCK; zvZ4n){4dM`*t7PsTKXoC*8G0;2K>g(H=df1G~mwLQwj#bP~oWwj6o1G;SnN(0{0LM z5DaK;56{y<848}K1DOGWq4(f12nGlS2nGm--dqlX0fGU70fOP(!KE-~FdWhfWfEit z$PADfATvN_c>5`b(H9^yKxTl<0GRnplSv2?xy)z3&GZx{_^K@=^slp~9S-JZ{u|Smy{7;G literal 0 HcmV?d00001 diff --git a/images/content/vision/position/pos_right.png b/images/content/vision/position/pos_right.png new file mode 100644 index 0000000000000000000000000000000000000000..bb8a46f45fb27824486e69f1d4f6452dca48a497 GIT binary patch literal 27613 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 paRJ5!xW9l$LgR6u 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 TxfJ|AFC=Wntl)#wrR)C<_ save -: Take a photo through the webcam, and save it to the provided location. +: Take a photo using the webcam, draw a box around the detected markers and save it to the provided location. ~~~~~ python from sr.robot3 import * @@ -55,7 +54,7 @@ robot.camera.save(robot.usbkey / "initial-view.png") ~~~~~ capture -: Take a photo through the webcam, and return the image data as an OpenCV array. +: Take a photo using the webcam, and return the image data as an OpenCV array. ~~~~~ python import cv2 @@ -64,15 +63,14 @@ robot = Robot() frame = robot.camera.capture() -# Flip the image with OpenCV -flipped = cv2.flip(frame, 0) +# Do some other vision algorithm with the OpenCV frame here ~~~~~ ### Frame argument The slowest part of vision is capturing the image. -You can use a frame with the other vision commands to avoid recapturing. +You can use the output of the `capture` method with the other vision commands to avoid recapturing. This may be useful if you wish to use both your own vision algorithms and our marker detection on the same frames. ~~~~~ python @@ -86,81 +84,67 @@ frame = robot.camera.capture() markers = robot.camera.see(frame=frame) # Save the frame with marker annotation -robot.camera.save("photo.jpg", frame=frame) +robot.camera.save(robot.usbkey / "photo.jpg", frame=frame) # Do some other vision algorithm with the OpenCV frame here ~~~~~ -### Field of View - -The Logitech C500 has a [field of view][fov] of 72° and the C270 has a field of view of 60°. - -[fov]: https://en.wikipedia.org/wiki/Field_of_view - -
-Note that the axes are all defined relative to the camera. -Since we have no way to know how you've mounted your camera, you may need to account for that in your usage of the vision system's data. -
- - ## Marker -A `Marker` object contains information about a *detected* marker. +A `Marker` object contains information about a detected marker. It has the following attributes: id : The id of the marker. size -: The physical size of the marker, as the vision system expects it. +: The physical size of the marker in millimetres, as the vision system expects it. pixel_centre -: A [`PixelCoordinates`](#pixelcoordinates) describing the position of the centre of the marker in the image. +: A [`PixelCoordinates`](#pixel-coordinates) object describing the position of the centre of the marker in the image. pixel_corners -: A list of 4 [`PixelCoordinates`](#pixelcoordinates) instances, each representing the position of a corner of the marker in the image. +: A list of 4 [`PixelCoordinates`](#pixel-coordinates) objects, each representing the position of a corner of the marker in the image. position -: A `Position` instance describing the position of the marker. +: A `Position` object describing the position of the marker. See the [Position page](./position) for detailed definitions and diagrams. distance : The distance between the camera and the centre of the marker, in millimetres. horizontal_angle - : Horizontal angle from the camera to the marker, in radians. + : Horizontal angle from the centre of the camera's view to the marker, in radians. Ranges -π to π. - Positive to the right. - Directly in front is 0. + Directly in front is zero, positive to the right. vertical_angle - : Vertical angle from the camera to the marker, in radians. + : Vertical angle from the centre of the camera's view to the marker, in radians. Ranges -π to π. - Positive values upwards. - Directly in front is 0. + Directly in front is zero, positive values upwards. orientation : An `Orientation` instance describing the orientation of the marker. See the [Orientation page](./orientation) for detailed definitions and diagrams. yaw - : The Yaw of the marker, a rotation about the vertical axis, in radians. + : The yaw of the marker, a rotation about the vertical axis, in radians. Positive values indicate a rotation clockwise from the perspective of the marker. Zero values have the marker facing the camera square-on. pitch - : The Pitch of the marker, a rotation about the transverse axis, in radians. + : The pitch of the marker, a rotation about the transverse axis, in radians. Positive values indicate a rotation upwards from the perspective of the marker. Zero values have the marker facing the camera square-on. roll - : The Roll of the marker, a rotation about the longitudinal axis, in radians. + : The roll of the marker, a rotation about the longitudinal axis, in radians. Positive values indicate a rotation clockwise from the perspective of the marker. Zero values have the marker facing the camera square-on. -### PixelCoordinates +### Pixel Coordinates A named tuple of `x` and `y` coordinates for the point, in pixels relative to the top left of the image. diff --git a/programming/vision/markers.md b/programming/vision/markers.md index 8bec106b..de555909 100644 --- a/programming/vision/markers.md +++ b/programming/vision/markers.md @@ -3,31 +3,44 @@ layout: page title: Markers --- -Markers -======= +# Markers An Example Marker: 0 -An example marker is given to the right; this one is `0`. The marker is the correct way up, as shown by the text in the bottom left corner. +An example marker is given to the right; this one is ID number `0`. +The marker is the correct way up, as shown by the text in the bottom left corner. -There is also some text in the bottom-left corner of the marker, in its padding: `Student Robotics APRILTAG_36H11 - Dev #0`. +There is also some text in the bottom-left corner of the marker, in its padding: `Student Robotics - #0`. -- `#0` means marker number 0 -- `APRILTAG_36H11` is the marker type -- `Dev` shows it's a development marker, rather than a competition marker +- `Student Robotics` means that it is one of our markers +- `#0` means that it is marker number 0 -Details of the types and size of markers used in the game can be found in the [rules](/docs/rules). +Details of the types and size of markers used in the game can be found in the [rules]({{ site.baseurl }}/rules). -You can download all the markers as a single [ZIP file](/docs/resources/2023/sr-markers-sr2023.zip) or individually from the [git repo](https://github.com/srobo/game-markers/tree/master/SR2023/markers). +You can download all the markers for this years game from the [resources page]({{ site.baseurl }}/competitor_resources/markers). -You must ensure that your PDF viewer is not resizing the documents when printing. -This can be checked by measuring the black marker and comparing this to the size defined in the rules. -If the printed marker is not the correct size then the distance information reported by the vision API will be wrong. +## Printing markers -Note that a different set of markers will be used in the arenas at the competition. -However, this is not something you need to worry about. -We will handle this for you automatically when your robot is started in competition mode. +The size of the printed markers is very important. +The vision system relies on the size of the marker to estimate its position. +If the marker is the wrong size the information returned by the API will be incorrect. -The white space around the markers is very important -- without it, the markers probably won't be recognised. This white border is needed to ensure there's enough contrast between the black marker and whatever it's attached to - its size isn't strictly important. The white space around the marker is the same size as one block within the marker, which is a good target for contrast. +When printing the markers you must ensure that your PDF viewer is not resizing the document. +This can be checked by measuring the black of the marker and comparing this to the size defined in the rules. -If the markers become damaged (scuff markers, tears, etc...) they will not function as well (if at all). If this happens, it is best to just print another one. +Once printed the marker should be mounted on a rigid backing material to keep the marker flat. +Any distortion in the marker will cause inaccuracies in the numbers reported by the API. + +If the markers become damaged (scuffs, tears, etc...) they will not function as well, if this happens, it is best to just print another one. + +## Marker detection + +An Example Marker: 0 + +All of the content within the grey line is important: + +- The white border is used to create contrast between the black of the marker and the background. +- The black border is used for [Pose Estimation](https://en.wikipedia.org/wiki/3D_pose_estimation) where the position of the four corners and the knowledge of the size of the marker is used to approximate its location in 3D space. +- The data bits in the centre are used to identify the ID of the marker and have enough polygons to not be confused with a real world object. + +The marker can be cut out up to the grey line. diff --git a/programming/vision/orientation.md b/programming/vision/orientation.md index 5bde483a..cd17ab34 100644 --- a/programming/vision/orientation.md +++ b/programming/vision/orientation.md @@ -13,17 +13,17 @@ The axis and rotations follow the [aircraft principal axis](https://en.wikipedia These properties can be accessed as follows: yaw -: A rotation about the vertical axis, in radians. +: The yaw of the marker, a rotation about the vertical axis, in radians. Positive values indicate a rotation clockwise from the perspective of the marker. Zero values have the marker facing the camera square-on. pitch -: A rotation about the transverse axis, in radians. +: The pitch of the marker, a rotation about the transverse axis, in radians. Positive values indicate a rotation upwards from the perspective of the marker. Zero values have the marker facing the camera square-on. roll -: A rotation about the longitudinal axis, in radians. +: The roll of the marker, a rotation about the longitudinal axis, in radians. Positive values indicate a rotation clockwise from the perspective of the marker. Zero values have the marker facing the camera square-on. diff --git a/programming/vision/position.md b/programming/vision/position.md index 5fee2655..caf393da 100644 --- a/programming/vision/position.md +++ b/programming/vision/position.md @@ -3,8 +3,41 @@ layout: page title: Position --- -Position -======== +# Position -TODO +Position represents the location of a marker in 3D space, relative to the camera. +These properties can be accessed as follows: + +distance +: The distance between the camera and the centre of the marker, in millimetres. + +horizontal_angle +: Horizontal angle from the centre of the camera's view to the marker, in radians. + Ranges -π to π. + Directly in front is zero, positive to the right. + +vertical_angle +: Vertical angle from the centre of the camera's view to the marker, in radians. + Ranges -π to π. + Directly in front is zero, positive values upwards. + +~~~~~ python +markers = robot.camera.see() + +for marker in markers: + print(marker.position.distance) + print(marker.position.horizontal_angle) + print(marker.position.vertical_angle) +~~~~~ + + +## Examples + +|horizontal_angle|vertical_angle|| +|-----|-----|---| +|0 |0 |![Position centre]({{ site.baseurl }}/images/content/vision/position/pos_centre.png "Position centre")| +|0 |0.12 |![Position up]({{ site.baseurl }}/images/content/vision/position/pos_up.png "Position up")| +|0.12 |0 |![Position right]({{ site.baseurl }}/images/content/vision/position/pos_right.png "Position right")| +|0 |-0.12|![Position down]({{ site.baseurl }}/images/content/vision/position/pos_down.png "Position down")| +|-0.12|0 |![Position left]({{ site.baseurl }}/images/content/vision/position/pos_left.png "Position left")| \ No newline at end of file From bac4f7c89eff37222a4dbb320f6925cf77f64532 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 4 Sep 2023 22:44:14 +0100 Subject: [PATCH 41/81] review tweaks --- programming/vision/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programming/vision/index.md b/programming/vision/index.md index 5edfb1e5..9baf5f77 100644 --- a/programming/vision/index.md +++ b/programming/vision/index.md @@ -69,7 +69,7 @@ frame = robot.camera.capture() ### Frame argument -The slowest part of vision is capturing the image. +The slowest part of marker detection is capturing the image. You can use the output of the `capture` method with the other vision commands to avoid recapturing. This may be useful if you wish to use both your own vision algorithms and our marker detection on the same frames. From 964723500ecc278f507a6de511374b5b4765514a Mon Sep 17 00:00:00 2001 From: JoshP Date: Wed, 6 Sep 2023 21:58:26 +0100 Subject: [PATCH 42/81] Fix vision code typo --- programming/vision/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/programming/vision/index.md b/programming/vision/index.md index 9baf5f77..f73bcb6b 100644 --- a/programming/vision/index.md +++ b/programming/vision/index.md @@ -34,7 +34,10 @@ while True: print("I can see", len(markers), "markers:") for marker in markers: - print(" - Marker #{0} is {1} metres away".format(marker.id, marker.distance / 1000)) + print("Marker #{0} is {1} metres away".format( + marker.id, + marker.position.distance / 1000, + )) ~~~~~
From a045a997bfbec274eea5bb683ff0709f4d5ab790 Mon Sep 17 00:00:00 2001 From: JoshP Date: Thu, 7 Sep 2023 19:29:39 +0100 Subject: [PATCH 43/81] Update top level kit page --- _data/sidebar_tree.yaml | 2 +- kit/index.md | 68 ++++++++++++++++++----------------------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index 39f993cd..9f8108c5 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -4,7 +4,7 @@ tree: - url: / title: Introduction - url: /kit/ - title: Kits + title: Kit tree: - url: /kit/batteries/ title: Batteries diff --git a/kit/index.md b/kit/index.md index 0620d276..3c08106b 100644 --- a/kit/index.md +++ b/kit/index.md @@ -3,13 +3,13 @@ layout: page title: Kit --- -Kit -=== +# Kit The Student Robotics kit is lent, free of charge, to each team and consists of various modules designed by us along with some ancillary parts to allow easy use of the modules. +All of the kit, except the wire, needs to be returned after the competition. -The Modules ------------ + +## The Modules We provide modules, known as 'boards', in each kit. These modules can be plugged together and as a whole form the basis for controlling your robot. @@ -18,39 +18,31 @@ The boards provided are as follows; more details can be found on their individua * [Brain Board](/docs/kit/brain_board) * [Power Board](/docs/kit/power_board) * [Motor Board x2](/docs/kit/motor_board) - * [Ruggeduino (including two screw shields)](/docs/kit/arduino) + * [Arduino (including two screw shields)](/docs/kit/arduino) * [Servo Board](/docs/kit/servo_board) -Ancillary Parts ---------------- - -As well as the aforementioned boards the kit also contains the following items. - -| Part | Qty | Specification | Part Number(s) | Notes -|------------------------|-----|-----------------------------------------------------------------------|---------------------------------------------|------ -| Battery Charger Supply | 1 | 12V 5A | N/A | -| Battery Charger | 1 | iMAX B6 or HobbyKing E4 | N/A | Documentation is available on [charging the batteries](/docs/kit/batteries) using this -| USB Hub | 2 | 7-Port | [StarTech ST7202USBGB][ST7202USBGB] | The hub does *not* require a power cable -| USB Memory Stick | 1 | Kingston DataTraveler 16GB SE9H | [DTSE9H/16GB][DTSE9H-16GB] | -| Standard USB cable | 3 | Standard USB A to B connector | N/A | For connecting the ruggeduino (via a hub) and USB hubs to the Brain Board -| Micro USB cable | 5 | Standard USB A to Micro USB B | N/A | For connecting the motor boards, servo boards and power board to the Brain Board -| Coloured Power Wire | 1 | | | To connect power from the power board to the motor/servo boards -| Black Power Wire | 1 | | | To connect power from the power board to the motor/servo boards -| CamCon | 10 | 2 way
7.5mm
12A | [Farnell 3882275][F-3882275] | To connect 12V from the power board to the motor and servo boards -| MiniCamCon | 7 | 2 way
5mm
12A | [Farnell 3881854][F-3881854] | To connect motors to the motor boards, and to connect an external power switch -| MicroCamCon | 1 | 2 way
3.81mm
12A | [Farnell 1717047][F-1717047] | To connect a 5V component to the power board or to connect an external start button -| USB Webcam | 1 | Either Logitech C500 or Logitech C270 | [Ebuyer 230435][EB-230435] | -| Battery (LiPo) | 2 | 11.1V 2.2Ah Lithium Polymer | N/A | Please read the [documentation](/docs/kit/batteries) for storage and usage information -| Battery Bag | 1 | HPI Plazma Pouch LiPo Safe Bag (18×22cm) or Overlander LiPo Safe Sack | [HPI 101289][HPI-101289] [0000153][0000153] | Batteries should always be stored in the battery bag -| Screwdriver | 1 | Duratool Precision 2.5mm Slot Screwdriver | [Farnell 2103271][F-2103271] | - -[ST7202USBGB]: https://uk.startech.com/Cards-Adapters/USB-2/Hub/7-Port-USB-20-Hub-UK~ST7202USBGB -[F-3882275]: https://uk.farnell.com/3882275 -[F-3881854]: https://uk.farnell.com/3881854 -[F-1717047]: https://uk.farnell.com/1717047 -[F-2103271]: https://uk.farnell.com/2103271 -[RS-748-2042]: https://uk.rs-online.com/web/p/products/748-2042 -[EB-230435]: https://www.ebuyer.com/230435-logitech-c270-hd-webcam-720p-hd-video-960-000582 -[0000153]: https://www.modelsport.co.uk/overlander-lipo-safe-sack/rc-car-products/38313 -[HPI-101289]: https://www.modelsport.co.uk/hpi-plazma-pouch-lipo-safe-bag-18x22cm-/rc-car-products/39499 -[DTSE9H-16GB]: https://www.amazon.co.uk/dp/B006YBARCA + +## Ancillary Parts + +As well as the boards the kit also contains the following items. + +| Part | Qty | Specification | Notes +|------------------------|-----|----------------------------------------------------|------- +| Battery Charger Supply | 1 | 12V 5A | For use with the battery charger +| Battery Charger | 1 | iMAX B6 or HobbyKing E4 | Documentation is available on [charging the batteries](/docs/kit/batteries) using this +| USB Hub | 3 | 7-Port | The hub does *not* require a power cable +| USB Memory Stick | 1 | | +| USB A to B cable | 3 | Standard USB A to B connector | For connecting the Arduino and USB hubs to the Brain Board +| Micro USB cable | 5 | Standard USB A to Micro USB B | For connecting the Power Board, Servo Board and Motor Boards to the Brain Board +| Wire | | | An assortment of wire for connecting boards together +| CamCon | 10 | CTB9400/2 connector
2 way, 7.5mm pitch, 12A | To connect 12V from the power board to the motor and servo boards ([CPC CN12236][CPC-CN12236]) +| MiniCamCon | 7 | CTB9200/2A connector
2 way, 5mm pitch, 12A | To connect motors to the motor boards, and to connect an external power switch ([CPC CN02129][CPC-CN02129]) +| MicroCamCon | 1 | CTB92HE/2 connector
2 way, 3.81mm pitch, 12A | To connect a 5V component to the power board or to connect an external start button ([CPC CN11584][CPC-CN11584]) +| USB Webcam | 1 | Either Logitech C500 or Logitech C270 | +| Battery (LiPo) | 2 | 11.1V 2.2Ah Lithium Polymer | Please read the [documentation](/docs/kit/batteries) for storage and usage information +| Battery Bag | 1 | LiPo Safe Bag (18×22cm) | Batteries should always be stored in the battery bag +| Screwdriver | 1 | 2.5mm Slot Screwdriver | + +[CPC-CN12236]: https://cpc.farnell.com/camdenboss/ctb9400-2/terminal-block-plug-in-2way/dp/CN12236 +[CPC-CN02129]: https://cpc.farnell.com/camdenboss/ctb9200-2a/free-terminal-2way/dp/CN02129 +[CPC-CN11584]: https://cpc.farnell.com/camdenboss/ctb92he-2/terminal-block-3-81mm-2-pole/dp/CN11584 From f14575ec430a268b3439d44e4f8c2bee3df6a8cf Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 13:48:31 +0100 Subject: [PATCH 44/81] Update intro page --- index.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/index.md b/index.md index 993182dc..f8f54d19 100644 --- a/index.md +++ b/index.md @@ -6,14 +6,13 @@ title: Documentation Introduction ============ -There are a number of sections in the documentation, - offering help for the [kit](/docs/kit/) and [programming](/docs/programming/). -Under the [tutorials](/docs/tutorials/) section, - a number of these things are combined to help you understand what you can, - or need, to do. - Navigation of the documentation can be done using the column to the left, - where everything is arranged alphabetically in the aforementioned sub-sections. +This documentation explains how to use the kit and the robot's Python API. +The information is spread across multiple sections: +- The [kit](/docs/kit/) section will give you an overview of the physical kit that is provided to your team, how to connect to each board and what features they have. +- The [programming](/docs/programming/) section talks through how to write code that interacts with all the boards. +- The [rules](/docs/rules/) section is important as it talks through the aim of this year's game and what task you are trying to achieve. +- [Tutorials](/docs/tutorials/) are a series of guides that will help you get started. Within this documentation, you will come across a number of boxes like this: @@ -21,15 +20,19 @@ Within this documentation, you will come across a number of boxes like this: # code example ~~~~~ -These are code examples provided to help you. +These are pieces of example code that you can run on your robot. -From time to time, - you may come across some warnings such as the following: +From time to time, you may come across some warnings such as the following: -
Charge Your Batteries!
+
Never leave batteries unattended when they are in use or charging.
-It would be advisable to take note of these, - especially that one! You will also come across some blue boxes providing information, - similar to the following: +It would be advisable to take note of these, especially that one! -
Some useful information... like the information given in the information box above.
+You will also come across some blue boxes providing key bits of information, similar to the following: + +
+Taking images while moving will cause them to be blurry, which will cause marker detection to fail. +Try pausing movement while taking an image. +
+ +These contain useful tips that will help you when building your robot. From 9ddbebeda045c57a2d65da95e0a0f14bb3ea3e7a Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 13:56:42 +0100 Subject: [PATCH 45/81] Update warning box to be less passive --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index f8f54d19..dd27b962 100644 --- a/index.md +++ b/index.md @@ -26,7 +26,7 @@ From time to time, you may come across some warnings such as the following:
Never leave batteries unattended when they are in use or charging.
-It would be advisable to take note of these, especially that one! +You should take note of these, they often contain important safety information. You will also come across some blue boxes providing key bits of information, similar to the following: From 0b09b9a7f49b83c396668bfb06361ed35fff0b46 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 18:21:54 +0100 Subject: [PATCH 46/81] Update robot API page - Add comp mode page --- programming/robot_api/comp_mode.md | 35 ++++++++- programming/robot_api/index.md | 119 ++++++++++++++--------------- 2 files changed, 90 insertions(+), 64 deletions(-) diff --git a/programming/robot_api/comp_mode.md b/programming/robot_api/comp_mode.md index fed92fb6..bcdb4a8f 100644 --- a/programming/robot_api/comp_mode.md +++ b/programming/robot_api/comp_mode.md @@ -5,4 +5,37 @@ title: Competition Mode # Competition Mode -TODO \ No newline at end of file + +## What is Comp Mode + +During a competition match a Competition Mode USB is inserted into a spare USB port on your robot (See the [kit assembly guide]({{ site.baseurl }}/tutorials/assembly) for details on the spare USB port you need to leave). +This will instruct your robot that it is in competition mode and will enable a game timeout, disable the WiFi and update certain `robot` attributes. + + +## Effects of Comp Mode + +* Enabling Game timeout + + When in competition mode the robot will automatically stop at the end of a match, where the duration of a match is defined in the [rules]({{ site.baseurl }}/tutorials/assembly). + The duration of the match is defined from when the start button is pressed. + +* Disabling of WiFi and Web interface + + During competition matches remote control of robots is forbidden ([rules]({{ site.baseurl }}/tutorials/assembly)), so the WiFi and web interface are disabled to ensure a fair game. + +* Updating of match-specific `robot` attributes + + Certain robot attributes will change when in comp mode, these can be used to change your robot's behaviour. + The affected attributes are: + + mode + : Whether the robot is running in competition mode. + When in a competition match, this will be `COMP`, and at all other times this will be `DEV`. + + zone + : The number of the scoring zone that the robot is associated with. + Between `0` and `3`. + + The zone you are in defines which arena markers are near your scoring zone. + You can use the knowledge of your zone to compensate for this, so your robot behaves correctly in all starting positions. + See the [rules]({{ site.baseurl }}/tutorials/assembly) for where the scoring zones and arena markers are positioned. diff --git a/programming/robot_api/index.md b/programming/robot_api/index.md index c099b96f..74ca744a 100644 --- a/programming/robot_api/index.md +++ b/programming/robot_api/index.md @@ -3,126 +3,119 @@ layout: page title: Robot API --- -Robot API -========= +# Robot API Student Robotics has written a module — `sr.robot3` — which is used to interface with the hardware. It handles all the low-level interactions so you don't have to. -To set the output power of output 0 of the first motor board to -30%, for example, you would simply write: -~~~~~ python -R.motor_board.motors[0].power = -0.3 -~~~~~ - -`-0.3` would be backwards (depending upon which way you wired up the motor) — 30% power in reverse. - -To gain access to all of this functionality, all you need to do is write: +For example, to set the power of output 0 on a Motor Board to 30%, you would simply write: ~~~~~ python -from sr.robot3 import * +robot.motor_board.motors[0].power = 0.3 ~~~~~ -...at the top of your code (before you use any of its functionality, basically). -This imports the Student Robotics module that we've written to interface with our hardware. +
+See the [Motor Board]({{ site.baseurl }}/programming/motors) page for more details about this example. +
-Then, within the `sr.robot3` module, there is a `Robot` class that should be instantiated, as follows: +To gain access to all of this functionality, all you need to do at the top of your code is the following: ~~~~~ python from sr.robot3 import * -R = Robot() +robot = Robot() ~~~~~ -Within your `Robot` (`R` in this case), you then have access to the following attributes: +This imports the Student Robotics module that we've written to interface with our hardware. +We then use the `Robot` class from within the `sr.robot3` module, to create a `robot` object that sets up and gives you access to your robot's hardware. -* [motors](/docs/programming/motors) -* [power](/docs/programming/power) -* [servos](/docs/programming/servos) -* [ruggeduinos](/docs/programming/arduino/) -* [vision](/docs/programming/vision/) +
+Most examples in the documentation will assume you have created a `robot` object from the `Robot` class. +If you see `robot` in a code example, it is assumed you have run the two lines above. +
-They can be used in your code just like the example above. -Note that `motors`, `ruggeduinos`, and `servos` are Python lists, and so should be accessed as such. -Here are some examples: +Then, within your `robot` you can use the following attributes to access to the robot's hardware: -~~~~~ python -R.motor_board.motors[0].power = 0.5 # WILL work, if motor 0 exists -R.motor_board.motors[1].power = -0.2 # WILL work, if motor 1 exists -R.motor_board.motors.power = 0.42 # WON'T WORK +* [kch]({{ site.baseurl }}/programming/leds) +* [motor_board]({{ site.baseurl }}/programming/motors) +* [power_board]({{ site.baseurl }}/programming/power) +* [servo_board]({{ site.baseurl }}/programming/servos) +* [arduino]({{ site.baseurl }}/programming/arduino/) +* [camera]({{ site.baseurl }}/programming/vision/) -# the above is similar to the situation for 'ruggeduinos' and 'servos' -~~~~~ +The functions of each board are described on their respective pages. -A number of examples in the documentation will assume you've instantiated the required `Robot` class and have called it `R`. -From here in, if you see a `R.something`, the requirement of the `sr.robot3` import line and the instantiation of `Robot` as `R` is implicit. -[Other Robot Attributes](#OtherRobotAttributes) {#OtherRobotAttributes} ----------------------- +## Other Robot Attributes -As well as the attributes listed above, the Robot class also has the following attributes, which you may find useful: +As well as the attributes listed above, the `Robot` class also has the following attributes, which you may find useful: zone -: The number of the zone that the robot is associated with. Between `0` and `3`. +: The number of the scoring zone that the robot is associated with. + Between `0` and `3`. + + This attribute is only available after the start button is pressed and will throw an error if accessed before. + See the [competition mode](./comp_mode) page for more information about this attribute. mode : Whether the robot is running in competition mode. - When in a competition match, this will be `RobotMode.COMP`, and at all other times this will be `RobotMode.DEV`. + When in a competition match, this will be `COMP`, and at all other times this will be `DEV`. + + This attribute is only available after the start button is pressed and will throw an error if accessed before. + See the [competition mode](./comp_mode) page for more information about this attribute. ~~~~~ python from sr.robot3 import * - R = Robot() + robot = Robot() - if R.mode == RobotMode.COMP: - print("This is the competition!") + if robot.mode == COMP: + print("This is the competition!") + elif robot.mode == DEV: + print("This is development") ~~~~~ usbkey -: The path to the USB memory stick. - Your code is unzipped and run from a temporary directory, therefore files you create will be lost when the kit is turned off. - You can use this to easily read from and write to files on the stick itself. - Note that the USB memory stick is mounted synchronously, so any writes to it will block until complete and may slow down your code. +: The path to the USB stick. + You can use this to easily read and write files on the USB stick itself. - An example of how the `usbkey` attribute might be used: + An example of how the `usbkey` attribute might be used to read a file called `my-file.txt` which is stored on the USB stick: ~~~~~ python from sr.robot3 import * import os - R = Robot() - print("The path to the USB key is:", R.usbkey) + robot = Robot() + print("The path to the USB stick is:", robot.usbkey) print("My file on the USB contains:") - with open(os.path.join(R.usbkey, 'my-file.txt'), 'r') as f: - print(f.read()) + with open(os.path.join(robot.usbkey, 'my-file.txt')) as file: + print(file.read()) ~~~~~ is_simulated : A boolean value indicating whether or not the code is running in the simulator. + This value is `True` when in the simulator and `False` when on the robot. + -[Custom Robot Object Initialisation](#CustomRobotInit) {#CustomRobotInit} ----------------------- +## Custom Robot Object Initialisation Normally the Robot object is initialised with the following: ~~~~~ python -R = Robot() +from sr.robot3 import * +robot = Robot() ~~~~~ -However if you want to: - - * customise your Ruggeduino firmware - * initialise some hardware or software before the start button is pressed - -Then Robot initialisation can be broken up as follows (this example is equivalent to the previous code excerpt): +By default your robot will pause on this line waiting for the start button to be pressed. +However if you want to initialise some hardware or software before the start button is pressed then Robot initialisation can be broken up as follows. ~~~~~ python -R = Robot(auto_start=True) +from sr.robot3 import * +robot = Robot(wait_for_start=False) # Initialisation phase. # Here you can perform hardware/software initialisation before start -R.wait_start() +robot.wait_start() ~~~~~ -During the initialisation phase, all hardware is accessible. -If you have any hardware which must be initialised before the start button is pressed, - the initialisation phase is the time to do so. +This will not pause on the line which creates the `robot` but will instead pause on the `robot.wait_start()` line, until the start button is pressed. From 7d76b112f73ce947d2dff2bdc16fee93895ac5cf Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 18:28:12 +0100 Subject: [PATCH 47/81] remove references to print wifi creds --- kit/brain_board/wifi.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kit/brain_board/wifi.md b/kit/brain_board/wifi.md index df5f840e..2c502435 100644 --- a/kit/brain_board/wifi.md +++ b/kit/brain_board/wifi.md @@ -23,11 +23,6 @@ If there is no `robot-settings.toml` on your USB drive, you can generate one by For more information see our docs on [Robot Settings]({{ site.baseurl }}/kit/brain_board#robot-settings).
-These details can also be printed using: -~~~~~ python -robot.print_wifi_details() -~~~~~ - If you are having any problems connecting to your robot, just head on over to the Discord and ask for help. From ff294f7c859a73193c7b58ddce69206f91efb205 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 22:25:43 +0100 Subject: [PATCH 48/81] Update page on python libraries --- kit/brain_board/python_libraries.md | 80 ++++++++++------------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/kit/brain_board/python_libraries.md b/kit/brain_board/python_libraries.md index c4acf4bb..c76cb2a0 100644 --- a/kit/brain_board/python_libraries.md +++ b/kit/brain_board/python_libraries.md @@ -3,74 +3,46 @@ layout: page title: Available Python Libraries --- + + + # Available Python Libraries +If you wish to use a library that isn't in the list, get in contact with us on Discord and have a chat with us about it. + +
+Note that for local development in the simulator you will need to install the libraries yourself. +Look at the guide on [setting up the simulator]({{ site.baseurl }}/tutorials/setting_up_simulator) to find out how to do this. +
+ ## Robot Kit The following python libraries are installed and available for use in your robot's software: -* [april-vision 1.0.2](https://pypi.org/project/april-vision) -* [astoria 0.11.1](https://pypi.org/project/astoria) -* [cached-property 1.5.2](https://pypi.org/project/cached-property) -* [click 8.1.2](https://pypi.org/project/click) -* [cycler 0.11.0](https://pypi.org/project/cycler) -* [dateutil 2.8.2](https://pypi.org/project/python-dateutil) -* [debugpy 1.6.3](https://pypi.org/project/debugpy) -* [dbus-next 0.2.3](https://pypi.org/project/dbus-next) -* [docopt 0.6.2](https://pypi.org/project/docopt) -* [gmqtt 0.6.11](https://pypi.org/project/gmqtt) -* [gpg 1.17.1](https://pypi.org/project/gpg) -* [iniparse 0.5](https://pypi.org/project/iniparse) -* [j5 1.1.2](https://pypi.org/project/j5) -* [kchd 0.4.1](https://pypi.org/project/kchd) -* [matplotlib 3.5.1](https://pypi.org/project/matplotlib) -* [numpy 1.22.3](https://pypi.org/project/numpy) -* opencv 4.5.5 -* [paho-mqtt 1.6.1](https://pypi.org/project/paho-mqtt) -* [pandas 1.4.2](https://pypi.org/project/pandas) -* [Pillow 9.0.1](https://pypi.org/project/Pillow) -* [prompt-toolkit 3.0.24](https://pypi.org/project/prompt-toolkit) -* [pyapriltags 3.3.0.post2](https://pypi.org/project/pyapriltags) -* [pydantic 1.9.1](https://pypi.org/project/pydantic) -* [PyGObject 3.42.0](https://pypi.org/project/PyGObject) -* [pyparsing 3.0.7](https://pypi.org/project/pyparsing) -* [pyquaternion 0.9.9](https://pypi.org/project/pyquaternion) -* [pyserial 3.5](https://pypi.org/project/pyserial) -* [pytz 2022.1](https://pypi.org/project/pytz) -* [pyudev 0.23.2](https://pypi.org/project/pyudev) -* [pyusb 1.2.1](https://pypi.org/project/pyusb) -* [pyyaml 6.0](https://pypi.org/project/PyYAML) -* [RPi.GPIO 0.7.0](https://pypi.org/project/rpi.gpio) -* [rtui 0.1.1](https://pypi.org/project/rtui) -* [setuptools 59.5.0](https://pypi.org/project/setuptools) -* [six 1.16.0](https://pypi.org/project/six) -* [sr.robot3 2023.2.0](https://pypi.org/project/sr.robot3) -* [tomli 2.0.1](https://pypi.org/project/tomli) -* [tomli-w 1.0.0](https://pypi.org/project/tomli-w) -* [typing-extensions 3.10.0.0](https://pypi.org/project/typing-extensions) -* [udiskie 2.3.3](https://pypi.org/project/udiskie) +* [debugpy 1.7.0](https://pypi.org/project/debugpy) +* [flask 2.3.3](https://pypi.org/project/flask) +* [matplotlib 3.7.2](https://pypi.org/project/matplotlib) +* [networkx 3.1](https://pypi.org/project/networkx) +* [numpy 1.24.4](https://pypi.org/project/numpy) +* [opencv-python-headless 4.8.0.76](https://pypi.org/project/opencv-python-headless) +* [pandas 2.0.3](https://pypi.org/project/pandas) +* [pillow 10.0.0](https://pypi.org/project/pillow) +* [scikit-learn 1.3.0](https://pypi.org/project/scikit-learn) +* [scipy 1.10.1](https://pypi.org/project/scipy) +* [shapely 2.0.1](https://pypi.org/project/shapely) +* [sr-robot3 2024.0.0](https://pypi.org/project/sr-robot3) -If you wish to use a library that isn't listed above, get in contact with us on Discord and have a chat with us about it. +## Simulator differences -## Simulator +Generally we will try to keep the libraries available in the simulator the same as what is available on the physical robot. +The list below outlines the differences. -The following libraries will be available to your robot code during the -competition. Note that for local development you will need to install these -yourself. - - - -* [matplotlib 3.5.1](https://pypi.org/project/matplotlib/3.5.1/) -* [numpy 1.22.3](https://pypi.org/project/numpy/1.22.3/) -* [pandas 1.4.2](https://pypi.org/project/pandas/1.4.2/) -* [typing-extensions 3.10.0.0](https://pypi.org/project/typing-extensions/3.10.0.0/) +* `flask` is not available in the simulator -If there are other libraries you would like included, please get in contact with -us on Discord and have a chat with us about it. From 20f052f6f07f8a05140a10607fc67a0777b31ded Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 22:30:44 +0100 Subject: [PATCH 49/81] Fix broken link on simulator page --- tutorials/setting_up_simulator.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tutorials/setting_up_simulator.md b/tutorials/setting_up_simulator.md index 8421cb06..0ad4832b 100644 --- a/tutorials/setting_up_simulator.md +++ b/tutorials/setting_up_simulator.md @@ -24,9 +24,8 @@ You will also need Python installed. | macOS | 3.8-3.10 | | Linux | 3.8-3.10 | -There are a small number of [external libraries]({{ site.baseurl }}/kit/brain_board/python_libraries#simulator) -which will be available to your robot code during the competition. Note that for -local development you will need to install these yourself. +There are a small number of [external libraries]({{ site.baseurl }}/kit/brain_board/python_libraries) which will be available to your robot code during the competition. +Note that for local development you will need to install these yourself. ### Installing the simulation From df73f21a245264a6dc3a9ca4690641c74eaf1c89 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 22:34:44 +0100 Subject: [PATCH 50/81] opencv is not in the simulator --- kit/brain_board/python_libraries.md | 1 + 1 file changed, 1 insertion(+) diff --git a/kit/brain_board/python_libraries.md b/kit/brain_board/python_libraries.md index c76cb2a0..563dc206 100644 --- a/kit/brain_board/python_libraries.md +++ b/kit/brain_board/python_libraries.md @@ -44,5 +44,6 @@ The list below outlines the differences. * `flask` is not available in the simulator +* `opencv` is not available in the simulator From 52c1b8176b1da25c8dd93cccc574664604c595dd Mon Sep 17 00:00:00 2001 From: Peter Law Date: Sat, 9 Sep 2023 22:47:18 +0100 Subject: [PATCH 51/81] Update Python & Webots versions the simulator uses This deliberately breaks the link to the simulation files as we have not yet released a version for SR2024, so any link would be incorrect relative to the documented versions. --- tutorials/setting_up_simulator.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tutorials/setting_up_simulator.md b/tutorials/setting_up_simulator.md index 8421cb06..3066313d 100644 --- a/tutorials/setting_up_simulator.md +++ b/tutorials/setting_up_simulator.md @@ -11,8 +11,7 @@ Setting up the simulator You need to download and install [Webots](https://cyberbotics.com/#download) (the download is around 300MB). This is the platform we run our simulation in. -Versions "R2022b" and "R2023a" of Webots are supported. -*Note*: versions of the simulation prior to sr2023.3 did not support Webots "R2023a". +Version "R2023b" of Webots is supported. #### Python Version @@ -20,9 +19,9 @@ You will also need Python installed. | Platform | Supported Python Version | |----------|--------------------------| -| Windows | 3.8-3.10 (64-bit) | -| macOS | 3.8-3.10 | -| Linux | 3.8-3.10 | +| Windows | 3.8-3.11 (64-bit) | +| macOS | 3.8-3.11 | +| Linux | 3.8-3.11 | There are a small number of [external libraries]({{ site.baseurl }}/kit/brain_board/python_libraries#simulator) which will be available to your robot code during the competition. Note that for @@ -31,7 +30,7 @@ local development you will need to install these yourself. ### Installing the simulation 1. Create a directory, perhaps called `simulation` where you will store your robot code. -2. [Download the simulation](https://github.com/srobo/competition-simulator/releases/download/sr2023.5/competition-simulator-sr2023.5.zip), the latest version is sr2023.5, released on 2023-02-22. +2. [Download the simulation](https://github.com/srobo/competition-simulator/releases/download/TODO/competition-simulator-TODO.zip), the latest version is TODO, released on TODO. 3. Unzip the simulation as a subdirectory of the directory you created in the first step: ``` simulation @@ -68,10 +67,10 @@ print(sys.executable) On Windows you can set the path to the Python version to use in Webots UI via **Tools** → **Preferences** → **General** → **Python command**. -Your Python path is likely similar to `C:\Users\\AppData\Local\Programs\Python\Python39\python.exe` when using Python 3.9, where `` is your login. +Your Python path is likely similar to `C:\Users\\AppData\Local\Programs\Python\Python311\python.exe` when using Python 3.11, where `` is your login. On Mac you can set the path to the Python version to use via **Webots** → **Preferences** ,. -Your Python path is likely similar to `/Library/Frameworks/Python.framework/Versions/3.9/bin/python3` when using Python 3.9. +Your Python path is likely similar to `/Library/Frameworks/Python.framework/Versions/3.11/bin/python3` when using Python 3.11. You'll need to ensure a matching version of Python is installed. If you're still having problems, ask for help in [`#simulator-help`][simulator-help] in From c850300e6bf4cf020d0e8ff4299cfc74e7eb29a6 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 9 Sep 2023 23:05:35 +0100 Subject: [PATCH 52/81] Review comments --- kit/brain_board/python_libraries.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kit/brain_board/python_libraries.md b/kit/brain_board/python_libraries.md index 563dc206..e4c9f263 100644 --- a/kit/brain_board/python_libraries.md +++ b/kit/brain_board/python_libraries.md @@ -4,8 +4,8 @@ title: Available Python Libraries --- - - + + # Available Python Libraries @@ -43,7 +43,9 @@ Generally we will try to keep the libraries available in the simulator the same The list below outlines the differences. +* `debugpy` is not available in the simulator * `flask` is not available in the simulator * `opencv` is not available in the simulator +* `sr-robot3` that comes with the simulator has the same API but is different to the one available on pypi From 342601444dc37e63096d42387470e738bfa67acb Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 10 Sep 2023 18:43:59 +0100 Subject: [PATCH 53/81] First parse at updating arduino API pages --- .spelling | 2 + _data/sidebar_tree.yaml | 4 + programming/arduino/custom_firmware.md | 174 ++++++++---------- programming/arduino/extended_sr_firmware.md | 77 ++++++++ programming/arduino/index.md | 141 ++------------ programming/arduino/sr_firmware.md | 130 +++++++++++++ .../kit/{ruggeduino-fw.ino => arduino-fw.ino} | 4 +- 7 files changed, 304 insertions(+), 228 deletions(-) create mode 100644 programming/arduino/extended_sr_firmware.md create mode 100644 programming/arduino/sr_firmware.md rename resources/kit/{ruggeduino-fw.ino => arduino-fw.ino} (96%) diff --git a/.spelling b/.spelling index 3a61a260..a45a7cd5 100644 --- a/.spelling +++ b/.spelling @@ -16,6 +16,8 @@ Plazma PULLUP Quarternion rtui +Arduino +Arduinos Ruggeduino Ruggeduinos Scarzy diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index 9f8108c5..fec1f323 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -60,6 +60,10 @@ tree: - url: /programming/arduino/ title: Arduino API tree: + - url: /programming/arduino/sr_firmware + title: SR Firmware + - url: /programming/arduino/extended_sr_firmware + title: Extended SR Firmware - url: /programming/arduino/custom_firmware title: Custom Firmware - url: /programming/cheat_sheet diff --git a/programming/arduino/custom_firmware.md b/programming/arduino/custom_firmware.md index 4fd43193..910badf9 100644 --- a/programming/arduino/custom_firmware.md +++ b/programming/arduino/custom_firmware.md @@ -3,148 +3,124 @@ layout: page title: Arduino custom firmware --- -Arduino custom firmware -======================= +# Arduino with custom firmware -
-This documentation refers to a feature which is only available on the physical robot kits. +
+On this page we talk specifically about Arduinos. +However this page is applicable to opening any device that shows up as a serial port.
-The Ruggeduino that came as part of your kit was shipped with a firmware that provides the functionality outlined in the [Ruggeduino](/docs/programming/arduino) page. -You may wish to extend the functionality of this firmware, or completely replace it. -The `sr.robot3` library provides support for three Ruggeduino firmware scenarios: - 1. Default SR firmware - 2. [Extended SR firmware](#extension): Firmwares that add commands to the default SR firmware. - 3. [Completely custom](#completely): Any firmware not derived from the SR firmware. +## Ignoring a device -By default, the [`sr.robot3`](/docs/programming/robot_api/) library assumes that all connected Ruggeduinos are running the SR firmware -or firmware which is compatible with the SR Ruggeduino firmware. -If you're using completely custom firmware, you'll need to tell the kit to ignore the ruggeduino so that you're able to define your own setup logic. +By default when the `robot` object is created it will try to communicate with all Arduinos and will expect them to respond in the way the SR firmware does. +If you want the API to not try connecting to a device we need to *ignore* the Arduino. -[Extension of the SR firmware](#extension) {#extension} ------------------------------- +To configure a `Robot` object to ignore a Arduino with custom firmware, you will need to provide it with the Arduino's serial number. +The Arduino serial number is a character string of numbers and letters, and is output in the robot log when you run a program on your robot with your +Arduino connected. -
-Because the API sets all pins to inputs when the robot is initialised, you cannot set pin modes in the setup function as these will be overridden. -In order to setup pins you can either create a function on the ruggeduino that you call after your robot is initialised or use the [Python functions](/docs/programming/arduino/#pin-modes) to setup the pins. -
- -You may wish to extend the SR firmware with additional functionality. -This will allow you to continue using the commands already provided by the SR firmware (e.g. `digital_read()`), - which means any existing robot code you have won't need modifying very much. -When you extend the SR firmware, you'll be adding at least one new command to the firmware. -There are almost limitless possibilities of what your commands may do, but here are some examples to give you an idea: - - * Talk to an SPI or I2C sensor. - * Read N input pins at the same instant in time. - * Time pulses received from an ultrasound sensor. - -There are three steps that you will need to go through to implement and use your custom commands: - -### Step 1: Add your command to the Ruggeduino firmware - -To extend the SR firmware, you will need to first download its [source code]({{ site.baseurl }}/resources/kit/ruggeduino-fw.ino), and edit it in the Arduino IDE. -When the SR ruggeduino python library wants the ruggeduino to run a command, it sends it a single character to tell it which command to run. -You'll find a `switch` statement in the `loop()` function that processes this command character: - -~~~~~ cpp -switch (selected_command) { - case 'a': - command_analogue_read(); - break; - case 'r': - command_read(); - break; - case 'l': - command_write(LOW); - break; - - // ... and so on ... -~~~~~ +You'll need the ID later, so it's best to save it into a variable: -For example, you can see in the above that when it receives an "a" character, it calls the `command_analogue_read()` function. -This function does pretty much what it says on the tin: it reads an analogue pin. +~~~~~ python +from sr.robot3 import * -You will need to add your own entry into this `switch` statement for your new command. -This will need to be represented by a character that doesn't already appear in the switch statement. -Let's say you chose "c"; your entry would look like this: +# Replace this with the actual serial number of your board +ARDUINO_SN = "752303138333517171B1" -~~~~~ cpp -switch (selected_command) { - case 'c': - command_bake_cake(); - break; +robot = Robot(ignored_arduinos=[ARDUINO_SN]) - // ... all the original entries ... +# The rest of your code ~~~~~ -You would then write your `command_bake_cake()` function. -Your command can read additional data from the serial port if it requires additional information to operate. -It can also write a response back to the host (your Python code). -Have a look at the `command_read()` function to see how to do this. -### Step 2: Use your new command from Python +## Opening a serial port -You can send a custom command from your Python code to the Ruggeduino to control your cake-baking. +If you need to communicate with a device, you will need to open its serial port. +If you want the `robot` object to do this and provide a serial port for your use, you will need to do the following. ~~~~~ python -cake_result = R.ruggeduino.command("c") -~~~~~ +from sr.robot3 import * -The `cake_result` variable will contain any response from your firmware, if you sent one. +# Replace this with the actual serial number of your board +ARDUINO_SN = "752303138333517171B1" -You're done! You can now use your custom cake-baking firmware! +# Set this to the baud rate that the device communicates with +SERIAL_BAUD_RATE = 115200 -If you have multiple Ruggeduino running custom firmware, you can keep track of which one is which -by using the serial number. +robot = Robot( + ignored_arduinos=[ARDUINO_SN], + raw_ports=[(ARDUINO_SN, SERIAL_BAUD_RATE)] +) -[Completely custom firmware](#completely) {#completely} ----------------------------- +# The rest of your code +~~~~~ -When configured correctly, the `Robot` object will perform absolutely no serial communications with a completely custom firmware. -We refer to this as *ignoring* a Ruggeduino. -To configure a `Robot` object to ignore a Ruggeduino with custom firmware, you will need to provide it with the Ruggeduino's ID. +This opens the serial connection to the device and the serial port is now available under: + +~~~~~ python +robot.raw_serial_devices[ARDUINO_SN] +~~~~~ -The Ruggeduino ID is a 20 character string of mostly numbers, and is output in the robot log when you run a program on your robot with your -Ruggeduino connected. +
+Note that all communications with the serial port is done with `bytes` rather than `strings` that you will be more familiar with. -You'll need the ID later, so it's best to save it into a variable: +You may need to convert from a string to bytes: ~~~~~ python -from sr.robot3 import * - -RUGGEDUINO_ID = "752303138333517171B1" # Replace this with the actual ID +bytes_object = string_object.encode() +~~~~~ -R = Robot(ignored_ruggeduinos=["752303138333517171B1"]) +or bytes to a string: -# The rest of your code +~~~~~ python +string_object = bytes_object.decode() ~~~~~ +
-If you need to communicate with the Ruggeduino firmware, you will need its serial device path. -This is accessible from the `ignored_ruggeduinos` dictionary. +### write + +`write` is used to send data to the serial port, the function will send whatever you provide. +Putting a `b` in front of a string is a short hand way of creating a bytes object. ~~~~~ python -ruggeduino_device = R.ignored_ruggeduinos[RUGGEDUINO_ID] +# This will send the message "data" over the serial port +robot.raw_serial_devices[ARDUINO_SN].write(b"data") +~~~~~ -# The rest of your code + +### read + +`read` is used to get some data from the serial port, the function will read the number of bytes specified. +If the port times out waiting for data, it may return less bytes that specified. + +~~~~~ python +# This will read 5 bytes from the serial port +received_data = robot.raw_serial_devices[ARDUINO_SN].read(5) ~~~~~ -The device path will look something like `/dev/ttyACM1`. -You may wish to use pyserial to communicate with the Ruggeduino, in which case you could open it like so: +### read until + +`read_until` is used to get some data from the serial port, it will read data until it reads the specified terminator. +If the port times out waiting for the terminator, it may return less data without the terminator on the end. + +For example this can be used to read in a single line of data, terminated with a newline character `\n`. ~~~~~ python -import serial -from sr.robot3 import * +# This will read in data until we get to a new line character +received_data = robot.raw_serial_devices[ARDUINO_SN].read_until(b"\n") +~~~~~ -RUGGEDUINO_ID = "752303138333517171B1" -R = Robot(ignored_ruggeduinos=[RUGGEDUINO_ID]) +### pyserial port -ser = serial.Serial(R.ignored_ruggeduinos[RUGGEDUINO_ID]) +The `Robot` uses pyserial to open the serial connection to the board. +If you would prefer you can access the pyserial object directly, like so: +~~~~~ python +serial_port = robot.raw_serial_devices[ARDUINO_SN].port ~~~~~ Refer to the [pyserial documentation](https://pyserial.readthedocs.org/en/latest/) for more information on how to use pyserial. diff --git a/programming/arduino/extended_sr_firmware.md b/programming/arduino/extended_sr_firmware.md new file mode 100644 index 00000000..136aa549 --- /dev/null +++ b/programming/arduino/extended_sr_firmware.md @@ -0,0 +1,77 @@ +--- +layout: page +title: Arduino extended firmware +--- + +# Arduino with extended SR firmware + +You may wish to extend the SR firmware with additional functionality. +This will allow you to continue using the commands already provided by the SR firmware (e.g. `digital_read()`), +which means any existing robot code you have won't need modifying very much. +When you extend the SR firmware, you'll be adding new commands to the firmware. +There are almost limitless possibilities of what your commands may do, but here are some examples to give you an idea: + +* Talk to an SPI or I2C sensor. +* Read N input pins at the same instant in time. +* Time pulses received from an ultrasound sensor. + +There are two steps that you will need to go through to implement and use your custom commands: + + +## Step 1: Add your command to the Arduino firmware + +To extend the SR firmware, you will need to first download its [source code]({{ site.baseurl }}/resources/kit/arduino-fw.ino), and edit it in the Arduino IDE. +When the SR Arduino python library wants the Arduino to run a command, it sends it a single character to tell it which command to run. +You'll find a `switch` statement in the `loop()` function that processes this command character: + +~~~~~ cpp +switch (selected_command) { + case 'a': + command_analog_read(); + break; + case 'r': + command_read(); + break; + case 'l': + command_write(LOW); + break; + + // ... and so on ... +~~~~~ + +For example, you can see in the above code that when it receives an "a" character, it calls the `command_analog_read()` function. +This function does pretty much what it says on the tin: it reads an analog pin. + +You will need to add your own entry into this `switch` statement for your new command. +This will need to be represented by a character that doesn't already appear in the switch statement. +Let's say you chose "s"; your entry would look like this: + +~~~~~ cpp +switch (selected_command) { + case 's': + command_read_sensor(); + break; + + // ... all the original entries ... +~~~~~ + +You would then write your `command_read_sensor()` function, which would implement reading the sensor. +Your function can read additional data from the serial port if it requires additional information to operate. +It can also write a response back to the host (your Python code). +Have a look at the `command_read()` function to see how to do this. + + +## Step 2: Use your new command from Python + +You can send a custom command from your Python code to the Arduino to read the sensor. + +~~~~~ python +sensor_data = robot.arduino.command("s") +~~~~~ + +The `sensor_data` variable will contain any response from your firmware, if you sent one. + +You're done! +You can now use your custom firmware that can read from a sensor. + +If you have multiple Arduinos running custom firmware, you can keep track of which one is which by using the serial number. diff --git a/programming/arduino/index.md b/programming/arduino/index.md index 98d08284..b2d81bb1 100644 --- a/programming/arduino/index.md +++ b/programming/arduino/index.md @@ -3,134 +3,21 @@ layout: page title: Arduino API --- -Arduino API -=========== +# Arduino API -The [Ruggeduino](https://web.archive.org/web/20170317171649/https://www.rugged-circuits.com/ruggeduino) -provides a total of 18 pins for either digital input or output (labelled 2 to 13 and A0 to A5), -including 6 for analogue input (labelled A0 to A5). +The Arduino that came as part of your kit was shipped with a firmware that provides the functionality outlined in the [Arduino API](/docs/programming/arduino/sr_firmware) page. +You may wish to use the Arduino as is, extend the functionality of this firmware, or completely replace it. -When a single Ruggeduino is connected to your robot, you can control it -using the `ruggeduino` object. +The `sr.robot3` library provides support for using the Arduino in three possible configurations: -~~~~~ python -R.ruggeduino.something... -~~~~~ + 1. [Default SR firmware](./sr_firmware): + The firmware that is shipped on the Arduino by default. + This firmware provides basic functionality to read the digital and analog values of pins and output digital on/off signals. + 2. [Extended SR firmware](./extended_sr_firmware): + Firmware that adds extra commands to the default SR firmware. + These extra commands can perform actions such as reading a sensor or measuring pulses on a pin. + 3. [Completely custom](./custom_firmware): + Either any firmware that is not derived from the SR firmware or any other serial device can be connected to via this method. -The serial number of each detected Ruggeduino is printed to the log when your robot starts. -It will look something like this: - -~~~~~ not-code -sr.robot3.robot INFO - Found Ruggeduino - 752303138333517171B1 -~~~~~ - -If you have more than one Ruggeduino attached, the `ruggeduinos` object -can be used to control a collection of Ruggeduinos. Similar to `motors` -and `servos`, `ruggeduinos` is a dictionary accessed by serial number. -For example, if you had a board whose serial number was "752303138333517171B1", -you could do this instead: - -~~~~~ python -R.ruggeduinos["752303138333517171B1"].something... -~~~~~ - -
- When you have more than one Ruggeduino board connected to your kit, - you must use `R.ruggeduinos` and index by serial number. This is so - that the kit knows which Ruggeduino you want to control. -
- - -[Setting pin modes](#pin-modes) {#pin-modes} --------------------------------------------------------------------------- - -To use one of the pins on the Ruggeduino, you must first set whether you want it to behave as an input or as an output. -You can do this with the following code: - -~~~~~ python -R.ruggeduino.pins[10].mode = MODE -~~~~~ - -The possible values for `MODE` are: - -`INPUT` -: set the pin to [input mode](#input) - -`OUTPUT` -: set the pin to [output mode](#output) - -`INPUT_PULLUP` -: set the pin to input mode with a [pull-up resistor](#pullup) - -An example of how to use this is below: - -~~~~~ python -# set Ruggeduino pin 2 to output -R.ruggeduino.pins[2].mode = OUTPUT -# set Ruggeduino pin 3 to input -R.ruggeduino.pins[3].mode = INPUT -# set Ruggeduino pin 4 to input and enable pull-up resistor -R.ruggeduino.pins[4].mode = INPUT_PULLUP -~~~~~ - -
You cannot use pins 0 and 1, as using these would disrupt communications between the Ruggeduino and the Power Board.
- -[Input](#input) {#input} -------- - -You can read a **digital** input pin with the following code: - -~~~~~ python -# R.ruggeduinos[RUGGEDUINO_BOARD_NUMBER].pins[PIN_NO].digital_read() - -# to read Ruggeduino's digital pin 3... -pin0 = R.ruggeduino.pins[3].digital_read() -~~~~~ - -`pin0` will now contain `True` or `False` depending on whether the pin was high (3.3v) or low (0v), respectively. - -You can read an **analogue** input pin with the following code: - -~~~~~ python -# R.ruggeduinos[RUGGEDUINO_BOARD_NUMBER].pins[PIN_NO].analogue_read() - -# to read Ruggeduino's analogue pin A0... -pin0 = R.ruggeduino.pins[A0].analogue_read() -~~~~~ - -The analogue pin numbers are available as `A0`, `A1`, `A2`, `A3`, `A4`, and `A5` respectively. - - -[Output](#output) {#output} --------- - -You can only set digital outputs (there's no analogue output, although you may feel free to modify the Ruggeduino's firmware to add the ability to output [PWM](https://wikipedia.org/wiki/Pulse-width_modulation "Pulse-width modulation") if you desire). To set a digital output pin, you would use the following: - -~~~~~ python -# R.ruggeduinos[RUGGEDUINO_BOARD_NUMBER].pins[PIN_NO].digital_write(VALUE) - -# to set Ruggeduinos pin 2 high: -R.ruggeduino.pins[2].digital_write(True) - -# to set Ruggeduino's pin 2 low: -R.ruggeduino.pins[2].digital_write(False) -~~~~~ - -[Pull-up resistors](#pullup) {#pullup} ----------------------------------------------------------------------- - -The Ruggeduino possesses the ability to enable a built-in pull-up resistor on any input pin. -This takes a small amount of explanation. - -Normally, input pins are not connected to anything - known as "floating". -In this state, they might read high or low, or different values depending on their environment. -This is obviously not good for consistent control. - -Many pieces of off-the-shelf electronics that have some form of standard I/O output will connect this pin to 3.3V (high) and 0V (low) when required, - so this is not a problem. However, for simple electronics, a microswitch for example, - you would normally be required to connect a resistor between the input pin and 3.3V (a pull-up resistor), - or between the input pin and 0V (a pull-down resistor) to keep the input in a known state until the switch overrides it by connecting directly to the opposite state. - -However, the built-in pull-up resistor alleviates this need. -It essentially wires in a resistor connected to 3.3V, meaning that when this option is enabled, an input pin will "default" to being high. -This means you can simply connect a switch between the input pin and a ground pin without any need of resistors - when the switch is open, the pin will read high; when closed, it will read low. +By default, the [`sr.robot3`](/docs/programming/robot_api/) library assumes that all connected Arduinos are running the SR firmware or firmware which is compatible with the SR Arduino firmware. +If you're using completely custom firmware, you'll need to tell the kit to ignore the device so that you're able to define your own setup logic. diff --git a/programming/arduino/sr_firmware.md b/programming/arduino/sr_firmware.md new file mode 100644 index 00000000..616b3be9 --- /dev/null +++ b/programming/arduino/sr_firmware.md @@ -0,0 +1,130 @@ +--- +layout: page +title: Arduino SR firmware +--- + +# Arduino with SR firmware + +The [Arduino]({{ site.baseurl }}/kit/arduino) provides a total of 18 pins for either digital input or output (labelled 2 to 13 and A0 to A5), pins A0 to A5 also support analog input. + +
+Digital pins 0 and 1 are reserved for internal communication with the rest of our kit. +
+ +The kit can control multiple Arduinos at once, however we only provide one in the kit. +If there is exactly one Arduino connected to your robot, it can be accessed using the `arduino` property of the `Robot` object. + +~~~~~ python +from sr.robot3 import * +robot = Robot() + +my_arduino = robot.arduino +~~~~~ + +The serial number of each detected Arduino is printed to the log when your robot starts. +It will look something like this: + +~~~~~ not-code +sr.robot3.robot INFO - Found Arduino: 752303138333517171B1 +~~~~~ + +If you have more than one Arduino attached, you need to specify which one you want to control. +This is done using the serial number of the board and the `arduinos` attribute. + +For example, if you had a board whose serial number was "752303138333517171B1", you need to do this instead: + +~~~~~ python +my_arduino = robot.arduinos["752303138333517171B1"] +~~~~~ + +
+When you have more than one Arduino connected to your kit, you can’t use `robot.arduino`. +This is because the kit needs to know which Arduino you want to control. +
+ + +## Setting pin modes + +To use one of the pins on the Arduino, you must first set whether you want it to behave as an input or as an output. +The possible modes for a pin are: + +`INPUT` +: set the pin to [input mode](#input) + +`OUTPUT` +: set the pin to [output mode](#output) + +`INPUT_PULLUP` +: set the pin to input mode with a [pull-up resistor](#pull-up-resistors) + + +An example of how to use this is below: + +~~~~~ python +# set Arduino pin 2 to output +robot.arduino.pins[2].mode = OUTPUT + +# set Arduino pin 3 to input +robot.arduino.pins[3].mode = INPUT + +# set Arduino pin 4 to input and enable pull-up resistor +robot.arduino.pins[4].mode = INPUT_PULLUP +~~~~~ + + +## Input + +You can read a **digital** input pin with the following code: + +~~~~~ python +# robot.arduinos[ARDUINO_SERIAL_NUMBER].pins[PIN_NO].digital_read() + +# to read Arduino's digital pin 3... +pin3 = robot.arduino.pins[3].digital_read() +~~~~~ + +`pin3` will now contain `True` or `False` depending on whether the pin was high (5v) or low (0v), respectively. + +You can read an **analog** input pin with the following code: + +~~~~~ python +# robot.arduinos[ARDUINO_SERIAL_NUMBER].pins[PIN_NO].analog_read() + +# to read Arduino's analog pin A0... +pinA0 = robot.arduino.pins[A0].analog_read() +~~~~~ + +The analog pin numbers are available as `A0`, `A1`, `A2`, `A3`, `A4`, and `A5` respectively. + + +## Output + +You can only set digital outputs (there's no analog_write, although feel free to modify the Arduino's firmware to add the ability to output [PWM](https://wikipedia.org/wiki/Pulse-width_modulation "Pulse-width modulation") if you desire). +To set a digital output pin, you would use the following: + +~~~~~ python +# robot.arduinos[ARDUINO_SERIAL_NUMBER].pins[PIN_NO].digital_write(VALUE) + +# to set Arduino's pin 2 high: +robot.arduino.pins[2].digital_write(True) + +# to set Arduino's pin 2 low: +robot.arduino.pins[2].digital_write(False) +~~~~~ + + +## Pull-up resistors + +The Arduino possesses the ability to enable a built-in pull-up resistor on any input pin. +This takes a small amount of explanation. + +Normally, input pins are not connected to anything - known as "floating". +In this state, they might read high or low, or different values depending on their environment. +This is obviously not good for consistent control. + +Many pieces of off-the-shelf electronics that have some form of standard I/O output will connect this pin to 5.0V (high) and 0V (low) when required, so this is not a problem. +However, for simple electronics, a microswitch for example, you would normally be required to connect a resistor between the input pin and 5.0V (a pull-up resistor) to keep the input in a known state until the switch overrides it by connecting directly to 0V. + +However, the built-in pull-up resistor alleviates this need. +It essentially wires in a resistor connected to 5.0V, meaning that when this option is enabled, an input pin will "default" to being high. +This means you can simply connect a switch between the input pin and a ground pin without any need of resistors - when the switch is open, the pin will read high; when closed, it will read low. diff --git a/resources/kit/ruggeduino-fw.ino b/resources/kit/arduino-fw.ino similarity index 96% rename from resources/kit/ruggeduino-fw.ino rename to resources/kit/arduino-fw.ino index 83817f38..a5970973 100644 --- a/resources/kit/ruggeduino-fw.ino +++ b/resources/kit/arduino-fw.ino @@ -27,7 +27,7 @@ void command_read() { } } -void command_analogue_read() { +void command_analog_read() { int pin = read_pin(); int value = analogRead(pin); Serial.print(value); @@ -50,7 +50,7 @@ void loop() { // Do something different based on what we got: switch (selected_command) { case 'a': - command_analogue_read(); + command_analog_read(); break; case 'r': command_read(); From c229fc7b3f5e255615c2e4a446089af3900b1c9d Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 10 Sep 2023 19:14:02 +0100 Subject: [PATCH 54/81] battery page formatting --- kit/batteries/index.md | 127 ++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 72 deletions(-) diff --git a/kit/batteries/index.md b/kit/batteries/index.md index 09e682fd..569f87a0 100644 --- a/kit/batteries/index.md +++ b/kit/batteries/index.md @@ -3,8 +3,7 @@ layout: page title: Batteries and Charging --- -Batteries and Charging -====================== +# Batteries and Charging ![Three cell, 2200mAh, lithium-ion polymer battery]({{ site.baseurl }}/images/content/kit/battery.png "Three cell, 2200mAh, lithium-ion polymer battery") @@ -18,83 +17,67 @@ Your kit will come with several pieces of battery related equipment: 1. Two 11.1V 2200mAh LiPo Batteries. 2. One battery charger. -3. One battery charging bag. +3. One battery charger power cable. +4. One battery safety bag. You must **not** use any batteries, chargers, bags or cables not explicitly authorised by Student Robotics. If you have any doubts or wish to request permission to use a battery, charger, charging bag or battery cable not provided by Student Robotics, please contact <{{ site.emails.kit-support }}>. -
-
    -
  • Never leave batteries unattended when they are in use or charging.
  • -
  • Always place the batteries in the provided charging bag when charging or storing.
  • -
  • Always follow the charging checklist precisely to make sure the charger is correctly configured. See the Charging Batteries section for more details.
  • -
  • Always leave the charging leads connected to the battery charger at all times.
  • -
  • Do not charge or use a damaged battery.
  • -
  • If a battery has any cuts, nicks, exposed copper on wires or is bulging to the point of no longer being squishy, contact {{ site.emails.kit-support }} immediately.
  • -
+
+* Never leave batteries unattended when they are in use or charging. +* Always place the batteries in the provided battery safety bag when charging or when being stored. +* Always follow the charging checklist precisely to make sure the charger is correctly configured. + See the [Charging Batteries](#charging-batteries) section for more details. +* Do not charge or use a damaged battery. +* If a battery has any cuts, nicks, exposed copper on the wires or is bulging to the point of no longer being squishy, contact <{{ site.emails.kit-support }}> immediately. +* Always leave the charging leads connected to the battery charger at all times.
-Storing Batteries ------------------ - -When your batteries are not actively in use, they should be safely stored. You -must disconnect the batteries from all electrical equipment, and place them in -the battery charging bag. You should then store the charging bag in a safe -location. - -If you are not going to use the batteries for a long period of time, such as -more than a week, then you should charge the batteries before hand. By -themselves, batteries will discharge very slowly, and over-discharging will -lead to damage. We recommend that you charge all batteries to at -least 70% of capacity before being stored for a long period. - -Operating Batteries -------------------- - -To use your batteries, you must connect them to the Student Robotics power -board. Do not tamper with the cable or connect the batteries to anything other -than the power board (or the charger when charging). - -During operation, the battery is protected by over-current protection and a fuse -in the power board. If any equipment is short circuited, the over-current -protection will activate - protecting the battery. In extreme circumstances the -fuse may blow to prevent damage to the battery. This is an important safety -feature: do **not**, under any circumstances, bypass the fuse. The fuse is not -user serviceable and if the fuse has blown then the power board must be replaced. -If you suspect the fuse has blown then please contact -<{{ site.emails.kit-support }}> straight away. - -Mechanical damage to a battery can be dangerous, and a puncture or large force -applied to a battery causes a serious risk of fire. To avoid this, your battery -should be shielded from mechanical damage while you operate it. Secure your -battery to your robot, so that it does not move or fall off while the robot -moves. You should also build a compartment for the battery to be placed in, so -that accidental collisions do not damage the battery. - -
-The smaller lead is only used for voltage measurement when charging and should -be left disconnected when the battery is connected to the robot kit. + +## Storing Batteries + +When your batteries are not actively in use, they should be safely stored. +To do this you must disconnect the batteries from all electrical equipment, and place them in the battery safety bag. +You should then store the battery safety bag in a safe location. + +If you are not going to use the batteries for a long period of time, such as more than a week, then you should charge the batteries before hand. +By themselves, batteries will discharge very slowly, and over-discharging will lead to damage. +We recommend that you charge all batteries to at least 70% of capacity before being stored for a long period. + + +## Operating Batteries + +To use your batteries, you must connect them to the Student Robotics power board. +Do not tamper with the cable or connect the batteries to anything other than the power board (or the charger when charging). + +During operation, the battery is protected by over-current protection and a fuse in the power board. +If any equipment is short circuited, the over-current protection will activate - protecting the battery. +In extreme circumstances the fuse may blow to prevent damage to the battery. +This is an important safety feature: do **not**, under any circumstances, bypass the fuse. +The fuse is not user serviceable and if the fuse has blown then the power board must be replaced. +If you suspect the fuse has blown then please contact <{{ site.emails.kit-support }}> straight away. + +Mechanical damage to a battery can be dangerous, and a puncture or large force applied to a battery causes a serious risk of fire. +To avoid this, your battery should be shielded from mechanical damage while you operate it. +Secure your battery to your robot, so that it does not move or fall off while the robot moves. +You should also build a compartment for the battery to be placed in, so that accidental collisions do not damage the battery. + +
+The smaller lead is only used for voltage measurement when charging and must **not** be connected to anything except the battery charger when the battery is charging.
+ ### Flat Batteries -When the battery has been almost completely discharged, the Power Board will -automatically turn off and the LED marked "Power / Flat Battery Indicator" -in the [diagram](/docs/kit/power_board#BoardDiagram) will flash red and green. -You should -immediately disconnect the battery, and begin charging it. You should keep -your second battery charged, so that you can immediately switch to using it. - -Do not store a discharged -battery for more than a few days without charging it, as it may discharge -further and become damaged. - -Charging Batteries ------------------- - -Student Robotics provides two different kinds of chargers with our kit, the -[iMAX B6](/docs/kit/batteries/imax_b6_charger), and the -[HobbyKing HKE4](/docs/kit/batteries/hke4_charger). You should only use the battery -charger provided by Student Robotics to charge the batteries provided in the -kit. For precise instructions on how to charge your batteries with the provided -charger, please see the charger specific documentation page. +When the battery has been almost completely discharged, the Power Board will automatically turn off and the LED marked "Power / Flat Battery Indicator" in the [diagram](/docs/kit/power_board#BoardDiagram) will flash red and green. +You should immediately disconnect the battery, and begin charging it. +You should keep your second battery charged, so that you can immediately switch to using it. + +Do not store a discharged battery for more than a few days without charging it, as it may discharge further and become damaged. + + +## Charging Batteries + +Student Robotics provides two different kinds of chargers with our kit, the [iMAX B6](/docs/kit/batteries/imax_b6_charger), and the [HobbyKing HKE4](/docs/kit/batteries/hke4_charger). +You should only use the battery charger provided by Student Robotics to charge the batteries provided in the kit. +For precise instructions on how to charge your batteries with the provided charger, please see the charger specific documentation page. From 1e111005ee8ef0266c2e65f5a8176e3febd1cfb8 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 11 Sep 2023 18:22:26 +0100 Subject: [PATCH 55/81] Fix incorrect links and details on path --- programming/robot_api/comp_mode.md | 8 ++++---- programming/robot_api/index.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/programming/robot_api/comp_mode.md b/programming/robot_api/comp_mode.md index bcdb4a8f..9278f3b7 100644 --- a/programming/robot_api/comp_mode.md +++ b/programming/robot_api/comp_mode.md @@ -9,19 +9,19 @@ title: Competition Mode ## What is Comp Mode During a competition match a Competition Mode USB is inserted into a spare USB port on your robot (See the [kit assembly guide]({{ site.baseurl }}/tutorials/assembly) for details on the spare USB port you need to leave). -This will instruct your robot that it is in competition mode and will enable a game timeout, disable the WiFi and update certain `robot` attributes. +This will instruct your robot that it is in competition mode and will have a number of effects, which are detailed below. ## Effects of Comp Mode * Enabling Game timeout - When in competition mode the robot will automatically stop at the end of a match, where the duration of a match is defined in the [rules]({{ site.baseurl }}/tutorials/assembly). + When in competition mode the robot will automatically stop at the end of a match, where the duration of a match is defined in the [rules]({{ site.baseurl }}/rules). The duration of the match is defined from when the start button is pressed. * Disabling of WiFi and Web interface - During competition matches remote control of robots is forbidden ([rules]({{ site.baseurl }}/tutorials/assembly)), so the WiFi and web interface are disabled to ensure a fair game. + During competition matches remote control of robots is forbidden ([rules]({{ site.baseurl }}/rules)), so the WiFi and web interface are disabled to ensure a fair game. * Updating of match-specific `robot` attributes @@ -38,4 +38,4 @@ This will instruct your robot that it is in competition mode and will enable a g The zone you are in defines which arena markers are near your scoring zone. You can use the knowledge of your zone to compensate for this, so your robot behaves correctly in all starting positions. - See the [rules]({{ site.baseurl }}/tutorials/assembly) for where the scoring zones and arena markers are positioned. + See the [rules]({{ site.baseurl }}/rules) for where the scoring zones and arena markers are positioned. diff --git a/programming/robot_api/index.md b/programming/robot_api/index.md index 74ca744a..df437afa 100644 --- a/programming/robot_api/index.md +++ b/programming/robot_api/index.md @@ -75,7 +75,7 @@ mode ~~~~~ usbkey -: The path to the USB stick. +: A [`Path`](https://docs.python.org/3/library/pathlib.html#basic-use) object containing the path to the USB stick. You can use this to easily read and write files on the USB stick itself. An example of how the `usbkey` attribute might be used to read a file called `my-file.txt` which is stored on the USB stick: From 0dc3f3aebc6423333a3ea74d21607b044d2fa752 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 11 Sep 2023 19:35:24 +0100 Subject: [PATCH 56/81] Talk about list_ports() --- programming/arduino/custom_firmware.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/programming/arduino/custom_firmware.md b/programming/arduino/custom_firmware.md index 910badf9..a280b715 100644 --- a/programming/arduino/custom_firmware.md +++ b/programming/arduino/custom_firmware.md @@ -124,3 +124,19 @@ serial_port = robot.raw_serial_devices[ARDUINO_SN].port ~~~~~ Refer to the [pyserial documentation](https://pyserial.readthedocs.org/en/latest/) for more information on how to use pyserial. + + +## Finding other serial devices + +If you are using your own serial device that you want to access via a [raw serial port](#opening-a-serial-port), you will need to know its serial number. +To do this we provide a helper function. +Running the below code example will print a list of all the devices connected to the system. + +Once you have found your device, and copied the serial number, you can follow the guidance in the [opening a serial port](#opening-a-serial-port) section. +If you need help finding which device is yours contact us on Discord for help. + +~~~~~ python +from sr.robot3 import list_ports + +list_ports() +~~~~~ From 1bbabbe750c47ee84632cda66511b000951549a6 Mon Sep 17 00:00:00 2001 From: JoshP Date: Mon, 11 Sep 2023 23:39:35 +0100 Subject: [PATCH 57/81] update programming page --- programming/index.md | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/programming/index.md b/programming/index.md index 3978fc0c..bddd8d1d 100644 --- a/programming/index.md +++ b/programming/index.md @@ -3,37 +3,36 @@ layout: page title: Programming --- -Programming Your Robot -====================== +# Programming Your Robot - +## Learning python - - +In order to develop the code for your robot, we recommend that you use a code editor. +A good code editor can provide you with features such as auto completion and syntax highlighting which will help you more easily write code. +For suggestions of suitable editors, and how to set them up see the [Code Editors]({{ site.baseurl }}/tutorials/editors/) section. From 33a3875384bc30bf2a418ea3ff440bdc20617a05 Mon Sep 17 00:00:00 2001 From: JoshP Date: Tue, 12 Sep 2023 20:36:16 +0100 Subject: [PATCH 58/81] Review comments --- programming/arduino/custom_firmware.md | 6 +++--- programming/arduino/index.md | 1 + programming/arduino/sr_firmware.md | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/programming/arduino/custom_firmware.md b/programming/arduino/custom_firmware.md index a280b715..312f6bcc 100644 --- a/programming/arduino/custom_firmware.md +++ b/programming/arduino/custom_firmware.md @@ -17,10 +17,9 @@ By default when the `robot` object is created it will try to communicate with al If you want the API to not try connecting to a device we need to *ignore* the Arduino. To configure a `Robot` object to ignore a Arduino with custom firmware, you will need to provide it with the Arduino's serial number. -The Arduino serial number is a character string of numbers and letters, and is output in the robot log when you run a program on your robot with your -Arduino connected. +The Arduino serial number is a string of numbers and letters, and is output in the robot log when you run a program on your robot with your Arduino connected. -You'll need the ID later, so it's best to save it into a variable: +You'll need the serial number later, so it's best to save it into a variable: ~~~~~ python from sr.robot3 import * @@ -86,6 +85,7 @@ Putting a `b` in front of a string is a short hand way of creating a bytes objec ~~~~~ python # This will send the message "data" over the serial port +# The b in front of the string is not a typo, this is a byte string robot.raw_serial_devices[ARDUINO_SN].write(b"data") ~~~~~ diff --git a/programming/arduino/index.md b/programming/arduino/index.md index b2d81bb1..7989837a 100644 --- a/programming/arduino/index.md +++ b/programming/arduino/index.md @@ -7,6 +7,7 @@ title: Arduino API The Arduino that came as part of your kit was shipped with a firmware that provides the functionality outlined in the [Arduino API](/docs/programming/arduino/sr_firmware) page. You may wish to use the Arduino as is, extend the functionality of this firmware, or completely replace it. +You can also add your own Arduinos, or other boards, to the kit, running either our firmware or your own. The `sr.robot3` library provides support for using the Arduino in three possible configurations: diff --git a/programming/arduino/sr_firmware.md b/programming/arduino/sr_firmware.md index 616b3be9..d9fe9a28 100644 --- a/programming/arduino/sr_firmware.md +++ b/programming/arduino/sr_firmware.md @@ -77,21 +77,23 @@ robot.arduino.pins[4].mode = INPUT_PULLUP You can read a **digital** input pin with the following code: ~~~~~ python -# robot.arduinos[ARDUINO_SERIAL_NUMBER].pins[PIN_NO].digital_read() - # to read Arduino's digital pin 3... -pin3 = robot.arduino.pins[3].digital_read() +value = robot.arduino.pins[3].digital_read() + +# to read Arduino's digital pin 7... +value = robot.arduino.pins[7].digital_read() ~~~~~ -`pin3` will now contain `True` or `False` depending on whether the pin was high (5v) or low (0v), respectively. +`value` will now contain `True` or `False` depending on whether the pin was high (5v) or low (0v), respectively. You can read an **analog** input pin with the following code: ~~~~~ python -# robot.arduinos[ARDUINO_SERIAL_NUMBER].pins[PIN_NO].analog_read() - # to read Arduino's analog pin A0... -pinA0 = robot.arduino.pins[A0].analog_read() +value = robot.arduino.pins[A0].analog_read() + +# to read Arduino's analog pin A4... +value = robot.arduino.pins[A4].analog_read() ~~~~~ The analog pin numbers are available as `A0`, `A1`, `A2`, `A3`, `A4`, and `A5` respectively. @@ -99,12 +101,10 @@ The analog pin numbers are available as `A0`, `A1`, `A2`, `A3`, `A4`, and `A5` r ## Output -You can only set digital outputs (there's no analog_write, although feel free to modify the Arduino's firmware to add the ability to output [PWM](https://wikipedia.org/wiki/Pulse-width_modulation "Pulse-width modulation") if you desire). +You can only set digital outputs (there's no analog_write, although feel free to modify the Arduino's firmware to add the ability to output [PWM](https://wikipedia.org/wiki/Pulse-width_modulation) (Pulse-width modulation) if you desire). To set a digital output pin, you would use the following: ~~~~~ python -# robot.arduinos[ARDUINO_SERIAL_NUMBER].pins[PIN_NO].digital_write(VALUE) - # to set Arduino's pin 2 high: robot.arduino.pins[2].digital_write(True) From 5121c6c53d56a29b06e58a317f81069645e282f9 Mon Sep 17 00:00:00 2001 From: JoshP Date: Wed, 13 Sep 2023 20:46:56 +0100 Subject: [PATCH 59/81] review tweak arduino --- programming/arduino/custom_firmware.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programming/arduino/custom_firmware.md b/programming/arduino/custom_firmware.md index 312f6bcc..a22b55e5 100644 --- a/programming/arduino/custom_firmware.md +++ b/programming/arduino/custom_firmware.md @@ -62,7 +62,7 @@ robot.raw_serial_devices[ARDUINO_SN] ~~~~~
-Note that all communications with the serial port is done with `bytes` rather than `strings` that you will be more familiar with. +Note that all communications with the serial port is done with `bytes` rather than strings that you will be more familiar with. You may need to convert from a string to bytes: From 5b4b72e877daeed478ec78954b31f5e7ec5e7711 Mon Sep 17 00:00:00 2001 From: JoshP Date: Wed, 13 Sep 2023 20:50:57 +0100 Subject: [PATCH 60/81] Update cheat sheet --- programming/cheat_sheet.md | 151 ++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 71 deletions(-) diff --git a/programming/cheat_sheet.md b/programming/cheat_sheet.md index b39ca5e5..4fbf0256 100644 --- a/programming/cheat_sheet.md +++ b/programming/cheat_sheet.md @@ -22,17 +22,17 @@ from sr.robot3 import * ### Standard Initialisation ~~~~~ python -R = Robot() +robot = Robot() ~~~~~ ### Initialisation without waiting for the start button ~~~~~ python -R = Robot(auto_start=True) +robot = Robot(wait_for_start=False) # Code here runs before the start button is pressed -R.wait_start() +robot.wait_start() # wait for the start button ~~~~~ ### Initialisation with extra logging @@ -40,7 +40,7 @@ R.wait_start() You can also tell the robot to print extra logging information, although this is quite noisy. ~~~~~ python -R = Robot(verbose=True) +robot = Robot(debug=True) ~~~~~ ## Selecting which board to control @@ -48,49 +48,48 @@ R = Robot(verbose=True) If you only have one board of a given type plugged into your robot, then you can use its singular name: ~~~~~ python -R.power_board.something -R.motor_board.something -R.servo_board.something -R.ruggeduino.something +robot.power_board +robot.motor_board +robot.servo_board +robot.arduino ~~~~~ If you have multiple boards of a given type plugged into your robot, you must index them by serial number: ~~~~~ python -R.motor_boards["srABC1"].something -R.servo_boards["srXYZ2"].something -R.ruggeduinos["1234567890"].something +robot.motor_boards["srABC1"] +robot.arduinos["1234567890"] ~~~~~ ## Power Board -The outputs on the power board will turn on when you initialise your robot. +The outputs on the power board will turn on when you initialise your robot and turn off when your code ends. ### Turn on and off the power outputs ~~~~~ python # Turn all of the outputs on -R.power_board.outputs.power_on() +robot.power_board.outputs.power_on() # Turn all of the outputs off -R.power_board.outputs.power_off() +robot.power_board.outputs.power_off() # Turn a single output on -R.power_board.outputs[OUT_H0].is_enabled = True +robot.power_board.outputs[OUT_H0].is_enabled = True # Turn a single output off -R.power_board.outputs[OUT_H0].is_enabled = False +robot.power_board.outputs[OUT_H0].is_enabled = False ~~~~~ ### Reading voltage and current ~~~~~ python # Read the current of an individual output -current = R.power_board.outputs[OUT_H0].current +current = robot.power_board.outputs[OUT_H0].current # Read the current and voltage from the LiPo battery -voltage = R.power_board.battery_sensor.voltage -current = R.power_board.battery_sensor.current +voltage = robot.power_board.battery_sensor.voltage +current = robot.power_board.battery_sensor.current ~~~~~ ### Buzzer @@ -99,10 +98,13 @@ The power board has an on-board piezoelectric buzzer. ~~~~~ python # Play a standard note C6 -> C8 included for 0.5s -R.power_board.piezo.buzz(0.5, Note.C6) +robot.power_board.piezo.buzz(Note.C6, 0.5) # Play a tone at 1047Hz for 1 second -R.power_board.piezo.buzz(1, 1047) +robot.power_board.piezo.buzz(1047, 1) + +# Play a tone at 500Hz tone for 2 seconds and wait for it to finish +robot.power_board.piezo.buzz(500, 2, blocking=True) ~~~~~ ## Motors @@ -114,25 +116,24 @@ You can set the power of each motor on the board between -1 and 1. If you change the power of your motor too rapidly, the overcurrent protection may be triggered. ~~~~~ python -R.motor_board.motors[0].power = 1 -R.motor_board.motors[1].power = -1 +robot.motor_board.motors[0].power = 1 +robot.motor_board.motors[1].power = -1 ~~~~~ -Setting a motor to `COAST` is equivalent to power level `0`. +### Special motor values + +Setting a motor to `BRAKE` is equivalent to power level `0`. ~~~~~ python # This is the same operation -R.motor_board.motors[0].power = COAST -R.motor_board.motors[0].power = 0 +robot.motor_board.motors[0].power = BRAKE +robot.motor_board.motors[0].power = 0 ~~~~~ -### Braking Motors - -You can also brake a motor, which will quickly slow the motor. +`COAST` will stop applying power to the motors. This will mean they continue moving under the momentum they had before and slowly come to a stop. ~~~~~ python -R.motor_board.motors[0].power = BRAKE -R.motor_board.motors[1].power = -1 +robot.motor_board.motors[0].power = COAST ~~~~~ ## Servos @@ -140,8 +141,8 @@ R.motor_board.motors[1].power = -1 You can set the position of each servo output on the board between -1 and 1. ~~~~~ python -R.servo_board.servos[0].position = -1 -R.servo_board.servos[1].position = 1 +robot.servo_board.servos[0].position = -1 +robot.servo_board.servos[1].position = 1 ~~~~~ You can also set the position to `0`, which is the approximate centre. @@ -153,7 +154,15 @@ You can also set the position to `0`, which is the approximate centre. It can sometimes be useful to save a photo of what markers the robot can see: ~~~~~ python -R.camera.save("my-photo.png") # Save my-photo.png to the USB drive +robot.camera.save("my-photo.png") # Save my-photo.png to the USB drive +~~~~~ + +### Capturing an openCV array + +Take a photo using the webcam, and return the image data as an OpenCV array: + +~~~~~ python +frame = robot.camera.capture() ~~~~~ ### Looking for markers @@ -161,7 +170,7 @@ R.camera.save("my-photo.png") # Save my-photo.png to the USB drive You can take a photo with the camera and search for markers: ~~~~~ python -markers = R.camera.see() +markers = robot.camera.see() ~~~~~ There are various bits of information available about visible markers: @@ -172,44 +181,39 @@ for marker in markers: marker.id # The ID of the marker marker.size # Physical size of the marker in mm. - marker.distance # Distance away from the camera in mm + marker.pixel_centre # The co-ordinates of the centre of the marker + marker.pixel_corners # A list of corners of the marker - # Cartesian coords of the marker - marker.cartesian.x - marker.cartesian.y - marker.cartesian.z - - # Spherical coords of the marker - marker.spherical.rot_x - marker.spherical.rot_y - marker.spherical.dist + # Position of the marker + marker.position.distance # Distance away from the camera in mm + marker.position.horizontal_angle # angle to the marker in radians + marker.position.vertical_angle # angle to the marker in radians # Orientation of the marker - marker.orientation.rot_x - marker.orientation.rot_y - marker.orientation.rot_z - marker.orientation.rotation_matrix + marker.orientation.yaw + marker.orientation.pitch + marker.orientation.roll ~~~~~ -## Ruggeduino +## Arduino ### Setting the mode of a pin ~~~~~ python -R.ruggeduino.pins[4].mode = OUTPUT -R.ruggeduino.pins[4].mode = INPUT -R.ruggeduino.pins[4].mode = INPUT_PULLUP +robot.ruggeduino.pins[4].mode = OUTPUT +robot.ruggeduino.pins[4].mode = INPUT +robot.ruggeduino.pins[4].mode = INPUT_PULLUP ~~~~~ ### Digital Write -You can set the output for a pin of the Ruggeduino: +You can set the output for a pin of the Arduino: ~~~~~ python -R.ruggeduino.pins[4].mode = OUTPUT +robot.ruggeduino.pins[4].mode = OUTPUT -R.ruggeduino.pins[2].digital_write(True) -R.ruggeduino.pins[2].digital_write(False) +robot.ruggeduino.pins[2].digital_write(True) +robot.ruggeduino.pins[2].digital_write(False) ~~~~~ ### Digital Read @@ -217,9 +221,9 @@ R.ruggeduino.pins[2].digital_write(False) You can read a digital value from the pins of the Ruggeduino: ~~~~~ python -R.ruggeduino.pins[3].mode = INPUT +robot.ruggeduino.pins[3].mode = INPUT -value = R.ruggeduino.pins[3].digital_read() +value = robot.ruggeduino.pins[3].digital_read() ~~~~~ ### Analogue Read @@ -227,7 +231,9 @@ value = R.ruggeduino.pins[3].digital_read() You can read an analogue value from the analogue pins of the Ruggeduino: ~~~~~ python -value = R.ruggeduino.pins[A0].analogue_read() +robot.ruggeduino.pins[A0].mode = INPUT + +value = robot.ruggeduino.pins[A0].analogue_read() ~~~~~ ## Metadata @@ -237,13 +243,7 @@ The API also makes some information about where your code is running ### Starting Zone for a match ~~~~~ python -zone = R.zone # -> 0, 1, 2, or 3 -~~~~~ - -### Arena Information - -~~~~~ python -arena = R.arena # -> 'A' +zone = robot.zone # -> 0, 1, 2, or 3 ~~~~~ ### Robot Mode @@ -251,15 +251,24 @@ arena = R.arena # -> 'A' This is set to `COMP` when your robot is in a match. ~~~~~ python -robot_mode = R.mode # -> DEV or COMP +robot_mode = robot.mode # -> DEV or COMP ~~~~~ -### USB Key Path +### USB stick Path -This is the path to where your USB key is mounted. +This is the path to where your USB stick is mounted. You can use this to save files and information to the drive. ~~~~~ python -usb_key_path = R.usbkey # -> pathlib.Path +usb_key_path = robot.usbkey # -> pathlib.Path +~~~~~ + +### Is simulated + +A boolean value indicating whether or not the code is running in the simulator. +This value is True when in the simulator and False when on the robot. + +~~~~~ python +value = robot.is_simulated ~~~~~ From a91deb98ea30dc16fa11cd97c65d7eff9a07a1d6 Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 15 Sep 2023 19:32:11 +0100 Subject: [PATCH 61/81] Update battery page --- kit/batteries/index.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/kit/batteries/index.md b/kit/batteries/index.md index 569f87a0..6d32ddea 100644 --- a/kit/batteries/index.md +++ b/kit/batteries/index.md @@ -34,17 +34,6 @@ If you have any doubts or wish to request permission to use a battery, charger,
-## Storing Batteries - -When your batteries are not actively in use, they should be safely stored. -To do this you must disconnect the batteries from all electrical equipment, and place them in the battery safety bag. -You should then store the battery safety bag in a safe location. - -If you are not going to use the batteries for a long period of time, such as more than a week, then you should charge the batteries before hand. -By themselves, batteries will discharge very slowly, and over-discharging will lead to damage. -We recommend that you charge all batteries to at least 70% of capacity before being stored for a long period. - - ## Operating Batteries To use your batteries, you must connect them to the Student Robotics power board. @@ -67,12 +56,25 @@ The smaller lead is only used for voltage measurement when charging and must **n
-### Flat Batteries +## Storing Batteries -When the battery has been almost completely discharged, the Power Board will automatically turn off and the LED marked "Power / Flat Battery Indicator" in the [diagram](/docs/kit/power_board#BoardDiagram) will flash red and green. -You should immediately disconnect the battery, and begin charging it. -You should keep your second battery charged, so that you can immediately switch to using it. +When your batteries are not actively in use, they should be safely stored. +To do this you must disconnect the batteries from all electrical equipment, and place them in the battery safety bag. +You should then store the battery safety bag in a safe location, away from anything flammable and where it wont be physically damaged. + + +### Fully charged batteries + +Storing a battery at 100% charge puts stress on the battery, reducing its life. +If you are planning on storing you battery for a long time (greater than 1 week) its best to discharge it to approximately 70%. +This can be done by running your robot. + +### Flat batteries + +When the battery has been almost completely discharged, the Power Board will automatically turn off and the LED marked "Power / Flat Battery Indicator" in the [diagram](/docs/kit/power_board#BoardDiagram) will flash red and green. +You should disconnect the battery, and begin charging it. +By themselves, batteries will discharge very slowly, and over-discharging will lead to damage. Do not store a discharged battery for more than a few days without charging it, as it may discharge further and become damaged. From 75587775da5fc036da8156fd13f3d02cd2f1d3e3 Mon Sep 17 00:00:00 2001 From: JoshP Date: Fri, 15 Sep 2023 19:35:04 +0100 Subject: [PATCH 62/81] page formatting --- kit/batteries/hke4_charger.md | 9 +++------ kit/batteries/index.md | 10 +++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/kit/batteries/hke4_charger.md b/kit/batteries/hke4_charger.md index 391a207a..275d6302 100644 --- a/kit/batteries/hke4_charger.md +++ b/kit/batteries/hke4_charger.md @@ -9,8 +9,7 @@ HobbyKing HKE4 Chargers ![hobby king e4 charger]({{ site.baseurl }}/images/content/kit/battery_charger_HKE4.png "A HobbyKing E4 battery charger") Some kits will contain a black HobbyKing HKE4 charger. -This charger is simpler and easier to use than the iMAX B6 charger, - while retaining all of the functionality required for charging the batteries supplied with the kit. +This charger is simpler and easier to use than the iMAX B6 charger, while retaining all of the functionality required for charging the batteries supplied with the kit. The charging lead is cable-tied to the charger and you must not remove this cable. The charging lead is terminated with 4mm bullet connectors, which present a very high risk of shorting the battery if removed from the charger. @@ -41,7 +40,5 @@ You must then: 1. Unplug the battery from the charging lead, do not unplug the charging lead from the charger. 1. Unplug the charger from the mains. -Generally, the battery does not have to be charged to 100% every time you want -to use it. However, before storing the battery for longer periods of time, it -is best to charge it to approximately 70% because storing it with 100% charge -puts stress on the battery, reducing its life. +Generally, the battery does not have to be charged to 100% every time you want to use it. +However, before storing the battery for longer periods of time, it is best to charge it to approximately 70% because storing it with 100% charge puts stress on the battery, reducing its life. diff --git a/kit/batteries/index.md b/kit/batteries/index.md index 6d32ddea..804ebd33 100644 --- a/kit/batteries/index.md +++ b/kit/batteries/index.md @@ -28,22 +28,22 @@ If you have any doubts or wish to request permission to use a battery, charger, * Always place the batteries in the provided battery safety bag when charging or when being stored. * Always follow the charging checklist precisely to make sure the charger is correctly configured. See the [Charging Batteries](#charging-batteries) section for more details. -* Do not charge or use a damaged battery. * If a battery has any cuts, nicks, exposed copper on the wires or is bulging to the point of no longer being squishy, contact <{{ site.emails.kit-support }}> immediately. +* Do not charge or use a damaged battery. * Always leave the charging leads connected to the battery charger at all times.
## Operating Batteries -To use your batteries, you must connect them to the Student Robotics power board. -Do not tamper with the cable or connect the batteries to anything other than the power board (or the charger when charging). +To use your batteries, you must connect them to the Student Robotics Power Board. +Do not tamper with the cable or connect the batteries to anything other than the Power Board (or the charger when charging). -During operation, the battery is protected by over-current protection and a fuse in the power board. +During operation, the battery is protected by over-current protection and a fuse in the Power Board. If any equipment is short circuited, the over-current protection will activate - protecting the battery. In extreme circumstances the fuse may blow to prevent damage to the battery. This is an important safety feature: do **not**, under any circumstances, bypass the fuse. -The fuse is not user serviceable and if the fuse has blown then the power board must be replaced. +The fuse is not user serviceable and if the fuse has blown then the Power Board must be replaced. If you suspect the fuse has blown then please contact <{{ site.emails.kit-support }}> straight away. Mechanical damage to a battery can be dangerous, and a puncture or large force applied to a battery causes a serious risk of fire. From ec1cf5ff3a99fb76df6b34f9415006bd504bd692 Mon Sep 17 00:00:00 2001 From: Josh Perriman Date: Sat, 16 Sep 2023 09:10:43 +0100 Subject: [PATCH 63/81] Update img release for this years initial image release (#513) --- Rakefile | 2 +- _data/kit_versions.yml | 7 ++++++- _includes/updates-list.html | 2 +- kit/brain_board/python_libraries.md | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index a0626950..4d7db5d0 100644 --- a/Rakefile +++ b/Rakefile @@ -48,7 +48,7 @@ task :validate_kit_versions do data = YAML.load_file('_data/kit_versions.yml') data.each do |entry| actual = entry.keys.to_set - expected = ['version', 'released', 'changelog'].to_set + expected = ['version', 'released', 'link', 'changelog'].to_set optional = ['yanked'].to_set missing = expected - actual - optional extra = actual - expected - optional diff --git a/_data/kit_versions.yml b/_data/kit_versions.yml index feba8730..583b7eee 100644 --- a/_data/kit_versions.yml +++ b/_data/kit_versions.yml @@ -2,7 +2,12 @@ # The format of this file should be like such: # - version: 20xx.y.z # released: 2038-01-19 +# link: https://link-to-download.img # changelog: # - Changed something # - Changed another thing too -[] \ No newline at end of file +- version: 2024.1.0 + released: 2023-09-14 + link: https://github.com/srobo/robot-image/releases/download/2024.1.0/Student.Robotics.OS-image-2024.1.0.img.xz + changelog: + - Initial release for SR2024. diff --git a/_includes/updates-list.html b/_includes/updates-list.html index 65d3240a..cb27e3a6 100644 --- a/_includes/updates-list.html +++ b/_includes/updates-list.html @@ -8,7 +8,7 @@

{{ version }} ({{ kit_version.released | date_to_string }})

This version is no longer available.

{% else %} - + Download {{ version }} {% endif %} diff --git a/kit/brain_board/python_libraries.md b/kit/brain_board/python_libraries.md index e4c9f263..9d2abce0 100644 --- a/kit/brain_board/python_libraries.md +++ b/kit/brain_board/python_libraries.md @@ -33,7 +33,7 @@ The following python libraries are installed and available for use in your robot * [scikit-learn 1.3.0](https://pypi.org/project/scikit-learn) * [scipy 1.10.1](https://pypi.org/project/scipy) * [shapely 2.0.1](https://pypi.org/project/shapely) -* [sr-robot3 2024.0.0](https://pypi.org/project/sr-robot3) +* [sr-robot3 2024.0.1](https://pypi.org/project/sr-robot3) From ca0874f9a1824635109eb5b92ded46193f58d180 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 17 Sep 2023 14:17:12 +0100 Subject: [PATCH 64/81] Kit assembly page, minor tweaks --- _data/sidebar_tree.yaml | 2 +- tutorials/assembly.md | 40 ++++++++++++++++++---------------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/_data/sidebar_tree.yaml b/_data/sidebar_tree.yaml index fec1f323..0b036ea9 100644 --- a/_data/sidebar_tree.yaml +++ b/_data/sidebar_tree.yaml @@ -90,7 +90,7 @@ tree: title: Tutorials tree: - url: /tutorials/assembly - title: Assembly + title: Kit Assembly - url: /tutorials/python title: Python - url: /tutorials/getting_code_on_the_robot diff --git a/tutorials/assembly.md b/tutorials/assembly.md index 7e5a8a03..accf2c46 100644 --- a/tutorials/assembly.md +++ b/tutorials/assembly.md @@ -3,29 +3,29 @@ layout: page title: Kit Assembly --- -Kit Assembly -============ +# Kit Assembly -The Student Robotics kit contains a number of separate modules which must be connected together control your robot. Below is a diagram of what your assembled kit should look like. +The Student Robotics kit contains a number of separate modules which must be connected together control your robot. +Below is a diagram of what your assembled kit should look like. ![Diagram of the kit assembly]({{ site.baseurl }}/images/content/kit/kit_assembly.png) -Preparation ------------ +## Preparation The power board contains a connector for an external On|Off switch. If you do not intend to connect a switch then you must still make this connection. You can do this by connecting a short loop of wire between the two terminals of a suitable CamCon and placing it in the external On|Off port. +This wire loop can be of any thickness. -[Connections](#Connections) {#Connections} ------------ + +## Connections Each of the modules in the kit needs to be provided with both a control signal and power in order to operate. All the boards use USB to connect to the [Brain Board](/docs/kit/brain_board) so it can tell them what to do. If you run out of USB ports on the Brain Board itself, then you can use the provided USB hubs to provide more ports. -Most of the boards (with the exception of the [Ruggeduino](/docs/kit/arduino)) also need an additional power connection. +Most of the boards (with the exception of the [Arduino](/docs/kit/arduino)) also need an additional power connection. This should be provided from the [Power Board](/docs/kit/power_board). The following table summarises the connections which need to be made for each board. @@ -34,20 +34,17 @@ Board | Power ----------------------------------------|------------------------------ [Brain Board](/docs/kit/brain_board) | 12V, must be connected to L2 [Power Board](/docs/kit/power_board) | 12V, via the yellow XT60 to the [battery](/docs/kit/batteries) -[Motor Board](/docs/kit/motor_board) | 12V -[Ruggeduino](/docs/kit/arduino) | via USB -[Servo Board](/docs/kit/servo_board) | 12V +[Motor Board](/docs/kit/motor_board) | 12V from the power board +[Arduino](/docs/kit/arduino) | via the USB +[Servo Board](/docs/kit/servo_board) | 12V from the power board In order to connect the Brain, Motor and Servo Boards to the Power Board, you will need to create some power cables. -This should be done using the provided red and black _power wire_ (this is the thicker wire provided in the kit) - and a pair of green CamCon connectors. The blue power wire should only be used to connect motors to the motor board. +This should be done using the provided red and black _power wire_ (this is the thicker wire provided in the kit) and a pair of green CamCon connectors. +The blue power wire should only be used to connect motors to the motor board. _Remember you **must** use black wire for any ground connections as defined in the [Rules](/docs/rules)._ -When creating your power cables be sure to refer to the pages for each board - so that you connect the wires the right way around. -In our diagrams, the + outputs from the - the Power Board should be connected to the + - inputs on the board being powered. +When creating your power cables be sure to refer to the pages for each board so that you connect the wires the right way around. +In our diagrams, the + outputs from the the Power Board should be connected to the + inputs on the board being powered.
The Brain Board *must* be plugged in to the L2 port on the Power Board in order for the kit to start up. @@ -56,11 +53,10 @@ In our diagrams, the + outputs from the so is the only one which can be used to power the Brain Board.
-Video ------ -The following video contains an overview which covers the [brain board](/docs/kit/brain_board), -[power board](/docs/kit/power_board) and a [motor board](/docs/kit/motor_board). +## Video + +The following video contains an overview of connecting the boards which covers the [brain board](/docs/kit/brain_board), [power board](/docs/kit/power_board) and a [motor board](/docs/kit/motor_board).
Proceed at your own risk. We only provide limited support for these advanced features. @@ -17,18 +19,20 @@ Please bear in mind the following: - We may need you to upgrade the OS which will overwrite any changes. - Any modifications you make must be in line with [the rules]({{ site.baseurl }}/rules). - We reserve the right to inspect your Brain Board at any time. -- Feel free to tinker. If you are stuck, ask in [Discord]({{ site.baseurl }}/team_admin/discord), although we may be unable to help. +- Feel free to tinker. If you are stuck, ask in [Discord]({{ site.baseurl }}/team_admin/discord), we may be able to help. + ## SSH Access You can access the robot over SSH: ```shell -ssh robot@robot +ssh robot@robot.lan ``` Your SSH client will prompt you for a password, which is `robot`. + ## Terminal Interface A terminal interface with similar functionality to the web interface is available as `rtui`. @@ -56,4 +60,4 @@ Some commands let you set data as well as get it: - `arena B` - Set the arena to `B` - `mode COMP` - Set the mode to `COMP` -- `zone 2` - Set the zone to `2` \ No newline at end of file +- `zone 2` - Set the zone to `2` diff --git a/kit/brain_board/index.md b/kit/brain_board/index.md index 541cab15..bf1bf9cd 100644 --- a/kit/brain_board/index.md +++ b/kit/brain_board/index.md @@ -10,24 +10,30 @@ The below will automatically calculate the latest version number, to be used whe {% assign latest_version = site.data.kit_versions | first %} {% assign latest_version = latest_version.version %} -Brain Board -=========== + +# Brain Board A photo of a brain board -The Brain Board provided with your kit is what runs the code you write and controls the other peripheral boards. It consists of a [Raspberry Pi 4B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) and a Student Robotics KCH HAT. -The LEDs on the HAT display the current status of the robot and can be used to help debug your robot. There are also 3 RGB (Red, Green, and Blue) LEDs that you can control from your code. +The Brain Board provided with your kit is what runs the code you write and controls the other boards. +It consists of a [Raspberry Pi 4B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) and a Student Robotics KCH. + +The KCH is the board installed on top of the Pi, it powers the brain and has LEDs to show the current status of the robot. +There are also 3 RGB (Red, Green, and Blue) LEDs that you can control from your code. + ## Board Diagram ![brain board assembly diagram]({{ site.baseurl }}/images/content/kit/brain-diagram.png "The Brain Board Assembly") +[test](/docs/kit/brain_board#brain-board) + ### Indicators | LED | Meaning |------------------|------------------------- | 5V Power | The Brain Board is powered -| 12V Power | The KCH is powered +| 12V Power | 12V power is being provided to the KCH | Reverse Polarity | The 12V power is reversed | Boot Progress | Progress Bar for Brain Boot Progress | Code | A USB containing code is plugged in @@ -37,6 +43,7 @@ The LEDs on the HAT display the current status of the robot and can be used to h | Start | The Robot is waiting to start | OK | Shows the code status, see below table + ### OK LED The OK LED shows the status of your code using different colours. @@ -50,43 +57,27 @@ The OK LED shows the status of your code using different colours. | Green | Your code has finished without errors | Red | Your code has crashed + ## Powering the Brain Board Your Brain Board will not power on unless it's connected to the "L2" port on the Power Board. -This is because the rest of the power outputs are disabled until the usercode runs. - -## Flashing the SD card - -The SD card is located on the underside of the board underneath the green power connector. Grab the SD card with your fingers and simply pull it out of the slot. - -To fully update your Brain Board's software, or refresh it if you think it's not working correctly, you can flash our SD card image onto the microSD card in your Brain Board. +This is because the rest of the power outputs are disabled until your code runs. -To update the SD card, you'll need to download our image from the [updates page]({{ site.baseurl }}/kit/brain_board/robot_os). The latest version is `{{ latest_version }}`. -The flashing procedure is identical to flashing Raspberry Pi images. -### Etcher +## Student Robotics OS -We recommend using [Etcher](https://etcher.io), as it's simple to use, and available on Windows, macOS and Linux. If you're familiar with Raspberry Pis or other similar boards and have flashed images before with a different tool, that should also work. +The Raspberry Pi runs a custom operating system that contains the Student Robotics software. +The OS contains a set of pre-installed python libraries that you can use, the list of which can be found [here](./python_libraries). -![Etcher example]({{ site.baseurl }}/images/content/kit/etcher.png) +Sometimes the OS on your Brain Board may need to be updated, the steps to do this can be found on the [tutorial page]({{ site.baseurl }}/tutorials/update_brain). +The latest version of the OS is currently `{{ latest_version }}`. +See the [Student Robotics OS](./robot_os) page for full details of the different versions. -
-If you choose to use a tool other than Etcher, you may need to extract the `srobo-robot-{{ latest_version }}.img.xz` to `srobo-robot-{{ latest_version }}.img`. There are many tools available for this, e.g. [7-zip](https://www.7-zip.org/). -
- -### Flashing - -1. Open Etcher and select the `srobo-robot-{{ latest_version }}.img.xz` file you downloaded -2. Select your SD card from the devices window -3. Click 'Flash!' -4. When the flash is complete you should safely eject the SD card.
- Your computer may complain that the SD card is no longer readable, however - this is expected as the data being written to the SD card is not in a format - that either Windows or macOS can natively understand. ## Robot Settings -Some of the features on your robot are configured using a settings file, called `robot-settings.toml`. This file is automatically created on your USB drive the first time that you run any code on your robot. +Some of the features on your robot are configured using a settings file, called `robot-settings.toml`. +This file is automatically created on your USB drive the first time that you run any code on your robot. You can edit the settings file using your IDE or any text editor. @@ -100,22 +91,9 @@ The robot settings file contains the following settings: | `wifi_enabled` | Enables the WiFi | `true` | | `usercode_entrypoint` | The entry point to your Python code | `robot.py` | -If your `robot-settings.toml` is not valid, it will be automatically regenerated with valid settings. This will reset any settings you have changed back to their default values. We therefore recommend that you do not change your settings file before a competition match. - -In software version `2023.1.0` and later, an error file will be generated if your settings are invalid: `robot-settings-error.txt`. This file contains a message explaining the problem and a copy of your old settings. - - -## Firewall - -
-This documentation refers to a feature which is only available from software version `2023.1.0` and later. -
- -The Brain Board has a [network firewall](https://en.wikipedia.org/wiki/Firewall_(computing)) that prevents access to applications and services on the Brain Board unless it is required. - -If you are running code that uses networking, you will need to use a port from the range that is allocated for competitor use: +If your `robot-settings.toml` is not valid, it will be automatically regenerated with valid settings. +This will reset any settings you have changed back to their default values. +We therefore recommend that you do not change your settings file before a competition match. -| Protocol | Start of Range | End of Range | -|----------|----------------|--------------| -| TCP | 7000 | 8999 | -| UDP | 7000 | 8999 | +An error file will be generated if your settings are invalid: `robot-settings-error.txt`. +This file contains a message explaining the problem and a copy of your old settings. diff --git a/kit/brain_board/python_libraries.md b/kit/brain_board/python_libraries.md index 4c8231f6..c4acf4bb 100644 --- a/kit/brain_board/python_libraries.md +++ b/kit/brain_board/python_libraries.md @@ -3,15 +3,15 @@ layout: page title: Available Python Libraries --- -Available Python Libraries -========================== -Robot Kit ---------- +# Available Python Libraries + + +## Robot Kit The following python libraries are installed and available for use in your robot's software: - + * [april-vision 1.0.2](https://pypi.org/project/april-vision) * [astoria 0.11.1](https://pypi.org/project/astoria) * [cached-property 1.5.2](https://pypi.org/project/cached-property) @@ -56,8 +56,8 @@ The following python libraries are installed and available for use in your robot If you wish to use a library that isn't listed above, get in contact with us on Discord and have a chat with us about it. -Simulator ---------- + +## Simulator The following libraries will be available to your robot code during the competition. Note that for local development you will need to install these diff --git a/kit/brain_board/robot_os.md b/kit/brain_board/robot_os.md index b13b7c12..41bc2ef5 100644 --- a/kit/brain_board/robot_os.md +++ b/kit/brain_board/robot_os.md @@ -1,23 +1,30 @@ --- layout: page -title: Robot OS +title: Student Robotics OS --- -Robot OS -======== -TODO +# Student Robotics OS -# Updates +The Raspberry Pi runs a custom operating system that contains the Student Robotics software. +The OS contains a set of pre-installed python libraries that you can use, the list of which can be found [here](./python_libraries). -Keeping your kit up to date is very important. It enables us to deploy new features, as well as fix bugs. -Once you have downloaded the file you need, refer to the documentation on [updating your brain board]({{ site.baseurl }}/kit/brain_board#flashing-the-sd-card) to apply the update. +## Updates -Each update file is a complete upgrade. Each file contains the changes of those before it. If you need to jump up multiple versions, you can do so by using the latest file. +Keeping your kit up to date is very important, it enables us to deploy new features, as well as fix bugs. + +Below is a list of the versions released, once you have downloaded the file you need, refer to the steps on the [tutorial page](/tutorials/update_brain) to apply the update. + +Each update file is a complete upgrade. +Each file contains the changes of those before it. +If you need to jump up multiple versions, you can do so by using the latest file. + + +## OS Versions {% if site.data.kit_versions.size > 0 %} {% include updates-list.html %} {% else %} There are currently no updates available. -{% endif %} \ No newline at end of file +{% endif %} diff --git a/kit/brain_board/wifi.md b/kit/brain_board/wifi.md index 6a4002f8..f90784eb 100644 --- a/kit/brain_board/wifi.md +++ b/kit/brain_board/wifi.md @@ -3,23 +3,25 @@ layout: page title: WiFi --- -WiFi -==== + +# WiFi The Raspberry Pi that makes up your kit's brain board contains a WiFi radio which allows you to interface with and debug your robot. You can connect to your robot using any WiFi capable device (laptop, tablet, phone, etc.) -Connecting to Your Robot ------------------------- + +## Connecting to Your Robot During the boot process you should notice the green LED on the KCH (the board on top of the Pi) labelled "WiFi" turn on. -Your robot has now set up its very own WiFi network! It will initially have a name starting with `robot-ZZZ` followed by some random numbers. +Your robot has now set up its very own WiFi network! +It will initially have a name starting with `robot-ZZZ` followed by some random numbers. You can now connect to your robot in the same way you normally connect to a WiFi network. You will need a WiFi key to be able to connect and you can find this inside `robot-settings.toml` on the USB drive containing your code.
- If there is no robot-settings.toml on your USB drive, you can generate one by first running any code on your robot. For more information see our docs on Robot Settings. + If there is no robot-settings.toml on your USB drive, you can generate one by first running any code on your robot. + For more information see our docs on Robot Settings.
These details can also be printed using: @@ -27,25 +29,22 @@ These details can also be printed using: R.print_wifi_details() ~~~~~ -If you are having any problems connecting to your robot, just head on over to the Discord -and ask for help. +If you are having any problems connecting to your robot, just head on over to the Discord and ask for help. -Using the robot interface -------------------------- + +## Using the robot interface Once you have a WiFi connection, visit `http://robot.lan` in a web browser to see the robot interface. -The robot interface gives you the ability to remotely start the code on your robot, -as well as view the logs. +The robot interface gives you the ability to remotely start the code on your robot, as well as view the logs. ![The robot interface on desktop]({{ site.baseurl }}/images/content/kit/wifi_interface.png) + ### Viewing logs -The robot interface allows you to see all messages from your code sent by `print(...)` -statements (and anything else that outputs to standard output or standard -error). It will also show messages from the initialisation of the robot's -hardware, as well as any errors that occurred when running your code. +The robot interface allows you to see all messages from your code sent by `print(...)` statements (and anything else that outputs to standard output or standard error). +It will also show messages from the initialisation of the robot's hardware, as well as any errors that occurred when running your code. You can also see your robot's logs on the USB stick, in a file called `log.txt`. @@ -53,13 +52,8 @@ You can also see your robot's logs on the USB stick, in a file called ### Setting up the Robot's Environment The toolbar present at the top of the page, or bottom of the page on mobile, allows you to change the robot's starting zone and mode. -Changing the starting zone allows you to test how your robot handles being started in a -different zone. +Changing the starting zone allows you to test how your robot handles being started in a different zone. -The WiFi interface will only be fully accessible when your robot is started in dev mode and will be restricted in competition mode. - -Changing to competition mode causes the development markers to be unreadable, -and instead reads the competition markers, which are different. Therefore, you -should keep your robot in development mode. +Changing mode applies from the next time your code runs. -Changing this applies from the next time your code runs. +The WiFi interface will only be fully accessible when your robot is started in dev mode and will be restricted in competition mode. diff --git a/tutorials/update_brain.md b/tutorials/update_brain.md new file mode 100644 index 00000000..3a741e58 --- /dev/null +++ b/tutorials/update_brain.md @@ -0,0 +1,48 @@ +--- +layout: page +title: How to update your robot +--- + +{% comment %} +The below will automatically calculate the latest version number, to be used when mentioning the version in this page. +{% endcomment %} + +{% assign latest_version = site.data.kit_versions | first %} +{% assign latest_version = latest_version.version %} + + +How to update your robot +======================== + +The SD card is located on the underside of the board underneath the green power connector. +Grab the SD card with your fingers and simply pull it out of the slot. + +To fully update your Brain Board's software, or refresh it if you think it's not working correctly, you can flash our SD card image onto the microSD card in your Brain Board. + +To update the SD card, you'll need to download our image from the [updates page]({{ site.baseurl }}/kit/brain_board/robot_os). +The latest version is `{{ latest_version }}`. +The flashing procedure is identical to flashing Raspberry Pi images. + + +### Etcher + +We recommend using [Etcher](https://etcher.io), as it's simple to use, and available on Windows, macOS and Linux. +If you're familiar with Raspberry Pis or other similar boards and have flashed images before with a different tool, that should also work. + +![Etcher example]({{ site.baseurl }}/images/content/kit/etcher.png) + +
+If you choose to use a tool other than Etcher, you may need to extract the `srobo-robot-{{ latest_version }}.img.xz` to `srobo-robot-{{ latest_version }}.img`. +There are many tools available for this, e.g. [7-zip](https://www.7-zip.org/). +
+ + +### Flashing + +1. Open Etcher and select the `srobo-robot-{{ latest_version }}.img.xz` file you downloaded +2. Select your SD card from the devices window +3. Click 'Flash!' +4. When the flash is complete you should safely eject the SD card.
+ Your computer may complain that the SD card is no longer readable, however + this is expected as the data being written to the SD card is not in a format + that either Windows or macOS can natively understand. From 67d14123a3381383a406bd98fc3362eafe920718 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 2 Sep 2023 16:11:48 +0100 Subject: [PATCH 35/81] robot instead of R --- kit/brain_board/wifi.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kit/brain_board/wifi.md b/kit/brain_board/wifi.md index f90784eb..2fc28c89 100644 --- a/kit/brain_board/wifi.md +++ b/kit/brain_board/wifi.md @@ -26,7 +26,7 @@ You will need a WiFi key to be able to connect and you can find this inside `rob These details can also be printed using: ~~~~~ python -R.print_wifi_details() +robot.print_wifi_details() ~~~~~ If you are having any problems connecting to your robot, just head on over to the Discord and ask for help. From 05f7e1f9c2661308be555e3b7b02a701d86a5eaa Mon Sep 17 00:00:00 2001 From: JoshP Date: Sun, 3 Sep 2023 18:25:09 +0100 Subject: [PATCH 36/81] vision orientation page --- programming/vision/index.md | 28 +++++++++----------- programming/vision/orientation.md | 44 +++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/programming/vision/index.md b/programming/vision/index.md index d42b1472..9449e800 100644 --- a/programming/vision/index.md +++ b/programming/vision/index.md @@ -3,8 +3,7 @@ layout: page title: Vision --- -Vision -====== +# Vision Your robot is able to use a webcam to detect [fiducial markers](https://en.wikipedia.org/wiki/Fiducial_marker). Specifically it can detect [AprilTags](https://april.eecs.umich.edu/software/apriltag), using the `36H11` marker set. @@ -17,8 +16,8 @@ Using the marker poses and their locations, we can either calculate the location For information on markers, see the [markers page](./markers). -[Camera](#camera) {#camera} -=========================== + +## Camera The interface to the vision system is through the camera, accessible through `R.camera`. @@ -69,8 +68,8 @@ frame = robot.camera.capture() flipped = cv2.flip(frame, 0) ~~~~~ -[Frame argument](#frame_args) {#frame_args} -------------------------------------------- + +### Frame argument The slowest part of vision is capturing the image. You can use a frame with the other vision commands to avoid recapturing. @@ -92,8 +91,8 @@ robot.camera.save("photo.jpg", frame=frame) # Do some other vision algorithm with the OpenCV frame here ~~~~~ -[Field of View](#fov) {#fov} ----------------------------- + +### Field of View The Logitech C500 has a [field of view][fov] of 72° and the C270 has a field of view of 60°. @@ -104,8 +103,8 @@ Note that the axes are all defined relative to the camera. Since we have no way to know how you've mounted your camera, you may need to account for that in your usage of the vision system's data.

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 Z=V$h4miEL7IQ5-KTSFgG`N;0&{{u+_CQ|?a literal 31739 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 + inkscape:export-filename="mcv4b_board_diagram.png" + inkscape:export-xdpi="97.663254" + inkscape:export-ydpi="97.663254" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> + + + + + inkscape:snap-bbox-midpoints="false" + inkscape:showpageshadow="2" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1"> Motor 0 + style="font-size:18px;line-height:1.25">Motor 1 Motor 1 + style="font-size:18px;line-height:1.25">Motor 0 M0 SpeedM1 SpeedM1 SpeedM0 Speed + Serial ExpansionM0 OutputStatus + M1 Outputconnector + x="359.76059" + y="877.27576" + id="tspan503-2">Status From 6e282c6e2ad44f43c2259e789b6f290f50794517 Mon Sep 17 00:00:00 2001 From: JoshP Date: Sat, 26 Aug 2023 17:11:37 +0100 Subject: [PATCH 06/81] motor board - talk about status LEDs now in FW --- kit/motor_board.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kit/motor_board.md b/kit/motor_board.md index d3fc451e..5a9c52b9 100644 --- a/kit/motor_board.md +++ b/kit/motor_board.md @@ -9,7 +9,7 @@ Motor Board A phot of an un-cased motor board The Motor Board can be used to control two 12V DC motors. Your kit contains two of these boards to allow you to control up to four motors. These can be used for moving your robot, although don't feel you are limited to using them for this purpose. -The speed and direction of the two outputs are controlled independently through the USB connection. The board also needs power delivered from a 12V port on the Power Board in order to drive the motors. +The speed and direction of the two outputs are controlled independently through the USB connection. The board also needs power delivered from a 12V port on the Power Board in order to drive the motors. The motor board uses [pulse-width modulation][wiki-pwm] (PWM) to control the amount of power that is sent to the motors. @@ -21,11 +21,6 @@ Board Diagram ![motor board diagram]({{ site.baseurl }}/images/content/kit/mcv4b_board_diagram.png "The Motor Board") -