diff --git a/docs/static/tutorials/calibrate-gyro.png b/docs/static/tutorials/calibrate-gyro.png new file mode 100644 index 00000000..518bf69d Binary files /dev/null and b/docs/static/tutorials/calibrate-gyro.png differ diff --git a/docs/static/tutorials/move-straight-with-gyro.png b/docs/static/tutorials/move-straight-with-gyro.png new file mode 100644 index 00000000..1161e565 Binary files /dev/null and b/docs/static/tutorials/move-straight-with-gyro.png differ diff --git a/docs/tutorials.md b/docs/tutorials.md index 861c7081..2b25e2a9 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -25,6 +25,11 @@ Step by step guides to coding your @boardname@. "description": "Use the color sensor to follow line or detect colors", "url":"/tutorials/color-sensor", "imageUrl":"/static/tutorials/what-color.png" +}, { + "name": "Gyro", + "description": "Drive straight or turn more precisely with the gyro", + "url":"/tutorials/gyro", + "imageUrl":"/static/tutorials/calibrate-gyro.png" }, { "name": "Ultrasonic Sensor", "description": "Use the ultrasonic sensor to detect obstacles", diff --git a/docs/tutorials/calibrate-gyro.md b/docs/tutorials/calibrate-gyro.md new file mode 100644 index 00000000..364dd9bf --- /dev/null +++ b/docs/tutorials/calibrate-gyro.md @@ -0,0 +1,37 @@ +# Calibrate Gyro + +## Introduction @fullscreen + +The gyroscope is a very useful sensor in the EV3 system. It detects the rotation rate +which can be very useful to correct the trajectory of the robot and do precise turns. + +However, the sensor can be imprecise and subject to drifting. It is recommend to +calibrate your sensor at least once after starting your brick. You don't have to +recalibrate on every run. + +* [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf) +* [EV3 Driving Base with Gyro](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-gyro-sensor-driving-base-a521f8ebe355c281c006418395309e15.pdf) + + +## Step 1 Show ports + +Add the ``||brick:show ports||`` to see the status of the gyroscope. + +```blocks +brick.showPorts() +``` + + +## Step 2 Calibration + +Add a ``||sensors:calibrate gyro||`` block to calibrate the gyro. The block +detects if the sensor is present and does a full reset of the sensor if necessary. + +```blocks +brick.showPorts() +sensors.gyro2.calibrate() +``` + +## Step 3 Download and run @fullscreen + +Download this program to your brick and press the ENTER button. \ No newline at end of file diff --git a/docs/tutorials/gyro.md b/docs/tutorials/gyro.md new file mode 100644 index 00000000..6ad1405f --- /dev/null +++ b/docs/tutorials/gyro.md @@ -0,0 +1,19 @@ +# Gyro tutorials + +## Tutorials + +```codecard +[{ + "name": "Calibrate", + "description": "Make sure you gyro sensor is ready to use", + "cardType": "tutorial", + "url":"/tutorials/calibrate-gyro", + "imageUrl":"/static/tutorials/calibrate-gyro.png" +}, { + "name": "Move Straight", + "description": "Use the gyro to correct the trajectory of the robot", + "cardType": "tutorial", + "url":"/tutorials/move-straight-with-gyro", + "imageUrl":"/static/tutorials/move-straight-with-gyro.png" +}] +``` \ No newline at end of file diff --git a/docs/tutorials/move-straight-with-gyro.md b/docs/tutorials/move-straight-with-gyro.md new file mode 100644 index 00000000..3ab2c2f3 --- /dev/null +++ b/docs/tutorials/move-straight-with-gyro.md @@ -0,0 +1,61 @@ +# Move Straight With Gyro + +## Introduction @fullscreen + +Rotating using a wheel is not precise. The wheel can slip or the motors +can be slightly different. +With the help of the gyro you can detect and correct deviations in your trajectory. + +* [EV3 Driving Base](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-rem-driving-base-79bebfc16bd491186ea9c9069842155e.pdf) +* [EV3 Driving Base with Gyro](https://le-www-live-s.legocdn.com/sc/media/lessons/mindstorms-ev3/building-instructions/ev3-gyro-sensor-driving-base-a521f8ebe355c281c006418395309e15.pdf) + + +## Step 1 Calibration + +Add a ``||sensors:calibrate gyro||`` block in a ``||brick:on button enter pressed||`` block so that you can manually start a calibration process. Run the calibration +at least once after connecting the gyro. + +```blocks +brick.showPorts() +sensors.gyro2.calibrate() +``` + +## Step 2 Compute the error + +Make a new **error** variable and drag the ``||sensors:gyro rate||`` +and multiply it by -1. Since the rate shows the rotation rate, we will +counter it by negating it. + +```blocks +let error = 0 +brick.showPorts() +sensors.gyro2.calibrate() +while (true) { + error = sensors.gyro2.rate() * -1 +} +``` + +## Step 3 Steer with feedback + +Drag a ``||motors:steer motors||`` block under the variable and pass +the **error** variable into the turn ratio section. + +If the robot is turning right, the gyro will report a positive rotation rate +and the turn ratio will be negative which will the turn the robot left! + +```blocks +let error = 0 +brick.showPorts() +sensors.gyro2.calibrate() +while (true) { + error = sensors.gyro2.rate() * -1 + motors.largeBC.steer(error, 50) +} +``` + +## Step 4 Run it! + +Download to your brick and test out if the robot is going straight. + +This kind of technique is called a proportional controller; +it corrects the inputs (motor speed) with a feedback proportional to the output (rotation rate). \ No newline at end of file diff --git a/libs/gyro-sensor/docs/reference/sensors/gyro/calibrate.md b/libs/gyro-sensor/docs/reference/sensors/gyro/calibrate.md index a2c51142..e840d2fe 100644 --- a/libs/gyro-sensor/docs/reference/sensors/gyro/calibrate.md +++ b/libs/gyro-sensor/docs/reference/sensors/gyro/calibrate.md @@ -1,6 +1,6 @@ # calibrate -Reset the zero reference for the gyro to current position of the brick. +Detects if the gyro is drifting and performs a full reset if needed. ```sig sensors.gyro2.calibrate() diff --git a/libs/gyro-sensor/gyro.ts b/libs/gyro-sensor/gyro.ts index 8b91d777..57a4339b 100644 --- a/libs/gyro-sensor/gyro.ts +++ b/libs/gyro-sensor/gyro.ts @@ -73,7 +73,7 @@ namespace sensors { } /** - * Forces a calibration of the with light progress indicators. + * Detects if calibration is necessary and performs a full reset, drift computation. * Must be called when the sensor is completely still. */ //% help=sensors/gyro/calibrate @@ -95,6 +95,19 @@ namespace sensors { // give time for robot to settle pause(700); + // compute drift + this.computeDriftNoCalibration(); + if (Math.abs(this.drift()) < 0.1) { + // no drift, skipping calibration + brick.setStatusLight(StatusLight.Green); // success + pause(1000); + brick.setStatusLight(statusLight); // resture previous light + + // and we're done + this.calibrating = false; + return; + } + // calibrating brick.setStatusLight(StatusLight.OrangePulse); diff --git a/sim/state/gyro.ts b/sim/state/gyro.ts index 32d3750a..1b901e3a 100644 --- a/sim/state/gyro.ts +++ b/sim/state/gyro.ts @@ -1,5 +1,5 @@ namespace pxsim { - const enum GyroSensorMode { + export const enum GyroSensorMode { None = -1, Angle = 0, Rate = 1, @@ -20,6 +20,7 @@ namespace pxsim { } setAngle(angle: number) { + angle = angle | 0; if (this.angle != angle) { this.angle = angle; this.setChangedState(); @@ -27,6 +28,7 @@ namespace pxsim { } setRate(rate: number) { + rate = rate | 0; if (this.rate != rate) { this.rate = rate; this.setChangedState(); diff --git a/sim/visuals/controlView.ts b/sim/visuals/controlView.ts index d0d6d03b..7d185c87 100644 --- a/sim/visuals/controlView.ts +++ b/sim/visuals/controlView.ts @@ -49,11 +49,13 @@ namespace pxsim.visuals { } private updateDimensions(width: number, height: number, strict?: boolean) { + width = Math.max(0, width); + height = Math.max(0, height); if (this.content) { const currentWidth = this.getInnerWidth(); const currentHeight = this.getInnerHeight(); - const newHeight = currentHeight / currentWidth * width; - const newWidth = currentWidth / currentHeight * height; + const newHeight = Math.max(0, currentHeight / currentWidth * width); + const newWidth = Math.max(0, currentWidth / currentHeight * height); if (strict) { this.content.setAttribute('width', `${width}`); this.content.setAttribute('height', `${height}`); diff --git a/sim/visuals/controls/rotationSlider.ts b/sim/visuals/controls/rotationSlider.ts index cdc00f8b..82b879fa 100644 --- a/sim/visuals/controls/rotationSlider.ts +++ b/sim/visuals/controls/rotationSlider.ts @@ -1,13 +1,16 @@ namespace pxsim.visuals { + const MAX_RATE = 40; + const MAX_ANGLE = 360; export class RotationSliderControl extends ControlView { private group: SVGGElement; private slider: SVGGElement; + private text: SVGTextElement; private static SLIDER_WIDTH = 70; - private static SLIDER_HEIGHT = 78; + //private static SLIDER_HEIGHT = 78; getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) { this.group = svg.elt("g") as SVGGElement; @@ -23,6 +26,14 @@ namespace pxsim.visuals { pxsim.svg.child(this.slider, "circle", { 'cx': 9, 'cy': 50, 'r': 13, 'style': 'fill: #f12a21' }); pxsim.svg.child(this.slider, "circle", { 'cx': 9, 'cy': 50, 'r': 12.5, 'style': 'fill: none;stroke: #b32e29' }); + this.text = pxsim.svg.child(this.group, "text", { + 'x': RotationSliderControl.SLIDER_WIDTH / 2, + 'y': RotationSliderControl.SLIDER_WIDTH * 1.2, + 'text-anchor': 'middle', 'dominant-baseline': 'middle', + 'style': 'font-size: 16px', + 'class': 'sim-text inverted number' + }) as SVGTextElement; + const dragSurface = svg.child(this.group, "rect", { x: 0, y: 0, @@ -61,7 +72,17 @@ namespace pxsim.visuals { return; } const node = this.state; - const percentage = node.getValue(); + let percentage = 50; + if (node.getMode() == GyroSensorMode.Rate) { + const rate = node.getValue(); + this.text.textContent = `${rate}°/s` + // cap rate at 40deg/s + percentage = 50 + Math.sign(rate) * Math.min(MAX_RATE, Math.abs(rate)) / MAX_RATE * 50; + } else { //angle + const angle = node.getValue(); + this.text.textContent = `${angle}°` + percentage = 50 + Math.sign(angle) * Math.min(MAX_ANGLE, Math.abs(angle)) / MAX_ANGLE * 50; + } const x = RotationSliderControl.SLIDER_WIDTH * percentage / 100; const y = Math.abs((percentage - 50) / 50) * 10; this.slider.setAttribute("transform", `translate(${x}, ${y})`); @@ -73,8 +94,14 @@ namespace pxsim.visuals { const bBox = this.content.getBoundingClientRect(); let t = Math.max(0, Math.min(1, (width + bBox.left / this.scaleFactor - cur.x / this.scaleFactor) / width)) + t = -(t - 0.5) * 2; // [-1,1] + const state = this.state; - state.setRate((1 - t) * (100)); + if (state.getMode() == GyroSensorMode.Rate) { + state.setRate(MAX_RATE * t); + } else { + state.setAngle(MAX_ANGLE * t) + } } } diff --git a/targetconfig.json b/targetconfig.json index d5be7f6b..15b5b064 100644 --- a/targetconfig.json +++ b/targetconfig.json @@ -18,6 +18,7 @@ "Touch Sensor Tutorials": "tutorials/touch-sensor", "Color Sensor Tutorials": "tutorials/color-sensor", "Ultrasonic Sensor Tutorials": "tutorials/ultrasonic-sensor", + "Gyro Sensor Tutorials": "tutorials/gyro", "Infrared Sensor Tutorials": "tutorials/infrared-sensor", "FLL / City Shaper": "tutorials/city-shaper", "Design Engineering": "design-engineering",