Содержание

Книга набора ​«Робоняша» и исходный код проектов

Электронная версия инструкции из набора «Робоняша». Здесь собраны все исходные коды экспериментов, подсказки и хаки по прохождению набора.

Электронная версия

Эксперименты

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 как на картинке.

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;
  }
});

Список всех кнопок ИК-пульта смотри в библиотеке для ИК-приёмника.

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);

Повысить скорость движения и точность следования по линии можно несколькими способами:

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.

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);