====== Книга набора «Робоняша» и исходный код проектов =====
Электронная версия инструкции из набора [[amp>product/robonyasha-iskra-js?utm_source=announce&utm_campaign=robot-njasha&utm_medium=wiki|«Робоняша»]].
Здесь собраны все исходные коды экспериментов, подсказки и хаки по прохождению набора.
{{ :robot:title.png?nolink |}}
===== Электронная версия =====
* {{:robot:robonyasha.pdf|Книга}} в формате .pdf
* {{:robot:poster-path-robo-002.pdf|Карта с чёрной линией}} для печати на листе формата А1
===== Эксперименты =====
==== 1. Прожектор ====
Давай подключим к Iskra JS простой модуль светодиода. Он будет служить нам прожектором, освещающим путь нашему роботу.
var projector = require('@amperka/led').connect(P8);
projector.turnOn();
==== 2. Сигнальная колонна ====
Привлечём к роботу побольше внимания, дадим знать всем окружающим о приближении робота. Сделаем для этого прожектор мигающим! Будем то включать, то выключать его.
var notice = require('@amperka/led').connect(P3);
notice.blink(0.1, 0.9);
==== 3. Сенсорный выключатель ====
Научимся включать и выключать прожектор с помощью сенсорной кнопки. Давай приспособим для этих целей цифровой датчик линии. Получится оптический бесконтактный выключатель.
var led = require('@amperka/led').connect(P3);
var button = require('@amperka/digital-line-sensor').connect(P4);
function myCoolButtonHandler() {
led.toggle();
}
button.on('white', myCoolButtonHandler);
==== 4. Миксер ====
Главная часть любого робота — двигатель. Подключим двигатель к устройству и научимся им управлять.
var motor = require('@amperka/motor');
var leftMotor = motor.connect(motor.MotorShield.M1);
leftMotor.write(-0.85);
==== 5. Одометр ====
Сколько метров проехал робот? На этот вопрос ответит одометр. Давай соберём его!
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’);
}
});
==== 6. Спидометр ====
Часто, помимо пройденного расстояния, интересно знать, с какой скоростью движется робот. Соберём спидометр.
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');
});
==== 7. Марсоход ====
Соберём, наконец, робота! Ни один ездящий робот не обходится без автономного питания и пульта управления.
Опечатка! На этапе сборки под номером 31 на странице 51 необходимо использовать деталь структора формата 5×5 шипов, а не 4×6 как на картинке.
{{ :robot:p51.jpg?nolink |}}
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;
}
});
Список всех кнопок ИК-пульта смотри в библиотеке [[js:ir-receiver|для ИК-приёмника]].
==== 8. Чистюля ====
Добавим немного автономности. Роботы-пылесосы, несмотря на кажущуюся сложность, устроены довольно просто. У них есть бампер с датчиками, чтобы чувствовать препятствия, а также инфракрасные датчики снизу, чтобы не падать с подиумов и лестниц. Мы можем с лёгкостью сделать робота, не падающего со стола.
Чтобы понять, как работать с аналоговыми сигналами, напишем небольшую программу. Будем каждые 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);
}
}
});
==== 9. Следопыт ====
Очень часто от роботов требуется доехать из одного места в другое, чтобы доставить груз. Для такой задачи роботу требуется знать маршрут движения.
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);
Повысить скорость движения и точность следования по линии можно несколькими способами:
* закрепить датчики линии ближе к полу;
* сделать Робоняше «фартук», закрывающий датчики от внешнего света (подойдёт бумага или непрозрачный скотч);
* увеличить переменную SPEED.
==== 10. Нехочуха ====
Скоро мы соберём робоняшу полностью. Прежде чем собрать голову, научимся поворачивать шею, чтобы робоняша мог смотреть из стороны в сторону.
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);
==== 11. Прилипала ====
Сделаем из робота верного домашнего питомца, который будет везде ходить по пятам. Задействуем для этого ультразвуковой дальномер.
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''.
{{ :robot:photo_2017-04-13_18-20-05.jpg?nolink&400 |}}
==== 12. Робо-сумо ====
Робо-сумо — популярный вид соревнований по робототехнике. В этом состязании участникам необходимо подготовить автономного робота, способного выталкивать противника за пределы ринга.
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);