Комментарии 21
Ожидал реализации отображения направления и подсказок по текущей обстановке в реальном времени, а оказалось описание, как (баннеры) картинки показать, сохраненные в постоянной памяти. Подсказка: можно сразу хранить адреса светодиодов без их пересчета на устройстве, и зачем баннерам еще и анимация, непонятно (чтобы пользователя отвлекать, предлагая купить галоши на оживленном перекрестке? Карта и реальные подсказки не отображаются все равно). Кстати, для отображения стрелки с направлением и поворотов достаточно лишь нескольких спрайтов, давно изобретенных в игровой индустрии… в одном байте можно аж 8 стрелочек закодировать.
Мы показываем не только картинки, но и анимации. Первостепенная задача дисплея и всего, что на нем отображается - улучшить пользовательский опыт и предоставить наглядные инструкции по пользованию сервисом. Именно это и указано в статье. Речи о "баннерах" и "покупке галош на оживленном перекрестке" не шло;
Как Вам известно, наш дисплей это один большой сдвиговый регистр => для изменения состояния светодиодов мы должны "провести" данные по всему сдвиговому регистру. Таким образом, об адресном обращении к конкретному светодиоду речи не идет;
Действительно, можно хранить массив положений светодиодов в сдвиговом регистре. Мы так и делаем в карте индексов, про создание и применение которой, я уверен, Вы читали. Дополнительные преобразования связаны с разделением программного уровня (графическое ядро) и аппаратного уровня. Бизнес и разработка не стоят на месте, и может случиться так, что мы захотим сменить дисплей и/или графическое ядро. А такое разделение, где каждый модуль имеет возможность преобразовать данные конкретно под себя, позволяет производить такие изменения без проблем;
Карта действительно не отображается. На данный момент разрешение дисплея этого не позволяет. Но с чего Вы взяли, что не отображаются подсказки? Вот ряд примеров:
Если пользователь покрутил руль самоката, то отобразится анимация с инструкцией как начать поездку
При сканировании QR-кода на самокате отображается анимация с последовательными шагами по старту поездки
Во время самой поездки отображается дашборд с информацией о скорости, степени нажатия ручки газа, степени нажатия ручки тормоза, скоростном режиме и т.п.
Судя по фразе: "в одном байте можно аж 8 стрелочек закодировать", Вы имеете ввиду, что хранить информацию о том, какую стрелочку сейчас вызвать, можно в одном байте, где каждый бит соответствует своему спрайту. Это абсолютно верно, но Вам всё равно придется где-то хранить эти спрайты, и именно о том, как их хранить и идет речь в статье.
Анимации на ходу для отвлечения внимания это просто убийство пользователей. В первую очередь, нужна простая стрелочка с указанием дистанции до ближайшего поворота и его направлением. Но именно этого необходимого функционала у вас и нет! Действительно, получается продажа калош на оживленном перекрестке. Все перекодирования можно проводить непосредственно при обновлении - скачали данные, перекодировали как удобнее отображать, сохранили на устройстве. Каждый раз выполнять преобразования на устройстве с ограниченной вычислительной мощностью бессмысленно. А при работе устройства оперируйте спрайтами, уже сохраненными в максимально удобном для отображения формате, а не графикой как таковой. Вы бы хоть с детским микроконтролллером "микробит" поработали, прежде чем продакшен решения пытаться делать - там в курсах для детей все ваши ошибки подробно обсуждаются и предлагаются решения.
Теоретически, человеческому глазу достаточно и 24 кадров в секунду
что значит теоретически??? Когда то весь кинематограф работал с этим рейтом. Причем следующий рейт который до сих пор используется это 25 кадров в секунду (просто чтобы быть кратным частоте сетевого переменного напряжения, поэтому 25 или 30).
Вы заставляете сомневаться в вашей компетенции в выбранной предметной области.
Я думаю на экране самоката вполне хватило бы и 10 кадров в секунду, глаз человека держит("помнит") изображение 1/7 секунды примерно, зачем вам 60 совершенно не понятно.
1) Как я упоминал в статье, мы изначально тоже считали,что хватит и 24 кадров в секунду. Но при проведении тестов обнаружили, что на таком FPS явно заметно мерцание, и сама анимация неприятна глазу. По этому было решено ориентироваться на 60 кадров в секунду;
2) Действительно, раньше весь кинематограф работал на 24 FPS. Но мир не стоит на месте. Если бы Ваше утверждение было верным, то не существовало бы всей индустрии мониторов и телевизоров которые выдают изображения на частотах в 60, 120, 144 и далее герц. И эту разницу человеческий глаз видит;
3) Я не знаю откуда Вы сделали вывод о достаточности 10 кадров в секунду. Если бы 1/7 секунды было достаточно для человеческого глаза, чтобы воспринимать анимацию/видео плавно, то весь современный кинематограф использовал бы именно эту частоту. Но это не так. Многие геймеры жалуются на отсутствие плавности в 30 и 60 кадрах. Что уж говорить о 10...
По этому было решено ориентироваться на 60 кадров в секунду;
ну я не знаю - почитайте про через строчную развертку что ли. 60 раз в секунду выбрали потому что сетевое напряжение в определенных странах 60 Гц, 110 Вольт, но кадров там было 30 с через строчной разверткой!!!
реальные 60 Гц нужны для High Definition видео которое подразумевает соответствующее разрешение, а если у вас разрешение 60 на 40 совсем не понятно зачем вам 60 Гц. Для схематичной анимации (а что еще можно нарисовать на 60х40 экране???) вполне хватит 10 Герц, я более чем уверен, и даже не то что уверен, я это много раз видел на практике.
Какие, нафик, геймеры жалуются с экраном 60х40???
Во-первых, разрешение нашего дисплея 48x24, а не 60х40.
Во-вторых, применение чересстрочной развертки в нашем случае имеет ряд минусов:
при использовании DMA, вместо одной отправки буфера со всей информацией о кадре, нужно будет отправлять два подкадра с четными и нечетными строками;
возможно дополнительное мерцание из-за попеременного зажигания строк дисплея;
на быстрых анимациях с движущимися объектами может появиться эффект "гребенки".
В-третьих, Ваш опыт, остается Вашим опытом. Как я упоминал, мы провели тестирование с различной частотой обновления кадров, сделали выводы и приняли решение, которое описано в статье.
То, что работало на кинескопах с постсвечением, не работает со светодиодами.
Так это они ШИМ называют частотой кадров. И для каждого такта ШИМ "анимируют" изображение. И для ШИМ в килогерц у них будет "1000 кадров в секунду". Видимо, бегущая строка на семисегментных индикаторах у них потребует минимум 64 бит арма для реализации. Какая тут компетенция, о чем вы.
это видимо мы с вами чего то не понимаем, видимо главная компетенция это умение сделать выводы и принять решение. Хотя и это не самое главное, это нужно чтобы потом следить чтобы все по струнке ходили в соответствии с этими выводами и решениями, то есть ни какой самодеятельности, все в соответствии с линией партии даже по техническим вопросам.
Нет, ШИМ нам необходим для того, чтобы на светодиодном дисплее, где каждый светодиод может быть либо полностью выключен, либо гореть с максимальной яркостью, отображать оттенки. Таким образом, каждый кадр анимации разбивается на подкадры и выводится на экран. В статье этот процесс описан.
Блин, честно говоря я про ШИМ пропустил - не стал вчитываться, а это действительно круто!
В свое оправдание и в качестве пожелания хочу сказать что такие хитрые штуки легко теряются в потоке другой технической информации, поэтому их надо как-то акцентировать-выделять, хотя я сам не уверен, что смог бы это сделать в вашем случае! Но у вас даже название отвлекает от этой умной хитрой штуки с регулировкой яркости с помощью многократной развертки с весами из последовательности единиц/нулей на пиксель, я при чтении пытался понять про сжатие, но сжатие оказалось не самой интересной техникой из всего повествования.
И мне тогда не понятно 60Гц это частота этих ПОДкадров или все таки интегральных кадров? Надо тогда две цифры приводить: если 16 подкадров в кадре, то действующая частота кадров = 60/16 > 4 кадров в секунду. У вас статья достаточно большая получилась, и понято что вы не расписали всю логику по ШИМу, поэтому не хватает инфы, мне кажется, чтобы не сильно погружаясь в математику это понять.
Я минусы не ставлю, и теперь с наилучшими пожеланиями вам плюс. Как говорится, чем могу :)!
Спасибо за совет. Надеюсь, это не последняя моя статья. Постараюсь учесть Ваши наставления в будущем)
Отвечая на вопрос по ШИМ. Например, у нас анимация длинной в 1 секунду. Тогда в памяти мы храним 60 спрайтов для этой анимации. При выводе на дисплей, каждый из этих 60 спрайтов дополнительно разбивается на 16 подкадров, благодаря которым обеспечивается ШИМ и появляется возможность отобразить оттенки. Т. е. за секунду дисплей обновится 60 * 16 = 960 раз.
то есть чтобы обеспечить половинную яркость (серость) пикселя он половину от 1/60 секунды горит, а вторую половину этой 1/60 не горит. Для такого способа управления яркостью (серостью) действительно нужна повышенная частота кадров,потому что на меньшей частоте это будет восприниматься как переключение пикселя, а не как его яркость (серость).
Я думаю можно что-то придумать все таки для снижения частоты кадров, но для этого надо изобретать эксперименты доказывающие эффективность улучшения, но это слишком узко-специальная задача чтобы кто-то ею занимался и возможно такой способ, в конечном итоге, и будет в каком то смысле просто кодированием со сжатием, поэтому выбранный вами способ со сжатием-кодированием наверно действительно лучший выход.
я раньше постоянно такие задачки решал, теперь их нет у меня, и я по ним скучаю, ваша мне покоя не дает :).
Я так понимаю вы привязали частоту развертки к частоте кадров, я думаю вам надо попробовать все таки ее отвязать, просто оставить кратной:
я думаю если вы пересчитаете из фреймов анимации частотой 60 в фреймы частотой 30 (значение яркости каждого пикселя фрейма30 = среднее арифметическое от значений в двух исходных фреймах60) и добавите возможность два раза отображать тот же фрейм при той же частоте развертки 960, вы не заметите разницы в изображении, попробуйте! И можно попробовать таким образом снизить количество фреймов в секунду и в 3 и даже в 4 раза я думаю! Я бы проверил такие варианты.
Еще есть вариант хранить в отдельном масиве количество повторений для каждого фрейма, и использовать это значение что бы повторять фрейм который не изменяется, то есть хранить только НЕ повторяющиеся фреймы, кол-во повторений для каждого фрейма. При отображении загружать фрейм и отображать его нужное количество раз в соответствии со значением из массива повторений - это фактически вариант простейшего сжатия, для этого нужна программка на ПК которая будет сравнивать исходные фреймы и генерировать такой масив и переписывать в сжатый поток фреймов без повторений, но с весами этих повторений, также эта програмка должна принимать значение для сравнения пикселей - например если значение яркости каждого пикселя не изменяется более чем на две единицы (во всем фрейме нет пикселей которые значимо изменяются в последовательности фреймов) считать такую последовательность повторением одного постоянного фрейма и хранить только этот фрейм и значение для его повторений.
Я так понимаю вы привязали частоту развертки к частоте кадров, я думаю вам надо попробовать все таки ее отвязать, просто оставить кратной
У нас частота развертки = частота кадров (FPS) * GRAY_SCALE (количество оттенков серого, которые хотим отображать) = 60 * 16 = 960. Таким образом, частота развертки всегда кратна частоте кадров, которую мы хотим получить.
я думаю если вы пересчитаете из фреймов анимации частотой 60 в фреймы частотой 30 (значение яркости каждого пикселя фрейма30 = среднее арифметическое от значений в двух исходных фреймах60) и добавите возможность два раза отображать тот же фрейм при той же частоте развертки 960, вы не заметите разницы в изображении, попробуйте!
Если я Вас верно понял, то вы говорите о ситуации, где у нас есть анимация, где яркость дисплея плавно изменяется с 0 до максимума за 1 секунду. У такой анимации в нашем случае будет 60 кадров. Однако если мы можем отображать только 16 оттенков, то яркость пикселя будет изменяться примерно раз в 3 кадра. В такие моменты можно не отрисовывать эти 3 кадра, а придержать один. Я помню, что по какой-то причине мы наблюдали мерцание дисплея, но такую ситуацию действительно интересно проверить. Я займусь этим когда освободится немного времени). Но не очень понимаю как это решать для комплексных и быстрых анимаций. Возможно действительно нужно будет писать стороннюю программу для обработки. Думаю, если мы и придем к этому, то это уже будет следующая итерация и отдельная большая задача.
Но не очень понимаю как это решать для комплексных и быстрых анимаций.
я думаю анимации частотой выше 20 кадров в секунду не имеет смысла при таком низком разрешении, то есть вы не увидете разницы если изменения 3-х соседних фреймов усредните и будете показывать один и тот же фрейм 3 раза вместо 3 фреймов с незначительными изменениями по очереди. Не может быть пикселей которые значительно переключают свою яркость чаще чем раз в 3 фрейма при частоте 60 фреймов в секунду.
Хотя нюанс один есть - пиксели все таки гаснут и зажигаются, усреднение в моменты таких гаснущих пикселей будет приводить к тому они гаснут не резко а плавно, но это нельзя назвать мерцанием, это другой эффект, я бы посмотрел на вашем месте как это выглядит на практике.
Я думаю мерцание было связано с тем что при уменьшении частоты кадров вы частоту развертки (ШИМ-а) снижали в уравнении:
частота развертки = частота кадров (FPS) * GRAY_SCALE (количество оттенков серого, которые хотим отображать) = 60 * 16 = 960.
как:
30 * 16 = 480
например, а частоту развертки нельзя снижать ниже некоторого порога, а частоту кадров можно, то есть у частоты кадров другой порог - ниже!
На этот счет какое-то время назад был изобретен протокол ExpressivePixels с открытым кодом.
В анимации на embedded самое сложное - создать саму анимацию.
Спасибо за действительно полезный комментарий и ссылку. Я ознакомился с источником и думаю, что будет интересно применить ExpressivePixels для более сложных и цветных анимаций, к которым мы, возможно, придем в будущем. Так же интересно будет измерить футпринт памяти и скорость в сравнении с нашей реализацией.
Сжатие графики при помощи алгоритма LZ4