ARDUINO для детей: "как Лего"

  • Форум обновлён до новой версии, должны появиться всякие новые приколы для юзеров и администрации, в том числе поддержка пользовательских статей (модуль уже работает). К сожалению были потеряны некоторые аватарки и иконки, а также некоторые другие не очень важные части форума. В ближайшие дни форум будет находиться в режиме тестирования и настройки, если что - можно будет откатиться к версии от 4 апреля. Для возмещения сломанных аватарок к форуму подключен сервис Gravatar. Авторизация через VK временно не работает, зайти можно по своему логину-почте-паролю от форума и вручную привязать ВК, если это нужно.
  • Если у вас есть какой-то большой текст, который вы запостили и он потерялся в связи с бэкапом, но вам очень не хочется его переписывать - пишите мне на alex@alexgyver.ru, вышлю текст из более свежего бэкапа, который на форуме поднять не удалось!

Насколько полезна тема и стоит ли расписывать детально далее?

  • 1. Полезно и очень, внимательно слежу за дополнениями. Аффтар! пиши исчо!

  • 2. Может кому-то и полезно, лично мне нет, я уже вырос, обучать? Есть кружки, школы..

  • 3. Может кому и полезно, но сам повторять не собираюсь, полно готовых изделий.

  • 4. Фигня и мазохизм. Проще купить готовое, тема бесполезна.

  • 5. Тема вредна, учит делать своими руками - кто покупать будет то, что я продаю?

  • 5. Мне все равно. И вообще читать не умею, а картинок мало.


Результаты будут видны только после голосования.

Arhat109

Пользователь
9 Июн 2019
250
68
28
@Roman_S, ну .. кроме своего у меня есть очень небольшой опыт подтягивания детей из его соревновательных команд, а также небольшой опыт преподавания детям Ардуино .. правда очень небольшой.

В целом, Вы безусловно правы: задача прививания интереса лежит совершенно в иной плоскости.

Ну а насчет "хобби" .. как-то всю сознательную жизнь, любую свою деятельность старался обрастить "интересом" и превратить в хобби. К примеру, Вы в курсе сколько способов существует забрасывания штукатурного раствора обыкновенной совковой лопатой в бетономешалку?
(* мой ответ от 1982года: 23 :) *)
 

Arhat109

Пользователь
9 Июн 2019
250
68
28
Как и обещал добавил фотографии и описания. Пока только в пост №7 про моторы, под спойлером.

Нашел наш "универсальный мотор-редуктор с энкодером", держите:
УнимоторЭнкодер2.jpg

Габарит - 24х24х88мм (3х3х11 "лего дырок"). Имеет конструкцию Арудуредуктора (желтый), на последнюю ось одет оптический энкодер от Downsol strong motor - надо же было их как-то пристроить! :)
Крышка редуктора - съемная, что позволяет менять ему шестеренки и получать разные редукторы с передаточным отношением от 27:1 до 160:1 или около того, не помню. Есть "таблица Excel" где проводится полный расчет "автоматом" от передаточного отношения до скорости вращения и максимального момента в зависимости от примененного мотора.
Мотор тут тоже "сменный" :)
В среднем, в диапазоне 30..60:1 и применении самодельных моторчиков этот мотор-редуктор имеет около 3вт механической мощности на валу, что несколько больше классического EV3 Large motor, но имеет габариты EV3 servo motor.
Поскольку энкодер стоит на предвыходном валу, а там шестернеки не сменные, то скорость его вращения не зависит от передаточного числа и всегда примерно в 6 раз выше чем обороты выходного вала.
Точность позиционирования - около 6 градусов.
:)
 
Последнее редактирование:

Arhat109

Пользователь
9 Июн 2019
250
68
28
УнимоторЭнкодер3.jpg

Ещё его фотка, извиняюсь что плохо снято, других нету..
 

Arhat109

Пользователь
9 Июн 2019
250
68
28
Не понимаю как пользоваться редактором постов .. пост №7 так и не смог привести в порядок. Теперь там торчат все миниатюры в конце поста, а надо, чтобы они стояли на месте картинок. Попытка "удалить" приводить к удалению как миниатюры так и самой картинки из сообщения .. одну уже так снес.
Если из модераторов кто-то может помочь - буду благодарен. Спасибо.
 

Arhat109

Пользователь
9 Июн 2019
250
68
28
@Wan-Derer, так можно убрать картинку из текста. Пгобовал. :) Но, если их убирать из текста, то как-то надо втыкнуть на это месту миниатюру, которые вот внизу, скопом. А миниатюры снизу поста как-то поудалять нафик... как? :)

P.S. о.. спасибо за то что убрали миниатюры снизу поста. Тоже не понял "как" .. :)
 

Wan-Derer

Модератор
Команда форума
31 Июл 2018
1,306
230
63
Москва
wan-derer.ru
@Arhat109, Как текст. Если надо вставить картинку, ставишь курсор на то место куда надо вставить и в списке картинок жмёшь "вставить".
Если надо удалить картинку из текста, ставишь курсор перед картинкой и жмёшь DEL
 
  • Лойс +1
Реакции: Arhat109

Chaika

Пользователь
29 Окт 2019
1
1
0
Проект просто супер! Постораюсь повторить в ближайшее время.
 
  • Лойс +1
Реакции: Arhat109

Arhat109

Пользователь
9 Июн 2019
250
68
28
Пробуйте, интересно что у Вас получится. В Сети видел несколько таких вариантов, а освновном на базе обычного Лего. Их недостаток - недостаточная прочность соединений обычных лего-кирпичиков. Техник в этом плане гораздо интересней.
Но, там есть и сильное достоинство - удобство изготовления и оформления деталек.

Как обычно, с чего начать?

1. Возьмите за прототип наши платы Arduino-NANO в оформлении лего. Это самое простое (и полезное) как оказалось из всех ардуино-плат. Расширительный шилд и его схему стабилизатора - выложу в ближайшее время, просто я её не рисовал даже, делал по даташиту на LM2593-ADJ, но сейчас у нас есть стабилизаторы и лучше и меньше - RT8289 сильно рекомендую затариться именно этими. Держат 5в с токами нагрузки до 5А очень надежно, мелкие, греются слабо и из-за высокой частоты преобразования требуют элементы меньших габаритов.

2. Драйвер двигателя. Ну куда в этом проекте без моторчиков? :) Есть его схема, разводка для домашнего изготовления, выложено в соотв. посту. Тупо повторяете и получаете "лучшее на сегодня". Мосфетные полумосты АО4606, можно ещё АО4616 (эти даже получше) стоит заказать на Али штук 50 сразу. Драйверов может понадобится не 1шт :) У нас их с десяток и без работы не лежали практически.

3. Моторчики. Самое простое - начать с Lego Power Function серии. Large мотор к сожалению у нас оказался не разборным, и его пришлось делать как на фото - резать провод. Medium моторы этой серии разобрались легко, поэтому были сделаны иначе. Сами моторчики можно точно также заказать на Алиэкспрессе - это примерно вдвое дешевле чем покупать оригиналы.
Браться за изготовление мотор-редукторов на начальном этапе - сильно не рекомендую. Там требуется точность позиционирования осей мотора менее 0.1мм, и модуль шестеренок лучше брать 0.5 - их просто полно на али, если все же возьметесь.
Могу выложить размеры деталек под редукторы, которые у нас оказались "лучшими"..
Желтые мотор-редукторы от Ардуино проектов - лучшее, что можно купить готовое. Но их прямое применение в проекте крайне неудобно из-за неудачного габарита и размеров.

4. Датчики.
4.1 ИК датчики лучше делать самим. То, что продается на Али - барахло откровенное. Они очень просты, к ним нужен только ОУ, подойдет любой, самые дешевые это LM358 по 2 ОУ в одном корпусе. ИК-свето, фото- диоды лучше брать сразу комплектом, они будут лучше соответствовать друг другу. Тоже можно сразу брать партию в 50шт (по 1руб/шт! или около того).

4.2. Узв. датчик HCSR-04 .. тут надо не ошибиться. Их продают несколько разных схем, есть откровенное барахло. Мне как-то повезло сразу, но встречал в Сети много нареканий .. тут не помогу, ибо не знаю КАК выглядят правильные и чем отличаются от "упрощенных".

4.3. Датчики цвета - лучшие, как понимаю это TCS-3200. У них кривое управление, но тут можно использовать ту библиотеку, что есть в теме. Она работает с ними "на ура". К сожалению, в габарит лего плату придется слегка "подтачивать" по краям. Благо есть небольшой запас..

4.4. Датчики касания (кнопки) .. кажется выкладывал наши на базе кнопок с разбора старых мышей. У нас зарекомендовали себя с самой лучшей стороны. Мелкие, удобные. Сделал ещё фоток, выложу в ближайшее время последний релиз датчиков касания..

4.5. Гироскопы, компасы, погодные и прочие датчики .. не описывал, поскольку ничего особенного их адаптация не представляет, модули не требуют "внешних" деталек, как например те же кнопки или ИК-датчики .. просто приклеивали лего детальку и ставили куда-то внутрь изделия..

4.6. Микрофоны. О .. были у нас "уши" робота, которые вертелись на серводвигателе и разворачивались в сторону макс. уровня приема сигнала .. было смешно даже.

5. Серво двигатели.
5.1. Ну конечно же во-первых SG-90 или им подобные! :) Их оформление приводил в соотв. посту, но .. оно недостаточно удачное. Придумать что-то лучше у нас не получилось. Ну и слабоваты они для Лего все же.

5.2. К ним очень хорошее подспорье это сервы следующего класса, типа 3003 (5-7кг*см) и ещё мощнее типа MG595 кажется (ок 12-15 кг*см). С последними надо быть крайне осторожными, соблюдать ТБ, ибо они легко ломают лего детальки, равно как и прищемляют плохо подставленные пальцы. И те и другие имеют одинаковый типоразмер - стандартный для серв..

Управление сервами простое, но .. вот тут как раз нужен хороший стабилизатор самой платы, т.к. даже мелкая SG-90 в режиме удержания легко жрет до 0.8А и в одиночку высаживает в ноль типовой стабилизатор плат Ардуино, что приводит к неконтролируемым перезагрузкам и отказам ПО.

6. Светодиоды .. тут у нас использованы мелкие 3мм светодиоды, тоже закупались набором из 300шт .. они очень хорошо вставляются в обычные лего-кирпичики вместе с гасящим резистором, что позволяет их использовать прямым подключением к Ардуино. Тоже сделал фотки, выложу в ближайшее время.
Есть комплект по 4шт: белые, красные, желтые, зеленые и синие.
Есть ещё 3-х цветные, но мы их так и не запилили в лего-детали...

7. Приемники-передатчики .. мы пользовали только модули на 433мгц, но они нам не понравились, возможно нам не повезло, они есть очень разные, поэтому и не описываю их тут.. к ним были взяты "джойстики", но тоже не понравились из-за очень малого хода.

P.S. Если хотите сделать что-то мощное на базе Мега2560 .. то сильно рекомендую мою плату (тут есть тема) с возможностью расширения ОЗУ до 512 килобайт. Но .. это уже "высший пилотаж", тут надо будет переходить в тут тему и консультировать уже там :)

Если есть вопросы - пишите, не стесняйтесь, можно тут, можно в личку.
 
  • Лойс +1
Реакции: Wan-Derer

Arhat109

Пользователь
9 Июн 2019
250
68
28
По ультразвуковым датчикам, дополню:

1. Их недостаток - типовой способ измерения дальности, тиражированный везде, где только можно. Во-первых, он - кривой откровенно и измеряет очень не точно, во-вторых, он просаживает исполнение программы пока не закончит замер, что достаточно критично для практически любого применения в самобеглых тележках даже для задач "объезд препятствия".

Замер надо делать исключительно или по прерываниям или на базе таймера (режим захвата сигнала). В этом случае точность замера составляет единицы миллиметров даже на расстояниях под 5 метров. И такой способ замера НЕ тормозит программу - замер идет "фоном".

2. Ещё один недостаток - широкая диаграмма срабатывания, что намного сильнее влияет на их точность. Решается путем одевания на излучатель и приемник фетровых конусных насадок, глушаших сигнал в боковом направлении. Но, это надо делать очень аккуратно, т.к. при сильном ограничении направленности излучателя и приемника падает чувствительность и дальность.

В остальном датчик очень надежный, проблем не вызывает. п.2. можно игнорить, особого интереса не представляет. Делалось в плане экспериментов что можно улучшить в этом девайсе .. в общем-то и так хорош. :)
 
  • Лойс +1
Реакции: Wan-Derer

Arhat109

Пользователь
9 Июн 2019
250
68
28
Ну и ещё:

Датчики линейноо перемещения деталек. У нас под них пошли в переделку пружинные амотризаторы лего. Амортизатор разбирается, внутрь вставляется кусок гвоздя в качестве подвижного сердечника, и сверху наматывается катушка очень тонким проводом. Изменение индуктивности при подвижках регистрируется ПО Ардуино.

Применение: датчик потери опоры ногой робота. Например, когда он пытается свалиться за край стола и опуская ногу .. не находит опоры. :)
 
  • Лойс +1
Реакции: Wan-Derer

Arhat109

Пользователь
9 Июн 2019
250
68
28
Ну и ещё. На все эти эксперименты требуется приличный запас самих лего-деталек. Решений несколько:

1. Алиэкспресс с аналогами. Есть разного качества, лучшее что нам понравилось наборы Lepin. но, надо критически выбирать продавана, смотреть отзывы. Попадаются подделки под Лепин :) Стоимость примерно в 2 раза ниже оригинала, есть крутые наборы с электрикой серии Power Function, например такие наборы как Lepin 20006, 20007, 20085, MOC Shaman (Lepin 23011), МОС Maroder (23007) и др. с кучей электрики и р/у от Power Function.

2. Докупка поштучная того, что надо. Тут могу порекомендовать сайтик"Лего поштучно" (раздел Parts) там можно выбирать "продавец из России", чтобы не париться с разного рода "преобразованиями" и доплатами за пересылку. Есть вполне вменяемые продавцы, с которыми работал на протяжении нескольких лет.
Там, есть шанс нарваться на неоригинал, но небольшой. В основном это распродажа из Лего-магазинов по всей стране разного рода некомплектов, неликвидов и непроданных наборов "поштучно" .. если разобраться с сайтом, то можно отслеживать изменения и докупаться раза в 2-3 дешевле чем наборами лего в магазинах.
Рекомендую, но это муторно, особенно на начальном этапе. :)
 
Последнее редактирование:

Arhat109

Пользователь
9 Июн 2019
250
68
28
Пока вставлю как новый пост, позже возможно перенесу куда следует то что нашел и зафотал на телефон, уж как смог, не стреляйте в пианиста, он играет как умеет :)

Новые и последние датчики касания. На контактах кнопки от мыши припаян резистор, номинал не помню, что-то около 4.7кОм:
Датчик касания новый.jpg

Последняя версия ИК-датчика с регулировочным резистором и отдельным выводом под освещение ИК-светодиодом. Можно как тут, воткнуть дополнительный резистор, чтобы не так ярко светило (базовое ограничение внутри - около 50мА) и/или подключать на управляющую ногу, чтобы включать его только в момент замера:
ИК-регулировка1.jpg

Первая версия светодиодов. Гасящий резистор под термоусадкой:
Светодиод ранний.jpg

Линейка светодиодов. Изготавливалась специально под машинку для соревнований. На каждый датчик - свой светодиод:
Светодиоды блок5.jpg

Те самые 3мм светодиоды для светофоров и прочего применения. Оказались очень удобны. 3мм светодиод, чисто входит в стандартное отверстие кирпича 1х1х1 лего, а гасящий резистор SMD - во втором кирпиче. На лего кирпич одевается прозрачный круглый светофильтр все из того же лего, что придает законченный вид изделию.
Тут гасящие резисторы подбирались специально так, чтобы яркость свечения разных цветов была примерно одинаковой. Где-то лежит табличка .. найти бы. :)
Светодиоды новые.jpg

Ну вот .. в основном вроде бы всё зафотал.
 
Последнее редактирование:
  • Лойс +1
Реакции: Старик Похабыч

Arhat109

Пользователь
9 Июн 2019
250
68
28
Нашел ПО нашей тележки по линии. Выкладываю код управления моторами с защитой от сквозных токов и одновременным управлением группой моторов. Переделал под явное описание групп моторов. Компилируется, остальное не гонял заново после переделки на "группы":
C:
/**
* Микропаузы переключения драйвером мотора туда-обратно в микросекундах
* Первая - время отключения нижних мосфетов драйвера - более 500нсек!
* Вторая - время отключения верхних мосфетов - более 2мксек! В среднем, втрое дольше..
* Тут задано больше для надежности..
*/
#define DRV_MICROSTOP     3
#define DRV_MICRODIR      (DRV_MICROSTOP * 3)

/** Направления вращения мотора @see drvIsRun(num); */
#define DRV_ISSTOP   0
#define DRV_FORWARD  1
#define DRV_BACK    -1

/** Допустимая минимальная скорость вращения ABS(Pwm), если надо.. */
#ifndef DRV_MIN_SPEED
  #define DRV_MIN_SPEED 0
#endif

/** допустимая наибольшая скорость мотора ABS(Pwm): [-255..+255] */
#ifndef DRV_MAX_SPEED
  #define DRV_MAX_SPEED 255
#endif

/** Описание мотора и текущая скорость */
typedef struct _dvr_motors_ {
  uint8_t dir;    // пин направления
  uint8_t pwm;    // пин скорости (ШИМ)
  int     speed;  // текущая скорость
  int     old;    // "предыдущая скорость"
  bool    change;
} DrvMotors;

/** Приведение указателя к структуре и получение адреса нужного мотора */
#define ptrDrvMotor(p)     ((DrvMotors *)(p))
#define ptrDrvMotors(p,n) (((DrvMotors *)(p))+(n))

/**
* Подключено всего моторов к проекту (драйвера на 2 или 4 мотора или сколько моторов всего)
* по умолчанию - 2 мотора.
*/
#ifndef DRV_MAX_MOTORS
  #define DRV_MAX_MOTORS    2
#endif

/**
* @global - Единый массив всех моторов проекта для всех драйверов, если их больше 1
*
* По умолчанию всего 2 мотора: к 4,5 и 6,7 пинам НАНО(328)
*/
#ifndef motors
DrvMotors motors[DRV_MAX_MOTORS] =
{
  { 4, 5, 0, 0, false},
  { 7, 6, 0, 0, false}
};
#endif

/** Удобства для: */
#define drvPinDir(n)     (motors[(n)].dir)
#define drvPinPwm(n)     (motors[(n)].pwm)
#define drvSpeed(n)      (motors[(n)].speed)
#define drvOldSpeed(n)   (motors[(n)].old)
#define drvIsChange(n)   (motors[(n)].change)

// ==================================== DEBUG SECTION ========================================== //

/**
* Проверка(вывод) данных по моторам в монитор
*
* @param bool isAll -- выводить всё или только скорости?
*/
void drvPrintMotors( bool isAll)
{
  for(int8_t i=0; i<DRV_MAX_MOTORS; i++)
  {
    if( isAll ){ Serial.println(); }

    Serial.print("m="); Serial.print(i,DEC);

    if( isAll ){
      Serial.print(", chg="); Serial.print(motors[i].change, DEC);
      Serial.print(", old="); Serial.print(motors[i].old,    DEC);
    }

    Serial.print(", spd="); Serial.print(motors[i].speed, DEC);
  }
}

// ==================================== PRIVATE SECTION ========================================== //
// но вдруг кому(когда) понадобится ..

/** private: Физический останов мотора без изменения данных о нем: */
#define _drvStop(num)                \
{                                    \
  digitalWrite(drvPinPwm(num), LOW); \
  delayMicroseconds(DRV_MICROSTOP);  \
}

/**
* private: Переключение заданного мотора c защитой от сквозного тока по его текущей скорости
* .. останавливает мотор физически, и НЕ меняет скорость у мотора!
*/
void _drvSwitchStop(uint8_t num)
{
   _drvStop(num);
   if( drvSpeed(num) >= 0 ){ digitalWrite(drvPinDir(num), LOW);  }
   else                    { digitalWrite(drvPinDir(num), HIGH); }
   delayMicroseconds(DRV_MICRODIR);
}

/** private: Включает текущую скорость физически и сохраняет её как .old */
void _drvSetSpeed(uint8_t num)
{
  analogWrite( drvPinPwm(num), abs(drvSpeed(num)) );
  drvOldSpeed(num) = drvSpeed(num);
}

/** private: Проверяет надо ли переключать мотор */
#define _drvMustChange(num) ((drvSpeed(num) < 0  && drvOldSpeed(num) >= 0) || (drvSpeed(num) >= 0 && drvOldSpeed(num) < 0))

// ==================================== PUBLIC METHODS ========================================== //

/** Возращает направление вращения мотора: -1 "назад", 0 - "останов", 1 - "вперед" */
#define drvIsRun(num) (drvSpeed(num) > DRV_MIN_SPEED? DRV_FORWARD : (drvSpeed(num) < -DRV_MIN_SPEED ? DRV_BACK : DRV_ISSTOP ))

/** Контроль скорости мотора по допустимым границам при управлении */
void drvMapSpeed(uint8_t num)
{
  if( drvSpeed(num) > DRV_MAX_SPEED      ){ drvSpeed(num) = DRV_MAX_SPEED; }
  if( drvSpeed(num) < -DRV_MAX_SPEED     ){ drvSpeed(num) = -DRV_MAX_SPEED; }
#if DRV_MIN_SPEED > 0
  // только если задана минимальная скорость вращения, иначе не зачем:
  if( abs(drvSpeed(num)) < DRV_MIN_SPEED ){ drvSpeed(num) = 0; }
#endif
}

/** Прибавлялка скорости мотору с контролем превышения пределов */
#define drvAddSpeed(num, step) \
{                              \
  drvSpeed(num) += step;       \
  drvMapSpeed(num);            \
}

/** Уменьшалка скорости мотора с контролем пределов */
#define drvSubSpeed(num, step) \
{                              \
  drvSpeed(num) -= step;       \
  drvMapSpeed(num);            \
}

/**
* public: Установить заданный мотор на новую скорость с переключением направления если надо
*/
void drvRunMotor(uint8_t num, int speed)
{
  drvSpeed(num) = speed;

  if( _drvMustChange(num) ){ _drvSwitchStop(num); }

  _drvSetSpeed(num);
}

/** public: остановить заданный мотор */
#define drvStopMotor(num) (drvRunMotor(num, 0))

// ================================ Групповое управление моторами ============================== //
// Время управления группой моторов практически одинаково с затратами на один мотор, в силу однократного
// включения пауз для переключения моторов из группы, также как и в управлении одним мотором.
// Группа - это количество моторов из глобала motors[] и их индексы в нем.
// Соответственно, группа может быть не привязана к одному драйверу мотора, но такое желательно.

/**
* Группа моторов, для единовременного управления @see grpMoveMotors(group);
* по умолчанию в системе 1 драйвер с 2-я моторами, которые и входят в группу по умолчанию
*/
typedef struct _grp_motors_ {
  uint8_t count;
  uint8_t *motors;
} GrpMotors;
/** Приведение указателя к заданной группе моторов */
#define ptrGrpMotors(p, num) ((GrpMotors *)(p) + num)

/** по умолчанию определяем 1 группу моторов с 2-я моторами по умолчанию 0 и 1 из глобала motors[] */
#ifndef motorGroups

uint8_t _grpWheels[2] = {0,1}; // определяем номера моторов "ведущие колеса" как 0 и 1 из глобала motors[]

GrpMotors motorGroups[1] = {
  {2, _grpWheels}
};

/** .. и совместимость с кодом тележки по линии */
#define drvMoveMotors() grpRunMotors( &motorGroups[0] )
#define drvStopMotors() grpStopMotors( &motorGroups[0] )
#define drvAbsStop(num) grpAbsStop( &motorGroups[0], num )
#endif

/**
* public: Одновременное(!) управление всеми моторами группы через MOSFET-драйвер(а)
*
* Самостоятельно контролирует переключение моторов вперед/назад и выдерживает все паузы переключений ОДНОВРЕМЕННО.
* Помнит предыдущую установленную скорость моторов.
*
* Назначение: максимально синхронное управление группой моторов.
*/
void grpRunMotors( GrpMotors * group )
{
  bool isStop = false;
  uint8_t num;
  uint8_t * motor;

  // 1. Надо ли переключать какой-либо мотор группы? и выключаем сразу тут
  num   = group->count;
  motor = group->motors;
  do{
    if( (drvIsChange(*motor) = _drvMustChange(*motor)) ){ // проверяем результат присваивания! не ошибка..
      isStop = true;
      digitalWrite(drvPinPwm(*motor), LOW);
    }
    motor++;
  }while(--num);

  // 2. Если останавливали мотор(ы) - держим паузу один раз
  if( isStop ){
    delayMicroseconds( DRV_MICROSTOP );

    // 2.1. .. меняем направление. Какому-то было точно надо..
    num   = group->count;
    motor = group->motors;
    do{
      if( drvIsChange(*motor) ){
        if( drvSpeed(*motor) >= 0 ){ digitalWrite( drvPinDir(*motor) , LOW );  }
        else                       { digitalWrite( drvPinDir(*motor) , HIGH ); }
      }
      motor++;
    }while(--num);
    // 2.2. .. держим паузу один раз для всех
    delayMicroseconds( DRV_MICRODIR );
  }

  // 5. И только теперь задаем скорость моторов и сохраняем предыдущую
  num   = group->count;
  motor = group->motors;
  do{
    _drvSetSpeed(*motor++);
  }while(--num);
}

/** public: останов всех моторов какие есть */
void grpStopMotors(GrpMotors *group)
{
  uint8_t num     = group->count;
  uint8_t * motor = group->motors;

  do{
    drvSpeed(*motor++) = 0;
  }while(--num);
  grpRunMotors(group);
}

/** public: Установка единой скорости моторам группы без управления драйвером! */
void grpSetSpeed(GrpMotors *group, int newSpeed)
{
  uint8_t num = group->count;
  uint8_t *motor = group->motors;

  do{
    drvSpeed(*motor++) = newSpeed;
  }while(--num);
}

/**
* public: ABS торможение всеми моторами группы, "жрет" по 30мсек на цикл торможения!
*
* @TODO Переработать под конечные автоматы из ka.h
*/
void grpAbsStop(GrpMotors * group, uint8_t absNum)
{
  do{
    grpStopMotors(group); // останов "на выбег"
    delay(15);
    grpSetSpeed(group, -160);
    grpRunMotors(group); // крутим назад
    delay(15);
  }while( --absNum );
  grpStopMotors(group); // моторы группы остаются "на выбег"
}

/**
* в Setup(): останавливаем моторы и выставляем их "вперед" с 0 скоростью
* .. перенесено из кода тележки для общности ..
*/
void setupMotors()
{
  uint8_t num=DRV_MAX_MOTORS-1;

  do{
    pinMode(drvPinPwm(num), OUTPUT);
    digitalWrite(drvPinPwm(num), LOW);

    pinMode(drvPinDir(num), OUTPUT);
    digitalWrite(drvPinDir(num), LOW);

    drvOldSpeed(num) = 0;
    drvSpeed(num)    = 0;
    drvIsChange(num) = false;
  }while(num--);
}
P.S. Упс .. переименовал функцию, заметил, исправил .. в т.ч. подчистил setupMotors().

P.P.S. Собственно так мы работаем с драйверами моторов из поста №8
 
Последнее редактирование:

Arhat109

Пользователь
9 Июн 2019
250
68
28
Шапка файла (побоялся что не влезет по объему поста):
C:
/**
 * Модуль работы с моторами и драйвером к ним "Ардуино как Лего".
 *
 * Плата драйвера не имеет аппаратной защиты от сквозных токов через силовые ключи моторов.
 * Основное назначение: управление моторами с учетом задержек на переключение силовых ключей.
 * При остановке мотора по линии PWM=0 (N-канал) реальная задержка ключа драйвера менее 1мксек.
 * При изменении направления (линия DIR, P-канал) реальная задержка ключа в районе 3мксек.
 * Здесь взято с запасом для надежности: + стекание токов с индуктивности моторов.
 *
 * Если ничего не задано до включения файла - предполагается создание типовой
 * глобальной структуры motors[2] на 2 мотора и работа с ними.
 *
 * DRV_MIN_SPEED - та минимальная скорость, при которой мотор ещё вращается под вашей нагрузкой реально.
 * Добавлено, дабы зря не расходовать батарейки.
 *
 * Скорость измеряется в единицах ШИМ (0..255) как "вперед" (больше нуля, >0) так и назад (меньше нуля, <0)
 * поэтому число целое.. нулевая скорость тоже считается как "вперед" при переключении направлений.
 *
 * @author Arhat109-20171009,
 * 20191208 -- добавлены комментарии, раздельное управление моторами.
 * 20191221 -- выделена работа с группой моторов как отдельного элемента. Моторы теперь можно собирать в группы.
 *
 * Группы моторов. Как их объявлять в ПО - показано тут.
 * По умолчанию создается группа из двух моторов _grpWheels,
 * которая и указывается в глобальном списке групп motorGroups[] как единственная из двух моторов.
 *
 * Функция ABS-торможения рассчитана на нашу тележку по линии. Параметры - подобраны экспериментально.
 */
 

Arhat109

Пользователь
9 Июн 2019
250
68
28
Блок кода обработки линейки датчиков линии (пост №9) из 5-и штук:

C:
/**
 * Модуль работы с датчиками линии
 *
 * @author Arhat109-20171009
 */

// количество усредняемых замеров
#define MAX_AVERAGE    3

// длительность в мсек. периода автонастройки уровней датчиков
#define MAX_AUTOLEVEL   10000

// всего датчиков линии. Оно же и ошибка направления - слева, + справа
// ВСЕГДА нечетное число .. важно!
#ifndef MAX_SENSORS
  #define MAX_SENSORS    5
#endif

/** данные по датчикам 12 байт на датчик */
typedef struct _sensor_ {
  uint8_t pin;
  int8_t  dir;   // "расстояние" датчика от середины: <0 - влево, >0 - вправо
  int     black; // автонастройка: минимальное черное
  int     white; // автонастройка: максимальное белое
  int     lvl;   // автонастройка: уровень между белым и черным;
  int     val;   // замер
  int     adc;   // результат преобразования в: 0 - белое, 1 - черное
} Sensors;
#define ptrSensor(p, n) (((Sensors *)(p))+n)

#ifndef sensor
  Sensors sensor[MAX_SENSORS] = {
    { A7, -4, 1024, -1, 0, 0, 0}, // самый левый
    { A6, -2, 1024, -1, 0, 0, 0},
    { A5,  0, 1024, -1, 0, 0, 0},
    { A4,  2, 1024, -1, 0, 0, 0},
    { A3,  4, 1024, -1, 0, 0, 0}, // самый правый
  };
#endif

#define snPin(p)        (sensor[(p)].pin)
#define snBlack(p)      (sensor[(p)].black)
#define snWhite(p)      (sensor[(p)].white)
#define snVal(p)        (sensor[(p)].val)
#define snLvl(p)        (sensor[(p)].lvl)
#define snAdc(p)        (sensor[(p)].adc)
#define snDir(p)        (sensor[(p)].dir)

/**
 * Вывод в монитор содержимого всех датчиков для проверок
 */
void printSensors()
{
  for( uint8_t i=0; i<MAX_SENSORS; i++){
    Serial.println();
    Serial.print("s="); Serial.print(i, DEC);
    Serial.print(", P="); Serial.print(snPin(i), DEC);
    Serial.print(", D="); Serial.print(snDir(i), DEC);
    Serial.print(", B="); Serial.print(snBlack(i), DEC);
    Serial.print(", W="); Serial.print(snWhite(i), DEC);
    Serial.print(", L="); Serial.print(snLvl(i), DEC);
    Serial.print(", V="); Serial.print(snVal(i), DEC);
    Serial.print(", A="); Serial.print(snAdc(i), DEC);
  }
}

/**
 * Начальная установка датчиков и зачистка данных по ним
 */
void setupSensors()
{
  for( uint8_t i=0; i<MAX_SENSORS; i++ ){
    pinMode( snPin(i), INPUT);
    snBlack(i) = 1024;
    snWhite(i) = -1;
    snVal(i) = snLvl(i) = snAdc(i) = 0;
  }
}

/** усреднение "бегущим средним" */
void getSensorsRun()
{
  for( uint8_t n=MAX_AVERAGE; n; n--){
    for( uint8_t i=0; i<MAX_SENSORS; i++){
      snVal(i) = (analogRead( snPin(i) )*5 + snVal(i)*3) / 8; // к-ты подбирать по устойчивости, если надо!
      snAdc(i) = (snVal(i) > snLvl(i)? 0 : 1);
    }
  }
}

/** усреднение арифметическое */
void getSensorsAvg()
{
  static int adcs[MAX_SENSORS][MAX_AVERAGE];
  int        summ;

  for( uint8_t n=MAX_AVERAGE; n; n--){
    for( uint8_t i=0; i<MAX_SENSORS; i++){
      adcs[i][n] = analogRead( snPin(i) );
    }
  }
  for( uint8_t i=0; i<MAX_SENSORS; i++){
    summ = 0;
    for( uint8_t n=MAX_AVERAGE; n; n--){
      summ += adcs[i][n];
    }
    snVal(i) = summ / MAX_AVERAGE;
    snAdc(i) = (snVal(i) > snLvl(i)? 0 : 1);
  }
}

/**
 * Чтение всех датчиков "оптом"
 * Усреднение удобно делать только на 4 замерах - деление заменяется свдигом!
 */
void getSensors()
{
  getSensorsRun();
}

/** Расчет уровней белого, черного и среднего на каждый вызов */
void calcLevel()
{
  getSensors();
  for(uint8_t i=0; i<MAX_SENSORS; i++)
  {
    if( snVal(i) < snBlack(i) ){ snBlack(i) = snVal(i); }
    if( snVal(i) > snWhite(i) ){ snWhite(i) = snVal(i); }
    snLvl(i) = (snWhite(i) - snBlack(i))/2 + snBlack(i);
  }
}

/**
 * Процедура автонастройки уровня датчиков
 * моргает всеми лампочками 2 раза быстро, настройка MAX_AUTOLEVEL сек, моргает 2 разf медленно
 * Пока настраивается горит средний светодиод
 */
void autoLevel()
{
  uint32_t time;

  blinkYellow(2, 150, 350);
  time = millis();
  while( millis() - time < MAX_AUTOLEVEL){
    blinkBlue(1,20,0);
    calcLevel();
  }
  blinkYellow(2, 500, 500);
}

/**
 * Проверяет как стоит машинка:
 * Считаем среднее кроме среднего датчика и если:
 * а) средний датчик показывает значительно меньше остальных, то он "на линии". Читаем уровни из EEPROM
 * б) иначе считаем что "все датчики на белом" - запускаем автонастройку
 * !!! Предполагается что все датчики показывают примерно одинаково на белом/черном !!!
 */
void getLevels()
{
  int avg = 0;
  int sensor0 = 0;

  getSensors();
  for( uint8_t i=0; i<MAX_SENSORS; i++){
    if( i != (MAX_SENSORS)/2 ){
      avg += snVal(i);
    }
  }
  avg /= (MAX_SENSORS - 1);
  sensor0 = snVal( (MAX_SENSORS)/2 );
//Serial.print(", sen0="); Serial.print((MAX_SENSORS)/2, DEC);
//Serial.print(", avg="); Serial.print(avg, DEC);
//Serial.print(", val0="); Serial.print(sensor0, DEC);
//Serial.println("");

  if( abs(avg - sensor0 ) > avg * 3/8 ){
    // стоим на линии, читаем EEPROM, моргаем средним и поехали:
    blinkBlue(4, 350, 150);
    readEEPROM(snEEPROM, (uint8_t *)sensor, sizeof(sensor));
  }else{
    // нет линии, моргаем крайними, настраиваем и сохраняем в EEPROM:
    blinkRed(3, 350, 150);
    autoLevel();
    writeEEPROM(snEEPROM, sensor, sizeof(sensor));
  }
}

/**
 * Считает величину ошибки линии от середины машинки
 * влево < 0, вправо > 0, ==0 -- линия посередине
 */
void calcDir()
{
  int      summ = 0;
  int8_t   count = 0;

//  Serial.println("");
  for( uint8_t i=0; i<MAX_SENSORS; i++ ){
    summ += snAdc(i) * snDir(i);
    count += snAdc(i);
    if( snAdc(i) == 1 ){
      digitalWrite(snLeds[i], HIGH); // датчик видит линию, включаем его светодиод
    }else{
      digitalWrite(snLeds[i], LOW); // не видит, выключаем.
    }
  }
//  Serial.print("summ="); Serial.print(summ, DEC);
//  Serial.print(", count="); Serial.print(count, DEC);

  // смотрим что получилось: если есть датчики на линии, то ошибка = сумма расстояний поделить на число датчиков на линии
  // иначе: линия потеряна и надо смотреть "где была" в прошлый замер
  prevDir = dir;
  if( count>0 ){
    dir = summ/count;
  }else{
    if     ( lastDir>0 ){ dir =  MAX_SENSORS; } // линия была справа - потерялась там же..
    else if( lastDir<0 ){ dir = -MAX_SENSORS; } // линия была слева ..
    else{
      // упс.. ехали прямо и потеряли линию .. продолжаем прямо
      dir = 0;
    }
  }

  // направление изменилось? нет: считаем сколько так уже едем, да: сохраняем текущее как "последнее" (last..), начинаем заново
  if( prevDir == dir ){
    dirCount++;
  }else{
    lastCount = dirCount;
    lastDir   = prevDir;
    dirCount = 1;
  }
//  Serial.print(", prevDir="); Serial.print(prevDir, DEC);
//  Serial.print(", dirCount="); Serial.print(dirCount, DEC);
//  Serial.print(", lastDir="); Serial.print(lastDir, DEC);
//  Serial.print(", lastCount="); Serial.print(lastCount, DEC);
//  Serial.print(", dir="); Serial.print(dir, DEC);
}
Все неопределенные тут переменные - глобалы из основного скетча движения по линии. Их значения должны сохраняться между вызовами loop().

Ошибка отклонения от линии считается как "среднее расстояние" всех датчиков на линии (что видят черное). Отсюда: важно иметь "средний или нулевой" датчик по центру линии. Его вес нулевой, но он участвует в количестве датчиков на линии, что позволяет отслеживать промежуточные значения от "веса" боковых датчиков +-1, +-3 и т.д. :)
Чтобы время нахождения датчиков четной ошибки и не четной было примерно одинаково, физическое расстояние между датчиками должно примерно соответствовать 2/3 от ширины линии. Это удобно для "узких" линий 15,20мм и не очень удобно для "русской" линии в 50мм. :)

Сохранение "предыдущих" значений в глобалах позволяет оперативно вычислять "радиус кривой" по которой сейчас едет тележка, что зная её вес, габариты, высоту центра тяжести и трение на колесах и передней опоре позволяет ТОЧНО вычислять максимально возможную скорость движения "конкретно сейчас" .. и быть "первым" по ТТХ моторов, веса и качества колес тележки.
:)

P.S. Собственно это всё, что надо знать, чтобы быть первым. :) Отсюда: ПИД-регулятро не есть "айс" .. :)
 

Arhat109

Пользователь
9 Июн 2019
250
68
28
Up.

На днях пришла таки идея как сделать телескопическую руку робота и ввернуть её в наш комплект "Ардуино как Лего", так чтобы провода не болтались как попало .. сделал эскизный проект, пошарился по Али .. и ничего не нашел.

Кстати, ссылки на моторы у Гайвера - битые частично. Обновить бы. :)

В общем, нашел что надо по отдельности, заказал. Пока буду делать звенья телескопа.

Итоговые размеры ожидаю такие:
сложенное состояние - 152х40х40мм (сама стрела без мотора, N20 - "снизу").
Развернутое состояние - 632х40х40мм :) 5 звеньев, с учетом корпуса.
Грузоподьемность около 1-2 кг.
Скорость выдвижения/втягивания около 1мм/сек.

Количество проводов удлинения - 16шт. С обоих сторон тупо разьемы.
Собственное оборудование - мотор (2 провода) + 2 концевых датчика (ещё 2 провода). Земля и Питание для концевиков из общей кучи удлининения.

Интересно, по результату - выкладывать?
 
Последнее редактирование:
  • Лойс +1
Реакции: Wan-Derer

Arhat109

Пользователь
9 Июн 2019
250
68
28
По вопросу "телескопическая рука робота" создал отдельную тему в разделе "проекты в разработке". Приглашаю всех заинтригованных, заинтересованных, просто знатоков - "все в сад", пардон, в эту тему: https://community.alexgyver.ru/threads/ocherednaja-roboruka-nado-li.2373/ :)
 

Arhat109

Пользователь
9 Июн 2019
250
68
28
Я придумал название: "Проект АРГО" :)
Поразмыслив, принял Вашу версию.

Пусть будет "АР-ГО" ("Ar-Go!"). Озадачил жену (дизайнер все же!) наклепать логотип, что-то типа такого вижу:

АРдуино -
_____ как леГО!

или
ARduino -
_____ as leGO!

подчерки - сдвиг. В две строки. Не умеет этот движок втыкать табуляции..

Допилю "робо-руку" (из раздела проекты в разработке) - можно будет сделать наклейки.

Спасибо.