Как стать автором
Обновить

Комментарии 96

И естественно, пытаясь реагировать на них каждые 200 миллисекунд, у нас получалось очень сильные действия по регулированию. Мы слишком много раз «тыкали на кнопки» при малейшем отклонении. А чем ближе к кораблю, тем сильнее значения смещения начинали скакать и мы фактически еще больше раскачивали корабль…

Но ведь ПИД же есть. :)
Да не только ПИД, а в принципе хорошо разработанная теория автоматического управления. Для систем с обратной связью надо обеспечивать стабильность, что, по-хорошему требует некоторых теоретических выкладок и построения диаграмм Боде. Если все нормально посчитать, то никакого ухода в автоколебания не будет — и это именно такая задача, которую должна решать настоящая система автоматической стыковки.
Сам симулятор расположен на сайте сайте и представляет собой, довольно проблематичную, на первый взгряд игрушку…
Ничего сложного в игрушке нет. Выводишь угловые отклонения в ноль и действуя только смещениями наводишься на центр шлюза. Даёшь импульс вперёд и короткими боковыми импульсами удерживаешь центр. Перед самой стыковкой тормозишь до минимума.
В общем, после стыковок в KSP, особенно когда смещён центр масс и цель тоже слегка поворачивается, эта игрушка проходится на ура с первого раза.

Хм… Могу только порадоваться за Вас:) Ибо я сначала пробовал только по визуальным ориентирам проходить, но безуспешно. До шлюза доходил и получал провал, не выполнив одно из условий.

Зачем по визуальным? Игра показывает угловые отклонения и угловые скорости, боковые отклонения и расстояние. Несколько не хватает индикации боковых скоростей, но тут уже проще.
Главное — не спешить, и изначально заходить максимально соосно, с нулевым поворотом и скоростями по всем трём осям.
Навыки входа в станцию без посадочного компьютера в Elite тоже способствуют…
ru.wikipedia.org/wiki/Elite
НЛО прилетело и опубликовало эту надпись здесь
На изображении клавиатурного управления небольшая ошибка — в левом блоке кнопка, отвечающая за смещение вправо, это «D», а не «F».
Спасибо. Внес правки.
Мне пришлось заменить outerText на textContent
Хм… Ну в принципе, да, можно и так. Вообще я писал свой код под Chrome и не стал проверят, в других браузерах.:)
Удивил факт, что было сложно найти код эмуляции кнопок, ибо даже заплюсованные ответы на StackOverflow почему-то не работали.
Плюсанул бы. Но карму ухекал :)
Помимо самих кнопок генерируется довольно много разных событий, которые могут требоваться для используемоего фреймворка.
Американские Шатлы всегда приземлялись только в ручном режиме. Это было удивительно, принимая во внимание тот факт, что компьютеры раньше были не мощнее калькулятора.

Спейс Шатлы садились в автоматическом режиме до высоты примерно в 15 тысяч метров и пилот брал управление на себя, когда скорость становилась уже дозвуковая, когда до посадки оставалась пару минут. Полная автоматическая посадка была доступна до касания ВПП и испытывалась в одной из первых миссий еще в 82-ом году. И то, что она была возможно только до касания ВПП, — было не техническим, а концептуальным ограничением. Невозможность посадки челнока без человека внутри было требованием пилотов при разработке: им хотелось хоть немного порулить, они ведь военные пилоты, а тут их хотят какой-то машиной заменить :)
Так что Буран здесь не совершил никакого прорыва.
Про Шаттл — очень живучий миф. Отсекается даже без знания фактологии, если знать когда была первая автоматическая посадка, и представлять, насколько сложно свести планер с орбиты с точностью достаточной для посадки на полосу с единственной попытки (человеку такое никогда бы не доверили). А еще ведь стабилизация есть на горячем участке, тоже та еще задачка.

Материалов вот маловато на тему того, что после Колумбии удалось заавтоматизировать на Шаттле тем самым проводом, а то таки мешало(?) нормальной посадке со всеми процедурами.
Материалов вот маловато на тему того, что после Колумбии удалось заавтоматизировать на Шаттле тем самым проводом, а то таки мешало(?) нормальной посадке со всеми процедурами.
Материалов вполне хватает (если владеть English), а «что мешало» — как ни странно, пресловутый «человеческий фактор».

В отличие от советской космонавтики, где космонавты не очень-то отличались от Белки и Стрелки (или чукчи из широко известного анекдота), американские программы всегда ориентировались на человека-пилота.

Поделитесь линком. Везде remote control capabilities без подробностей, т.е. не ясно, старт ВСУ, выпуск атмосферных сенсоров и т.д. выполнялись полностью автоматически или по команде с Земли. Костыль всё-таки, да и писали бы прямым текстом про автономность, но вот нет.

Сорри, сейчас не могу поделиться; когда-то находил (упорно гуглил и искал на сайте NASA), но впервые увидел описание автоматики на экспозиции шаттла Atlantis, в выставочном центре на мысе Канаверал.
Ну, это Вы зря.
Я п присоветовал с вопросом-то разобраться.
Очень много ручных операций на орбите. НАчиная от ручных стыковок (бывают) до перестыковки кораблей от одного стыковочного узла на другой. Вручную…
Очень высокая подготовка космонавтов, именно как пилотов кораблей. И не у всех, кстати, получается — очень специфичная техника…
Ну и кучи аварийных ситуаций, где пристыковывались из, казалось, невозможных ситуаций менно вручную.
И под Буран был готов целый отряд, «Волчья стая», куда были набраны высококлассные лётчики-испытатели…
Так что про белок-стрелок-чукч, это вы мимо… уж извините. Это, если вкратце…
А насчёт автоматики — так этоизначально так должно быть. Мало ли что с космонавтом. Он должен быть жив-здоров, пристыкован/отстыкован и вернут на Землю… Такая практика шла изначально. И у нас, и у них…
Farik196, не поймите меня неправильно: я отнюдь не отрицаю высокую профессиональную подготовку советских и российских космонавтов, а также их исключительно высокие качества (за исключением тех моментов, когда некоторые из них вещают с трибуны ГосДуры о лично наблюдаемых с орбиты «жыдобандеровцах, обстреливающих мирное население Донбасса). Отсылаю Вас к великолепному труду Бориса Чертока „Ракеты и люди“ — там эта проблема рассмотрена и объяснена (с точки зрения тогдашней „правды“) самим творцом всей советской космической автоматики.
Абсолютно не хочу понять Вас неправильно, но, пожалуй, Вы валите всё в одну кучу и космонавтику и «жыдобандеровцев». Даже не хочу про это.
ЗЫ: Бориса Евсеевича имел честь хоть чуть-чуть, но знать лично. Книга его перечитана не раз.
С уважением.
Я не «валю в кучу», а просто к слову пришлось; я бы тоже не хотел, но это, к сожалению, факт (небольшой штрих касательно высокой подготовки советских/российских космонавтов).

Да и фраза "компьютеры раньше были не мощнее калькулятора" мягко говоря смущает. Из вики:


Бортовой комплекс управления состоит примерно из пятидесяти программных систем, на базе компьютера IBM System/370. Часть системных команд из IBM-набора S/370 не была реализована, в то же время было добавлено много оригинальных команд общего назначения, не имеющих аналогов в IBM-наборе. На борту корабля находилось два комплекта БЦВМ «Бисер-4» по четыре аппаратно-параллельных компьютера и аппаратного компаратора, допускающего автоматическое отключение подряд двух компьютеров в случае аварийных результатов (4 основных + 4 резерв).

Я так думаю, что этот "набор" с десяток пилотов (работающих слаженно) заменил. Поэтому преподносимый автопилот как какое-то достижение по мне как раз наоборот.

Насколько я помню из книги американского космонавта Майка Маллейна, система автоматической посадки таки была, но ей ни разу не воспользовались по указаным Вами причинам.

Не помню уже источник, но очень нравится это высказывание (своими словами): "… в СССР космос осваивали ракетчики, а в Штатах — пилоты самолетов"

Под спойлером полный код. Вы можете сами проверить его работоспособность на сайте iss-sim.spacex.com

Инструкция б не помешала бы. Заодно может организовать соревнования по наилучшему (самому быстрому, самому экономному) алгоритму автоматической стыковки?
Условный расход топлива можно считать по количеству импульсов и их длительности.
Инструкция б не помешала бы.

1) Открыть сайт и запустить эмулятор.
2) Открыть консоль F12
3) Открыть вкладку Console
4) Вставить код из под «Спойлера». Нажать Enter. :)
5) Наслаждаться. :)
Заодно может организовать соревнования по наилучшему (самому быстрому, самому экономному) алгоритму автоматической стыковки?

Я только ЗА! :)
Но я не у верен, что найдутся еще энтузиасты.
Я бы поучаствовал, только я питонист. Вот думаю, как реализовывать получение данных.
Можно взять мой код и выкинуть из него сам алгоритм. Получение данных и функцию клика можете оставить и сотворить свой вариант.:)
Идея хорошая. Только там нет длительности импульсов. Можно будет считать только количество.

Экономный будет довольно долгим, но около 20 импульсов хватит: 6 на выравнивание по курсу/крену/тангажу, 4 на грубое выравнивание осей, остальное — на разгон/торможение и тонкое выравнивание. Главное — не пытаться постоянно держать строго нули, позволить немного "гулять", окончательно свести оси лишь непосредственно перед касанием парой-другой импульсов.

это что он курить такое?)))
Что я делаю не так?
Скрытый текст
image
Хм… Вообще из коробки должно работать. Повторятся часто?
то же самое
Возможно поможет совет
Мне пришлось заменить outerText на textContent
Вообще конечно больно смотреть на этот способ регулирования :) реализовали бы хотя бы ПИД, как советовали выше — было бы намного быстрее и проще.
У меня аналогично. Из двух попыток — два провала. Rotation Speed — как и на этом скриншоте.

PS: браузер — Firefox. Пришлось заменить outerText на textContent — как советовали выше.
Поменял в статье outerText на textContent и проверил на FireFox.
У меня вышло успешно. У Вас не получилось?
Сначала скрипт не стартовал — ругался на ошибку. После замены outerText на textContent — перестал ругаться и начал работать. Но стыковка завершилась неудачей. Пробовал два раза — оба раза фейл с выдачей сообщения «Rotation Speed».
Собственно ваша вторая итерация — как я понимаю — попытка привнести дифференцирующее звено в ваш контур управления, поэтому стало работать лучше :) но вы пытаетесь оценить скорость движения странными дискретными кусками по секунде.
Это похоже на Arduino стайл :-)
В финале, я фактически забил на скорость изменения, а лишь пытаюсь привести систему в состояние равное 0. Дифференцирующее звено — даст нам скорость изменения (если я не ошибаюсь). Конечно, в определенной степени, за это показатель, отвечает поле «Speed». Но основная суть — просчитать поведение и давать такое управляющее воздействие, дабы стабилизировать все в 0.

По Поводу ПИД — в ваших руках сделать лучше) Я не против посоревноваться. Мне кажется, проблема в очень дискредитизированных значениях, которые мы считываем… Они не только ведь дискретны, но и еще идут с определённой задержкой от управляющего воздействия. Но, как я уже сказал, я готов посмотреть и возможно даже посоревноваться с Вашим решением.
Ваш вариант ещё надо очень дорабатывать. Слишком медленно и нестабильно.

Стыковка по-ковбойски (судя по всему люди, а не скрипты):
27 секунд


23 секунды


Кто уложится в 20 секунд?
НЛО прилетело и опубликовало эту надпись здесь
Это просто примеры «быстрого прохождения» игры. Где-то в комментариях их сравнили с ковбоями.
Видимо в том смысле что весело, задорно и с упором на свое мастерство вместо безопасности стыковки?
такое вообще возможно?
Я думаю, что с помощью NES speedrun-а это вполне возможно. :)
На первом ролике — ювелирная работа. Прямо в яблочко! Второй — так себе — на грани фола. Сумел дёрнуться в самом конце и за счёт этого попал. Можно сказать — случайность.
за 27 секунда человек не сможет. Скриптом даже сложно такое время сделать.
В настройках игры порадовала опция «Плоская Земля»
Там еще пасхалка есть. С обратной стороны, машина, которую Маск в космос запустил.
Обьясните новичку, а куда вставлять этот код, чтоб работал?
Я чуть выше писал инструкцию.
1) Открыть сайт и запустить эмулятор.
2) Открыть консоль F12
3) Открыть вкладку Console
4) Вставить код из под «Спойлера». Нажать Enter. :)
5) Наслаждаться. :)
Хотя это скорее инженерная работа Королёва Сергея Павловича, который к сожалению, так и не побывал в космосе, о котором мечтал…


Ну, баллистика-то скорее работа не С.П.Королева, а все-таки баллистиков. Например, достаточно известен один из руководителей отдела Энергии Р.Ф. Аппазов, про которого даже страница в Википедии есть, и который эту самую баллистику нам читал в свое время.

Простите, немного не по теме, но давно мучает вопрос: почему нельзя в космосе на определенном моменте сближения пробросить с одного корабля на другой трос и потом просто притянуть один корабль к другому. Мне кажется, автоматическую систему бросания/ловления троса реализовать совсем просто. А если стягивать по оси стыковочных узлов, то корабли автоматически центрируются.

Я полагаю, что проблема в отсутствии сопротивления. Космические корабли — не морские суда.
Вы притягиваете трос и начинаете двигаться к станции. Во-первых Вы двигаете еще и станцию, меняя ее орбиту. Во вторых вы начали двигаться в направлении станции, как тормозить?

Потому что космос — не вода. Если корабль смотрит носом не точно на лебёдку и если трос прикреплен к носу корабля, то при первоначальном рывке троса корабль начнет вращаться и остановить вращение можно будет только двигателями.


Да и вообще, как вы представляете перебрасывание троса? Это надо буквально выстрелить им из станции и попасть в какой-то приемный порт корабля. Учитывая, что корабль тоже движется и вращается. И что там снаружи торчат хрупкие антенны и солнечные батареи.


Проще уж воспользоваться канадармом, но он есть только на МКС. Т.е. это — не универсальное решение.

Трос создаст вращающий момент, который необходимо будет компенсировать. Вряд ли тянущее усилие будет проходить строго через центры масс обоих аппаратов.
Ну и кроме соосности в стыковочном узле необходимо ещё совпадение угла поворота, иначе не сработают сцепные механизмы шлюза.
Если честно то и сейчас американские грузовые корабли просто сближаются и их захватывает Сanadarm(аналог троса) и точно притягивает последние 5-10 метров
Прикольная штука.
Кто в элиту играл тот Маска не боиться. Я тоже первым делом подумал о стыковке в первой элите )
Можно не трогать DOM а использовать их собственный API: camera.rotation.x/.y/.z — pitch/yaw/roll, issObject.position.x — camera.position.x (/y/z) — координаты, ну и управлять translateRight/yawRight и проч. У меня с копипастами вышло полтора килобайта, думаю можно уложиться в твит при желании.
Выложи код сюда.
function sr(n,rr,l,m){
    var d = camera.rotation[n]
    if(Math.abs(d)<0.002)
        return

    tr = d/5
    r = rr()/10*toRAD
    if(r > tr)
        l()
    else
        m()
}

function sp(n,l,m) {
    var d = issObject.position[n] - camera.position[n]
    if(Math.abs(d) < 0.07)
        return

    tr = d * 0.01
    cr = motionVector[n]

    if(cr > tr) {
        l()
    } else {
        m()
    }
}

function setZ() {
    if(Math.abs(issObject.position.x - camera.position.x) > 0.2
        || Math.abs(issObject.position.y - camera.position.y > 0.2) ) {
            return
        }

    var d = issObject.position.z - camera.position.z
    var tr = Math.max(0.002 + d*d/20000, 0.1)
    var cr = -motionVector.z

    if(cr < tr) {
        translateForward()
    } else {
        translateBackward()
    }
}

function zeroIn() {
    sr('y', ()=>rateRotationY,yawLeft,yawRight)
    sr('x',()=>rateRotationX,pitchUp,pitchDown)
    sr('z',()=>rateRotationZ,rollLeft,rollRight)
    sp('y', translateDown, translateUp)
    sp('x', translateLeft, translateRight)
    setZ();
}

window.clearInterval(iii)
var iii = window.setInterval(zeroIn, 200)

Упс, поменяйте Math.max(0.002 + d*d/20000, 0.1) на Math.min, а то оно будет стыковаться на полной скорости :-)
Поробовал два раза. Оба раза провальные из-за скорости (при min)



Но вообще, идет к цели хорошо…
А на какой скорости оно стыкуется? У меня оно за 2 минуты подходит на 10 метров, а потом мееедленно крадется на 0.03m/s
Сначала долго стояли и выравнивались… А потом просто разогнались до довольно внушительной скорости и врезались на ней.
Очень похоже на Math.min -> Math.max. Сделайте page refresh, и запустите правильный код — должно заработать. Правда, будет мучительно медленно, особенно на последних метрах :-)
За «полсекунды» до удара.
Ага, надо поиграть с константой максимальной скорости, и вообще похоже что в Хроме оно резче реагирует на элементы управления — корабль разворачивает гораздо бодрее. Может, от производительности браузера зависит?

Запостил сюда, кстати github.com/vkalinsky/spacexiss-autodocker
Может, от производительности браузера зависит?/blockquote>
Ну что-то в этом точно есть. Так что, не мой алгоритм «медленный» просто комп тормозит :D
С «camera» хак интересный… Я о таком не знал…
Хотя… Получается это чуть-чуть, в обход «обозначеных условий» задачи. Вопрос уже не к решению, а к постановке. Но пусть каждый решает сам для себя насколько допустимо такое.
Штука прикольная, но у меня скрипт не смог пристыковаться. (((
Плюс очень уж много манипуляций происходит. Если подсчитать расход топлива, то совсем неэффективно получается.
Пилот «принял на грудь». Идея классная, но реализация — дрянь. Код примитивный и работает очень плохо. Особенно убило, что умудряется провалить стыковку, даже когда уже полтора метра до шлюза осталось и можно просто откинуться в кресле.
Вот все жду, когда «высококлассные программисты», коих полно на Хабре, выдадут на гора решение этой простой задачи из области управления с обратной связью, да чего-то уже два дня прошло, а решения все нет и нет.

Походу все-таки придется мне сесть и написать свое решение…
Вперёд. Сделаете решение лучше или проще, то вопросов нет. Я уже сказал, что буду рад посоревноваться. И, может, даже улучшить свой код.
Я учитывая, что мое решение, которое я писал в свободное после работы время пару вечеров назвали «дрянью», очень жду решения от вас.
Не, вопросы есть.
Я могу сделать решение на основе релейной логики или ТАУ с описанием модели КК в виде интеграторов. Но это же классика!
Где ИИ? Где нейросети и машинное обучение? Вот это я ожидал увидеть, а не джаваскрипт, который даже не может пристыковаться с первого раза. Решение задачи в виде «я скормил параметры yaw, pitch и speed моему пет-проектному ИИ фреймворку, который выдал мне команды на нажатие кнопок и все заработало» — где оно?

Да хоть как-нибудь сделайте, а потом говорите, что мое решение — дрянь.
Код получился сложнее, но работает гораздо лучше.
m = {

sleep: function (ms) {
return new Promise(resolve => setTimeout(resolve, ms));
},

round: function (f, digits = 0) {
if (digits) {
let multiplier = Math.pow(10, digits);
return Math.round((f + Number.EPSILON) * multiplier) / multiplier;
} else {
return Math.round(f);
}
},

press: async function (code, cnt = 1) {
let lookup_tbl = {
'e': 69, 'q': 81, 'a': 65, 'd': 68, 'w': 87, 's': 83,
'left': 37, 'up': 38, 'right': 39, 'down': 40,
'<': 103, '>': 105
};

let e1 = new Event('keydown');
let e2 = new Event('keyup');
e1.keyCode = lookup_tbl[code];
e2.keyCode = lookup_tbl[code];
//кол-во повторений
while (cnt--) {
document.dispatchEvent(e1);
document.dispatchEvent(e2);
await this.sleep(1);
}
},

get_state: function (s) {
let o = window.document.getElementById(s);
let v1 = o.children[0].innerHTML;
let v2 = o.children[1].innerHTML.split(' ')[0];
v1 = parseFloat(v1);
v2 = parseFloat(v2);
return {'offset': v1, 'speed': v2, 'offset_abs': Math.abs(v1), 'speed_abs': Math.abs(v2)}
},

get_xyz: function () {
let x = window.document.getElementById('x-range');
let y = window.document.getElementById('y-range');
let z = window.document.getElementById('z-range');
x = x.children[0].innerHTML.split(' ')[0];
x = parseFloat(x);

y = y.children[0].innerHTML.split(' ')[0];
y = parseFloat(y);

z = z.children[0].innerHTML.split(' ')[0];
z = parseFloat(z);

return {'x': x, 'y': y, 'z': z, 'x_abs': Math.abs(x), 'y_abs': Math.abs(y), 'z_abs': Math.abs(z)}
},
/*
логика такая: включаем наблюдателя, ловим изменение значение и замеряем скорость,
ловим 2 раз и определяем скорость, отключаем наблюдателя и возвращаем результат
в промис.
*/
calc_pos: async function (axis) {
let el = window.document.getElementById(axis + '-range').children[0];
let t1, t2, tt1, tt2, v1, v2, ms, speed, counter = 0;

//отслеживать изменения значений будем через наблюдателя
//callback функция для наблюдателя
return new Promise(resolve => {

let callback = (mutationsList, observer) => {
let m = mutationsList[0];
let ischanged;
//если у нас уже есть t1
//отловим изменение скорости на 2 единицы, чтобы
//вычисления были точнее
//ловить будем только, если что-то поменялось
try {
ischanged = m.addedNodes[0].nodeValue !== m.removedNodes[0].nodeValue;
} catch (e) {
//pass
}

if (ischanged) {
let islowspeed = t1 && (performance.now() - t1) >= 1300;
if (counter == 40 || islowspeed){
t2 = performance.now();
v2 = m.addedNodes[0].nodeValue;
v2 = parseFloat(v2.split(' ')[0]);
ms = t2 - t1;
speed = (v1 - v2) * 1000 / ms;
//вырубаем наблюдателя
observer.disconnect();
resolve(this.round(speed, 2));
} else {
//замер делаем только на первом цикле
if (!counter) {
t1 = performance.now();
v1 = m.addedNodes[0].nodeValue;
v1 = parseFloat(v1.split(' ')[0]);
}
//увеличиваем счетчик циклов
++counter;
}
}
//на случай, если скорость не изменяется в течение 5 секунд
if (!tt1) {
tt1 = performance.now();
//если не было ни одного изменения
} else if (!counter) {
tt2 = performance.now() - tt1;
if (tt2 > 5000) {
//вырубаем наблюдателя и возвращаем 0
observer.disconnect();
resolve(0);
}
}
};

let observer = new MutationObserver(callback);

observer.observe(el, {
characterData: false,
childList: true,
attributes: false
});
});

},

//возвращает нужный ключ массива и его порядковый номер
get_idx: function (a, val, column = 0) {
if (isNaN(val)) {
return null;
}
for (let i = 0; i < a.length; ++i) {
//если значение меньше ключа, значит мы у цели
if (a[i][column] > val) {
//предыдущий индекс(если есть) и есть искомый ключ
i = i > 0 ? i - 1 : i;
return i;
}
}
//если ничего лучше нет, отдаем последний индекс
return a.length - 1;
},

flip_obj: function (table) {
res = {}
let keys = Object.keys(table);
for (let i = 0, len = keys.length; i < len; ++i) {
let key = keys[i];
res[table[key]] = key;
}
return res;
},

//таблица скоростей и соответствующих им расстояний до точки начала координат
lookup_table: [
[0, 0],
[0.01, 0.2],
[0.02, 0.5],
[0.05, 1.5],
[0.1, 2],
[0.15, 2.5],
[0.2, 3],
[0.3, 3.5],
[0.35, 5],
[0.4, 5.5],
[0.5, 6],
[0.55, 10],
[0.6, 20],
[0.65, 30],
[2.2, 80],
[3, 120],
[4, 150],
],

adjust_pos: async function (axis, speed = null) {
//соответствие осей и кнопок управления по осям
let ss = {'x': ['q', 'e'], 'y': ['d', 'a'], 'z': ['w', 's']}
if (this.abort) {
return null;
}

//получаем данные о положении
let pos = this.get_xyz();
//таблица подстановки вектора скорости движения и положения на оси относительно 0
let lookup_table = this.lookup_table;

//рабочий цикл будет включаться только при наличии данных о векторе скорости
if (speed !== null) {
//console.log('axis:', axis, 'speed:', speed, 'pos:', pos[axis]);

//получаем индекс положения корабля на оси
//расстояния в таблице подстановок только положительные, поэтому берем
//расстояние по модулю. расстояния в колонке 1 таблицы
let pos_idx = this.get_idx(lookup_table, pos[axis + '_abs'], 1);

//теперь получаем индекс из таблицы векторов скорости
//если положение корабля отрицательное к нулю,
//перевернем вектор скорости, чтобы отрицательные значения движения к нулю
//были положительными
if (pos[axis] < 0) {
speed = -speed;
}

//берем значение скорости и смотрим по таблице
//скорости в нулевой колонке
let speed_idx = this.get_idx(lookup_table, Math.abs(speed), 0);
//если хотя бы одного индекса нет, выходим
if (pos_idx === null || speed_idx === null) {
console.log('error! pos_idx or speed_idx not found.');
return false;
}
//console.log('speed:', speed, 'speed_idx:', speed_idx, 'pos_idx:', pos_idx);
//теперь мы можем определить направление и число импульсов
//для каждого расстояния есть желаемая скорость.
//для расстояния 0 скорость 0, для расстояния 0.5, скорость 0.02
//кол-во импульсов вычисляется исходя из того, что импульс = 0.02м/с
//формула: impulse = Math.floor(speed - lookup_table[pos_idx][0]) / 0.02)
//т.е. вычитая из текущей скорости желаемую скорость, мы определяем
//скорость которую надо прибавить или отнять, чтобы получить нужную
//делим на 0.03 (скорость 1 импульса) и получаем кол-во импульсов

//если отклонение от нужной скорости до 5%, ничего не делаем
let diff = speed - lookup_table[pos_idx][0];
let diff_percent = Math.abs(diff / lookup_table[pos_idx][0]);
if( diff_percent > 0.05) {

let impulse = Math.floor(diff / 0.03);

//направление импульса зависит от 2 вещей: направления вектора скорости,
// положение корабля относительно 0 осевой точки
//это индексы направлений импульса
let directions = {'1': 0, '-1': 1, '0': 0};

//определяем направление импульса
let direction = Math.sign(impulse) * Math.sign(pos[axis]);
//определяем кнопку
direction = directions[direction.toString()];
let key = ss[axis][direction]

//передаем импульс, если отклонение от нужной скорости более 5%
console.log('axis:', axis, 'speed:', speed, 'key:', key, impulse);
if(Math.abs(impulse) >= 200){
console.log('impulse >= 200, skip.', impulse);
}else {
await this.press(key, Math.abs(impulse));
}
}
}

//запускаем себя заново с данными о скорости
return this.calc_pos(axis).then(speed => this.adjust_pos(axis, speed))
},
//этим методом и производится автоматическая стыковка корабля
autodock: async function (abort = false) {
this.abort = abort;
this.adjust_pos('x');
this.adjust_pos('y');
this.adjust_pos('z');
this.adjust_axis();
},

adjust_axis: async function () {
let yaw = m.get_state('yaw');
let roll = m.get_state('roll');
let pitch = m.get_state('pitch');
let run = true;
while (run) {
if (this.abort) {
return null;
}
//console.log(yaw['offset'], pitch['offset'], roll['offset']);
if (yaw['offset'] > 0) {
m.set_axis('yaw', Math.sqrt(yaw['offset_abs'] / 10));
} else {
m.set_axis('yaw', -Math.sqrt(yaw['offset_abs'] / 10));
}
if (pitch['offset'] > 0) {
m.set_axis('pitch', Math.sqrt(pitch['offset_abs'] / 10));
} else {
m.set_axis('pitch', -Math.sqrt(pitch['offset_abs'] / 10));
}
if (roll['offset'] > 0) {
m.set_axis('roll', Math.sqrt(roll['offset_abs'] / 10));
} else {
m.set_axis('roll', -Math.sqrt(roll['offset_abs'] / 10));
}

await m.sleep(50);
yaw = m.get_state('yaw');
roll = m.get_state('roll');
pitch = m.get_state('pitch');
}

},

set_axis: async function (s, speed) {
speed = Math.round((speed + Number.EPSILON) * 10) / 10;
let ss = {'yaw': ['left', 'right'], 'roll': ['<', '>'], 'pitch': ['up', 'down']}
if (!(s in ss)) {
console.log(s + ' not in ', ss);
return;
}

let o = m.get_state(s);
speed *= 10
let cur_speed = o['speed'] * 10;
let distance = 0;

distance = speed - cur_speed;

if (distance > 0) {
while (distance--) {
m.press(ss[s][1]);
}
} else {
distance = Math.abs(distance);
while (distance--) {
m.press(ss[s][0]);
}
}
},

abort: false,

}

m.autodock();
//m.autodock(1); чтобы остановить автопилот
Попробовал код.
Ну… У меня fail (дважды)


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

Саму реализацию пока не смотрел, возможно вечерком гляну, в свободное время.
Проблема со спидометром. Время беру точное, а вот реальная скорость движения с учетом рендеринга меняется в результате скорость выдает иногда с большим отклонением. Надо подумать как еще повысить точность.
Так весь прикол задачи в этом и есть:
* Мы не имеем точных данных о состоянии объекта
* Мы не имеем нормальных метрик относительно системы
* Мы не имеем (по факту) возможность подать не «единичное» усилие. [можно обойти это ограничение, ну почти… Но в этом тоже есть свой «шарм»]

В реальных условиях мы бы еще имели бы кучу проблем, о которых писалось выше, типа смешения центра масс или еще чего-то. Но в симуляторе этого нет.

Было бы просто, я бы статью не стал бы пилить.)
Так весь прикол задачи в этом и есть:
* Мы не имеем точных данных о состоянии объекта
* Мы не имеем нормальных метрик относительно системы
* Мы не имеем (по факту) возможность подать не «единичное» усилие. [можно обойти это ограничение, ну почти… Но в этом тоже есть свой «шарм»]

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

Для этого во первых надо сделать нормальную дискретизацию — то есть если у нас алгоритм будет выполняться с периодом в 1с, то все входные параметры и измерения должны быть дискретизированы в этот домейн. Причем пусть будет задержка, фильтрация — неважно, это все можно скомпенсировать. Главное, чтобы это было учтено и мы в итоге получали скорость каждую секунду. И так далее. Потом надо обеспечить, чтобы алгоритм действительно выполнялся каждую секунду, а не как попало.
Я не силен в JS, поэтому и не участвую в вашей гонке, но на 90% решение задачи лежит за этим вещами.
Так нет у нас их… Надо принять как данность, что есть значения с десятичной точностью… Которых явно не хватает на последнем участке. И по-хорошему нужно учесть, что изначальное состояние может быть любое… Просто эмулятор всегда стартует с определённых значений.
JS «С» подобный язык. А в решаемой задаче не требуется специфических знаний именно JS языка.
Как получить значения параметров можете взять из любого приведённого здесь кода.
На последнем участке, скорей всего, надо работать не с данными, а по своей модели — т.е по данным изредка корректироваться, а расчетный импульс коррекции рассчитывать внутри кода и выдавать с четкими временными уставками.
Под импульсом коррекции я понимаю последовательность из двух противоположных клавиш через определенное время, которые либо поворачивают корабль на определенный угол, либо двигают его на определенное расстояние с возвращением к нулевым скоростям после этого.
Ты почитал код моей реализации?
Только я делаю это на всем протяжении пути, а не только на последнем участке.
Добавил автопилоту «человечности», передача управляющих команд происходит с интервалом 1мс, без интервала автопилот может парковаться на сумашедших скоростях.
>Но мало кто знает, что его единственный полет был беспилотным.
А вот тут я с Вами не согласен. Ибо ЛЮБАЯ российская публикация о полете Бурана содержит ОСОБОЕ упоминание об этом факте. Т.к. это единственное, в чем СССР обошел супостата.
По алгоритмам/реализациям автоматических тыковок надо у Orbiter-ов посмотреть. Возможно что уже существуют удачные реализации стыковки ;)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории