Помогите найти причину неправильной работы кода

Тема в разделе "Iskra JS, Espruino, Йодо", создана пользователем wariored, 21 янв 2019.

  1. wariored

    wariored Нуб

    Здравствуйте!

    Играюсь с Робоняшей. Решил сделать повороты на определённый угол, а не просто "пока не остановишь". Идея с подбором времени для поворта и включением STOP по прошествии оного, кажется мне скучной и не универсальной. Поэтому решил поворачивать, опираясь на показания одометра, расстояние между колёсами и формулу 2*π*R, а дальше берём ту часть от длинны окружности, которая соответствует желаемому углу поворота.
    Код, в меру своего освоения JS, сделал.

    Код (Javascript):
    // Подключение библиотек
    var marsohod = require('@amperka/robot-2wd')
      .connect();
    var receiver = require('@amperka/ir-receiver')
      .connect(P1);
    var encoder_right = require('@amperka/digital-line-sensor')
      .connect(P10);
    var encoder_left = require('@amperka/digital-line-sensor')
      .connect(P9);

    // Постоянные переменные
    var SPEED = 0.8;
    var STEP = 1 / 12;
    var RADIUS_WH = 32;
    var WHEEL_LENGTH = 2 * Math.PI * RADIUS_WH;

    // Изменяемые переменные (значения по умолчанию)
    var revolutions_right = 0;
    var revolutions_left = 0;
    var distance = 0;
    var distance_right = 0;
    var distance_left = 0;
    var distance_turn = 0;

    // Одометр правого колеса
    encoder_right.on('black', function() {
      revolutions_right = revolutions_right + STEP;
      distance_right = revolutions_right * WHEEL_LENGTH;
    });

    // Одометр левого колеса
    encoder_left.on('black', function() {
      revolutions_left = revolutions_left + STEP;
      distance_left = revolutions_left * WHEEL_LENGTH;
    });

    // Вычисление среднего пройденного расстояния
    distance = ( distance_right + distance_left ) / 2;

    // Управление ИК пультом
    receiver.on('receive', function(code) {
      // Движение по стрелкам

      if (code === receiver.keys.RIGHT) {
        distance_turn = 0;
        marsohod.go({l: SPEED, r: -SPEED});
        for (;;) {
          if (distance_turn < 330) {
             distance_turn = Math.abs(distance_left - distance_turn);
             print(distance_turn);
          }
          else break;
        print("success");
        marsohod.stop();
        marsohod.go({l: SPEED, r: SPEED});
        }
      }
    }
    Но Робоняша не работает - происходит бесконечный цикл, из которого она не может выбраться, так как команда на начало движения колёс почему-то не отрабатывает. И значит показания одометра distance_left не растут, и цикл не прерывается.
    print(distance_turn); и print("success"); - добавлены для отладки.
    330 в if - это 2*π*R / 4 в параметрах Робоняши. Что должно соответствовать повороту на 90 градусов
    Помогите пожалуйста найти неправильное понимание мною JS в разрезе данного примера :)
     
  2. sys

    sys Злобный Буратино Модератор

    Не используйте for и while для организации бесконечных и условно бесконечных циклов - это приводит к зависаниям и ошибкам. Используйте для этого setInterval() или замыкания и setTimeout()...
     
  3. wariored

    wariored Нуб

    Здравствуйте!

    Спасибо за ответ и идею. Переделал кусок кода с setInterval вместо цикла - заработало:
    Код (Javascript):
     
    var RADIUS_BR = 105;
    var RADIUS_SMR = RADIUS_BR / 2;
    var BR_LENGTH = 2 * Math.PI * RADIUS_BR;
    var SMR_LENGTH = 2 * Math.PI * RADIUS_SMR;

    if (code === receiver.keys.RIGHT) {
        marsohod.go({l: SPEED, r: -SPEED});
        distance_turn = distance_left;
        var id_r = setInterval(function() {
          if ((((distance_left + Math.abs(distance_right)) / 2) - distance_turn) < (SMR_LENGTH / 4)) {
          }
          else {
             marsohod.stop();
             clearInterval(id_r);
          }
        }, 10);
      }
    Изменения ещё - 330 заменил на переменную SMR_LENGTH (так универсальней) и в вычислении пройденного колесом при повороте расстояния, беру значение обоих колёс по модулю и нахожу среднее из них (думаю, что так точнее будет). Есть, правда, небольшие проскакивания и повороты чуть больше, чем надо, иногда. Но грешу на плохую калибровку датчика энкодера - буду смотреть ещё.

    И, всё же, непонятен остался вопрос по циклам. Почему они то нормально не работают? То есть у меня мотор даже не начинал крутиться, хотя команда на его запуск стоит до цикла. А раз не крутится - то и счётчик расстояния не растёт, что и делает цикл бесконечным. Пробовал писать то же по другому:
    Код (Javascript):
      if (code === receiver.keys.RIGHT) {
        marsohod.go({l: SPEED, r: -SPEED});
        for (distance_turn = 0; distance_turn < 330; distance_turn = Math.abs(distance_left - distance_turn)) { }
        marsohod.stop();
        marsohod.go({l: SPEED, r: SPEED});
      }
    Всё равно не работало.
    Это непонимание мною JavaScript? Или особенности работы с циклами Iskra JS(Espruino)? Хотелось бы прояснить этот вопрос на будущее.
     
  4. Asper Daffy

    Asper Daffy Иксперд

    Код приведён не полностью, поэтому ничего сказать нельзя. Например, совершенно неясно где, когда и при каких обстоятельствах получает своё значение переменная code с которой у Вас там идёт сравнение. Вы вообще уверены. что Вы внутрь if попадаете? Может не попадаете, потому и мотор не стартует? Можете проверить?

    А если не попадаете, то попробуйте ответить на вопрос, Вы точно знаете, что делаете, когда используете "===" в сравнении? Вы ясно осознаёте, чем он отличается от "==" и что Вам нужно именно "==="?
     
  5. parovoZZ

    parovoZZ Гуру

    пападробности здесь
    https://habr.com/ru/post/138272/
     
  6. wariored

    wariored Нуб

    Здравствуйте!

    Спасибо за ответ.
    1. Код полностью приведён в первом сообщении. В третьем, для экономии места приведены только изменения к нему.
    2. Внутрь if я попадаю. И разницу между == и === понимаю. О попадании внутрь if говорит и тот момент, что с setInterval всё заработало, о чём и написано в третьем сообщении.
     
  7. Asper Daffy

    Asper Daffy Иксперд

    Тогда ответьте (себе самому) на мой вопрос
    Вы ведь сравниваете эту code с чем-то, а никакого значения ей не присваиваете. Или я не заметил?
     
    Последнее редактирование: 23 янв 2019
  8. wariored

    wariored Нуб

    Это же ИК-приёмник. Вот начало в коде приведённом в первом сообщении:
    Код (Javascript):
    receiver.on('receive', function(code) {
    А вот описание работы с ним на сайте Амперки

    Но это не совсем в контексте моего вопроса. Так как в if обработка точно попадает. Мне непонятно, почему конструкция с setInterval работает, а с циклом - нет?
     
  9. Asper Daffy

    Asper Daffy Иксперд

    Ага, это параметр, понятно. Ну, давайте смотреть на Ваш цикл.
    Код (C++):
    for (;;) {
      if (distance_turn < 330) {
        distance_turn = Math.abs(distance_left - distance_turn);
        print(distance_turn);
      }
      else break;
      print("success");
      marsohod.stop();
      marsohod.go({l: SPEED, r: SPEED});
    }
    Итак, что имеем:
    1)Проверяем условие - оно изначально ложно, потому переходим сразу к "print".
    2) после принт, останавливаем двигатель
    3) запускаем двигатель

    И снова - проверяем условие ....

    Похоже, Вы так быстро останавливаете двигатель после каждого запуска, что он бедняга и запуститься-то не успевает. нет? Зачем Вы это делаете?
     
  10. wariored

    wariored Нуб

    Хм. Мне видится несколько иначе.
    Запускаем двигатель на поворот ДО цикла:
    Код (Javascript):
    if(code === receiver.keys.RIGHT){
        distance_turn =0;
        marsohod.go({l: SPEED, r:-SPEED});
        for (;;) {..... тут код цикла }
    Условие изначально ИСТИННО (distance_turn установлен в 0, а потом должен расти по мере оборотов колеса и станет БОЛЬШЕ 330 не сразу):
    Код (Javascript):
    if (distance_turn < 330)
    print, остановка и далее запуск обоих моторов вперёд, идёт в после окончания цикла, который, ввиду его условной бесконечности, может быть прерван только при выполнении блока else, после того, как distance_turn станет больше 330:
    Код (Javascript):
      else break;
      print("success");
      marsohod.stop();
      marsohod.go({l: SPEED, r: SPEED});
    }
    Это косвенно подтверждается отсутствием "success" в выводе в консоль, при отладке. Тогда как по описанной вами логике, там были бы сплошные "success". А там только результат команды
    Код (Javascript):
    print(distance_turn);
    со значением "0".
     
  11. sys

    sys Злобный Буратино Модератор

    просты циклы for и while выполняются со скоростью что-то около 4000 итераций в секунду.... :)
     
  12. wariored

    wariored Нуб

    Скорость, конечно, пугающая :) Но ведь он всё равно должен был работать. Или на такой скорости на работу времени не остаётся? :D
     
  13. Asper Daffy

    Asper Daffy Иксперд

    Смотрите на код и считайте фигурные скобки
    Код (C++):
    for (;;) { // <-- НАЧАЛО ЦИКЛА
      if (distance_turn < 330) {
        distance_turn = Math.abs(distance_left - distance_turn);
        print(distance_turn);
      }
      else break;
      print("success");
      marsohod.stop();
      marsohod.go({l: SPEED, r: SPEED});
    } // <-- КОНЕЦ ЦИКЛА
    Видите?

    Программа работает так, как написана, а не так, как Вы хотели написать.