Технический форум по робототехнике.
newbe » 15 авг 2017, 17:07
Здравствуйте, достал из шкафа давний проект, хотел продолжить над ним работу. Возникла проблема, в движении ног.
Если осуществлять движение серв через цикл For, не получается одновременно двигать сеорвы на разные углы, отпадает вариант.
Пробовал через таймер millis, все делает хорошо, но двигать 12 сервами по очереди, просто не получается. Вообще millis, идиально бы подошел, так как в нем нет пауз вроде delay. Но вот как осуществлять движение серв поочередно? Как я только уже не ухищрялся. Помогите советом, может осенит меня. Спасибо.
- Вложения
-
-
Angel71 » 15 авг 2017, 17:42
п.с. шестерни пластиковые, быстро сотрутся. надумаете лучше делать, берите с металлическими.
глубоко единообразно, цикл или таймер. для начала переделываете логику работы, начиная с выкидывания delay и прочих подобных задержек. это в общем, но как сделать пока до вас врядли не дойдёт и даже если произойдёт чудо и даже получится хоть кое-как, всё-равно ложите пока его обратно в столик и вникаете в то, что пропустили.
по даташиту, на форумах, сайтах и т.д. изучаете, некоторые особенности работы мк - частота, такты, за сколько выполняется та или иная операция, как определять сколько времени тактов прошло (системный таймер и/или дебаггер и/или симулятор). изучаете, как работает delay, потренируйтесь определять примерное время работы различных кусков своего кода. быстро или долго и нудно, но изучив всё это и весь свой код, вы должны примерно понимать какие куски кода сколько выполняются. как минимум для начала прикидываете сколько одна итерация вашего того цикла управления сервами выполняется, сколько минимум выполняются delay, delaymicroseconds,... но это минимум, по хорошему лучше всё, что используете или может пригодится, особенно тяжелвые вещи на подобии printf, всякие математические операции и функции. это первый кирпичик, в лоб или в фундамент.
если ещё не вникали, изучаете как устроена серва, как работает и как ей управлять. при работе с новыми платками, когда низкоскоростные интерфейсы, в том числе и с сервами,очень удобно использовать даталоггер. залил код и смотрите, правильно все импусьсы идут.
а дальше, если нормально делать, будет чуть сложнее. хз, что у вас там за код, но как-то же уже угол поворота серв вы уже высчитываете. секвенсор сделали или просчитываете углы поворотов, пока не важно - надо будет алгоритм менять или нет, то уже потом. вроде ардуина, т.е. дёргается servo.write. куда провернуть данные есть, вот в цикле или в прерывании периодически дёргать для всех серв servo.write и будет всё более-менее нормально проворачиваться, по крайней мере для следуешего шага вполне сойдёт. а дальше смотря что вы там от этого четвероногого хотите, какие алгоритмы захотите. может понадобится чуть ещё в работу мк вникать. про то, что некоторые блоки авр параллельно работают, а вот сам код выполняется последовательно. вникать в код библиотеки работы с сервами, как работают прерывания и т.д.
newbe » 16 авг 2017, 03:07
Я знаю как устроена серва и как происходит управление ею, если вы о широтно импульсной модуляции говорите. И как работает временная задержка тоже.
Я не задаюсь координатами постановки лап, как это делается в инверсной или прямой кинематике, я просто хочу сделать алгоритм движения вперед, циклирующийся.
Вот скажем с миллис, поправте если я не понял как он работает:
unsigned long lasttime;
int interval=50;
int servo_pos=0;
lasttime=millis() в начале программы, мы уравниваем переменную и время.
if(servo_pos<60) задаем угол к котрому стремится серво
{
if((millis()-lasttime)>=interval) то есть, когда таймер насчитает скажем 60мс из которого вычитаем lasttime и получаем 60, что больше interval, тогда функция пойдет дальше?
{
servo_pos++; я так понимаю 1 градус раз в 50мс
Servo.write(servo_pos)
lasttime=millis() этим я уравниваю время с переменной? lasttime становится равным 60?
}
}
А как теперь можно отсрочить время на выполнение задачи первого серво? Я пытался сдедать так.
Задавал отдельную переменную, time=3000 так как interval=50, значит первая серва совершит свои 60 градусов через 3000мс (50×30 или я ошибся?) и уже в движении следующей сервы писал:
if ((millis()-lasttime)>=interval+time)
Я наверное что-то недопонял, так как сервы двигаются одновременно всеравно.. Коряво все описано, но вы должны понять ход моих мыслей
loox » 16 авг 2017, 22:17
Newbe
Давайте сначала.
Отложим обсуждение текста программы, опишите Вашу прблему по управлению серврприводами
Итак. ?..
Loox
Добавлено спустя 31 секунду:
Newbe
Давайте сначала.
Отложим обсуждение текста программы, опишите Вашу прблему по управлению серврприводами
Итак. ?..
Loox
newbe » 16 авг 2017, 22:37
В общем, я начал действовать по другому. Путь тернистый и запутаный.
К примеру:
Int point1;//вводим переменные для начала
Int point2; работы серв.
if(position1<180)
{
if((millis()-lasttime)>=interval)
{
position1++;
servo1.write(position1)
lasttime=millis();
}
}
If(position1==180)// если позиция первого.
серво достигла 180,
только тогда
начинается движение.
второй
{
Point2=1;
}
If(point2=1)
{
If(servo2<60)
{
If((millis()-lasttime)>=interval)
{
Position2++;
Servo2.write(position2);
lasttime=millis();
}}}
Все грамоздко, но работает. Поавда я уже 7й час пишу первый шаг, с креном корпуса, но во всяком случае проблемы из-за путаницы решаются, хоть на это и ухтдит время.
if
Добавлено спустя 3 минуты 12 секунд:loox писал(а):Newbe
Давайте сначала.
Отложим обсуждение текста программы, опишите Вашу прблему по управлению серврприводами
Итак. ?..
Loox
Добавлено спустя 31 секунду:
Newbe
Давайте сначала.
Отложим обсуждение текста программы, опишите Вашу прблему по управлению серврприводами
Итак. ?..
Loox
Получается так, что при описании движения обного сервлпривода, а после второго, они начинают выполнять работу одновременно. Я ищу способ, что бы ими можно было управлять последовательно, но без задержек по времени в виде delay.
loox » 16 авг 2017, 22:45
Еще раз
Не надо писать текст программы. Читать чужой код. ..... невозможно
В чем прблема..?.. Опишите словами
Loox
newbe » 16 авг 2017, 22:54
loox писал(а):Еще раз
Не надо писать текст программы. Читать чужой код. ..... невозможно
В чем прблема..?.. Опишите словами
Loox
Я хотел бы, при помощи таймера millis, управлять сервомоторами поочередно и в некоторых моментах одновременно. Но оказалось, что поочередно двигать сервомоторами оказалось не просто. Это собственно и есть проблема. Я ищу более легкий путь к управлению серво поочередно, при том что их регулярно нужно возвращать скажем от 0 до 180 и обратно. Но так как millis не останавливает программу, а читает весь код от начала до конца регулярно, то поворот от 180 и обратно это проблема. Потому что эти два поворота сами себя исключают.
Angel71 » 16 авг 2017, 22:58
поняли, но ничего не поняли
servo_pos++; я так понимаю 1 градус раз в 50мс
1 попугай почти раз в 50мс. после калибровки попугай может быть примерно и грудус. со временем и пониманием, как что работает, ещё хуже. т.е. ничего не изменилось, игрушку в стол/шкаф. к тому, что раньше написано ещё добавляется, что раз в голове не можете прикидывать, как работают и конкретные куски кода и всё в общем, так начинать можно хоть по старинке - отлаживать на бумажке или в табличном редакторе. в первом столбце описание какая стадия или событие. потом время, тики или ещё что, связанное со временем. а потом столбцы для переменных. как именно делать, вариантов масса.
не хотите вы эту бредятину с тупо линейной цикличностью и вычислениями даже не по времени или по предсказаниям погоды на марсе. в общем пока не видно даже намёка на желание прислушиваться и тем более сделать хотя бы простенькую скелетную анимацию, таймлайны, процедурное управление движениями. ну и ладно, дело ваше. вот набросок, почти на си (как минимум типы подрихтовать, может ещё чего) и может быть даже кое-как рабочий.
- Код: Выделить всё • Развернуть
class Leg
{
private:
ulong startOn;
ulong cycleDuration_ms;
Servo s1;
Servo s2;
//start устанавливается меньше end
int degreeStart1;
int degreeStart2;
int degreeEnd1;
int degreeEnd2;
int degreeCurrent1;
int degreeCurrent2;
float degPerMs1;
float degPerMs2;
int s1CycleTime_ms;
int s2CycleTime_ms;
int direction; //1, -1
int thisCycleExeTime_ms;
int lastCycleExeTime_ms;
public:
Leg(int s1p, int s2p, ulong starton, ulong cycledur, int ds1, int ds2, int de1, int de2, int s1ct, int s2ct, int dir)
{
s1.attach(s1p);
s2.attach(s2p);
startOn = starton;
cycleDuration_ms = cycledur;
degreeStart1 = ds1;
degreeStart2 = ds2;
degreeEnd1 = de1;
degreeEnd2 = de2;
s1CycleTime_ms = s1ct;
s2CycleTime_ms = s2ct;
direction = dir;
lastCycleExeTime_ms = 0;
degPerMs1 = (degreeEnd1 - degreeStart1) / s1CycleTime_ms;
degPerMs2 = (degreeEnd2 - degreeStart2) / s2CycleTime_ms;
if (dir >= 0) direction = 1;
else direction = -1;
if (direction == 1)
{
degreeCurrent1 = degreeStart1;
degreeCurrent2 = degreeStart2;
}
else
{
degreeCurrent1 = degreeEnd1;
degreeCurrent2 = degreeEnd2;
}
s1.Write(degreeCurrent1);
s2.Write(degreeCurrent2);
}
void Tick(ulong currentTime)
{
if (currentTime < startOn) return;
thisCycleExeTime_ms = (int)(currentTime - (ulong)((currentTime - startOn) / cycleDuration_ms) * cycleDuration_ms);
if (lastCycleExeTime_ms > thisCycleExeTime_ms) direction *= -1;
lastCycleExeTime_ms = thisCycleExeTime_ms;
if (thisCycleExeTime_ms >= s1CycleTime_ms)
{
if (direction == 1) degreeCurrent1 = degreeEnd1;
else degreeCurrent1 = degreeStart1;
}
else
{
if (direction == 1) degreeCurrent1 = degreeStart1 + (int)(degPerMs1 * thisCycleExeTime_ms);
else degreeCurrent1 = degreeEnd1 - (int)(degPerMs1 * thisCycleExeTime_ms);
}
if (thisCycleExeTime_ms >= s2CycleTime_ms)
{
if (direction == 1) degreeCurrent2 = degreeEnd2;
else degreeCurrent2 = degreeStart2;
}
else
{
if (direction == 1) degreeCurrent2 = degreeStart2 + (int)(degPerMs2 * thisCycleExeTime_ms);
else degreeCurrent2 = degreeEnd2 - (int)(degPerMs2 * thisCycleExeTime_ms);
}
s1.Write(degreeCurrent1);
s2.Write(degreeCurrent2);
}
}
Leg leg1(пинсервы1, пинсервы2, 3000, 6000, 60, 60, 90, 120, 3000, 8000, 1); //8000, ой
Leg leg2(пинсервы1, пинсервы2, 6000, 4000, 60, 60, 90, 100, 4000, 4000, 1);
Leg leg3(пинсервы1, пинсервы2, 10000, 5000, 60, 60, 90, 110, 4000, 5000, 1);
Leg leg4(пинсервы1, пинсервы2, 14000, 6000, 60, 60, 90, 100, 6000, 6000, 1);
ulong time;
void loop()
{
time = mills();
leg1.Tick(time);
leg2.Tick(time);
leg3.Tick(time);
leg4.Tick(time);
delay(2);
}
пока на этом закончим, дальше от вас зависит.
п.с. и это, ещё раз - шестерни пластиковые. даже с железными при начальной отладке всякие лапы в воздухе должны болтаться.
loox » 16 авг 2017, 23:13
1. millis не таймер
2. Для чего Вам нужен таймер?
3. Нет стособа узнать достига ли серво заданного положения
Опишите как Вы запланировали цикл управления движением
Loox
newbe » 17 авг 2017, 00:15
Angel71 писал(а):поняли, но ничего не поняли
servo_pos++; я так понимаю 1 градус раз в 50мс
1 попугай почти раз в 50мс. после калибровки попугай может быть примерно и грудус. со временем и пониманием, как что работает, ещё хуже. т.е. ничего не изменилось, игрушку в стол/шкаф. к тому, что раньше написано ещё добавляется, что раз в голове не можете прикидывать, как работают и конкретные куски кода и всё в общем, так начинать можно хоть по старинке - отлаживать на бумажке или в табличном редакторе. в первом столбце описание какая стадия или событие. потом время, тики или ещё что, связанное со временем. а потом столбцы для переменных. как именно делать, вариантов масса.
не хотите вы эту бредятину с тупо линейной цикличностью и вычислениями даже не по времени или по предсказаниям погоды на марсе. в общем пока не видно даже намёка на желание прислушиваться и тем более сделать хотя бы простенькую скелетную анимацию, таймлайны, процедурное управление движениями. ну и ладно, дело ваше. вот набросок, почти на си (как минимум типы подрихтовать, может ещё чего) и может быть даже кое-как рабочий.
- Код: Выделить всё • Развернуть
class Leg
{
private:
ulong startOn;
ulong cycleDuration_ms;
Servo s1;
Servo s2;
//start устанавливается меньше end
int degreeStart1;
int degreeStart2;
int degreeEnd1;
int degreeEnd2;
int degreeCurrent1;
int degreeCurrent2;
float degPerMs1;
float degPerMs2;
int s1CycleTime_ms;
int s2CycleTime_ms;
int direction; //1, -1
int thisCycleExeTime_ms;
int lastCycleExeTime_ms;
public:
Leg(int s1p, int s2p, ulong starton, ulong cycledur, int ds1, int ds2, int de1, int de2, int s1ct, int s2ct, int dir)
{
s1.attach(s1p);
s2.attach(s2p);
startOn = starton;
cycleDuration_ms = cycledur;
degreeStart1 = ds1;
degreeStart2 = ds2;
degreeEnd1 = de1;
degreeEnd2 = de2;
s1CycleTime_ms = s1ct;
s2CycleTime_ms = s2ct;
direction = dir;
lastCycleExeTime_ms = 0;
degPerMs1 = (degreeEnd1 - degreeStart1) / s1CycleTime_ms;
degPerMs2 = (degreeEnd2 - degreeStart2) / s2CycleTime_ms;
if (dir >= 0) direction = 1;
else direction = -1;
if (direction == 1)
{
degreeCurrent1 = degreeStart1;
degreeCurrent2 = degreeStart2;
}
else
{
degreeCurrent1 = degreeEnd1;
degreeCurrent2 = degreeEnd2;
}
s1.Write(degreeCurrent1);
s2.Write(degreeCurrent2);
}
void Tick(ulong currentTime)
{
if (currentTime < startOn) return;
thisCycleExeTime_ms = (int)(currentTime - (ulong)((currentTime - startOn) / cycleDuration_ms) * cycleDuration_ms);
if (lastCycleExeTime_ms > thisCycleExeTime_ms) direction *= -1;
lastCycleExeTime_ms = thisCycleExeTime_ms;
if (thisCycleExeTime_ms >= s1CycleTime_ms)
{
if (direction == 1) degreeCurrent1 = degreeEnd1;
else degreeCurrent1 = degreeStart1;
}
else
{
if (direction == 1) degreeCurrent1 = degreeStart1 + (int)(degPerMs1 * thisCycleExeTime_ms);
else degreeCurrent1 = degreeEnd1 - (int)(degPerMs1 * thisCycleExeTime_ms);
}
if (thisCycleExeTime_ms >= s2CycleTime_ms)
{
if (direction == 1) degreeCurrent2 = degreeEnd2;
else degreeCurrent2 = degreeStart2;
}
else
{
if (direction == 1) degreeCurrent2 = degreeStart2 + (int)(degPerMs2 * thisCycleExeTime_ms);
else degreeCurrent2 = degreeEnd2 - (int)(degPerMs2 * thisCycleExeTime_ms);
}
s1.Write(degreeCurrent1);
s2.Write(degreeCurrent2);
}
}
Leg leg1(пинсервы1, пинсервы2, 3000, 6000, 60, 60, 90, 120, 3000, 8000, 1); //8000, ой
Leg leg2(пинсервы1, пинсервы2, 6000, 4000, 60, 60, 90, 100, 4000, 4000, 1);
Leg leg3(пинсервы1, пинсервы2, 10000, 5000, 60, 60, 90, 110, 4000, 5000, 1);
Leg leg4(пинсервы1, пинсервы2, 14000, 6000, 60, 60, 90, 100, 6000, 6000, 1);
ulong time;
void loop()
{
time = mills();
leg1.Tick(time);
leg2.Tick(time);
leg3.Tick(time);
leg4.Tick(time);
delay(2);
}
пока на этом закончим, дальше от вас зависит.
п.с. и это, ещё раз - шестерни пластиковые. даже с железными при начальной отладке всякие лапы в воздухе должны болтаться.
Сегодня осваивал классы, с ними точно было бы проще. Придется потратить не мало времени на них, но спасибо.
А робот кстати подвешен, в сантиметре от пола)
Добавлено спустя 8 минут 45 секунд:loox писал(а):1. millis не таймер
2. Для чего Вам нужен таймер?
3. Нет стособа узнать достига ли серво заданного положения
Опишите как Вы запланировали цикл управления движением
Loox
Ну как это нету? Я сегодня делал эксперемент со светодиодом. При помощи millis, серво меняет угол на один градус каждый заданый промежуток времени, к примеру мы идем из 0 к 30 градусам. А когда значение позиции доходит до заданых 30 загорается светодиод. Я на этом принципе поочередного движения добился, но как я говорил, это слишком заморочено.
Angel71 » 17 авг 2017, 02:15
остановившиеся механические часы могут два раза в день правильное время показывать. в этой вашей текущей примитивной физической модели кое-как учитывается только время, всё остальное просто подогнано. изменится сопротивление провода, напряжение питания, максимальный ток, который источник питания выдать может, нагрузка на серву и т.д., всё, разница между фактическим и расчётным положением измениться в любую сторону. без внесения корректировок или обратной связи, допустим по потенциометрам, максимум это постоянно подгонять модель.
loox » 17 авг 2017, 10:28
Newbe
Пожалуста отнеситесь к нашим вопросам конструктивно, очень трудно понять логику, когда коллега, задавший ворос, не описывает проблему, а показывает кусок кода, без коментариев и не всегда грамотно написанный.
Итак:
1. Цикл управления сервоприводом 20 миллисекунд, это связано с конструктивными особенностями сервопривода.
2.Управление приводом - задать угол, дождаться года серво выполнит команду.
Самая сложная проблема - это из программы понять, установилось ли заданное положение угла
а) два одинаковых серво имеют разные скорости - это из их кострукции
б) нагруженная на вал и без нагрузки - то же разные скорости
Обычно в любительских конструкциях применяют следующий код:
Servo.wrire(angl);
delay(ждем);
......
Напишем код
void loop ()
{
myservo.write(60); // установить в исходное состояние
delay(500); // не заю в каком состоянии находится механизм все должно встать на место
// это пример
myservo.write(120); //повернуться на угол 120
delay(400); // не знаю какое время нужно для поворота, подберу в эксперименте
myservo.write(60);
while(1) {};
}
В этом куске кода серво должно повернуться из исходного состояния 60 в положение 120 а потом вернуться снова в 60
delay(400) -эту часть кода, а вернее 400 надо подобрать таким образом, чтобы серво повернуласть уверенно, зафиксировала свое положение а потом вернулассь назад.
Это понятно?
Есть ли у Вас циклограмма движения?
В вашем коде, есть непонятная конструкция - это главный бесконечный цикл, непонятно
Looc
newbe » 17 авг 2017, 13:26
Angel71 писал(а):остановившиеся механические часы могут два раза в день правильное время показывать. в этой вашей текущей примитивной физической модели кое-как учитывается только время, всё остальное просто подогнано. изменится сопротивление провода, напряжение питания, максимальный ток, который источник питания выдать может, нагрузка на серву и т.д., всё, разница между фактическим и расчётным положением измениться в любую сторону. без внесения корректировок или обратной связи, допустим по потенциометрам, максимум это постоянно подгонять модель.
Конечно обратная связь очень важный элемент.Вот по изменению тока, это же скорее всего нужно по амперметру на каждую сервомашинку, верно? Или по вольтметру, тоже можно. Или есть способы считывать нагрузочные токи без сторонних модулей? Я хотел поставить на каждую лапу по нажимному контакту, тогда можно было распознавать, когда лапа уверенно соприкоснулась с землей и останавливать движение.
А в общем вы правы, без классов тут не обойтись. И физику движений тоже нужно расчитывать.
До этого я собирал 4х лапого паука с 3dof
Я расчитывал движения с циркулем и линейкой. Но там проще обстоят дела, так как вращение конечностей происходит от самого начала луча.
Добавлено спустя 4 минуты 24 секунды:loox писал(а):Newbe
Пожалуста отнеситесь к нашим вопросам конструктивно, очень трудно понять логику, когда коллега, задавший ворос, не описывает проблему, а показывает кусок кода, без коментариев и не всегда грамотно написанный.
Итак:
1. Цикл управления сервоприводом 20 миллисекунд, это связано с конструктивными особенностями сервопривода.
2.Управление приводом - задать угол, дождаться года серво выполнит команду.
Самая сложная проблема - это из программы понять, установилось ли заданное положение угла
а) два одинаковых серво имеют разные скорости - это из их кострукции
б) нагруженная на вал и без нагрузки - то же разные скорости
Обычно в любительских конструкциях применяют следующий код:
Servo.wrire(angl);
delay(ждем);
......
Напишем код
void loop ()
{
myservo.write(60); // установить в исходное состояние
delay(500); // не заю в каком состоянии находится механизм все должно встать на место
// это пример
myservo.write(120); //повернуться на угол 120
delay(400); // не знаю какое время нужно для поворота, подберу в эксперименте
myservo.write(60);
while(1) {};
}
В этом куске кода серво должно повернуться из исходного состояния 60 в положение 120 а потом вернуться снова в 60
delay(400) -эту часть кода, а вернее 400 надо подобрать таким образом, чтобы серво повернуласть уверенно, зафиксировала свое положение а потом вернулассь назад.
Это понятно?
Есть ли у Вас циклограмма движения?
В вашем коде, есть непонятная конструкция - это главный бесконечный цикл, непонятно
Looc
Спасибо за старания) Но то что вы мне предлагаете отпало еще год назад. В вашем примере серво меняет угол с максимальной скоростью. Можно сделать конечно и плавно, но это не сработает, когда нужно будет делать движение робота вперед или поворот на месте, так как будет задействовано одновремено 4-8 серв и они должны будут двигаться одновременно и на абсолютно разные углы.
У меня есть график движения лап и корпуса на диаграме x,y. Но он на листе бумаги)
Сегодня я хочу сделать блок-схему движений, мне кажется так будет проще работать. Вот изначальный проект человека, который это задумал
http://www.robotshop.com/letsmakerobots/quadУ него шаги осуществляются как вы видите: задняя правая, передняя поавая, потом левая сторона, а у меня крест на крест. То есть передняя правая-задняя левая, передряя левая-задняя правая
newbe » 17 авг 2017, 17:48
А что если так. Серво1 стремится от 0 до 60, с интервалом 10мс на один градус.
если отталкиваться от времени таким образом if(millis()-lastime)=angle×interval)
{
Position =1;
}
тоесть сравнивать время с произведением угла на интервал, когда время сравняется то позиции присваевается новое значение и начинает работу другая серва
Angel71 » 17 авг 2017, 18:10
вас оказывается трое - один иногда читать умеет, второй даже немного писать, третий делает
if (direction == 1) degreeCurrent1 = degreeStart1 + (int)(degPerMs1 * thisCycleExeTime_ms);
else degreeCurrent1 = degreeEnd1 - (int)(degPerMs1 * thisCycleExeTime_ms);
передайте третьему, что будет весело, когда дело дойдёт до попытки обрабатывать данные о токе, напряжении и нажатии кнопочек.