Dado que comunicar Matlab con el código C/CUDA (o C/C++) no es la principal preocupación para la tesis, se aplica un poca investigación al tema y se implementa rápidamente un protocolo que permite comunicar entradas y salidas. En este caso, la comunicación consta de varias matrices de entrada y varias de salida. En todos los casos se trata de números flotantes.
Para que la ejecución del programa escrito en C sea transparente, se crea una función Matlab con el mismo nombre que el programa C, esta función será la encargada de grabar en disco las variables Matlab, llamar al ejecutable escrito en C, esperar que concluya exitosamente y leer las variables de salida desde el disco.
Las variables en disco se crean en una ubicación definida por el código Matlab (generalmente en una carpeta temporal) y se comunican al programa C mediante argumentos
Este esquema tiene un payload asociado a la lectura/escritura en disco de las variables que podría evitarse con algún método más directo...
A futuro, se recomienda utilizar la compilación MEX de Matlab:
http://www.mathworks.com/help/techdoc/matlab_external/f23224.html
Blog anotador para la tesis de grado sobre sistemas de control de fluidos a lazo cerrado en real-time mediante método ARX usando programación GPU.
sábado, 17 de septiembre de 2011
jueves, 1 de septiembre de 2011
Librerías sobre Operaciones matemáticas en C/C++
A la hora de realizar los programas de prueba en C/C++, previo a codificarlos en C/CUDA fue necesario definir ciertas funciones de soporte matemáticas.
Como todo sistema de operaciones básicas comenzó con una clase Matrix<T> propia, con operaciones simples como multiplicar o sumar. A medida que el sistema creció, fue necesario implementar transposiciones y vistas de columnas o filas de la matriz. En ese punto, y buscando una implementación de inversión de matrices, resultaba obvia la necesidad de buscar un mejor soporte en librerías existentes.
A continuación, una colección de links relacionados:
Como todo sistema de operaciones básicas comenzó con una clase Matrix<T> propia, con operaciones simples como multiplicar o sumar. A medida que el sistema creció, fue necesario implementar transposiciones y vistas de columnas o filas de la matriz. En ese punto, y buscando una implementación de inversión de matrices, resultaba obvia la necesidad de buscar un mejor soporte en librerías existentes.
A continuación, una colección de links relacionados:
- C/C++
- Gnu Scientific Library, con muchas operaciones y TDAs escritos en C
- Projectos VS2010 para compilar GSL en Windows: una contribución de Brian Gladman donde se descomprimen los proyectos dentro de la carpeta de descarga del GSL y con una lectura del readme se obtienen las librerías
- Clase Matrix con operaciones de inversión de Mike Dinolfo
- Numerical Recipes, se trata de librerías matemáticas muy usadas y conocidas. Son pagas pero con un bajo costo
- Matlab
- Utilización de código Matlab desde C/C++
- Pequeño instructivo para utilizar GSL en Matlab
- Python
- Plataforma para python
- SAGE, otra librería para python
jueves, 14 de julio de 2011
Simulando modelos conocidos
Matlab provee varias funciones que son de utilidad para simular modelos del tipo SISO, MIMO, etc. Una de las más útiles es sim que, dado un conjunto de datos de entrada y/o salida más un modelo ya definido, aplica el modelo a los datos y retorna la salida ulterior.
Veamos un ejemplo:
%genero un modelo según y(n) = u(n) - 2u(n-1)
A = [1] ; B = [1 -2];
modelo = idpoly(A, B);
%genero los datos para la simulación:
% ejemplos:
% iddata([], [1:100]') para salida nula y entrada del 1 al 100
% iddata([], idinput(100)) para salida nula y entrada aleatoria -1;1
data = iddata([],[1:10]');
%simulo
simulacion = sim(modelo, data);
plot(simulacion.y);
Bastante sencillo, si se cuenta con los datos de entrada e información sobre el modelo podemos calcular rápidamente la salida.
Obviamente, se pueden aplicar relaciones mucho más complejas cambiando los coeficientes de A y B. Tanto es así que son esos vectores los que definen la transferencia G(s) que relaciona Y(s) y U(s) (salida con entrada). Entonces, el numerador evaluado en el operador de regresión q se convierte en A(q) mientras que el denominador hace lo propio en B(q). Si observamos con atención, podemos ver esa información en la variable modelo:
>> modelo
Discrete-time IDPOLY model: y(t) = B(q)u(t) + e(t)
B(q) = 1 - 2 q^-1
This model was not estimated from data.
Sampling interval: 1
miércoles, 15 de junio de 2011
Y dónde está cutil64.dll??
Muchos de los ejemplos que circulan por internet utilizan esta DLL. Tiene utilitarios generales para CUDA y viene dentro del "GPU Computing SDK examples". Para facilitar las cosas, es conveniente tomar esa librería (la versión correcta y para la plataforma correcta) de su carpeta en el SDK y colocarla dentro de la carpeta de instalación de CUDA. Lo mismo ocurre con los correspondientes archivos .lib.
Entonces, para un sistema 64 bits, copiamos:
- Most samples link to a utility library called "cutil" whose source code
is in "NVIDIA CUDA SDK\common". The release and emurelease versions of
these samples link to cutil[32|64].lib and dynamically load
cutil[32|64].dll. The debug and emudebug versions of these samples link
to cutil[32D|64D].lib and dynamically load cutil[32D|64D].dll.
To build the 32-bit and/or 64-bit, release and/or debug configurations
of the cutil library, use the solution files located in
"NVIDIA CUDA SDK\common". The output of the compilation goes to
"NVIDIA CUDA SDK\common\lib":
- cutil[32|64].lib and cutil[32D|64D].lib are the release and debug
import libraries,
- cutil[32|64].dll and cutil[32D|64D].dll are the release and debug
dynamic-link libraries, which get also copied to
"NVIDIA CUDA SDK\bin\win[32|64]\[release|emurelease]" and
"NVIDIA CUDA SDK\bin\win[32|64]\[debug|emudebug]"
respectively;
Entonces, para un sistema 64 bits, copiamos:
- <NVIDIA GPU Computing SDK Folder>\C\inc\cutil.h en <CUDA Folder>\include
- <NVIDIA GPU Computing SDK Folder>\C\bin\win64\Release\cutil64.dll en <CUDA Folder>\bin64
- <NVIDIA GPU Computing SDK Folder>\C\bin\win64\Debug\cutil64D.dll en <CUDA Folder>\bin64
- <NVIDIA GPU Computing SDK Folder>\C\common\lib\cutil64.lib en <CUDA Folder>\lib64
- <NVIDIA GPU Computing SDK Folder>\C\common\lib\cutil64D.lib en <CUDA Folder>\lib64
- Most samples link to a utility library called "cutil" whose source code
is in "NVIDIA CUDA SDK\common". The release and emurelease versions of
these samples link to cutil[32|64].lib and dynamically load
cutil[32|64].dll. The debug and emudebug versions of these samples link
to cutil[32D|64D].lib and dynamically load cutil[32D|64D].dll.
To build the 32-bit and/or 64-bit, release and/or debug configurations
of the cutil library, use the solution files located in
"NVIDIA CUDA SDK\common". The output of the compilation goes to
"NVIDIA CUDA SDK\common\lib":
- cutil[32|64].lib and cutil[32D|64D].lib are the release and debug
import libraries,
- cutil[32|64].dll and cutil[32D|64D].dll are the release and debug
dynamic-link libraries, which get also copied to
"NVIDIA CUDA SDK\bin\win[32|64]\[release|emurelease]" and
"NVIDIA CUDA SDK\bin\win[32|64]\[debug|emudebug]"
respectively;
lunes, 13 de junio de 2011
Volviendo el tiempo atrás... CUDA 2.3
Lamentablemente, y aunque la sintaxis de CUDA 4 está muy buena, debemos volver a la versión 2.3 sobre Windows y compilar el código del Onera [1] con una resolución del Lucas-Kanade en GPU.
Lamentablemente, como suele suceder, aparecen nuevos problemas a salvar:
[1] Onera - FOLKI GPU
[2] Post compilación x64
Lamentablemente, como suele suceder, aparecen nuevos problemas a salvar:
- NVCC sin un compilador no funciona. Necesitamos el Visual Studio...
- Visual Studio 2008 Express no tiene soporte para procesadores x64 con lo que hay que optar por una versión Trial del Professional.
- Para compilar por línea de comandos nos quedamos con un ejemplo pequeño de archivo .cu y logramos compilarlo sólo luego de leer un post interesante [2] en el foro de nvidia: es necesario ejecutar un batch que setea el entorno del VS en modo amd64 y luego indicar algunos parámetros extras al NVCC.
- Dentro del VS2008, NVCC sigue sin compilar. Es necesario actualizar el archivo nvcc.profile para que tenga un include extra del visual:
INCLUDES += "-I$(TOP)/include" "-I$(TOP)/include/cudart" "-IC:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/include" $(_SPACE_)
[1] Onera - FOLKI GPU
[2] Post compilación x64
viernes, 10 de junio de 2011
Eligiendo la cámara - Conclusiones
Ganador momentaneo: PixelFly
Conclusiones generales:
Conclusiones generales:
- La Pulnix es demasiado lenta. Además no ofrece ninguna ventaja significativa.
- La PixelFly tiene un buen tiempo de transmisión con binning activado. Según las pruebas de concepto se obtendrían 10 ms entre frame y frame con lo cual se puede usar esta cámara.
- La PixelFly tiene además modo de doble shutter que no fue analizado pero brinda más alternativas.
- La SpeedCam, aún siendo la de mejor calidad y velocidad no tuvo buenos resultados. La transferencia resulta muy lenta con lo que queda descartada por el momento.
- Es importante aclarar que el análisis se basa en la necesidad de hacer un algoritmo lo más veloz posible y poder realiar cálculos en el momento. En caso de no ser esto así se puede reconsiderar los resultados y optar por la SpeedCam en lugar de la PixelFly, por ejemplo. Esto podría ocurrir en caso que se necesite tiempo extra para realizar procesamiento, no requerir 1 frame cada 10 ms sino 2 frames espaciados en 10 ms para, más tarde, repetir la toma de fotos y el proceso.
sábado, 4 de junio de 2011
Tiempos - Usando la SpeedCam programáticamente
En este punto, la experiencia se convirtió en algo bastante pesado. La SpeedCam no tiene una API de acceso programática. Tampoco permite sacar fotogramas de forma unitaria, sólo se puede sacar una ráfaga que es almacenada en el buffer interno de la cámara y luego transmitido a la PC.
La cámara posee código hecho en Java del cual se pueden obtener los .jar para emplearlos en un código de prueba.
Consultamos entonces a los fabricantes: nos responden que la compañía cambió de manos. Consultamos a los nuevos responsables y obtenemos como información que tenemos tres caminos:
Luego de varios días de revisar los resultados del Java Decompiler. Obtenemos una sucesión de pasos que permite obtener las imágenes con resultados bastante malos:
Pero entonces surge una duda ¿Y si los tiempos son lentos por estar utilizando java? Para tratar de falsear esta teoría, recurrimos a código C++ que intente levantar las DLLs directamente y no utilizar minivis.jar. Nuevamente tenemos que decompilar, en este caso decompilamos el archivo minivis.dll y hacemos un matcheo contra lo observado en minivis.jar. Igualmente, debemos notar que ambos archivos se comunican gracias a que se empleó JNI en C++. Lamentablemente esto simplemente complica la tarea de decompilación y reuso de la DLL original.
Con una sola prueba de concepto nos queda claro que ese no es el mejor camino:
Aquí el código Java:
Aquí el código C++:
La cámara posee código hecho en Java del cual se pueden obtener los .jar para emplearlos en un código de prueba.
Consultamos entonces a los fabricantes: nos responden que la compañía cambió de manos. Consultamos a los nuevos responsables y obtenemos como información que tenemos tres caminos:
- utilizar una API a nivel registro con la cual se accede a la cámara enviando códigos de operación
- revisar los archivos minivis.jar y minivis.dll
- comprar otra cámara o adquirir el nuevo software.
Luego de varios días de revisar los resultados del Java Decompiler. Obtenemos una sucesión de pasos que permite obtener las imágenes con resultados bastante malos:
- Snapshots tomados: 200
- fps entre Snapshots: 2500
- Tiempo Total: 7800ms (sólo en la transferencia, ya que la toma de imágenes se debe hacer previa a la este paso)
- Tiempo Promedio: 39ms (es decir, 25 fps)
Pero entonces surge una duda ¿Y si los tiempos son lentos por estar utilizando java? Para tratar de falsear esta teoría, recurrimos a código C++ que intente levantar las DLLs directamente y no utilizar minivis.jar. Nuevamente tenemos que decompilar, en este caso decompilamos el archivo minivis.dll y hacemos un matcheo contra lo observado en minivis.jar. Igualmente, debemos notar que ambos archivos se comunican gracias a que se empleó JNI en C++. Lamentablemente esto simplemente complica la tarea de decompilación y reuso de la DLL original.
Con una sola prueba de concepto nos queda claro que ese no es el mejor camino:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention
Aquí el código Java:
1: class DummyConsumer implements CallbackConsumer {
2: public void modeChanged(int paramInt){
3: }
4: public void statusChanged(int paramInt) {
5: }
6: public void shutterChanged(boolean paramBoolean, short paramShort) {
7: }
8: }
9:
10: public class Application {
11: public static void main(String[] arguments) {
12: int numberOfImages = Integer.parseInt(arguments[0]);
13:
14: MinivisFactory factory = MinivisFactory.getInstance();
15: try {
16: factory.discover();
17: TypedNetAddress[] addresses = factory.getKnownDevices();
18: String mac = addresses.length > 0 ? addresses[0].getEntryName() : "00-50-C2-1D-7E-AB";
19: DummyConsumer callbackConsumer = new DummyConsumer();
20: Proxy camera = factory.getCamera(mac, callbackConsumer);
21: try {
22: camera.connect();
23: try {
24: takeSnapshots(camera, numberOfImages, debug);
25: }
26: finally {
27: camera.disconnect();
28: }
29: }
30: finally {
31: camera.release();
32: }
33: }
34: catch(Exception e) {
35: e.printStackTrace();
36: }
37: }
38: private static void takeSnapshots(Proxy camera, int numberOfImages, boolean debug) throws IOException, MDriverException, Exception {
39: camera.setMode(4); //0 LIVE 1 RECORD 2 TRIGGERED 3 DRAM 4 LOWLIGHT
40: camera.trigger();
41:
42: //missing code here: wait for trigger to finish
43:
44: MFrameInfo frameInfo = new MFrameInfo();
45: for(int i = 0; i < numberOfImages; ++i) {
46: byte[] frameBytes = camera.getFrame(-1, -1 , frameInfo);
47: }
48: }
49: }
Aquí el código C++:
1:
2: #include "stdafx.h"
3: #include <iostream>
4:
5:
6: int _tmain(int argc, _TCHAR* argv[])
7: {
8: HMODULE hMod = LoadLibrary("minivis.dll");
9: typedef long (*MinivisFactory_Create)();
10: typedef long (*GetCamera)(long, char*, void*);
11: MinivisFactory_Create n_MinivisFactory_Create = (MinivisFactory_Create)GetProcAdress("_Java_com_artho_visart_plugins_minivis_internal_jni_MinivisFactory_n_1MinivisFactory_1Create@8");
12: GetCamera n_GetCamera = (GetCamera)GetProcAdress("_Java_com_artho_visart_plugins_minivis_internal_jni_MinivisFactory_n_1GetCamera@24");
13:
14:
15: long adapterPtr = n_MinivisFactory_Create();
16: void* consumer = NULL;
17: long pointer = n_GetCamera(adapterPtr, "00-50-C2-1D-7E-AB", consumer);
18:
19: std::cout << "Camera pointer:" << pointer << std::endl;
20: std::cout << "Press any key to continue..." << std::endl;
21: char character;
22: std::cin >> character;
23: return 0;
24: }
Suscribirse a:
Entradas (Atom)