Поиск по этому блогу

суббота, 7 декабря 2013 г.

Автобот-художник aka Drawbot. Part 3.

Йоу! Продолжаем нашу работу с Drawbot'ом - пошаговые инструкции )) Итак, я сконструировал крепления для аккумуляторов, собрал всё и получил следующую конструкцию:
После чего я приклеил плату с аналогом Ардуинки:
Поставил аккумы:
А теперь заливаем самую простую тестовую прогу (машинка будет ехать прямо и мигать диодом).

int m1FPin = 7;
int m1BPin = 11;
int m2FPin = 10;
int m2BPin = 9;



int sens_raw;

const byte DIR_FORWARD = 0;
const byte DIR_BACKWARD = 1;
const byte DIR_LEFT = 2;
const byte DIR_RIGHT = 3;
const byte DIR_STOP = 4;





void walkDir( byte dir ){
    if( dir==DIR_FORWARD ){
        analogWrite( m1FPin, 0 );
        analogWrite( m1BPin, 50 );
        analogWrite( m2FPin, 0 );
        analogWrite( m2BPin, 50 );
    }
    else if( dir==DIR_LEFT ){
        analogWrite( m1FPin, HIGH );
        analogWrite( m1BPin, LOW );
        analogWrite( m2FPin, LOW );
        analogWrite( m2BPin, HIGH );
    }
    else if( dir==DIR_STOP ){
        analogWrite( m1FPin, LOW );
        analogWrite( m1BPin, LOW );
        analogWrite( m2FPin, LOW );
        analogWrite( m2BPin, LOW );
    }
}

int led = 13;

// the setup routine runs once when you press reset:
void setup() {              
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);   
}

// the loop routine runs over and over again forever:



void loop() {

        walkDir( DIR_FORWARD );
        digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
        delay(1000);               // wait for a second
        digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
        delay(1000);               // wait for a second
// Serial.print(sens_raw);
// delay(500);
}
Проверяем, как всё работает )))
Вот видяшка нашего тест-драйва Drawbot_v1 :DDDDDDDDDDDD На фоне - руки Олега, мой ржач и озвучка Стаса :DDDDD
Далее я хочу сделать следующее - подружить Drawbot с Matlab'ом, чтобы он умел рисовать любые функции. Дружить Ардуинку и Матлаб я знаю как, теперь будем писать софт для нашего робота-художника )))

воскресенье, 10 ноября 2013 г.

Автобот-художник aka Drawbot. Part 2.

В этом посте будем разбирать создание "мозгов" для Drawbot'a со стороны "железа".
Управлением нашего автобота-художника будет заниматься Arduino Uno, а точнее его клон Shrimp, как решилось в итоге. Так же нам понадобится драйвер для двигателей.
Собираем схему на макетной плате:

Подробное описание схемы можно посмотреть тут - http://www.poprobot.ru/home/handmadearduino и тут http://robotclass.ru/?p=185
Для проверки, работает ли всё использовали светодиод:
Дальше мы решили сделать нормальные проводки для движка, а также просверлить углубления под болты в корпусе ("body"):
Далее подсоединяем питание к плате, устанавливаем все на базу. В итоге у нас стал вырисовываться такой монстрик (аккумы пока взяли такие, чтобы не запариваться):
Конечно же проверяем с помощью светодиода как всё работает:
Теперь следующие этапы - проектирование креплений под аккум, оптимизация железа и написание софта.

суббота, 9 ноября 2013 г.

Автобот-художник aka Drawbot. Part 1.

Привет. Недавно, после прогулок по сайтам с различными робототехническими проектами, у меня появилась идея замутить своего робота-художника, рисующего маркерами. Вообще эта фишка не является чем-то новым, довольно много таких проектов, но все же мне такое показалось интересным. 

Составные части моего Drawbot'а:
  • DC Boarduino;
  • колеса с редуктором;
  • шаровая опора; 
  • макетная плата 5.4х8.2 см;
  • аккумулятор;
  • две базовых детали, напечатанных на 3D-принтере.  
Как следует из списка составных частей, изначально я сел проектировать в SketchUp'е базовые детали, на которых и будут крепиться все остальные части. Базу я решил разбить на две части, поскольку так лучше для 3D-печати, да и при необходимости можно легко поменять головную часть с маркерами не затрагивая всего остального. Для первой модели я решил не заморачиваться и изготовить "голову" с креплениями под три маркера, которые фиксируются шурупами.

Головная часть с креплениями под маркеры.
На "body" же крепятся абсолютно все части Drawbot'a. 
Процесс печати на 3D-принтере "body"-части.
  В итоге получилась такая красивая деталька ^^ :
После чего я приступил к сборке базы моего автобота-художника.
Составные части базы Drawbot'a.

 Сначала цепляем колеса на "body":
Затем собираем шаровую опору и соединяем её вместе с body и головной частью (да, скорее всего понадобится дополнительная подложка, как в моей модели, или же придется сделать дополнительную часть к body):
В итоге мы получаем вот такую адскую машину ))))) :
Собранная база для Drawbot'a )))
В следующей части будет собственно про "мозги" Drawbot'a, про программы и прочие интересные вещи )))

четверг, 24 октября 2013 г.

MakeItLab - первый хакспейс Екатеринбурга.

Привет! Сегодня я расскажу про такое интересное явление, как "хакспейс" или же "хакерспейс". Для России это понятие ещё относительно новое, ведь первые "официальные" хакспейсы появились у нас году так в 2011 (согласно Википедии). Но в своей заметке я рассмотрю подробно не так давно появившийся (летом 2013го года) хакспейс MakeItLab, находящийся в городе Екатеринбурге. Итак, поехали!
Начнем с того, что я сам занимаюсь робототехникой, люблю пособирать интересные механизмы, а заодно и применить на них свои программные или математические разработки. И вот, работая над одним своим проектом, я абсолютно случайно узнал от знакомых, что у нас в городе появилось такое место, куда можно придти, пообщаться с "себе подобными", попросить совета, поучаствовать в крутом проекте, получить возможность изготовить нужные детали на 3D-принтере, ну и плюс еще куча-куча возможностей. Так что же такое хакспейс?
Hackerspace (хакерспейс) или hackspace (хакспейс) (от англ. hacker и англ. space) — реальное (в противоположность виртуальному) место, где собираются люди со схожими интересами, чаще всего научными, технологическими, в цифровом или электронном искусстве, общении и совместном творчестве.
Типичные действия в хакспейсах включают:
  • получение знаний и обмен ими
  • презентации и лекции
  • социальную активность, включая игры и развлекательные мероприятия
Хакспейсы предоставляют инфраструктуру необходимую для этих действий: помещения, серверы и компьютерные сети, разнообразнейшие инструменты и т.д.
Но это всё вы можете прочитать и в Википедии, а вот что увидел я, зайдя через черный ход старинного особняка, находящегося в самом центре города? Первое, что мне бросилось в глаза - это большой стол с разложенной трассой для движения роботов, самих роботов, квадрокоптер на стойке в углу, жужжащий 3D-принтер, кучу инструментов...  Короче, тут ты сразу чувствуешь что попал в свой собственный техно-рай ;) 
Няшка-робот, ездящий по трассе ^^
 
 
Детали и фигурки, напечатанные на 3D-принтере.

Конечно же очень полезной для себя я считаю возможность обмена опытом. Ведь одно дело, когда ты спрашиваешь что-то в группе в соцсети или на форуме, или же ты можешь пообщаться вживую с людьми, которые занимаются интересующей тебя задачей. Кстати, именно в хакспейсе можно получить необходимые практические навыки работы с железом и программированием реальных контроллеров, поскольку в универе часто лабы ограничены лишь моделированием на компе.  Так же можно поделать различные лабораторки, основанные на работе с Raspberry, научиться собирать простенький компьютер, поиграться в старые игрушки на собранном здесь же аркадном автомате, провести выходные, запуская квадрокоптер или орнитоптер, да много чего ещё. Вот тут можно посмотреть список доступных компонентов и оборудования хакспейса. А если вас заинтересовала эта небольшая заметка и вы хотите сюда попасть - вот ссылка на сайт нашего хакспейса со всеми контактами - http://www.makeitlab.ru/ 

 
 
 

среда, 11 июля 2012 г.

Учимся параллелить )))

В этом семестре в универе по одному из предметов у нас была такая лаба - написать программу, реализующую какой-либо алгоритм с помощью распараллеливания по ядрам процессора. Ну все, разумеется, понимают, что пока нет специального софта, то толку от количества ядер нет, вот мы и займёмся написанием такового, а заодно и посмотрим - насколько будет лучше результат ))))
Для выполнения поставленной задачи был выбран алгоритм быстрой сортировки:

  • Выбираем в массиве некоторый элемент, который будем называть опорным элементом. С точки зрения корректности алгоритма выбор опорного элемента безразличен. С точки зрения повышения эффективности алгоритма выбираться должна медиана, но без дополнительных сведений о сортируемых данных её обычно невозможно получить. Известные стратегии: выбирать постоянно один и тот же элемент, например, средний или последний по положению; выбирать элемент со случайно выбранным индексом.
  • Операция разделения массива: реорганизуем массив таким образом, чтобы все элементы, меньшие или равные опорному элементу, оказались слева от него, а все элементы, большие опорного — справа от него. Обычный алгоритм операции:
  1. Два индекса — l и r, приравниваются к минимальному и максимальному индексу разделяемого массива соответственно.
  2. Вычисляется индекс опорного элемента m.
  3. Индекс l последовательно увеличивается до тех пор, пока l-й элемент не превысит опорный.
  4. Индекс r последовательно уменьшается до тех пор, пока r-й элемент не окажется меньше либо равен опорному.
  5. Если r = l — найдена середина массива — операция разделения закончена, оба индекса указывают на опорный элемент.
  6. Если l < r — найденную пару элементов нужно обменять местами и продолжить операцию разделения с тех значений l и r, которые были достигнуты. Следует учесть, что если какая-либо граница (l или r) дошла до опорного элемента, то при обмене значение m изменяется на r-й или l-й элемент соответственно.
  • Рекурсивно упорядочиваем подмассивы, лежащие слева и справа от опорного элемента.
  • Базой рекурсии являются наборы, состоящие из одного или двух элементов. Первый возвращается в исходном виде, во втором, при необходимости, сортировка сводится к перестановке двух элементов. Все такие отрезки уже упорядочены в процессе разделения.

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

Также возможен режим работы программы без распараллеливания.


По результатам выполнения работы программы, которые выводятся в консоль, видно, что время, затраченное на выполнение процесса сортировки без распараллеливания существенно больше, чем при работе в параллельном режиме (на 4х-ядерном процессоре  у нас примерно получилось, что время различается в два раза).
Графики загрузки ядер и быстродействие при выполнении программы можно посмотреть запустив диспетчер задач.



Листинг 1. Program.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        public static int hhhhhhhh=0;
        static int ProcessorCount;
        static void Main(string[] args)
        {
            //количество процессоров
            ProcessorCount = Environment.ProcessorCount;
            //ProcessorCount = 2;
            //список, содержащий части массива

            Console.WriteLine("1 - режим без распараллеливания. 2 - режим с распаралливанием. Сделайте выбор:");
            String s = Console.ReadLine();

           
            Random rnd1 = new Random();
            //      2147483647
            int n = 150000000;
            Console.WriteLine("Генерация массива из {0} записей типа integer", n);
            int[] b = new int[n];
            GC.Collect();
            int max = 100;
            for (int i = 0; i < b.Length; i++)
                b[i] = rnd1.Next(0, max);

            Console.WriteLine("Запись в файл");
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"2.txt"))
                foreach (int t in b)
                    file.Write(t.ToString() + " ");

            int rrrr = int.Parse(s);
            massiv y = new massiv(b, 0, n-1, null);
            if ((rrrr == 1)||(ProcessorCount == 1))
            {
                Console.WriteLine("Выполнение сортировки");
                Stopwatch sw = null;
                sw = System.Diagnostics.Stopwatch.StartNew();
                qSort1(y);
                sw.Stop();
                var elapsed = sw.ElapsedMilliseconds;
                Console.WriteLine("Затраченное время: {0}", elapsed);
            }
            else
            {
                parall(b, n);
            }

            Console.WriteLine("Запись в файл");
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"3.txt"))
                foreach (int t in b)
                    file.Write(t.ToString() + " ");
            Console.WriteLine("Нажмите любую клавишу для завершения");
            Console.ReadKey();
        }

        static void parall(int[] b, int n)
        {
            List<massiv> B = new List<massiv>();
            Console.WriteLine("Разделение по массивам");

            //ThreadPool.QueueUserWorkItem(new WaitCallback(qSort1), new massiv(b, 0, n));
            qSortbegin(b, 0, n - 1, 0, B);

            Stopwatch sw = null;
            var threads = new List<Thread>();
            ManualResetEvent[] handles = new ManualResetEvent[B.Count];
            for (int h = 0; h < B.Count; h++)
            {
                massiv y = new massiv(new int[B[h].high - B[h].low + 1], 0, B[h].high - B[h].low, null);
                for (int i = B[h].low; i < B[h].high + 1; i++)
                    y.B[i - B[h].low] = B[h].B[i];
                B[h] = y;

                //ThreadPool.QueueUserWorkItem(new WaitCallback(qSort1), B[h]);
                 //threads.Add(thread);
                Console.WriteLine("B={0}", B[h].high - B[h].low + 1);
            }

            Console.WriteLine("Выполнение сортировки");
            sw = System.Diagnostics.Stopwatch.StartNew();

            for(int h = 0; h < B.Count; h++)
            {
                massiv bbbbb = B[h];

                /*var thread = new Thread(() =>
                {
                    qSort1(bbbbb);
                  /*  if (threads.All(t => t.ThreadState == System.Threading.ThreadState.Stopped))
                    {
                        sw.Stop();
                        var elapsed = sw.Elapsed;
                    }
                });*/
                Thread t = new Thread(qqsort);
                handles[h] = new ManualResetEvent(false);
                bbbbb.handl = handles[h];
                t.Start(bbbbb);

                //threads.Add(thread);
            }
            WaitHandle.WaitAll(handles);
            sw.Stop();
            String elapsed = sw.ElapsedMilliseconds.ToString();

           // var action = new Action(() =>
         //   {
               
                //Thread.Sleep(50000);

                Console.WriteLine("Затраченное время: {0}", elapsed);
                Console.WriteLine("Сбор в один массив");
                int k = 0;
                for (int h = 0; h < B.Count; h++)
                {
                    for (int i = 0; i < B[h].B.Length; i++)
                    {
                        b[k] = B[h].B[i];
                        k++;
                    }
                }
         //   });
            /*var thread1 = new Thread(() =>
            {
                qSort1(B[0]);
                if (threads.All(t => t.ThreadState == System.Threading.ThreadState.Stopped))
                {
                    sw.Stop();
                    var elapsed = sw.Elapsed;
                    action();
                }
            });
            threads.Add(thread1);
            var thread2 = new Thread(() =>
            {
                qSort1(B[1]);
                if (threads.All(t => t.ThreadState == System.Threading.ThreadState.Stopped))
                {
                    sw.Stop();
                    var elapsed = sw.Elapsed;
                    action();
                }
            });
            threads.Add(thread2);
            var thread3 = new Thread(() =>
            {
                qSort1(B[2]);
                if (threads.All(t => t.ThreadState == System.Threading.ThreadState.Stopped))
                {
                    sw.Stop();
                    var elapsed = sw.Elapsed;
                    action();
                }
            });
            threads.Add(thread3);
            var thread4 = new Thread(() =>
            {
                qSort1(B[3]);
                if (threads.All(t => t.ThreadState == System.Threading.ThreadState.Stopped))
                {
                    sw.Stop();
                    var elapsed = sw.Elapsed;
                    action();
                }
            });
            threads.Add(thread4);

            */

           /* foreach (var thread in threads)
            {
                thread.Start();
            }*/
           
            //thread1.Start();

                B.Clear();
                threads.Clear();
        }

        public struct massiv
        {
            public int[] B;
            public int low;
            public int high;
            public ManualResetEvent handl;
            public massiv(int[] _B, int _low, int _high, ManualResetEvent _handl)
            {
                B = _B;
                low = _low;
                high = _high;
                handl = _handl;
            }
           
        };

        static int medial(int[] b, int low, int high)
        {
            int medial1;
            decimal s = 0;
            for (int i = low; i < high+1; i++)
            {
                s = s + (decimal)b[i];
            }
            medial1 = (int) (s / (high - low));

            int find = 0;
            int l = Math.Abs(b[low] - medial1);
            for (int i = low+1; i < high; i++)
            {
                if(Math.Abs(b[i]-medial1) < l)
                {
                    l = Math.Abs(b[i] - medial1);
                    find = i;
                }
            }
                return find;
        }
       
        static void qSortbegin(int[] A, int low, int high, int k, List<massiv> B)
        {
            k++;
            //int max = 100;
            int i = low;
            int j = high;
          
            int n = medial(A, low, high);
            int x = A[n];
            do
            {
                while (A[i] < x) ++i;
                while (A[j] > x) --j;
                if (i <= j)
                {
                    int temp = A[i];
                    A[i] = A[j];
                    A[j] = temp;
                    i++; j--;
                }
            } while (i <= j);

            //int A1 = A.ToList().FindIndex(q => q == x);
            //int A1 = j; 
            if (k * 2 == ProcessorCount)
            {
                if (low < j)
                    B.Add(new massiv(A, low, j, null));
                if (i < high)
                    B.Add(new massiv(A, j+1, high, null));
            }
            else
            {
                if (low < j) qSortbegin(A, low, j, k, B);
                if (i < high) qSortbegin(A, j+1, high, k, B);
            }
        }

        static void qSort1(massiv m)
        {
            int[] A = m.B;
            int i = m.low;
            int j = m.high;

            massiv m1 = m;
            int x = A[(m.low + m.high) / 2];
            do
            {
                while (A[i] < x) ++i;
                while (A[j] > x) --j;
                if (i <= j)
                {
                    int temp = A[i];
                    A[i] = A[j];
                    A[j] = temp;
                    i++; j--;
                }
            } while (i <= j);

            if (m1.low < j)
            {
                m = m1;
                m.B = A; m.high = j;
                qSort1(m);
            }
            if (i < m1.high)
            {
                m = m1;
                m.B = A; m.low = i;
                qSort1(m);
            }
            hhhhhhhh++;
           
        }

        static void qqsort(Object state)
        {
            massiv m = (massiv)state;
            qSort1(m);
            m.handl.Set();
        }
    }
}