Подвес для фотоаппарата

Тема в разделе "Глядите, что я сделал", создана пользователем BossTon, 1 апр 2013.

  1. BossTon

    BossTon Нерд

    Ага я им тоже завидую)) обычно у меня все похоже на
    [​IMG]
    А тут както все случайно получилось))) решил просто хоть раз в жизни попытаться ченить аккуратно сделать....
     
  2. BossTon

    BossTon Нерд

    Немного обновил конструкцию:
    [​IMG]
    1. Потенциометр.
    2. Блок шестерней для соединения потенциометра с валом. (одной обойтись не смог, нехватает места, а больших шестеренок не было.)
    Вид спереди:
    [​IMG]
    Осталось только прикрепить к к валу рейку для фотоаппарата. проблема как закрепить его на валу... всю голову сломал. Немогу найти подходящих по размеру деталей.
     
    nailxx нравится это.
  3. BossTon

    BossTon Нерд

    Катастрофически не хватает времени. Но я пытаюсь продолжать собирать Панорамную головку.
    С механикой вроде разобрался, застрял на этапе управления фотоаппарата.
    В сети нашел код управления антологичной панорамной головкой с таким же фотоаппаратом как и у меня, но на шаговых двигателях.
    В Скетче есть Подпрограмма подачи ИК сигналя для спуска зотвора камеры NEX5.
    Я решил использовать её чтобы проверить работоспособность, но у меня ничего не получилось.
    Не могу понять в чем дело. Запускаю скетч смотрю на Ик через камеру телефона-никакой реакции.
    Ну и соответственно фотиком он не управляет.
    Изучая код наткнулся на странность-У автора Ик светодиод подключается к 10 пину, но в скетче я не понимаю в какой момент отдается команда именно 10 пину подавать сигнал.
    Помогите разобраться.
    Ниже код полностью, но меня интересует только часть void IR_Sony()

    Код (Text):
    // Adafruit Motor shield library
    // copyright Adafruit Industries LLC, 2009
    // this code is public domain, enjoy!
    // Съемка сферической панорамы камерой NEX-5
    // www.rwpbb.ru, 2011
    #include <AFMotor.h>
    int pulscount=1;  // Счетчик числа импульсов в пакете ИК
    int puls = 4;   // Число пакетов ИК
     
    AF_Stepper motor1(48, 1); // Число шагов на один оборот мотора для 1 двигателя
    AF_Stepper motor2(200, 2);
     
    int buttonPin = 9;    // Ножка перемычки. Если 5В, то съемка с насадкой в два ряда
    int buttonState = 0;    // Состояние перемычки
    int wzat = 3000;        // Время нажатия кнопки затвора - 3 с
    int wp = 4000;        // Пауза перед спуском затвора - 4 с
    int mstepx = 280;       // Число шагов двигателя при повороте на единичный угол по горизонтали (360 - 1400)
    int nmstepx = 4;        // 5 снимков в ряду по горизонтали для съемки с насадкой
    int mstepy = 1700;      // Число шагов двигателя при повороте на единичный угол по вертикали  (180 - 5000)
    int nmstepy = 1;
    void setup()
    {
    TCCR1A = _BV(COM1A1) | _BV(COM1B1) ; // phase and frequency correct mode. NON-inverted mode
    TCCR1B = _BV(WGM13) | _BV(CS10);    // Делитель N = 1 - (_BV(CS10)); 8 - (_BV(CS11))
    OCR1B = 0;            // Скважность 102 - рабочий сигнал, 0 - сигнал отсутствует
    ICR1 = 210;         // Частота 16мГц/2*N*ICR1 = 16000кГц/(2*1*210)=38095Гц
    pinMode(10, OUTPUT);    // К 10 ножке подключен ИК светодиод АЛ107 через сопротивление 1 кОм
     
    pinMode(buttonPin, INPUT); // Включаем ножку на прием сигналов
    digitalWrite(buttonPin, LOW); //включаем подтягивающие резисторы на землю
    buttonState = digitalRead(buttonPin); // Чтение состояния перемычки
     
    motor1.setSpeed(40);        // Скорость вращения  40 оборотов в минуту
    motor1.release();           // Отключаем питание двигателя
    motor2.setSpeed(15);
    motor2.release();
    if (buttonState == LOW)
        {
          mstepx = 140;
          nmstepx = 9;
          mstepy = 1500;
          nmstepy = 2;
        }
        motor2.step((mstepy*nmstepy/2), FORWARD, DOUBLE);  // Поворачиваем вниз
        motor2.release();  
     
    for (int i=0; i <= nmstepx; i++)
        {
       
        IR_Sony();  // Нажимаем спусковую кнопку
       
        motor1.step(mstepx, FORWARD, DOUBLE);
        motor1.release();
        }
     
     
        motor1.step((mstepx/2), FORWARD, DOUBLE);  // Поворачиваем на пол угла по горизонтали
        motor1.release();
        motor2.step(mstepy, BACKWARD, DOUBLE);// Поворачиваем камеру вверх
        motor2.release();
          for (int i=0; i <= (nmstepx-1); i++)
              {
             
              IR_Sony();  // Нажимаем спусковую кнопку
                 
              motor1.step(mstepx, FORWARD, DOUBLE);
              motor1.release();
              }
       
        IR_Sony();  // Нажимаем спусковую кнопку
        motor1.step((mstepx/2), FORWARD, DOUBLE);  // Поворачиваем на пол угла по горизонтали
        motor1.release();
     
    if (buttonState == LOW)
        {
        motor2.step(mstepy, BACKWARD, DOUBLE);// Поворачиваем камеру вверх
        motor2.release();
          for (int i=0; i <= (nmstepx); i++)
              {
             
              IR_Sony();  // Нажимаем спусковую кнопку
                 
              motor1.step(mstepx, FORWARD, DOUBLE);
              motor1.release();
              }
       
        }
     
        motor2.step((mstepy*nmstepy/2), FORWARD, DOUBLE);  // Поворачиваем вниз в исходное состояние
        motor2.release();                        
           
    }
    void loop()  {
     
        }
     
    void IR_Sony()  { //Подпрограмма подачи ИК сигналя для спуска зотвора камеры NEX5
      digitalWrite(13, HIGH);//Индикация паузы светодиодом на плате
      delay(wp);// Пауза перед спуском затвора
      digitalWrite(13, LOW);
      pulscount=1;
      while (pulscount<puls)          // цикл пакета импульсов
       
        {OCR1B = 102;  // включаем светодиод
        delayMicroseconds(2432);      // светодиод горит микросекунд
        OCR1B = 0;  // выключаем светодиод
        delayMicroseconds(576);   // светодиод выключен  микросекунд
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(640);  
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(576);
        OCR1B = 102;  // включаем светодиод
        delayMicroseconds(1216);     
        OCR1B = 0;  
        delayMicroseconds(11136);
        pulscount++;
        }
        digitalWrite(13, HIGH);
        delay(wzat); // Время нажатия кнопки затвора
        digitalWrite(13, LOW);  
    }
     
  4. Megakoteyka

    Megakoteyka Оракул Модератор

    Вот здесь подробно описано, как реализован спуск затвора в данном проекте:
     
  5. BossTon

    BossTon Нерд

    Megakoteyka У меня немного другая проблема. Я собственно оттуда и брал код, он готовый и специально для NEX5, проблема то не в том что неправильно срабатывает команда, а она вообще не срабатывает. Т.Е. если загрузить этот скетч, и смотреть на сведодиод через камеру, невидно никакой его реакции- он просто не срабатывает.
    Я вот и не могу понять почему....
    я не понимаю что за переменная OCR1B = 102; // включаем светодиоди на какой пин отправляются данные комманды на включение или выключение светодиода.
    Автор в начале обозначил что на 10 пине у него ИК светодиод, и больше нигде не ссылается на него и никаких команд на него не отсылает, либо я просто скорее всего чего-то не понимаю, собственно и прошу помочь в поиске данной связи в скетче.
     
  6. Megakoteyka

    Megakoteyka Оракул Модератор

    Вот про ШИМ подробно: http://roboforum.ru/wiki/AVR:ШИМ

    А у камеры нет разъема для подключения пульта (спускового тросика)?

    Попробуйте просто постоянно включить светодиод и посмотреть на него через камеру.
    Кстати, иногда в камерах стоят ИК-фильтры - тогда Вы не увидите свечения. Попробуйте камеру мобильного телефона или дешевую мыльницу.
     
  7. BossTon

    BossTon Нерд

    Все. разобрался. Обратился напрямую к автору. Он пояснил что на 10 ножке это относится к Nano, а у меня Mega. Там просто нужно прописать 12 ножку.
    Все заработало. Осталось съездить поменять/купить новый потенциометр и установить его.
    Надеюсь на след неделе все соберу окончательно.
     
  8. mike_KH

    mike_KH Гик

    Только есть вопрос как вы потанциометр используете??
     
  9. Megakoteyka

    Megakoteyka Оракул Модератор

    Потенциометр используется для определения угла поворота вала сервы.
     
  10. sanik

    sanik Гик

    О при разборе принтера долны были наткнуться на датчики фотоинтерапторы отличная вещь для этого случая как раз и движки там подходящие я сам разрабатываю слайдер на двух движках один по рельсам гоняет второй как раз крутит фотик причем тут уже можно и посерьезней фотик вешать так как в принтере мощьные движки + родные шестеренки вот тут ветка по этому случаю спасибо Megakoteyka он помог разобраться с кодом а как датчики подключать если заинтересует я нарисую я скачивал сервис манул своего принтера и выискивал по схеме ) а управление с камеры я использую горячий башмак родной синхроимпульс, можно еще задействовать встроеную вспышку с дешевым студийным ик триггером его тоже пробовал работает... http://forum.amperka.ru/threads/Преобразование-значений.1390/
     
  11. BossTon

    BossTon Нерд

    принтер был простой.там 2 обычных двигателя а вот позиционировались при помощи оптопар (если я правильно их называю) еще разобрал з мышки.думал использовать оптопары из колесика.но это все неудобно при монтаже и требует большой аккуратности. а потенциаметр прост до безобразия.соединил 2 вала любым способом и все.
     
  12. sanik

    sanik Гик

    Да я просто к тому что например в моем принтере на шестеренке к "оптопаре" был диск с рисками позиционирование очень точное!!!Там риски оптические нанесены на расстоянии 0,3мм ! А называется этот датчик фотоинтерраптор..:)
     
  13. BossTon

    BossTon Нерд

    Всем привет!)) все, я сдаюсь.... казалось бы все готово подсказки перед глазами, в теории все элементарно, но нет, Я НИКАК НЕ МОГУ дописать этот .... код...(((
    Я вроде как написал код который автоматически снимает один ряд панорамы, т. е. камера делает 9 остановок и 9 кадров, чему безумно был рад...Козалось бы осталось дописать элементарную команду, чтобы после 9 кадров камера опускалась вниз и повторяла 9 кадров и потом еще раз... и все, я счастлив, но нет.... волосы вырываются с затылка ибо логика не работает. РЕбята, вы последняя надежда....
    Вот работающий код для горизонтального вращения
    Код (C):
    #define ANGLE_LIMIT 63
    #define ANGLE_STEP  7
    #include <Servo.h>
    #define STOP_SPEED  90
    #define CW_SPEED    (STOP_SPEED -10)
    #define CCW_SPEED   (STOP_SPEED + 10)
    int pulscount=1;  // Счетчик числа импульсов в пакете ИК
    int puls = 4;   // Число пакетов ИК
    int wzat = 3000;         // Время нажатия кнопки затвора - 3 с
    int wp = 4000;         // Пауза перед спуском затвора - 4 с
     Servo servo;      // Серва для вращения в горизонтальной плоскости
     void setup(){
       TCCR1A = _BV(COM1A1) | _BV(COM1B1) ; // phase and frequency correct mode. NON-inverted mode
      TCCR1B = _BV(WGM13) | _BV(CS10);   // Делитель N = 1 - (_BV(CS10)); 8 - (_BV(CS11))
      OCR1B = 0;              // Скважность 102 - рабочий сигнал, 0 - сигнал отсутствует
      ICR1 = 210;            // Частота 16мГц/2*N*ICR1 = 16000кГц/(2*1*210)=38095Гц
      pinMode(12, OUTPUT);  // К 12 (на mega) ножке подключен ИК светодиод АЛ107 через сопротивление 1 кОм
     servo.attach(10);
     }
    int currentAngle()
    {
        return map(analogRead(A10), 0, 1023, 0, 207);//где-то  55  один шаг для моей сервы
    }
     
    void loop()
    {
        for (int targetAngle = 0; targetAngle < ANGLE_LIMIT; targetAngle += ANGLE_STEP) {
     
            servo.write(CW_SPEED); // запускаем движение вперёд
            while (currentAngle() < targetAngle)
                ; // ждём пока докрутимся до нужного угла
     
            servo.write(STOP_SPEED); // стоп машина!
            delay(50); // ждём полной остановки
                IR_Sony();   // Нажимаем спусковую кнопку
        }
     
        servo.write(CCW_SPEED); // запускаем движение назад
        while (currentAngle() > 0)
            ; // ждём пока докрутимся до старта
    }
     void IR_Sony()  { //Подпрограмма подачи ИК сигналя для спуска зотвора камеры NEX5
      digitalWrite(13, HIGH);//Индикация паузы светодиодом на плате
      delay(wp);// Пауза перед спуском затвора
      digitalWrite(13, LOW);
      pulscount=1;  
       while (pulscount<puls)         // цикл пакета импульсов  
       
        {OCR1B = 102;   // включаем светодиод
        delayMicroseconds(2432);       // светодиод горит микросекунд
        OCR1B = 0;   // выключаем светодиод
        delayMicroseconds(576);    // светодиод выключен  микросекунд
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
         OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(640);    
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(576);
        OCR1B = 102;   // включаем светодиод
        delayMicroseconds(1216);       
        OCR1B = 0;   
        delayMicroseconds(11136);
        pulscount++;
        }
        digitalWrite(13, HIGH);
        delay(wzat); // Время нажатия кнопки затвора
        digitalWrite(13, LOW); 
    }
     
  14. BossTon

    BossTon Нерд

    Но как только я попытался дописать сюда пару строк, добавить еще одну серву и еще один цикл, все рушится.... ну вот где я лажанулся?? я убрал часть кода про спуск затвора, т.к она очень длинная и 100% работает (рабочий код в один ряд в предыдущем сообщении)
    Код (C):

    #define ANGLE_LIMIT_VERT 120
    #define ANGLE_LIMIT_HOR 56
    #define ANGLE_STEP_VERT 40
    #define ANGLE_STEP_HOR  7
     #include <Servo.h>
    #define STOP_SPEED  90
    #define CW_SPEED    (STOP_SPEED -10)
    #define CCW_SPEED   (STOP_SPEED + 10)
     Servo servo_Hor;
     Servo servo_Vert;
     void setup(){
     servo_Hor.attach(10);
     servo_Vert.attach(9);
     }
     int currentAngleVert()
     {
     return map(analogRead(A10), 0, 1023, 0, 200);//
     }
    int currentAngleHor()
    {
        return map(analogRead(A11), 0, 1023, 0, 207);// 55 = один оборот
    }
     
    void loop()
    {
       
    for (int targetAngleVert = 0; targetAngleVert < ANGLE_LIMIT_VERT; targetAngleVert += ANGLE_STEP_VERT)
           {
     
            servo_Vert.write(CW_SPEED); // запускаем движение вверх
            while (currentAngleVert() < targetAngleVert)    ; // ждём пока докрутимся до нужного угла
     
            servo_Vert.write(STOP_SPEED); // стоп машина!
            delay(100); // ждём полной остановки
           
            for (int targetAngleHor = 0; targetAngleHor < ANGLE_LIMIT_HOR; targetAngleHor += ANGLE_STEP_HOR)
                 {
     
                servo_Hor.write(CW_SPEED); // запускаем движение вперёд
                while (currentAngleHor() < targetAngleHor)
                ; // ждём пока докрутимся до нужного угла
     
                servo_Hor.write(STOP_SPEED); // стоп машина!
                delay(2000); // ждём полной остановки
            // Делаем КАДР IR SONY         
                  }
     
        servo_Hor.write(CCW_SPEED); // запускаем движение назад
        while (currentAngleHor() > 0); // ждём пока докрутимся до старта
          }
            servo_Vert.write(CCW_SPEED); // запускаем движение вниз
        while (currentAngleVert() > 0); // ждём пока докрутимся до старта
    }
     
     
     
  15. nailxx

    nailxx Официальный Нерд Администратор

    Хм… а как ведёт себя конструкция в этом случае?
     
  16. BossTon

    BossTon Нерд

    Ведет себя странно, по логике должен сделать шаг вверх и оборот с 9 остановками,
    а в реале получается он начинает подниматься и параллельно делать один шаг в горизонтальной плоскости, притом не останавливаясь (ну или просто это не заметно глазу). т.е. врубается servo_Vert и не останавливается а servo_Hor делает свои 9 шагов и возвращается обратно.
    думал проблема в вертикальном узле, но отдельно проверял потенциометр (работает нормально, выдает от 0 до 1022) делал скетч управления сервой потенциометром, все работает.... и даже запускал скетч горизонтальный из все тоже работает... 9 шагов и обратно... а вот все вместе не работает((( меня начинает дико бесить эта ситуация, по логике все должно работать...
    МОжет пойти другим путем? отталкиваясь от рабочего скетча горизонтального? Как бы туда вкорячить команду не замарачиваясь с циклом, чтоб после выполнения горизонтального цикла делалься шаг вверх и снова запускался цикл горизонтальный и так 3 раза
    ??
     
  17. nailxx

    nailxx Официальный Нерд Администратор

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

    Код (C):

    void loop()
    {

        for (int targetAngleVert = 0; targetAngleVert < ANGLE_LIMIT_VERT; targetAngleVert += ANGLE_STEP_VERT)
        {

            servo_Vert.write(CW_SPEED); // запускаем движение вверх
            while (currentAngleVert() < targetAngleVert)    ; // ждём пока докрутимся до нужного угла

            servo_Vert.write(STOP_SPEED); // стоп машина!
            delay(100); // ждём полной остановки    
        }
        servo_Vert.write(CCW_SPEED); // запускаем движение вниз
        while (currentAngleVert() > 0); // ждём пока докрутимся до старта
    }
     
     
  18. BossTon

    BossTon Нерд

    Хорошая идея, но думаю будет работать....они же идентичны... в общем буду пробовать..
    а нельзя никак вертикальной серве дать команду типа
    Код (C):

    int currentAngleVert = analogRead(A10)/10;
    int Vertstep=0
    //////

    servo_Vert.write(CW_SPEED);
    while (currentAngleVert() < (Vertstep+=25))

     
    И по идее каждый цикл вертикальная серва будет пытаться подняться на шаг=25
    а весь код запустить в цикле который исполняется 3 раза.....
    Это первое что возникло в моей не разбирающейся в программировании голове, просто у меня предчувствие что проблема связна с оператором return map(analogRead(A10), 0, 1023, 0, 200);
    Да и кстати объясните мне что делает return ... зачем map я прекрасно понимаю.
     
  19. Unixon

    Unixon Оракул Модератор

    return возвращает результат функции.
    Код (C):

    int foo(int x)
    {
     int y = map(...);
     return y;
    }
     
     
  20. Unixon

    Unixon Оракул Модератор

    Кстати, map() внутри целочисленный и поэтому из-за ошибок округления работает отвратительно.
    Есть несколько возможностей улучшить ситуацию.
    1) решение "в лоб": использовать вещественные числа с фиксированной или плавающей точкой. переводить число в вещественное перед преобразованием подобия, и округлять в целое после преобразования.
    2) решение в обход: пересчитываем ручками пределы и шаги из углов и т.п. в отсчеты АЦП (т.е. делаем этот же map заранее и наоборот) и далее работаем везде в единицах отсчетов, без использования map() вообще.