Электронная версия инструкции из набора «Робоняша». Здесь собраны все исходные коды экспериментов, подсказки и хаки по прохождению набора.
Давай подключим к Iskra JS простой модуль светодиода. Он будет служить нам прожектором, освещающим путь нашему роботу.
var projector = require('@amperka/led').connect(P8); projector.turnOn();
Привлечём к роботу побольше внимания, дадим знать всем окружающим о приближении робота. Сделаем для этого прожектор мигающим! Будем то включать, то выключать его.
var notice = require('@amperka/led').connect(P3); notice.blink(0.1, 0.9);
Научимся включать и выключать прожектор с помощью сенсорной кнопки. Давай приспособим для этих целей цифровой датчик линии. Получится оптический бесконтактный выключатель.
var led = require('@amperka/led').connect(P3); var button = require('@amperka/digital-line-sensor').connect(P4); function myCoolButtonHandler() { led.toggle(); } button.on('white', myCoolButtonHandler);
Главная часть любого робота — двигатель. Подключим двигатель к устройству и научимся им управлять.
var motor = require('@amperka/motor'); var leftMotor = motor.connect(motor.MotorShield.M1); leftMotor.write(-0.85);
Сколько метров проехал робот? На этот вопрос ответит одометр. Давай соберём его!
var motor = require(‘@amperka/motor’); var leftMotor = motor.connect(motor.MotorShield.M1); leftMotor.write(-0.5); var encoder = require(‘@amperka/digital-line-sensor’).connect(P10); var RADIUS = 31; var WHEEL_LENGTH = 2 * Math.PI * RADIUS; var speed = 0; var counter = 0; var lastTime = getTime(); encoder.on(‘black’, function() { counter++; if (counter % 12 === 0) { var deltaTime = getTime() - lastTime; speed = WHEEL_LENGTH / deltaTime / 1000; lastTime = getTime(); print(speed.toFixed(2), ‘m/s’); } });
Часто, помимо пройденного расстояния, интересно знать, с какой скоростью движется робот. Соберём спидометр.
var motor = require('@amperka/motor'); var leftMotor = motor.connect(motor.MotorShield.M1); leftMotor.write(0.3); var encoder = require('@amperka/digital-line-sensor') .connect(P10); var RADIUS = 32; var WHEEL_LENGTH = 2 * Math.PI * RADIUS; var STEP_LENGTH = WHEEL_LENGTH / 12 / 1000; var speed = 0; var lastTime = getTime(); encoder.on('black', function() { var currentTime = getTime(); var deltaTime = currentTime - lastTime; speed = STEP_LENGTH / deltaTime; lastTime = currentTime; print(speed.toFixed(2), 'm/s'); });
Соберём, наконец, робота! Ни один ездящий робот не обходится без автономного питания и пульта управления.
Опечатка! На этапе сборки под номером 31 на странице 51 необходимо использовать деталь структора формата 5×5 шипов, а не 4×6 как на картинке.
var SPEED = 0.5; var marsohod = require(‘@amperka/robot-2wd’).connect(); var receiver = require(‘@amperka/ir-receiver’).connect(P3); receiver.on(‘receive’, function(code) { switch(code) { case receiver.keys.TOP: marsohod.go({l: SPEED, r: SPEED}); break; case receiver.keys.BOTTOM: marsohod.go({l: -SPEED, r: -SPEED}); break; case receiver.keys.LEFT: marsohod.go({l: 0, r: SPEED}); break; case receiver.keys.RIGHT: marsohod.go({l: SPEED, r: 0}); break; case receiver.keys.POWER: marsohod.stop(); break; default: break; } });
Список всех кнопок ИК-пульта смотри в библиотеке для ИК-приёмника.
Добавим немного автономности. Роботы-пылесосы, несмотря на кажущуюся сложность, устроены довольно просто. У них есть бампер с датчиками, чтобы чувствовать препятствия, а также инфракрасные датчики снизу, чтобы не падать с подиумов и лестниц. Мы можем с лёгкостью сделать робота, не падающего со стола.
Чтобы понять, как работать с аналоговыми сигналами, напишем небольшую программу. Будем каждые 100 миллисекунд читать значение напряжения на сигнальном пине датчика линии.
var analogSensor = require('@amperka/analog-line-sensor') .connect(A0); var showSignal = function() { print('signal:', analogSensor.read('V'), 'volts'); }; setInterval(showSignal, 100);
Загрузи код в Iskra JS, поставь Робоняшу на светлый стол, а затем нажми кнопку PLAY на пульте.
var FORWARD_SPEED = 0.3; var BACKWARD_SLOW_SPEED = 0.3; var BACKWARD_FAST_SPEED = 0.6; var BORDER_VALUE = 0.5; var intervalID; var cleaner = require(‘@amperka/robot-2wd’).connect(); var receiver = require(‘@amperka/ir-receiver’).connect(P3); var lineSensor = require(‘@amperka/analog-line-sensor’); var leftSensor = lineSensor.connect(A0); var rightSensor = lineSensor.connect(A1); function waitRollBack() { setTimeout(function() { cleaner.go({l: FORWARD_SPEED, r: FORWARD_SPEED}); intervalID = setInterval(clean, 10); },800); } function clean() { intervalID = clearInterval(intervalID); if (leftSensor.read() > BORDER_VALUE) { cleaner.go({r: -BACKWARD_SLOW_SPEED, l: -BACKWARD_FAST_SPEED}); waitRollBack(); } else if (rightSensor.read() > BORDER_VALUE) { cleaner.go({r: -BACKWARD_FAST_SPEED, l: -BACKWARD_SLOW_SPEED}); waitRollBack(); } else { cleaner.go({l: FORWARD_SPEED, r: FORWARD_SPEED}); intervalID = setInterval(clean, 10); } } receiver.on(‘receive’, function(code) { if (code === receiver.keys.PLAY) { if (!intervalID) { leftSensor.calibrate({white: leftSensor.read()}); rightSensor.calibrate({white: rightSensor.read()}); intervalID = setInterval(clean, 10); } else { cleaner.stop(); intervalID = clearInterval(intervalID); } } });
Очень часто от роботов требуется доехать из одного места в другое, чтобы доставить груз. Для такой задачи роботу требуется знать маршрут движения.
var SPEED = 0.3; var detective = require(‘@amperka/robot-2wd’).connect(); var lineSensor = require(‘@amperka/analog-line-sensor’); var leftSensor = lineSensor.connect(A0); var rightSensor = lineSensor.connect(A1); var lineFollower = require(‘@amperka/pid’).create({ target: 0, kp: 0.35, ki: 0.05, kd: 1.5, outputMin: -1.5, outputMax: 1.5 }); lineFollower.run(function() { var right = rightSensor.read(); var left = leftSensor.read(); var error = left - right; var output = lineFollower.update(error); detective.go({ l: SPEED + output, r: SPEED - output }); }, 0.02);
Повысить скорость движения и точность следования по линии можно несколькими способами:
Скоро мы соберём робоняшу полностью. Прежде чем собрать голову, научимся поворачивать шею, чтобы робоняша мог смотреть из стороны в сторону.
var neck = require('@amperka/servo').connect(P8); var angle = 90; var STEP = 5; setInterval(function() { if (angle <= 30 || angle >= 150) { STEP = -STEP; } angle = angle + STEP; neck.write(angle); }, 100);
Сделаем из робота верного домашнего питомца, который будет везде ходить по пятам. Задействуем для этого ультразвуковой дальномер.
var SPEED = 0.5; var DISTANCE_MIN = 10; var DISTANCE_MAX = 14; var sticker = require('@amperka/robot-2wd') .connect(); var ultrasonic = require('@amperka/ultrasonic').connect({ trigPin: P12, echoPin: P13 }); var neck = require('@amperka/servo').connect(P8); neck.write(90); /* Поэкспериментируй со значением угла поворота сервопривода от 0 до 180, чтоб голова робота смотрела прямо перед собой*/ function check(distance) { if (distance > DISTANCE_MAX) { sticker.go({l: SPEED, r: SPEED}); } else if (distance < DISTANCE_MIN) { sticker.go({l: -SPEED, r: -SPEED}); } else { sticker.stop(); } } setInterval(function() { ultrasonic.ping(function(error, value) { if (!error) { check(value); } }, 'cm'); }, 100);
Если при отключении от компьютера Робоняша ведёт себя странно, сделай из подручных материалов перемычку между контактами 5V
и VIN
.
Робо-сумо — популярный вид соревнований по робототехнике. В этом состязании участникам необходимо подготовить автономного робота, способного выталкивать противника за пределы ринга.
var FORWARD = 0.4; var BACKWARD = 0.6; var ROTATE = 0.3; var BORDER_VALUE = 0.5; var MAX_DISTANCE = 80; var sumoist = require(‘@amperka/robot-2wd’).connect(); var intervalID; var neck = require(‘@amperka/servo’).connect(P8); neck.write(113); var ultrasonic = require(‘@amperka/ultrasonic’).connect({ trigPin: P12, echoPin: P13 }); var lineSensor = require(‘@amperka/analog-line-sensor’); var leftSensor = lineSensor.connect(A0); var rightSensor = lineSensor.connect(A1); var save = false; function waitRollBack() { setTimeout(function() { sumoist.go({l: ROTATE, r: -ROTATE}); intervalID = setInterval(detectBorder, 10); },500); } var detectBorder = function() { intervalID = clearInterval(intervalID); if (leftSensor.read() > BORDER_VALUE) { save = true; sumoist.go({r: -BACKWARD, l: -BACKWARD}); waitRollBack(); } else if (rightSensor.read() > BORDER_VALUE) { save = true; sumoist.go({r: -BACKWARD, l: -BACKWARD}); waitRollBack(); } else { save = false; intervalID = setInterval(detectBorder, 10); } }; intervalID = setInterval(detectBorder, 10); var scan = function() { ultrasonic.ping(function(error, value) { if (!error && value < MAX_DISTANCE) { if (!save) { sumoist.go({l: FORWARD, r: FORWARD}); } } else { sumoist.go({l: ROTATE, r: -ROTATE}); } }, ‘cm’); }; setInterval(scan, 100);