Как независимо запускать позиционные и непрерывные сервоприводы с помощью millis()?
Я настраиваю 3 сервопривода (2 сервопривода положения и 1 сервопривод непрерывного вращения на 360градусов) с помощью Arduino. но мне не удалось запустить их самостоятельно. Он бежит один за другим. Я хочу запустить его в цикле независимо друг от друга. Вот код, который я использую для запуска позиционного сервопривода и сервопривода CR (360). позиции сервопривода по умолчанию равны 90. *Сервопривод CR 360 должен работать независимо 90-60-120-90 (в цикле) *Сервопривод Pos должен работать один за другим (в цикле)
Для запуска позиционных сервоприводов (наклон/панорамирование):
//сервопривод наклона
for (pos = 90; pos >= 60; pos -= 1) //90 ~ 60
{
tiltservo.write(pos);
delay(30);
}
for (pos = 60; pos <= 120; pos += 1) //60 ~ 120
{
tiltservo.write(pos);
delay(30);
}
for (pos = 120; pos >= 90; pos -= 1) //120 ~ 90
{
tiltservo.write(pos);
delay(30);
}
//сервопривод панорамирования
for (pos = 90; pos >= 60; pos -= 1) //90 ~ 60
{
panservo.write(pos);
delay(30);
}
for (pos = 60; pos <= 120; pos += 1) //60 ~ 120
{
panservo.write(pos);
delay(30);
}
for (pos = 120; pos >= 90; pos -= 1) //120 ~ 90
{
panservo.write(pos);
delay(30);
}
Для запуска сервопривода CR 360:
legservo.writeMicroseconds(1600); //CW
delay(5000); //run for 5s
legservo.writeMicroseconds(1300); //CCW
delay(5000); //run for 5s
Попробовал это для независимого запуска сервоприводов CR, но как поставить некоторое ожидание (3s-4s) между FW и RW.
if (millisNow - lastMillis >= 10000)
{
lastMillis = millisNow;
if (crservoState == 0)
{
legservo1.writeMicroseconds(2150); //FW
crservoState = 1;
}
else
{
legservo1.writeMicroseconds(850); //RW
crservoState = 0;
}
}
Пробный код после предложений:
uint32_t milliSNow = millis();
if (milliSNow - lastTimeServoRotate >= 1000)
{
if (crservoState == 0)
{
if (milliSNow - lastTimeServoRotate >= 10000)
{
legservo1.writeMicroseconds(2150); //FW run
crservoState = 2;
lastTimeServoRotate = milliSNow;
}
}
if (crservoState == 1)
{
if (milliSNow - lastTimeServoRotate >= 10000)
{
legservo1.writeMicroseconds(850); //RW run
crservoState = 3;
lastTimeServoRotate = milliSNow;
}
}
if (crservoState == 2)
{
if (milliSNow - lastTimeServoRotate >= 3000)
{
legservo1.writeMicroseconds(1500); //FW STOP
crservoState = 1;
lastTimeServoRotate = milliSNow;
}
}
if (crservoState == 3)
{
if (milliSNow - lastTimeServoRotate >= 3000)
{
legservo1.writeMicroseconds(1500); //RW STOP
crservoState = 0;
lastTimeServoRotate = milliSNow;
}
}
}
@Emlinux, 👍2
Обсуждение1 ответ
Лучший ответ:
Это описано в учебнике по Arduino "Мигание без задержки". Позвольте мне перефразировать основную часть примера кода здесь:
uint8_t ledState = LOW;
uint32_t lastTimeLedChanged = 0;
void loop() {
uint32_t now = millis();
if (now - lastTimeLedChanged >= 1000) {
// Сохраните последний раз, когда мы обновляли светодиод.
lastTimeLedChanged = now;
// Вычислите новое состояние светодиода.
if (ledState == LOW) {
ledState = HIGH;
} else { // ledState is HIGH
ledState = LOW;
}
// Установите индикатор в вычисленное состояние.
digitalWrite(LED_BUILTIN, ledState);
}
}
Это позволит светодиоду переключаться между двумя возможными состояниями с требуемой скоростью. Вы можете применить этот принцип как есть к вашему сервоприводу непрерывного вращения.
Для позиционных сервоприводов все еще можно применить тот же принцип, но
есть одна загвоздка. В то время как и светодиод, и сервопривод CR переключаются между
двумя состояниями, пара позиционных сервоприводов проходит через множество состояний
(92 240, если я правильно посчитал). Таким образом, Вам понадобится:
- дополнительные переменные для хранения текущего состояния сервопары
- больше кода для вычисления нового желаемого состояния
- больше кода для обновления состояния
Чтобы понять, как представить состояние, вам нужно посмотреть на
ваш текущий код на основе функции delay ()
, посмотреть, сколько вызовов функции delay()
выполняется в каждом полном цикле, и выяснить, как идентифицировать каждый из
этих вызовов. Каждая из этих задержек соответствует одному состоянию вашей
системы. Я бы сказал, что вам нужна одна переменная, чтобы знать, в каком из циклов
for
вы находитесь, и другая переменная, чтобы знать последнюю позицию, которую вы
записали на сервопривод в этом цикле. Я буду называть каждый из оригиналов для
зацикливает “фазу”. Существует два из шести таких этапов:
- фаза 0: наклон: 90° → 60°
- фаза 1: наклон: 60° → 120°
- фаза 2: наклон: 120° → 90°
- фаза 3: панорамирование: 90° → 60°
- фаза 4: панорамирование: 60° → 120°
- фаза 5: панорамирование: 120° → 90°
Обратите внимание, что в исходном вопросе было только два
цикла for, но
затем он был отредактирован, чтобы включить шесть циклов, поэтому я отредактировал свой ответ.
Затем вам нужно будет сохранить время последнего обновления, таким образом:
int phase = 0; // в какой из шести фаз мы находимся
int pos = 90; // последняя позиция, записанная на сервопривод
uint32_t lastTimeServoMoved = 0;
Как только у вас появятся правильные переменные для описания состояния, обновить его будет несложно. Если вы достигли конца фазы, переходите к следующей. Затем обновите позицию на один шаг. Таким образом:
void loop() {
uint32_t now = millis();
if (now - lastTimeServoMoved >= 30) {
// Сохраните последний раз, когда мы обновляли позиционный сервопривод.
lastTimeServoMoved = now;
// Вычислите новое состояние.
if (phase == 0 && pos == 60)
phase = 1;
else if (phase == 1 && pos == 120)
phase = 2;
else if (phase == 2 && pos == 90)
phase = 3;
else if (phase == 3 && pos == 60)
phase = 4;
else if (phase == 4 && pos == 120)
phase = 5;
else if (phase == 5 && pos == 90)
phase = 0;
if (phase == 1 || phase == 4)
pos += 1;
else
pos -= 1;
// Установите сервопривод в вычисленное состояние.
if (phase < 3)
tiltservo.write(pos);
else
panservo.write(pos);
}
}
Лично мне не нравится такой повторяющийся код. Если вас устраивают структуры данных, я бы рекомендовал использовать массив структур для хранения всей информации, описывающей действия, выполняемые на каждом этапе:
struct {
Servo *servo; // какой сервопривод перемещать
int step; // шаги, по которым он перемещается
int final_pos; // конечное угловое положение
} moves[] = {
{ &tiltservo, -1, 60 }, // tilt: 90 -> 60
{ &tiltservo, +1, 120 }, // tilt: 60 -> 120
{ &tiltservo, -1, 90 }, // tilt: 120 -> 90
{ &panservo, -1, 60 }, // pan: 90 -> 60
{ &panservo, +1, 120 }, // pan: 60 -> 120
{ &panservo, -1, 90 } // pan: 120 -> 90
};
Тогда фактический код становится проще:
void loop() {
uint32_t now = millis();
if (now - lastTimeServoMoved >= 30) {
// Сохраните последний раз, когда мы обновляли позиционный сервопривод.
lastTimeServoMoved = now;
// Compute the new state.
if (pos == moves[phase].final_pos) {
phase +=1;
if (phase == 6)
phase = 0;
}
pos += moves[phase].step;
// Установите сервопривод в вычисленное состояние.
moves[phase].servo->write(pos);
}
}
большое вам спасибо за подробное объяснение с примером. Это хорошо понимать. Я попробовал это, но сервопривод CR не ждет 5 секунд между движением CW и CCW.
Фактическая операция должна быть:
Сервопривод CR: (в цикле)
запуск CW в течение 5 секунд,
подождите 2 секунды,
CCW для 5s
Сервопривод Pos (панорамирование/наклон): (в цикле)
1-й сковородный ход с 90~60, 60~120, 120~90 тогда
2-й ход наклона от 90~60, 60~120, 120~90
Пожалуйста, предложите., @Emlinux
@Emlinux: Похоже, это не соответствует коду в вашем вопросе. Пожалуйста, обновите вопрос соответствующей информацией. Постарайтесь быть как можно более ясным, чтобы избежать большего недопонимания ваших требований., @Edgar Bonet
извините за недоразумение. Я соответствующим образом обновил его. Спасибо., @Emlinux
Спасибо за обновление решения. Я это понял. Хотя я не очень часто использовал структуру данных, но здесь хорошо использовать ее эффективно. Я предпочту учиться и использовать его таким образом. поскольку в комплект входит еще 1 leg_servo (CR 360), я попытался добавить его в цикл с помощью сервоприводов положения, подобных этому. но я не могу приостановить это на 2-3 секунды после запуска CW/CCW в течение 10 секунд. if (millisNow - lastMillis >= 10000) { lastMillis = millisNow; if (crservoState == 0) { legservo1.writeMicroseconds(2150); crservoState = 1; } else { legservo1.writeMicroseconds(850); crservoState = 0; } }, @Emlinux
Пожалуйста, предложите что-нибудь. Спасибо., @Emlinux
@Emlinux: Пожалуйста, попытайтесь понять _ общий подход_, проиллюстрированный этим ответом: разбиение цикла на фазы, определение отдельных шагов в рамках каждой фазы, выяснение того, как кодировать и обновлять состояние... Хотя код, который я показываю, специфичен для сервоприводов наклона и панорамирования, подход является общим, и, если вы его понимаете, вы можете применить его к сервоприводу CR. **Не спешите с написанием кода**: сначала подумайте и проанализируйте., @Edgar Bonet
да @Эдгар Бонет, я попробовал то же самое, поэтому я поделился несколькими строками кода в своем предыдущем посте. поскольку мне нужно запускать сервоприводы CR независимо от сервоприводов POS (панорамирование/наклон), я попытался запустить его независимо для 10 секунд FW и 10 секунд RW, и он работает, но я не уверен, как поместить некоторый wait_delay (например, 3 секунды) между FW и RW. Спасибо., @Emlinux
поскольку код, упомянутый в моих предыдущих комментариях, плохо виден, я поместил его в конец поста. пожалуйста, проверьте и направьте. Спасибо., @Emlinux
- Как заставить сервопривод вращаться на угол больше 180°
- Использовать timer0, не влияя на millis() и micros().
- Торговый автомат Arduino для мониторинга ввода монет в слот во время ожидания ввода пользователя
- Подключение Arduino к сервоприводу с внешним источником питания
- Мой сервопривод не работает плавно
- Бесполезная проводка коробки и код
- Почему сервопривод не перемещается по углам должным образом
- Микро сервопривод не работает с Arduino UNO
https://arduinoprosto.ru/q/83977/how-to-run-2-different-servo-continuous-rotation-and-regular-servo-in-parallel/83984#83984, @ArduinoFan
@ArduinoFan спасибо, что поделился ссылкой, я пробовал это, но это для управления сервоприводами того же типа, в моем случае мне также нужно настроить позиционный сервопривод и CR, поэтому его работа становится смешанной. во время выполнения панорамирования/наклона один за другим с непрерывно работающим сервоприводом CR CW/CCW со временем ожидания 2 с. любая помощь будет признательна. Спасибо., @Emlinux
Последний фрагмент кода, который вы опубликовали, должен работать, хотя и с 10-секундными (а не 3-4-секундными) паузами между FW и RW. Если это не работает, у вас есть проблема в другом месте вашего кода. Может быть, вы делитесь переменной "lastMillis" с кодом, обрабатывающим позиционные сервоприводы?, @Edgar Bonet
@EdgarBonet Спасибо за ваш комментарий. Этот код работает для запуска CR таким образом.. 1. при запуске он запускается через 10 секунд 2. серво1 работает в течение 10 секунд FW, затем немедленно запускается в течение 10 секунд RW в цикле. но я хочу подождать / подождать 3 секунды после 10 секунд FW и 10 секунд RW каждый. 3. "lastMillis" используется только для сервопривода CR, он не конфликтует с переменной сервопривода POS., @Emlinux
Re “ _ Я хочу подождать / подождать 3 секунды после 10 секунд FW и 10 секунд RW, запускаемых каждый раз”: Этого нет в вашем фрагменте кода. Если вам нужны эти паузы, вы должны явно попросить сервопривод остановиться, и ваш цикл состоит из **четырех** фаз: FW, пауза, RW, пауза., @Edgar Bonet
@EdgarBonet Спасибо тебе. Я загрузил его как "Пробный код", не могли бы вы, пожалуйста, проверить его? так как он работает, но время выполнения fw/rw составляет 3 секунды вместо 10 секунд, а время удержания-10 секунд вместо 3 секунд, хотя я определил, что он будет работать в течение 10 секунд и удерживать/останавливать в течение 3 секунд. где я ошибаюсь? пожалуйста, предложите~, @Emlinux
@EdgarBonet Я только что перевернул (milliSNow - lastTimeServoRotate), проверил от 10000 до 3000, и он запустился. (milliSNow - lastTimeServoRotate), это проверяет прошедшее время. Но при запуске он ждет запуска в течение 10 секунд, даже если я установил его на проверку >= 1000 (~1 с), я его не получил. Пожалуйста, предложите., @Emlinux