roboforum.ru

Технический форум по робототехнике.

AXON и управление сервами [из Хексапод - как минимизировать

AXON и управление сервами [из Хексапод - как минимизировать

romick » 10 июн 2010, 13:59

=DeaD= писал(а):Какую точность оно у вас даёт? Плавные движения уже реализовали? Без них будет полный кирдык :)

Надеюсь что реализовал :D Пока сервы не получу на руки, могу только предполагать. Приращения скорости использую пока линейные. Проще наверно исходники показать, чем сто раз объяснять :) Сразу предупреждаю, баги присутствуют, код в процессе разработки.
Код: Выделить всёРазвернуть
//attempt at using the Axon and Axon II as a servo controller


//define how data should be sent (choose only one)
//#define U0
#define USB

//declare microcontroller
//#define AXON
#define AXON2

#define DEBUG


//WebbotLib Includes
#ifdef AXON
   #include "sys/axon.h"
#endif
#ifdef AXON2
   #include "sys/axon2.h"
#endif

//#include "buffer.h"
#include "iopin.h"      
#include "timer.h"      
#include "rprintf.h"   //use for UART
#include "inttypes.h"


//UART defines (name your UART)
#define U0_UART UART0
#define USB_UART UART1
//UART baud defines (change baud rate)
#define U0_BAUD (BAUD_RATE)230400
#define USB_BAUD (BAUD_RATE)230400
//UART define which uart to use
#define U0_ACTIVATE &uart0SendByte
#define USB_ACTIVATE &uart1SendByte
//getting data
#define GET_DATA_U0 uart0GetByte()
#define GET_DATA_USB uart1GetByte()


//create a big buffer for UART
#ifdef U0
   #define UART0_RX_BUFFER_SIZE 100
#endif
#ifdef USB
   #define UART1_RX_BUFFER_SIZE 100
#endif

#include "hardware.h"   //declare your servo and sensor ports here


int8_t lastByte=-1;         //store last recieved character
//lastByte = -1;
int8_t buff_array[100];   //store last command
uint8_t buff_pos=0;      //record current position in array
uint8_t buff_read=0;   //record current position in array
boolean command_received=0;   //active last command
SERV *serva[32];

int count_us;
enum command_state commst=empty;


void save_move(uint8_t *channel, uint16_t *pulsewidth, uint16_t *speed, uint16_t *time)
{
   if (*pulsewidth)
   {
      for(int i=0; i<32; i++)
      {
         if (serva[i]->channel == *channel)
         {
            if (*time)
            {
               long int tbs;
               if (serva[i]->target > serva[i]->posnow)
               {
                  tbs = (serva[i]->target - serva[i]->posnow) / *time * 1000;
               } else {
                  tbs = (serva[i]->posnow - serva[i]->target) / *time * 1000;
               }
               if (tbs < *speed)
               {
                  *speed = tbs;
               }
            }
            serva[i]->target = *pulsewidth;
            serva[i]->speed = *speed;
            rprintf("Parsed command: channel %d, target %d, speed %d,\n", *channel, *pulsewidth, *speed);
         }
      }
      *channel=0;
      *pulsewidth=0;
      *speed=0;
   }
}


//initialize hardware, ie servos, UART, etc.
void appInitHardware(void)
{

   #ifdef AXON2
      led_off();
   #endif
      
   //setup UART0 or USB
   #ifdef USB
      uartInit(USB_UART, USB_BAUD);
      rprintfInit(USB_ACTIVATE);
   #endif
   #ifdef U0
      uartInit(U0_UART, U0_BAUD);//bluetooth
      rprintfInit(U0_ACTIVATE);
   #endif
   
   for (int i; i<32;i++) //init servo data
   {
      //serva[i] = &(SERV);
      serva[i]->channel = i;
      serva[i]->target = 1500;
      serva[i]->posnow = 1500;
      serva[i]->speed = 0;
   }
   
}


TICK_COUNT appInitSoftware(TICK_COUNT loopStart)
   {
   rprintf("init\n");
   count_us = clockGetus();
   return 0;
   }

   
// This is the main loop
TICK_COUNT appControl(LOOP_COUNT loopCount, TICK_COUNT loopStart)
   {
   ////////////GET COMMAND/////////////
   #ifdef USB
      lastByte=GET_DATA_USB;
   #endif
   #ifdef U0
      lastByte=GET_DATA_U0;
   #endif

   //fill up array with command recieved from UART
   if(lastByte!=-1)//has new data
   {
      buff_array[buff_pos]=lastByte;
      buff_pos++;

      if(lastByte == 0x0d)//if command is finished
         command_received=1;
   }

   //when command is received, parse it
   if(command_received)
   {
      uint8_t incoming_channel=0;
      uint16_t incoming_pulsewidth=0;
      uint16_t incoming_speed=0;
      uint16_t incoming_time=0;
      commst = empty;
      rprintf("Command received:");
      rprintfMemoryDump(buff_array, 0, buff_pos);
      rprintf("\n");
      //look for 'T' command first
      buff_read=0;
      while(buff_read <= buff_pos)
      {
         if (buff_array[buff_read] == 'T' || buff_array[buff_read] == 't')
         {
            commst = time;
         } else {
            if (commst == time)
            {
               incoming_time = incoming_time * 10 + (buff_array[buff_read] & 0x0F);
            } else {
               commst = empty;
            }
         }
         buff_read++;
      }
      
      //while the full array hasn't been read yet
      //parse the rest of commands
      buff_read=0;
      while(buff_read<=buff_pos)
      {
         if (buff_array[buff_read] > 0x2F && buff_array[buff_read] < 0x3A && commst )
         {
            switch (commst)
            {
               case channel:
                  incoming_channel = incoming_channel * 10 + (buff_array[buff_read] & 0x0F);
                  break;
               case pulsewidth:
                  incoming_pulsewidth = incoming_pulsewidth * 10 + (buff_array[buff_read] & 0x0F);
                  break;
               case speed:
                  incoming_speed = incoming_speed * 10 + (buff_array[buff_read] & 0x0F);
                  break;
               case time: case empty: case positionoffset:
                  break;
            }
         } else {
            switch (buff_array[buff_read])
            {
               case 0x0d:
                  save_move(&incoming_channel, &incoming_pulsewidth, &incoming_speed, &incoming_time);
                  break;
               case '#':
                  save_move(&incoming_channel, &incoming_pulsewidth, &incoming_speed, &incoming_time);
                  commst = channel;
                  break;
               case 'P': case 'p':
                  commst = pulsewidth;
                  break;
               case 'S': case 's':
                  commst = speed;
                  break;
               case 'T': case 't':
                  commst = empty;
                  break;
               default:
                  commst = empty;
            }
         }
         buff_read++;
      }
      buff_pos=0;//reset array
      command_received=0;
   }

   
   // send pulses to servos every 18 ms
   if (clockHasElapsed(count_us, 18000))
   {
      count_us = clockGetus();
      rprintf("Time is: %d\n", count_us);
      
      //define short-term target for each servo
      long int pos = 0;
      for (int i; i<32;i++)
      {
         pos = serva[i]->posnow;
         if (serva[i]->target > pos)
         {
            pos += serva[i]->speed/50;
            if (pos > serva[i]->target) pos = serva[i]->target;
         }  else {
            pos -= serva[i]->speed/50;
            if (pos < serva[i]->target) pos = serva[i]->target;
         }
         serva[i]->posnow = pos;
      }
      //sort servos ascending by short-term target
      int isDone=0;
      SERV *tmp;
      for (int i=0;(i<32-1)&&(!isDone);i++)
      {
         isDone=1;
         for (int j=0;j<32-i-1;j++)
         {
            if (serva[j]->posnow > serva[j+1]->posnow)
            {
               // keep number and port in sync with the position so we know which port to change!
               tmp = serva[j];
               serva[j] = serva[j+1];
               serva[j+1] = tmp;

               isDone=0;
            }
         }
      }
      //*tmp = 0;
      
      #ifdef DEBUG
      for (int i; i<32;i++)
      {
         rprintf("Servo state: order: %d, channel: %d, target: %d, posnow: %d, speed: %d", i, serva[i]->channel, serva[i]->target, serva[i]->posnow, serva[i]->speed);
      }
      #endif
      cli(); //block interrupts
      
      // put all servos' pin to high
      for (int i; i<32;i++)
      {
         pin_high(serva[i]->pin);
      }
      
      // put pins low based on short-term target
      int offset=0;
      for (int i=0;i<32;i++)
      {
         pos = serva[i]->posnow;
         if (pos > 0)
         {
            if (pos > offset)
            {
               // wait until this servo's position offset
               delay_us(pos - offset);
               offset = pos;
            }
            // turn off this servo and continue with next servos
            pin_low(serva[i]->pin);
         }
      }
      
      
      sei(); //unblock interrupts
   }



   return 0;
   }


=DeaD= писал(а):Нет, нет и еще раз нет :) мы можем продавать изделие как пообещали за 1700р. А можем не заморачиваться с поиском правильных подрядчиков, переговорами, выбором поставщиков материала и компонент и продавать набор за 3000р с маржой 5% :)

При любых расчетах в экономике подразумевается, что целью хозяйствующего субъекта является максимизация извлечения прибыли. И чтобы у вас был покупатель, вы будете вынуждены искать пути снижения себестоимости. Иначе покупатель предпочтет конкурентный товар (в данном случае самостоятельное изготовление). Но это все конечно чисто теоретически.
=DeaD= писал(а):А гадать есть что, у нас с ними разная входящая себестоимость была на один и тот же товар. Но цены по которым мы отпускали товар в свой магазин и к ним были одинаковые :)
Фишка в том, что они неправильно управляли запасами и товар у них лежал по 3 месяца. А цены на компьютерную технику имеют свойство снижаться достаточно быстро.

Это же я и говорил про входящую себестоимость, только другими словами. Себестоимость товара при прочих равных условиях определяется обычно 2 факторами: объемами закупок и каналом поставки. А затраты на управление запасами уже определяют размер коммерческих расходов. На рынке робототехники пока вроде нет признаков "рынка компьютеров". Предлагаю свернуть тему ценообразования, потому что мы уже от основной мысли ушли.

=DeaD= писал(а):Комплект механики без серв для шестинога на базе HXT900.

А когда ожидать на базе стандартных серв?
Последний раз редактировалось Vooon 10 июн 2010, 19:17, всего редактировалось 1 раз.
Причина: [code=cpp]!!!

Re: Хексапод - как минимизировать - собираем идеи :)

=DeaD= » 10 июн 2010, 14:13

romick писал(а):целью хозяйствующего субъекта является максимизация извлечения прибыли. И чтобы у вас был покупатель, вы будете вынуждены искать пути снижения себестоимости. Иначе покупатель предпочтет конкурентный товар (в данном случае самостоятельное изготовление). Но это все конечно чисто теоретически.

Это очень теоретически :) покупатель предпочтет конкурентный товар при некотором уровне цены независимо от нашей себестоимости и маржи :) вот если мы потратили 3 дня своего времени, провели переговоры и нашли где брать подшипники за 10р/шт - почему мы должны подарить своё время кому-то просто так? Для нас какой резон тогда бегать? Так что вопрос маржи - это всего лишь вопрос добавленной стоимости товара, которую мы генерим - если у нас маржа 1000% и все выбирают нас, значит мы буквально из ничего делаем конфетку и всех это устраивает. А смотреть надо на то, что мы предлагаем и за какие деньги :) а делаем мы это сами или заказываем где-то и относим ли на себестоимость - вопрос десятый.

Добавлено спустя 1 минуту 10 секунд:
romick писал(а):А когда ожидать на базе стандартных серв?

Хороший вопрос, попробую на днях нашарить 3 шт HXT12k из старых запасов и собрать одну ногу. Если всё ок - значит будем считать что почти готовы чертежи и надо собирать прототип как только ко мне придет посылка с 20 сервами.

Добавлено спустя 3 минуты 28 секунд:
romick писал(а):Надеюсь что реализовал :D Пока сервы не получу на руки, могу только предполагать. Приращения скорости использую пока линейные. Проще наверно исходники показать, чем сто раз объяснять :) Сразу предупреждаю, баги присутствуют, код в процессе разработки.

Не будет работать - вы потеряете всю инфу из UART'а, потому как заблокировали прерывания при выдаче сигналов на сервы.

Re: Хексапод - как минимизировать - собираем идеи :)

romick » 10 июн 2010, 14:45

{игнорирую аргументы оппонента на экономическую тематику}
=DeaD= писал(а):Не будет работать - вы потеряете всю инфу из UART'а, потому как заблокировали прерывания при выдаче сигналов на сервы.

А иначе будут дрожания сервы, потому как прерывания будут изменять длину импульса. Проблему можно решить на стороне, отсылающей команду, - после отправления команды ожидать контрольную сумму команды, при отклонении - пересылать команду еще раз. Пока буду смотреть, как реализовано у SSC-32.
Кстати, делалось на базе другой прошивки, которая, я так понимаю, успешно работает. http://www.roborealm.com/downloads/Axon ... ler_II.zip

Re: Хексапод - как минимизировать - собираем идеи :)

=DeaD= » 10 июн 2010, 14:52

romick писал(а):А иначе будут дрожания сервы, потому как прерывания будут изменять длину импульса.

Если реализовывать без специального железа - именно так и будет.

Вот только проблема в том, что программы разработанные для SSC-32 ничего не знают про ваши игры с таймерами и контрольные суммы, поэтому они тупо не будут работать при таком подходе :(

Re: Хексапод - как минимизировать - собираем идеи :)

romick » 10 июн 2010, 15:08

А где можно посмотреть, как это реализовали в ORC-32? Не спасет ли ситуацию хардварный UART и буфер для него? Особенно с учетом того, что прерывания блокируются на 2 мс (за это время вроде может поступить около 30 байт при скорости 115кбит)? Есть вариант повесить код, ответственный за формирование импульса на таймер, как думаете?

Re: Хексапод - как минимизировать - собираем идеи :)

=DeaD= » 10 июн 2010, 15:11

У нас совершенно по другому организована генерация импульсов на контроллере - через аппаратные ШИМ-генераторы и внешние счетчики. Читать тут метод 4.5 - [[Управление сервами с МК]]. Мы специально это делали в железе, чтобы в программе никаких проблем не иметь.

Добавлено спустя 35 секунд:
Вешать код формирования имульсов на таймер можно, только точность там средняя.

Re: Хексапод - как минимизировать - собираем идеи :)

romick » 10 июн 2010, 15:17

У вас получается 32 ШИМ-генератора? Для каждой сервы ведь может быть свой импульс.

Re: Хексапод - как минимизировать - собираем идеи :)

setar » 10 июн 2010, 15:20

:) для того чтобы сделать 32 ШИМ не требуется 32 генераторов
требуется 32 счётчика которые показывают на каких ногах шим находится в состоянии 1 и сколько ему ещё таковым оставаться.

Re: Хексапод - как минимизировать - собираем идеи :)

=DeaD= » 10 июн 2010, 15:22

romick писал(а):У вас получается 32 ШИМ-генератора? Для каждой сервы ведь может быть свой импульс.

У нас 4 ШИМ-генератора переключающих 4 счетчика по 8 серв на каждом.

Re: Хексапод - как минимизировать - собираем идеи :)

Michael_K » 10 июн 2010, 15:24

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

(ну то есть килограммами наверное еще смешнее, но и так неплохо)

Re: Хексапод - как минимизировать - собираем идеи :)

romick » 10 июн 2010, 15:42

Возвращаясь к Axon'у - у него аппаратный ШИМ реализован только на 12 ногах, что для шестинога недостаточно. Вопрос: в Axon'е на каждом ШИМе - только 3 ноги, вы говорите о 8. Если ли объективные факторы,которые могут препятствовать тому чтобы вешать на каждый ШИМ по 8 ног?
Возвращаясь к ORC-32 - на каких OR-модулях эта прошивка работает и поддерживает ли она полностью набор команд SSC-32?

Re: Хексапод - как минимизировать - собираем идеи :)

Grem » 10 июн 2010, 15:53

2romick, OR-AVR-M128-S

Re: Хексапод - как минимизировать - собираем идеи :)

blindman » 10 июн 2010, 16:29

romick писал(а):Вопрос: в Axon'е на каждом ШИМе - только 3 ноги, вы говорите о 8

Один выход таймера МК управляет одним внешним счетчиком с 8 выходами.

Re: Хексапод - как минимизировать - собираем идеи :)

romick » 10 июн 2010, 16:39

Не, я конечно тугодум, и ни разу не технический специалист, но я понял как реализован аппаратный ШИМ в ORC-32. Что я не могу понять, это почему автор Axon'а не поставил внешние счетчики с 8 выходами, а только с 3-мя. Вдруг на это у него какая-то причина была...

Re: Хексапод - как минимизировать - собираем идеи :)

blindman » 10 июн 2010, 16:44

Наверно не понял все-таки. Потому что в этом аксоне нету внешних счетчиков. Совсем. Ни с тремя, ни с 8, ни с 1000 выходами. Там тупо голый контроллер с минимальной обвязкой.


Rambler\'s Top100 Mail.ru counter