Category: авто

Category was added automatically. Read all entries about "авто".

QuatCore

Быстрее, меньше, точнее!

Начинаем тестировать новую процедуру арктангенса. Сначала на симуляции. Получаем дамп памяти:


Переписываем числа в таблицу:


Первая половина таблицы очень многообещающая: ошибка не более 1 единицы младшего разряда, да и та случается в 6 случаях из 16, это уже прогресс по сравнению с предыдущим алгоритмом!

Но вот со второй половиной какая-то фигня: мы каким-то образом перепутали знак! Причём если посмотреть 4 варианта знака для (32052;6813):

(32052;6813)
(-32052;6813)
(-32052;-6813)
(32052;-6813)

то окажется: только когда оба числа положительных, мы выставили знак "+", во всех остальных случаях был знак "-".

Кто бы мог подумать: ассемблерная функция не заработала правильно с первого раза! Что ж, надо разобраться.

Collapse )

Итак: быстрее в 4 раза, меньше на 36 байт, точнее вдвое (максимальная ошибка в 1 единицу младшего разряда вместо 2). Чебышёв на пару с Ремезом рулят!

Наверное, пора успокоиться с арктангенсом и двинуть дальше. И макет довести уже до ума, и об его юстировке громко подумать...
QuatCore

atan1 "на четырёх умножениях" на ассемблере

Немножко подправил таблицу результатов для первого алгоритма. В этот раз вместо "правильных углов" 0°, 12°, 24° и т.д (те, которые я ХОТЕЛ взять для тестов) я взял те целочисленные значения X/Y, которые в действительности выступили входными данными для процедуры. Думал, что ошибка немного уменьшится, т.к часть ошибки была вызвана неточным представлением этих углов. На деле она возросла, как ни странно:



Только в 4 случаях из 15 получили все 16 бит верно, ещё в 8 случаях ошиблись на единичку младшего разряда, и в трёх случаях - на две единицы (в прошлый раз, как ни странно, такой случай был единственный).

В целом, поведение ожидаемое, когда производится 5 умножений комплексных чисел с хранением промежуточных чисел в 16-битном виде. Если каждое округление до 16 бит даёт нам случайную ошибку в 0,316 единицы младшего разряда (таково среднеквадратичное отклонение при равномерном распределении чисел перед округлением, один на корень из 10), и ошибки независимы друг от друга, то поскольку у нас 5 раз сохраняется по 2 числа (т.е действительная и мнимая часть), то ожидаем увеличения ошибки ещё в корень из 10 раз, до 1 единицы младшего разряда. Но распределение будет примерно Гауссовым, с дисперсией 1, и там вероятность, что ошибка будет [-0,5;0,5], т.е ни один бит не пострадает: 38%. Вероятность ошибки в 1 единицу младшего разряда (т.е [-1,5;-0,5] и [0,5;1,5]): 48%, в две единицы и выше: 14%. Худо-бедно похоже на то, что мы увидели.

Вообще, такая точность меня вполне устраивает, но "из спортивного интереса" хочу попробовать реализовать более быстрый и точный алгоритм.

Collapse )

Итого, 26 слов кода (1 лишнее из-за Hazard на одновременное чтение и запись памяти, транслятор вставляет NOP), или 52 байта. Не используется сегмент данных (только 3 значения хранятся на стеке). То есть, вышло чуточку компактнее предыдущей программы, которая занимала 29 слов кода и 15 слов данных.

Компилируется без проблем, вообще редчайшая вещь у нас - процедура совсем без ветвления! А испытывать начнём в следующем посте.
QuatCore

Испытываем новую команду "S"

В прошлый раз мы ввели новую команду, "выдать флаг знака на шину данных", сокращённо "S". Подправили компилятор, запустили старую программу - по крайней мере ничего не поломали.

Теперь надо попробовать ею воспользоваться, и для начала я хочу переписать кусочек кода в процедуре FindRoll (найти крен). Там мы нашли синус и косинус угла крена, сокращённо si и co, и хотим превратить их в кватернион поворота по крену, по сути в синус и косинус половинного угла, не прибегая к хитрючей тригонометрии. К счастью, соответствующий "рецепт" уже был у нас в ликбезе:

Если co>0, то


в противном случае



и затем кватернион нормируется. Длина ненормированного кватерниона будет от 0,707 (один на корень из двух) до 1, так что итераций для нормировки нужно не шибко много, хватит 4..5, чтобы выйти на полную точность, доступную в 16 битах.

Значение co у нас лежало в [Z], si в [Z+1], компоненты кватерниона мы хотим запихать в [X] и [X+1], для чего применяли такой вот упоротый код:

	;i=j=k=Inv=0				
	ABS	[Z+k]			
	DIV2	Acc
	ADD	16384
	;флаг S (sign) сейчас показывает знак co, что для нас очень полезно
	JGE	@@skip
	i	1	;выходит i=S
@@skip:	X	DA_Quat0
	[X+i]	Acc
	j	1
	DIV2	[Z+1]
	[X+i^j]	Acc	;по сути, Y+(~S)


Нужно его немножко упростить!

Collapse )



Уже лучше. Первый раз кватернион 32767 + 0i, т.е кватернион нулевого поворота. Второй раз -1+32767i, это практически поворот на 180 градусов, как и было в этом "сценарии".

Ещё показания дальности изменились после добавления одной итерации в NormSiCo, теперь "мантисса" 0x953A = 38202, а дальность 38202/32768 * 29-1 = 298,45 метров. Логично: у нас до этого нормировка до конца не доходила, поэтому масштаб выходил меньше и соответствовал большей дальности. Бывает и так: я координаты точек когда-то "сочинял", пытаясь учесть все проблемы, которые могут возникнуть с фотоприёмной матрицей: неравномерность чувствительности, "мёртвые зоны" и пр., может при правильных вычислениях и должно было получиться так...

В следующий раз громко думаем над знаками для FMPM, после чего тестируем арктангенс.
beaver with chainsaw

Как бы Штиль

Мне тут отдали даром бензопилу, один из Сафроновцев. Когда он её из сумки начал вытаскивать, я немножко прифигел, неужто новенького Штиля отдаёт??

Но он тут же сам пояснил: ЭТО НЕ ШТИЛЬ, только притворяется :)

IMG20210816145427.jpg

Ну да, НАКЛЕЙКА вместо выбитого логотипа, надпись на ней MS660 (это чуть ли не самая мощная пила штилёвская, на 7 л.с, и она явно должна бы быть покрупнее и потяжелее) и орфографическая ошибка "rEted power" (вместо rAted) какбе намекают!

Collapse )

Ни разу ещё этой пилой не пользовались, только один раз плеснули топлива, убедились, что она в принципе заводится - и всё.

Пригодится :) Идея была в том, чтобы логистику упростить - пусть одна пила лежит на даче, другая в Москве. Потому как работать эта штуковина всё ж должна. Не знаю, как долго, поговаривают, что главная проблема таких пил - "большой разброс". Может попасться хороший экземпляр, который служит верой и правдой, а другой через неделю сдохнет.
QuatCore

"16-битный" передатчик UART

До сих пор у меня передатчик UART подсоединялся к 16-битной шине данных процессора, но брал из неё только младшие 8 бит, остальные игнорировал. Для передачи текстовой информации самое то, особенно с появлением байтового режима доступа к памяти. Но вот передавать 16-битные данные становится очень большой проблемой. Для дампа памяти ещё нашлось решение, как раз-таки через байтовый режим прочитать сначала все старшие байты, потом все младшие, а уже на компьютере их "склеить", но тоже костыль, что ни говори.

Всё-таки я уже решил: текстовую информацию буду выводить на ЖК-экранчик, чтобы наиболее наглядно продемонстрировать, что он уже "сам" всё обнаруживает и вычисляет. (когда оно подключено к компьютеру, могут возникнуть сомнения - может компьютер тоже вносит посильную лепту?) А уж если мы к этому делу приплели компьютер, надо уже переходить на передачу информации в "машинном" виде, тем более что рано или поздно предстоит заменить UART и RS485 на МКО, он же МКИО, он же Mil-Std 1553, он же ГОСТ Р 52070-2003, а там данные идут 16-битными словами.

То есть, я хочу, чтобы одна команда OUT, когда источником выбран UART, передавала бы два байта подряд. Какой сначала - старший или младший (endianness пресловутый) - это на наше усмотрение, главное потом на принимающей стороне не ошибиться.

Вроде бы халявная задача, а чего-то сегодня полдня соображал "как лучше" это сделать...

Collapse )

Завтра попробуем запустить. Сегодня ждал приезда заказчиков из РКК Энергия, приехал по такому случаю с утра, а оказалось, они так и не приедут, узнал об этом где-то час назад. Чего-то утром гораздо хуже мне соображается. Куда-то совсем не в ту степь полез, и только к 16:55 понял, как надо :) А тут уж и уходить пора.
QuatCore

Оба алгоритма захвата - в загрузочный сектор! (512 байт)

Наконец-то соединил между собой алгоритмы захвата ближней и дальней дистанции. Довольно существенная часть для них является общей - это перемножение матриц, выделение крена, масштаба и нахождение вектора, и даже процедура SwapPoints (поменять две точки местами).

Всего они заняли 254 слова кода, или 508 байт, то есть по-прежнему влезли в один блок внутренней памяти ПЛИС (БВП, он же EAB, Embedded Array Block). Ровно в той ПЛИС, которую я мучаю, 5576ХС4Т, таких блоков 24 штуки, а в той, куда хотелось бы вписаться, 5576ХС6Т (дофига радстойкая, в т.ч к тяжёлым заряженным частицам) - их всего 10.

Ещё один блок, как минимум, должен уйти на оперативную память.
Но ещё где-то два (один на оперативную, один на код) - под алгоритм обнаружения точек на фотоприёмной матрице, тот самый "супрематический", который я ковырял-ковырял, да так до конца ещё не отладил.

Итого уже 40% доступной памяти для ХС6Т мы заняли, а ещё алгоритм сопровождения нужен! Он довольно-таки "мерзопакостный"...

Collapse )

И поглядим, что получается "по дальней дистанции", старые добрые координаты, соответствующие 300 метрам "прямой наводкой":


Ярко-зелёным показаны 4 точки, после того как их идентифицировали. В этот раз наиболее удобным мне показался порядок МБД-МДД2-МДД1-МДД3.

Синим выделен вектор с общей "экспонентой" 9, т.е значения X (от 0 до 2), Y, Z (от -1 до +1) нужно домножить на 29-1 = 256 метра.

Имеем X = 0x9591 = 38289 = 1,168 (в формате Q1.15). Умножаем на 256 - и выходит 299,13 метров.
Далее, Y = 0x0031 = 49 = 0,00149 (в формате Q1.15). Умножаем на 256 - выходит 38 сантиметров. Похоже, я и тут пожадничал в 4 раза (чтобы точность не потерять раньше времени), на самом деле должно быть 9,5 см "сдвиг вправо", т.к смотрим мы "левым глазом", а летим прямо по курсу.

И наконец, Z = 0xFE3D = -451 = -0,0138 (в формате Q1.15). Умножаем на 64 (помня, что "пожадничали") и получаем -0,881 метра. Это мы сейчас установили "началом координат мишени" центр стыковочного узла, но прибор закреплён на те самые 0,881 метра ВЫШЕ, вот и воспринимает картинку как сдвинутую вниз.

Кватернион (выделен фиолетовым) вообще получился единичным, т.е выражающий нулевой поворот. Да, так и должно быть, идём "прямой наводкой".

Это у нас работа по модельным данным, я макет мишени дальней дистанции так и не собрал до сих пор, всё с ближней дистанцией возился. По модельным данным оно приятно - всё совпадает :)
QuatCore

Алг. ближней дистанции, вычисление крена

Всех с прошедшими первомаем, днём радио и днём Победы!

А мы продолжим ковырять штуку... В общем-то, я говорил, что после вычисления аффинного преобразования (матрица 2х2 и вектор 1х2) дальше уже всё готово. Так примерно и есть, но мы чуть поменяли то, в каких регистрах что лежит, и не хочется грубо всё переопределять перед уже написанным кодом, лучше этот код чуточку подрихтовать, и посмотреть свежим взором, нельзя ли его улучшить?

Начнём с самой странной процедуры FindRoll - "нахождение крена". Она сначала находит синус и косинус угла крена, затем преобразует их в кватернион, выражающий этот крен (по сути, находит синус и косинус половинного угла), а под конец убирает из матрицы 2х2 аффинного преобразования крен, чтобы там остались только масштаб и "ракурсы"...

Collapse )

Теперь программа "ближней дистанции" компилится в 157 слов кода (314 байт), вышли-таки на 8-битную адресацию ПЗУ, ну ничего не поделаешь, сложный алгоритм всё-таки. И сейчас посмотрим на симуляции, правильно ли всё работает.
QuatCore

Баг воспроизведён!

Запустил свой многострадальный алгоритм обнаружения на симуляции, с полноценной картинкой 1024х720 - и ровно в том же месте словил ту самую ошибку, UFLO (исчерпание заданий GPU). Это хорошо, значит, сейчас можно будет её рассмотреть "под микроскопом".



Начнём с предыдущей строки, где всё казалось мирным...


Только закончили отправлять задания по этой строке и теперь начали запрашивать результаты. Синхроимпульс прошёл около 30 тактов назад, сейчас идёт "полочка". Поэтому ждём.

Collapse )

Да, не ожидал такой ошибки, всё думал либо на недостаточное время обработки, то ли на глюк в программе, но никак не на ошибку АЛУ, которая там сидела и ждала своего часа СВЫШЕ ГОДА! Ну что ж, теперь первые 8 мс симуляции (а дальше я не делал) проходят правильно.

Сейчас попробую снова "в железе", может сейчас оно возьмёт и сделается от начала до конца?
QuatCore

Алг. обнаружения под новый АЛУ

Возвращаемся к нашим баранам, к алгоритму обнаружения ярких пятен на изображении В РЕАЛЬНОМ ВРЕМЕНИ. Когда последний раз его мучали на симуляции (на уменьшенном изображении всего с одним пятном), он тупо не успел обработать данные и вовремя не отправил задания видеопроцессору. Проблема в том, что начни обрабатывать настоящую мишень на минимальной дальности 0,5 метра и при самом неудачном ракурсе, когда 4 пятна встанут в ряд - и времени там не хватит точно также.

Поэтому ещё на 2 недели отвлёкся - дорабатывал АЛУ и потом отлаживал его. И теперь, прежде чем испытать алгоритм обнаружения, хочу "оптимизировать" его под новый АЛУ, переставить местами команды (где это допустимо) так, чтобы процессор максимально "распараллеливался" - пока АЛУ думает о своём, остальной процессор может шуровать в памяти, общаться с видеопроцессором и т.д.

Почему бы не отладить старую версию для начала - а потому что оптимизировать уже не захочется, "работает - не лезь!" А раз уж она всё равно не работает (на реальном железе тоже какая-то фигня происходит) - то сначала доработаем, а потом будем приводить в чувства!

Collapse )

В общем, ничего особенно не сделали, по мелочи. Но хоть вспомнили, что за зверь.

Испытания новой АЛУ, нахождение крена

Четвёртая часть аффинного алгоритма, одна из самых укуренных. Сначала мы находим синус и косинус угла крена, потом превращаем их в компоненты кватерниона (по сути, в синус и косинус половинного угла), а потом "исправляем" крен в исходной матрице, чтобы проще было найти масштаб. Всё это - на сложениях, умножениях и взятии модуля :) Вот код:

;состояние регистров к этому моменту
;X = AffineMat (константная матрица 3х4, чтобы посчитать преобр)
;Y = Points2D
;Z = Matrix (матрица 2х2 и вектор 1х2, пока не AfTransf)
;i=j=k=0
;Inv неизвестен
;C, Acc - пофиг наверное
FindRoll	proc
	;нам понадобятся [Z]+[Z+3] и [Z+1]-[Z+2]
		Y		QuatY 	;в этом кватернионе построим,используя Y/Z значения как временные
					;(потом они занулятся)
					;а можно Y вместо X, если надо
		i		1
		j		3
@@sico:		Acc		[Z+i]
		Inv		i
		PM		[Z+i^j]
		[Y+i]		Acc
		iLOOP		@@sico
	
;теперь наших друзей надо отнормировать, причём динамический диапазон очень велик
;максимум 13 итераций, давайте столько и делать...
;здесь у нас появляется беззнаковое число, без него получалось бы очень грустно	

		CALL NormSiCo

;здесь у нас i=j=k=Inv=0
	
;теперь синус-косинус превращаем в кватернион, то есть в синус-косинус половинного угла
;если co>0, то
;QuatX = (1+abs(co))/2,
;QuatY = si/2
;в противном случае
;QuatX = si/2,
;QuatY = (1+abs(co))/2
	
;сейчас X = AffineMat (матрица 3х4, уже неактуальна)
;Y = QuatY (компоненты co / si),
;Z = Matrix (матрица 2х2 и вектор 1х2, к ним ещё вернёмся)
;i=j=k=Inv=0
					
		ABS		[Y+k]			
		DIV2		Acc
		ADD		16384

	;флаг S (sign) сейчас показывает знак co, что для нас очень полезно
		JGE		@@skip
		i		1		;выходит i=S
@@skip:		X		QuatA
		[X+i]		Acc
		j		1
		DIV2		[Y+1]
		Y		QuatA
		[X+i^j]		Acc		;по сути, Y+(~S)
		CALL		NormSiCo	;подготовили первые 2 компонента кватерниона
					
	;сейчас X=Y=QuatA (2 компоненты кватерниона),
	;Z = Matrix
	;теперь помножаем две матрицы, одна в явном виде - Matrix (Z)
	;вторая - задана неявно, в виде co / si (в QuatY)
	;результат идет в AfTransf
	;здесь i=1, k неизвестен, j=0, Inv=0
		Y		QuatY
		X		AfTransf	;сюда результат складируем
		i		1	;номер строки результата, и строки co/si
@@i_loop:	k		1	;номер столбца результата, и столбца AfTransf
@@k_loop:	j		1	;номер столбца co/si и строки AfTransf
		ZAcc		RoundZero	;обнулить до 1/2 мл. разр
@@j_loop:	C		[Y+i^j]	
		FMPM		[Z+2j+k]
		jLOOP		@@j_loop
		[X+2i+k]	Acc
		kLOOP		@@k_loop
		iLOOP		@@i_loop

	;здесь у нас i=j=k=0
	;теперь у нас должна получиться матрица, включающая в себя масштаб и ракурс (ужатие вдоль некоторой оси), но не крен...
	;ещё кватернион подчистим - по Y,Z может лежать всякий мусор
		[Y+k]		0
		[Y+1]		0
	FindRoll	endp


Как обычно, сначала прикинем, как можно этот код "оптимизировать" под новое АЛУ, не увеличивая его размера, и сравним выполнение на старом и новом АЛУ.

Collapse )

Что ж, целых две ошибки обнаружили - обе исправили. Выполнение самую чуточку ускорилось, и то хлеб.

Следующая часть: измерение масштаба.