Juan González Gómez
1/Feb/2002
Mediante una plataforma de codiseño, como la tarjeta LABOMAT, es posible delegar esta tarea en un hardware, liberando al microprocesador, que se puede emplear para realizar cálculos de más alto nivel, como la generación de los vectores de posicionamiento y sin consumir CPU en el posicionamiento en sí.
En este proyecto se implementa un módulo hardware encargado de la generación de las señales PWM necesarias para el control de los servos y a través de un interfaz muy sencillo, el software que se ejecuta en el microprocesador puede posicionar estos servos. Se desarrollan para ello unas librerías que permiten que cualquier usuario pueda utilizarlas para el control de robots articulados sin tener que conocer los detalles internos.
Como aplicación práctica, se controlan 4 servos que permiten orientar dos minicámaras y que mediante un programa se mueven según una secuencia de movimiento fija.
Otro de los objetivos es optimizar el diseño hardware lo máximo posible para valorar cuánto de buena es la solución SW/HW frente a la puramente SW. Esta valoración no viene determinada por la velocidad, sino más bien por los recursos consumidos. Con un microcontrolador de 8 bits se pueden controlar 4 servos, quedando todavía CPU para la aplicación en sí. Cuantos más servos se puedan controlar desde el Hardware, mejor será esta solución. Será por tanto objetivo de este trabajo el determinar el número máximo de ellos que se pueden controlar.
El empleo de servomecanismos de bajo precio para la creación de robots prototipos se está volviendo muy popular. Así existen hexápodos[2], brazos articulados[4], robots artículados de diferentes tipos[3][5] y hasta robots humanoides [1], todos ellos realizados con servos muy similares. En la figura 1 se muestra un gusano robot, desarrollado por el autor de este proyecto y que está constituido por 4 servos que conforman las 4 articulaciones. Está controlado por dos microcontroladores 6811 de motorola, uno dedicado exclusivamente a la generación de las señales de control de los servos y otro que almacena y genera las secuencias de movimiento.
Para posicionar este tipo de servos hay que introducirles una señal PWM de unas ciertas características, que dependiendo del ciclo de trabajo hace que el servo se mueva hacia una u otra posición.
En la mayoría de los prototipos, esta señal la genera un microcontrolador, es decir, se obtiene mediante software, aunque algunos microcontroladores disponen de hardware específico que permite su generación.
En la mayoría de los microcontroladores que se emplean para estas aplicaciones de robótica de bajo presupuesto, no está disponible un módulo hardware para posicionar los servos, teniéndose que realizar por software con la consecuente pérdida de recursos que se podrían dedicar a otras tareas. También ocurre que es el software el que se tiene que adaptar al hardware disponible en el microcontrolador: temporizadores, comparadores... no adaptándose al 100%.
Un enfoque muy interesante es el de diseñar un módulo hardware que se implemente en una FPGA de manera que liberere al microcontrolador de todas las operaciones de posicionamiento del servo, además de hacer abstracción de los mecanismos necesarios para ello, es decir, el poder posicionar un servo sin tener que saber el mecanimos necesario para ello. De esta manera, pequeños microcontroladores de 8 bits podrán generar fácilmente las secuencias de movimiento de varios servos perdiendo muy pocos recursos en ello.
Con este proyecto se pretende utilizar la plataforma LABOMAT para convertirla en un sistema de desarrollo para robótica, comprobando la viabilidad de utilizar una solución basada en SW/HW frente a la puramente SW y determinar cuánto más de buena o mala es. Como aplicación práctica se desarrolla un programa que permite orientar dos minicámaras, controlándose 4 servos. Para ello se contruye un pequeño módulo (la placa PLM-3003) que por un lado se conecta a la LABOMAT y por otro se le conectan los 4 servos que mueven las cámaras.
En el apartado 4 se describe el servomecanismo Futaba 3003, que es con el que se trabajará. Casi todos los servos son compatibles con el Futaba 3003, que se está conviertiendo en un estándar ``de facto'', por lo que todo lo desarrollado con este proyecto será válido para otros modelos de servos. Se presta especial atención a la señal de control, y se definen una serie de parámetros que la caracterizan.
En el apartado 5 es donde se encuentra lo más gordo del trabajo. Se propone un circuito de control genérico y se obtienen las ecuaciones necesarias que permiten su dimensionamiento, de manera que se pueda optimizar lo máximo posible según la precisión requerida para la aplicación. Se aplican las fórmulas para obtener un esquema hardware con una precisión suficientemente buena para la aplicación del movimiento de las minicámaras. El modelo hardware se amplía para el control de varios servos.
Todos los programas en VHDL se encuentran en el apartado 6, mostrándose los correspondientes a dos versiones del proyecto, la 0.6 en la que sólo se mueve un servo y ha servido para validar el modelo así como realizar las mediciones oportunas y la versión 1.0 que se corresponde con la aplicación práctica del movimiento de las minicámaras. En el apartado 7 se presenta el software desarrollado para ambas versiones, la 0.6 y la 1.0, que permite comunicarse con el hardware para establecer la posición de los servos.
El apartado 8 se dedica a la explicación de la aplicación práctica y finalmente en los apartados 9 y 10 se muestran los resultados obtenidos y las líneas futuras de trabajo.
Los servos futaba modelo 3003 son muy empleados en robótica de bajo presupuesto por sus características de reducido precio, alto par y bastante buena precisión. En la figura 2 se muestra el aspecto de uno de estos servos.
Una de las principales ventajas es su reducido tamaño y la posibilidad de desmontarlo completamente, pudiendo convertirlo en un motor de corriente continua normal (eliminando el circuito de control) o conectando un cable al potenciómetro interno de manera que nuestro sistema de control pueda cerrar el bucle.
El movimiento de estos servos se realiza en ``bucle abierto''. Es decir, se le envía una señal de control que le indica la posición en la que debe situarse el eje, pero no tenemos una señal de realimentación que nos indique cuando se ha realizado la operación. El bucle lo cierra internamente el propio Servo. Esta forma de control es muy buena en aplicaciones en las que el servo tiene fuerza de sobra para poderse posicionar, sabiendo que no van a existir obstáculos y que transcurrido un tiempo máximo el servo se posicionará.
En [7] se pueden encontrar las características de toda la familia de servos Futaba. Las del servo S3003 se resumen a continuación:
Alimentación: | 4.5-6v |
---|---|
Velocidad: | 0.23 seg/60![]() |
Par de salida: | 3.2 Kg-cm |
Tamaño: | 40.4 x 19.8 x 36mm |
Peso: | 37.2g |
Recorrido del eje: | 180![]() |
En la figura 3 se ha dibujado el esquema de un servo acotado en milímetros.
El conexionado del servo con los circuitos de control se realiza a través de un conector plano hembra de 3 vías al que se encuentran conectados los tres cables de acceso al servo. Estos cables son:
El control de la posición de los servos se hace aplicando una señal de control como la mostrada en la figura 5.
Se trata de una señal periódica, de periodo T=20ms, que se encuentra activa sólo durante un pequeño intervalo de tiempo (Mientras el servo reciba ``señal'' permanecerá en la posición indicada, ejerciendo ``fuerza'' (y por tanto consumiendo de manera proporcional a la fuerza que esté realizando para mantenerse en ella). Si la señal se deja de aplicar, el servo no ejerce fuerza y se puede mover con la mano (consumiendo sólo lo necesario para la alimentación de su circuito de control).
La nomenclatura empleada para los parámetros de la señal PWM es la siguiente, que será empleada en el apartado 5.1.2 para realizar cálculos:
En la figura 6 se muestra un primer esquema para la generación de la señal PWM de control de un servo.
El diseño es muy simple. Mediante el módulo interfaz, el software envía la posición del servo que es almacenada en un registro, que denominaremos registro de posición. El reloj del sistema incrementa un contador, de manera que se lleva la cuenta de cuántos tics de reloj han transcurrido desde que inicialmente se encontraba a cero.Mediante un comparador se comprueba si el número de tics recibidos es menor que la posición especificada. Esta posición, que es en realidad el ancho del pulso de la señal PWM, se expresa también en tics de reloj, por lo que mientras que los tics contados sean menores que los tics del ancho del pulso, la señal de salida del comparador, L, permanecerá a nivel alto. En cuanto los tics superen este valor, la señal L se pone a cero.
Tanto el periodo T de la señal PWM como el ancho del pulso ()
se especifican en tics del reloj principal, de manera que cuanto mayor
sea la frecuencia del reloj, mayor número de tics habrá que contar
para tener el mismo ancho del pulso (el sistema necesitará un mayor
número de bits) y cuando menor frecuencia el error en la determinación
del ancho exacto es mayor , el error de discretización es mayor.
Tomando como punto de partida la precisión en la posición del servo (P) determinaremos los siguientes parámetros para poder implementar el esquema de la figura 6:
Los cálculos se tienen que hacer en dos fases. En la primera se determina
a partir de la preción P con lo que calculamos el número
mínimo de bits necesarios para el contador y el registro de posición.
Sin embargo, el contador debe ser módulo
, es decir, que
una vez que haya contado todos los tics del periodo de la señal, deberá
comenzar desde cero. Por ello hay que tomar
y volver a calcular los parámetros anteriores, obteniéndose una nueva
precisión P' que será mejor o igual que la precisión tomada inicialmente
para los cálculos (P'<=P).
y su inversa nos determina la frecuencia mínima de funcionamiento:
obtenemos la ecuación:
Cuanta más precisión en la posición del servo (menor valor de P),
mayor será el número de tics que se deben contar y por tanto mayor
será el número de bits del contador, por lo que cuanto
mayor precisión queramos, más recursos emplearemos en la implementación.
que indica que el contador debe tener los suficientes bits como para
poder contar los tics de reloj del periodo de la señal PWM, al menos.
Despejando de esta fórmula:
que expresa que necesitaremos un número de bits suficiente como para almacenar la anchura máxima de la señal PWM:
Objetivo: Obtener el periodo de la señal de reloj ()
o su frecuencia(
) así como el mínimo número de bits
del contador y del registro de posición, a partir de la precisión
en la posición (P). Además el contador tiene que ser módulo
, de manera que una vuelta completa de este contador tarde lo mismo
que el periodo de la señal PWM.2
Teniendo esto en cuenta, la forma de realizar los cálculos es la siguiente:
Aplicaremos las fórmulas para calcular los parámetros de dimensionamiento de la unidad de PWM para una precisión inicial de P=1 y P=0.5.
Precisión (P) | ||
P=1 | P=0.5 | |
![]() |
11.11 ![]() |
5.55 ![]() |
![]() |
90 KHZ | 180 KHZ |
![]() |
9.766 ![]() |
4.883 ![]() |
![]() |
102.4 KHZ | 204.79 KHZ |
![]() |
11 bits | 12 bits |
![]() |
8 bits | 9 bits |
![]() |
236 tics | 471 tics |
![]() |
31 tics | 62 tics |
P' | 0.88 ![]() |
0.44 ![]() |
Una precisión de es suficiente para la mayoría de las
aplicaciones con este tipo de servos. Además el que el registro de
posición sea de 8 bits es muy adecuado para enviar esta información
desde un microcontrolador de 8 bits de una manera sencilla, utilizando
sólo un puerto de 8 bits o mediante un bus serie (como el SPI de Motorola).
Con los datos obtenidos en el apartado 5.1.2.4, para
una precisión máxima de P=1, y teniendo en cuenta que
hay que minimizar el diseño lo máximo posible para conseguir el mayor
número de controladores PWM en la FPGA, se puede dar una arquitectura
más detallada del sistema de generación de PWM, que se muestra en
la figura 7.
Es necesario un contador de 11 bits y un comparador de 8 bits. Del contador se segregan los tres bits más segnificativos, introduciéndose los 8 restantes en el comparador. Los 8 bits del registro de posición se comparan directamente con los 8 bits de menor peso del contador. Los tres bits de mayor peso del contador, junto con la señal G del comparador (que se activa siempre que A>B, o sea, cuando el contador es mayor que el registro de posición) se introducen en una puerta NOR para obtener la señal PWM de salida.
A continuación veremos la justificación de esta puerta NOR. Llamaremos
a los 3 bits más significativos del contador.
Puesto que el comparador es de 8 bits, la señal L (A<B) se activará
siempre que los 8 bits de menor peso del contador sean menores que
los 8 bits del registro de posición. Sin embargo esto sólo es válido
siempre que los bit de mayor peso del contador sean 0 (
).
En el caso de que alguno de estos sea '1', el contador tendrá un número
mayor que la posición y por tanto la señal PWM deberá estar a cero.
A la salida del comparador habrá que añadir una lógica adicional que
tenga esto en cuenta. Habrá que diseñar un circuito combinacional
que tengas como entradas
y L, y que vendrá
descrito por la siguiente tabla:
![]() |
![]() |
![]() |
L | PWM | Descripcion |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | El valor del contador es MAYOR o IGUAL que el del registro de posición |
0 | 0 | 0 | 0 | 1 | El valor del contador es menor que el del registro de posición |
0 | 0 | 1 | x | 0 | El valor del contador es MAYOR que el del registro de posición. La señal L hay que ignorarla |
0 | 1 | 0 | x | 0 | idem |
0 | 1 | 1 | x | 0 | idem |
1 | 0 | 0 | x | 0 | idem |
1 | 0 | 1 | x | 0 | idem |
1 | 1 | 0 | x | 0 | idem |
1 | 1 | 1 | x | 0 | idem |
Desarrollando la señal PWM se obtiene que:
y si en lugar de la señal L tomamos la señal G del comparador (Ya
que podemos considerar que aproximadamente L=,
descartando la igualdad), la ecuación queda:
Es decir, que con una única puerta NOR de 4 entradas implementamos la función PWM.
A la hora de implementar el esquema del apartado 5.1.3 en la tarjeta LABOMAT aparece un problema con el reloj. La señal de reloj que se aplica a la FPGA es programable y se puede variar en un amplio rango. Sin embargo, al configurarlo para trabajar a 102.400 HZ, no aparece ningún mensaje de error y aparentemente se ha configurado correctamente, pero al medir con el osciloscopio se comprueba que esta señal se ha disparado a valores superiores a 50MHZ.
En general, señales menores de 400KHZ producen este efecto. Para solucionarlo hay que configurar el reloj a una frecuencia de 409.600 HZ e implementar un divisor de frecuencia que divida esta señal entre 4, de manera que se obtengan los 102.400 KHZ necesarios. Este divisor lo denominaremos ``prescaler'', quedando la arquitectura definitiva de la unidad de PWM como la mostrada en la figura 8.
Dentro de la línea de puntos se ha encerrado la unidad de pwm, que consta de las siguientes señales de interfaz:
Aprovechando que en la tarjeta LABOMAT las transferencias de información entre la CPU y la FPGA pueden ser de 32 bits, el esquema propuesto para el control de varios servos se muestra en la figura 9.
El sistema tiene un registro de 32 bits por cada 4 servos a controlar. Cada grupo de 8 bits determina la posición de un servo. Para un mayor aprovechamiento de los recursos de la FPGA, el número de servos a controlar debe ser múltiplo de 4. Así por ejemplo, para controlar 8 servos se requerirían 2 registros de 32 bits. Si el número no es múltiplo de 4 se estará desaprovechando parte de la información de uno de los registros de 32 bits.En la figura 9 se han delimitado por líneas de puntos las partes del hardware que están dentro de la FPGA. Tanto la señal de reset como el reloj están en la tarjeta LABOMAT. Este es el esquema que se emplea para la implementación de la aplicación práctica. (Apartado 8)
Vamos a describir dos versiones del proyecto. La versión 0.6 en la que sólo se mueve un servo y que se ha empleado para validar el funcionamiento de la unidad de pwm y la versión 1.0 que se corresponde con la aplicación práctica del movimiento de los 4 servos para la orientación de las minicámaras. Estas dos versiones sólo se diferencian en la entidad de superior (Top-level), siendo las inferiores las mismas para ambas versiones. El software que se ejecuta en la CPU es también diferente.
La versión 0.6 permite posicionar sólo un servo y se ha empleado para realizar mediciones con el osciloscopio y comprobar que la parte de interfaz entre la CPU y la FPGA funciona correctamente. El interfaz no se ha optimizado, sino que se ha tomado el desarrollado para el ejemplo del multiplicador[8] y se ha adaptado para el manejo de la unidad PWM en vez del multiplicador. Por ello dispone de dos registros de 32 bits, mapeados en las direcciones 0x16000000 y 0x16000004, que se pueden leer y escribir. Sólo el byte más bajo del primer registro es el que se utiliza para posicionar el servo.
En la figura 10 se ha dibujado la entidad pwm_unit, especificándose los puertos de entrada, salida y las señales intermedias usadas, así como las entidades de menor nivel empleadas: entidad comparador y entidad contador.
El diseño superior de la jerarquía para la versión 0.6 se muestra en la figura 11. Está constituida por una máquina de estados que actúa de interfaz entre la CPU y la FPGA y dos registros de 32 bits que se pueden leer y escribir. Los 8 bits de menos peso del registro 0 son los que se introducen en la entidad pwm para el posicionamiento del servo. El reloj principal (clk) se introduce en la entidad prescaler para dividirlo por 4 y obtener así una frecuencia de 102.4KHZ (clk_4) que es la que necesita la entidad pwm_unit. El objetivo de la entidad perif_access es el de poder validar el correcto funcionamiento de la unidad de PWM, sabiendo que la máquina de estados de interfaz funciona correctamente, puesto que sólo se ha modificado la salida del registro 0, que en vez de introducirse como entrada del multiplicador, se hace como entrada de la unidad pwm. Es en la versión 1.0 donde se modifica este interfaz para adaptarlo exactamente a las necesidades de la aplicación.
Los ficheros vhdl empleados son los siguientes, que serán los mismo que para la versión 1.0 salvo el de la entidad superior.
La versión 1.0 permite controlar 4 servos y es la desarrollada para la aplicación del movimiento de las minicámaras. El interfaz se ha optimizado de modo que sólo se utiliza un registro de 32 bits mapeado en la dirección 0x16000000.
Estas dos versiones sólo se diferencian en el interfaz y en la cantidad de unidades PWM que contienen, por lo que sólo es diferente la entidad superior (perif_acces.vhd), siendo iguales el resto. En la figura 12 se presenta un esquema de la entidad perif_acces.vhd. La señales de interfaz son las mismas, por lo que no se han dibujado.
En este apartado se muestran los programas en vhdl de las diferentes entidades que componen la versión 0.6
- contador.vhd. Juan Gonzalez. Feb-2002 -
- Licencia GPL. -
------------------------------
- PROYECTO LABOBOT -
------------------------------
- Contador de 11 bits -
- -
- Entradas: clk : Reloj -
- reset : Puesta a cero asíncrona (Nivel Alto) -
- Salidas: -
- -q : Datos de salida -
------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity contador is
port (clk : in std_logic; - Reloj
reset : in std_logic;
q : out std_logic_vector (10 downto 0)); -Salida
end contador;
architecture beh of contador is
begin
output: process(clk,reset)
variable cuenta : std_logic_vector(10 downto 0);
begin
- Actualizar la cuenta
if (reset='1') then - Reset asíncrono
cuenta:=(others=>'0'); - Inicializar contador
q<=cuenta;
elsif (clk'event and clk='1') then - Flanco de subida en reloj
cuenta:=(cuenta+1); - Incrementar contador
q<=cuenta;
end if;
end process;
end beh;
- comparador.vhdl. Juan Gonzalez. Feb-2002 -
- Licencia GPL. -
---------------------------
- PROYECTO LABOBOT -
---------------------------
- Comparador de 8 bits -
- Sólo se ha implementado la salida correspondiente-
- a G (La función mayor que). -
- -
- Entradas: opa : Operando A -
- opb : Operando B -
- Salidas: -
- G : (A>B) -
---------------------------
library ieee;
use ieee.std_logic_1164.all;
entity comparador is
port (opa : in std_logic_vector(7 downto 0); - Operador A
opb : in std_logic_vector(7 downto 0); - Operador B
G : out std_logic); - (A>=G)
end comparador;
architecture beh of comparador is
begin
process (opa, opb)
begin
if (opa<opb) then
G <= '0';
else
G <= '1';
end if;
end process;
end beh;
- prescaler.vhd. Juan Gonzalez. Feb-2002 -
- Licencia GPL. -
------------------------------
- PROYECTO LABOBOT -
---------------------------------
- Prescaler de 2 bits (Divide por 4) -
- -
- Entradas: pres_clk : Reloj -
- pres_reset : Puesta a cero asíncrona (Nivel Alto) -
- Salidas: -
- pres_q : Salida -
---------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity prescaler is
port (pres_clk : in std_logic;
pres_reset: in std_logic;
pres_q : out std_logic);
end prescaler;
architecture beh of prescaler is
begin
output: process(pres_clk,pres_reset)
variable cuenta : std_logic_vector(1 downto 0);
begin
- Actualizar la cuenta
if (pres_reset='1') then
cuenta:=(others=>'0');
pres_q<='0';
elsif (pres_clk'event and pres_clk='1') then
cuenta:=(cuenta+1);
pres_q<=cuenta(1);
end if;
end process;
end beh;
- pwm_unit.vhdl Juan Gonzalez. Febrero-2002 -
- Licencia GPL. -
---------------------------
- PROYECTO LABOBOT -
---------------------------
- UNIDAD DE PWM: -
- -
- ENTRADAS: -
- -pwm_clk : Señal de reloj -
- -pwm_pos : Posicion del servo (8 bits) -
- -pwm_reset: Reset -
- SALIDAS: -
- -pwm_o: Señal PWM -
---------------------------
library ieee;
use ieee.std_logic_1164.all;
entity pwm_unit is
port (pwm_pos : in std_logic_vector (7 downto 0); - Posicion
pwm_clk : in std_logic; - Reloj
pwm_reset: in std_logic; - Reset.
pwm_o : out std_logic); - Señal PWM
end pwm_unit;
architecture beh of pwm_unit is
component contador is
port (clk : in std_logic; - Reloj
reset : in std_logic;
q : out std_logic_vector (10 downto 0)); -Salida
end component;
component comparador is
port (opa : in std_logic_vector(7 downto 0); - Operador A
opb : in std_logic_vector(7 downto 0); - Operador B
G : out std_logic);
end component;
signal ntic : std_logic_vector (10 downto 0); - Numero de tics de reloj
signal ntic_ms: std_logic_vector (7 downto 0); - 8 bits menos peso de ntic
signal sg : std_logic; - Señal G
begin
CONT1: contador port map (clk=>pwm_clk, q=>ntic, reset=>pwm_reset);
COMP1: comparador port map (opa=>ntic_ms, opb=>pwm_pos, G=>sg);
- Obtener los 8 bits menos significativos del contador
ntic_ms<=ntic(7 downto 0);
- Señal PWM de salida
pwm_o<= (not ntic(8)) and (not ntic(9)) and (not ntic(10))
and (not sg);
end beh;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use IEEE.std_logic_unsigned.all;
entity perif_access is
port (
data : inout std_logic_vector(31 downto 0);
adr : in std_logic;
cs0 : in std_logic;
reset : in std_logic;
ws : in std_logic;
rs : in std_logic;
clk : in std_logic;
ack : out std_logic;
pwm : out std_logic;
exit_clk : out std_logic
);
end perif_access;
architecture Final of perif_access is
type mem is array (natural range <>) of std_logic_vector(31 downto 0);
type state is (IDLE, RDOK, WROK);
signal memoria_rw : mem(0 to 1);
signal estado : state;
signal n_estado : state;
signal load : std_logic;
--- ***** ---
-signal operando1 : std_logic_vector(15 downto 0);
-SIGNAL operando2 : std_logic_vector(15 downto 0);
-SIGNAL resultado : std_logic_vector(31 downto 0);
signal pos : std_logic_vector(7 downto 0);
signal clk_4 : std_logic;
--- ***** ---
component pwm_unit is
port (pwm_pos : in std_logic_vector (7 downto 0); - Posicion
pwm_clk : in std_logic; - Reloj
pwm_reset: in std_logic; - Reset.
pwm_o : out std_logic); - Señal PWM
end component;
component prescaler is
port (pres_clk : in std_logic;
pres_reset: in std_logic;
pres_q : out std_logic);
end component;
begin
exit_clk <= clk;
process(clk, reset, load, memoria_rw) is
variable i : integer;
begin
if(reset = '1') then
estado <= IDLE;
for i in 0 to 1 loop
memoria_rw(i) <= (others => '0');
end loop;
elsif (clk = '1' and clk'event) then
estado <= n_estado;
if(load = '1') then
if(adr = '1') then
memoria_rw(1) <= data;
else
memoria_rw(0) <= data;
end if;
end if;
end if;
-operando1 <= memoria_rw(0)(31 downto 16);
-operando2 <= memoria_rw(0)(15 downto 0);
pos<=memoria_rw(0)(7 downto 0);
-memoria_rw(1) <= resultado;
end process;
process(estado, cs0, ws, rs)
begin
case estado is
when IDLE =>
ack <= '1';
load <= '0';
if(cs0 = '0' and ws = '0' and rs = '1') then
n_estado <= WROK;
load <= '1';
elsif(cs0 = '0' and rs = '0' and ws = '1') then
n_estado <= RDOK;
else
n_estado <= IDLE;
end if;
when WROK=>
ack<='0';
load <= '0';
if(cs0 = '1') then
n_estado <= IDLE;
else
n_estado <= WROK;
end if;
when RDOK =>
ack <= '0';
load <= '0';
if(cs0 = '1') then
n_estado <= IDLE;
else
n_estado <= RDOK;
end if;
end case;
end process;
process(estado, memoria_rw, adr)
begin
if(estado = RDOK) then
if(adr = '1') then
data <= memoria_rw(1);
else
data <= memoria_rw(0);
end if;
else data <= (others => 'Z');
end if;
end process;
PRES1: prescaler port map (pres_clk=>clk,
pres_reset=>reset,
pres_q=>clk_4);
PWM1: pwm_unit port map (pwm_pos => pos,
pwm_reset=>reset,
pwm_clk=>clk_4,
pwm_o=>pwm);
end Final;
# PROYECTO LABOBOT. Juan Gonzalez. Febrero 2002 -
#-------------------------
# Fichero de restricciones -
#-------------------------
# V0.6 #
##################################################
#-- Entidad top_pwm:
NET clk LOC=P124; # - Señal de reloj
NET reset LOC=P63; # - Reset
NET pwm LOC=P127; # - PWM: Salida: Pin A0, conector J27
#-- Interfaz con CPU
NET ack LOC=P239;
NET cs0 LOC=P142;
NET ws LOC=P183;
NET rs LOC=P153;
NET adr LOC=P188;
NET data<0> LOC=P206;
NET data<1> LOC=P207;
NET data<2> LOC=P208;
NET data<3> LOC=P209;
NET data<4> LOC=P210;
NET data<5> LOC=P213;
NET data<6> LOC=P214;
NET data<7> LOC=P215;
NET data<8> LOC=P216;
NET data<9> LOC=P217;
NET data<10> LOC=P218;
NET data<11> LOC=P220;
NET data<12> LOC=P221;
NET data<13> LOC=P223;
NET data<14> LOC=P224;
NET data<15> LOC=P225;
NET data<16> LOC=P226;
NET data<17> LOC=P228;
NET data<18> LOC=P229;
NET data<19> LOC=P230;
NET data<20> LOC=P231;
NET data<21> LOC=P232;
NET data<22> LOC=P233;
NET data<23> LOC=P234;
NET data<24> LOC=P177;
NET data<25> LOC=P173;
NET data<26> LOC=P159;
NET data<27> LOC=P152;
NET data<28> LOC=P148;
NET data<29> LOC=P141;
NET data<30> LOC=P129;
NET data<31> LOC=P123;
En esta versión se mueven 4 servos, en vez de 1. Para el envío de las posiciones de los servos se utiliza un único registro de 32 bits, que se divide en 4 partes de 8 bits. Cada parte contiene la posicion de uno de los servos.
Obsérvese que se ha suprimido la línea de direcciones (adr). Cómo sólo se ha mapeado un registro, basta con escribir en cualquier dirección en la que se active el chip select 0 (cs0): 16000000-1600FFFF.
- perif_access.vhd. Juan Gonzalez. Feb-2002 -
- Licencia GPL. -
-------------------------------
- PROYECTO LABOBOT -
-------------------------------
- TOP-level. Se implementan 4 unidades de PWM y la mßqui- -
- na de estados para la transferencia de informacion -
- entre la CPU y la FPGA. -
- -
-------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use IEEE.std_logic_unsigned.all;
entity perif_access is
port (
- Señales usadas para el interfaz entre FPGA y CPU
data : inout std_logic_vector(31 downto 0);
adr : in std_logic;
cs0 : in std_logic;
ws : in std_logic;
rs : in std_logic;
ack : out std_logic;
- Señales empleadas para las unidades PWM
reset : in std_logic;
clk : in std_logic;
pwm1 : out std_logic;
pwm2 : out std_logic;
pwm3 : out std_logic;
pwm4 : out std_logic);
end perif_access;
architecture Final of perif_access is
type state is (IDLE, RDOK, WROK);
signal estado : state;
signal n_estado : state;
signal load : std_logic;
- Registros de posicion de los servos (Interfaz con CPU)
signal reg_pos : std_logic_vector(31 downto 0);
-----------------------
- Señales especificas de las unidades PWM -
-----------------------
- Posiciones de los servos
signal pos1 : std_logic_vector(7 downto 0);
signal pos2 : std_logic_vector(7 downto 0);
signal pos3 : std_logic_vector(7 downto 0);
signal pos4 : std_logic_vector(7 downto 0);
- Reloj para el PWM. Funciona a una frecuencia 4 veces
- inferior que la señal de reloj global (clk)
signal clk_4 : std_logic;
-----------------
- Declaracion de componentes -
-----------------
- Unidad de PWM
component pwm_unit is
port (pwm_pos : in std_logic_vector (7 downto 0); - Posicion
pwm_clk : in std_logic; - Reloj
pwm_reset: in std_logic; - Reset.
pwm_o : out std_logic); - Se al PWM
end component;
- Divisor de la frecuencia del reloj
component prescaler is
port (pres_clk : in std_logic;
pres_reset: in std_logic;
pres_q : out std_logic);
end component;
begin
-------------------
- MAQUINA DE ESTADOS DE INTERFAZ -
-------------------
process(clk, reset, load, reg_pos) is
variable i : integer;
begin
if(reset = '1') then - Si hay reset...
estado <= IDLE; - Estado inicial
reg_pos<=(others => '0'); - Inicializar registro de posicion
elsif (clk = '1' and clk'event) then - Flanco de subida
estado <= n_estado;
if(load = '1') then
reg_pos <= data; - Lectura de la posicion de los servos
end if;
end if;
- Cada grupo de 8 bits del registro de posicion se envia a
- una unidad pwm diferente
pos1<=reg_pos(7 downto 0);
pos2<=reg_pos(15 downto 8);
pos3<=reg_pos(23 downto 16);
pos4<=reg_pos(31 downto 24);
end process;
process(estado, cs0, ws, rs)
begin
case estado is
when IDLE =>
ack <= '1';
load <= '0';
if(cs0 = '0' and ws = '0' and rs = '1') then - Ciclo de escritura
n_estado <= WROK;
load <= '1';
elsif(cs0 = '0' and rs = '0' and ws = '1') then - Ciclo de lectura
n_estado <= RDOK;
else
n_estado <= IDLE;
end if;
when WROK =>
ack <= '0';
load <= '0';
if(cs0 = '1') then
n_estado <= IDLE;
else
n_estado <= WROK;
end if;
when RDOK =>
ack <= '0';
load <= '0';
if(cs0 = '1') then
n_estado <= IDLE;
else
n_estado <= RDOK;
end if;
end case;
end process;
process(estado, reg_pos)
begin
if(estado = RDOK) then
data <= reg_pos;
else data <= (others => 'Z');
end if;
end process;
----------
- PRESCALER -
----------
PRES1: prescaler port map (pres_clk=>clk,
pres_reset=>reset,
pres_q=>clk_4);
-----------
- UNIDADES DE PWM -
-----------
PWM_U1: pwm_unit port map (pwm_pos => pos1,
pwm_reset=>reset,
pwm_clk=>clk_4,
pwm_o=>pwm1);
PWM_U2: pwm_unit port map (pwm_pos => pos2,
pwm_reset=>reset,
pwm_clk=>clk_4,
pwm_o=>pwm2);
PWM_U3: pwm_unit port map (pwm_pos => pos3,
pwm_reset=>reset,
pwm_clk=>clk_4,
pwm_o=>pwm3);
PWM_U4: pwm_unit port map (pwm_pos => pos4,
pwm_reset=>reset,
pwm_clk=>clk_4,
pwm_o=>pwm4);
end Final;
# PROYECTO LABOBOT. Juan Gonzalez. Febrero 2002 -
#-------------------------
# Fichero de restricciones -
#-------------------------
# V0.8 #
##################################################
#-- Senales del PWM:
NET clk LOC=P124; # - Señal de reloj
NET reset LOC=P63; # - Reset
NET pwm1 LOC=P127; # - PWM1: Salida: Pin A0, conector J27
NET pwm2 LOC=P126; # - PWM2: Salida: Pin A1, conector J27
NET pwm3 LOC=P125; # - PWM3: Salida: Pin A2, conector J27
NET pwm4 LOC=P117; # - PWM4: Salida: Pin A3, conector J27
#-- Interfaz con CPU
NET ack LOC=P239;
NET cs0 LOC=P142;
NET ws LOC=P183;
NET rs LOC=P153;
NET adr LOC=P188;
NET data<0> LOC=P206;
NET data<1> LOC=P207;
NET data<2> LOC=P208;
NET data<3> LOC=P209;
NET data<4> LOC=P210;
NET data<5> LOC=P213;
NET data<6> LOC=P214;
NET data<7> LOC=P215;
NET data<8> LOC=P216;
NET data<9> LOC=P217;
NET data<10> LOC=P218;
NET data<11> LOC=P220;
NET data<12> LOC=P221;
NET data<13> LOC=P223;
NET data<14> LOC=P224;
NET data<15> LOC=P225;
NET data<16> LOC=P226;
NET data<17> LOC=P228;
NET data<18> LOC=P229;
NET data<19> LOC=P230;
NET data<20> LOC=P231;
NET data<21> LOC=P232;
NET data<22> LOC=P233;
NET data<23> LOC=P234;
NET data<24> LOC=P177;
NET data<25> LOC=P173;
NET data<26> LOC=P159;
NET data<27> LOC=P152;
NET data<28> LOC=P148;
NET data<29> LOC=P141;
NET data<30> LOC=P129;
NET data<31> LOC=P123;
Este programa se ha realizado para comprobar que la unidad pwm está funcionando. Simplemente se le pide al usuario una posición y se envía al registro 0, que se encuentra mapeado en la dirección 0x16000000.
/* PROYECTO LABOBOT. Juan Gonzalez. Febrero 2002 */
/* LICENCIA GPL. */
/********************************************************/
#include <stdio.h>
#include "work.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <bsp.h>
#include <m68360.h>
#include <rtems/error.h>
#include <rtems/rtems_bsdnet.h>
#include <rtems/tftp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <nc360/md.h>
#include <nc360/cpld.h>
#include <nc360/mubus.h>
#include <nc360/clock.h>
#include <nc360/flash.h>
#include <nc360/interrupts.h>
#include <nc360/identification.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/timeb.h>
#include <time.h>
#include <netdb.h>
#include <math.h>
#include <stdio.h>
#include <sys/time.h>
#include "labomat3.h"
#include "rlib.h"
#include "usuals.h"
#define FPGA 0x16000000
void main_work(void)
{
Card32 *punt;
Card32 dato;
char buffer[100];
/* Puntero de acceso a la FPGA */
punt=(Card32 *)FPGA;
while (1) {
printf ("Posicion (hex): ");
read_line(buffer,sizeof(buffer));
sscanf(buffer,"%x",&dato);
(*punt)=dato;
printf ("Valor: %x (%x)\n",dato,*punt);
}
return;
}
Para el control de los servos se han realizado unas funciones que permiten al usuario posicionar los servos utilizando grados sexagesimales, en vez de tics de reloj, y además se oculta la forma de acceso al servo (el usuario no tiene que saber nada sobre direcciones de memoria, ni registros mapeados).
Las dos funciones de interfaz son:
Posicionar todos los servos. Los valores de las posiciones están comprendidas
en el rango [-90,90], correspondientes a los dos extremos opuestos,
separados
Posocionar el servo indicado, sin modificar la posición del resto de servos. El parámetro ``ns'' indica el número de servo, comprendido entre [1-4]
de forma que cuando:
Estas librerías de pruebas se encuentran en el mismo programa de pruebas (labobot.c) en el que se ha realizado la aplicación de las minicámaras. El listado completo se puede encontrar en el apartado 8.4.
Una vez desarrollado el hardware y el software para el control y posicionamiento de los servos, se va a aplicar a un sistema real, compuesto por dos minicámaras con salida de vídeo, colocadas en sendos soportes que disponen de dos servos cada uno, de manera que las minicámaras se pueden orientar hacia cualquier dirección, dentro del campo de movimiento de los servos.
La aplicación pretende demostrar que tanto el software como el hardware funcionan correctamente y que este trabajo no ha quedado sólo en algo teórico. No obstante la aplicación no hace ``nada útil''. Simplemente permite que las dos minicámaras sigan dos secuencias programadas, una de 4 posiciones y otra de 3.
Para la conexión de los servos a la tarjeta LABOMAT se ha construido
una placa de interconexión, denominada PLM-3003 (PLM
son las siglas de Periférico para LaboMat),
que se muestra en la figura
Servo | Pin conector J27 | Pin FPGA 4013 | Señal |
---|---|---|---|
1 | A0 | 127 | IO32 |
2 | A1 | 126 | IO33 |
3 | A2 | 125 | IO34 |
4 | A3 | 117 | IO35 |
En la versión 0.6 de Labobot sólo se utiliza el servo 1, puesto que sólo hay una unidad de PWM en la FPGA.
La estructura mecánica se muestra en la figura 14. Está constituida por una base de PVC blanco de 1cm de espesor, a la que se han colocado los servos 1 y 3 utilizando cuatro varillas roscadas de 3mm de diámetro y 6cm de largo. A estos dos servos llevan atornillados a sus ejes una pieza en forma de ``L'' de alumínio de 1mm, en donde se encuentran los servos 2 y 4 respectivamente. A estos dos servos a su vez van conectadas las minicámaras 1 y 2.
El software que permite reproducir las secuencias de movimiento, así como las librerías de control se muestran a continuación (Fichero labobot.c):
/* PROYECTO LABOBOT. Juan Gonzalez. Febrero 2002 */
/* LICENCIA GPL. */
/********************************************************/
#include <stdio.h>
#include "work.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <bsp.h>
#include <m68360.h>
#include <rtems/error.h>
#include <rtems/rtems_bsdnet.h>
#include <rtems/tftp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <nc360/md.h>
#include <nc360/cpld.h>
#include <nc360/mubus.h>
#include <nc360/clock.h>
#include <nc360/flash.h>
#include <nc360/interrupts.h>
#include <nc360/identification.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/timeb.h>
#include <time.h>
#include <netdb.h>
#include <math.h>
#include <stdio.h>
#include <sys/time.h>
#include "labomat3.h"
#include "rlib.h"
#include "usuals.h"
/********************************************************************/
/* MODULO SERVO. Rutinas de posicionamiento de 4 servos conectados */
/* a la tarjeta PLM-3003 enchufada al conector J27 de la LABOMAT */
/********************************************************************/
/*---------*/
/* DEFINICIONES */
/*---------*/
/* Direccion de acceso a la FPGA, donde esta mapeado */
/* el registro de posicion de los servos, de 32 bits */
#define FPGA 0x16000000
/* Anchura minima, media y maxima de la señal PWM, que se corresponde con */
/* las posiciones -90, 0 y 90 grados del servo */
#define TIC_MIN 0x20
#define TIC_MAX 0xEC
/*----------------*/
/* VARIABLES INTERNAS DEL MODULO */
/*----------------*/
/* Copia del registro de posicion */
static Card32 reg_pos_copy=0;
/* Puntero al registro de posicion en la FPGA */
static volatile Card32 *reg_pos=(Card32 *)FPGA;
/*--------------*/
/* FUNCIONES DE INTERFAZ */
/*--------------*/
void servo_posicionar_all(int pos1, int pos2, int pos3, int pos4)
/********************************************************************/
/* Posicionar los 4 servos. Las posiciones de los servos vienen */
/* en grados sexagesimales [-90,90] */
/********************************************************************/
{
/* Posiciones en tics de reloj */
Card8 pos1_tic;
Card8 pos2_tic;
Card8 pos3_tic;
Card8 pos4_tic;
/* Realizar la conversion de grados a tics */
pos1_tic=((TIC_MAX - TIC_MIN)*(pos1+90))/180 + TIC_MIN;
pos2_tic=((TIC_MAX - TIC_MIN)*(pos2+90))/180 + TIC_MIN;
pos3_tic=((TIC_MAX - TIC_MIN)*(pos3+90))/180 + TIC_MIN;
pos4_tic=((TIC_MAX - TIC_MIN)*(pos4+90))/180 + TIC_MIN;
/* Actualizar copia del registro de posicion */
reg_pos_copy = (pos4_tic << 24) | (pos3_tic << 16) | (pos2_tic << 8) | pos1_tic;
/* Enviar posiciones al registro de posicion "verdadero" */
*reg_pos=reg_pos_copy;
}
void servo_posicionar(int ns, int pos)
/************************************************/
/* Posicionar solo un servo y dejar los demas */
/* en la misma posicion en la que estaban */
/* ENTRADAS: */
/* ns : Numero del servo a posicionar [1-4] */
/* pos: Posicion en grados [-90,90] */
/************************************************/
{
Card8 pos_tic;
/* Numero de servo incorrecto. No se posiciona */
if (ns<1 || ns>4) return;
ns-; /* Internamente se usa la numeracion 0-3 para los servos */
/* Obtener la nueva posicion */
pos_tic=((TIC_MAX - TIC_MIN)*(pos+90))/180 + TIC_MIN;
printf ("Posicion %d: \n",pos);
printf ("Posicion en tics: %x\n",pos_tic);
/* Actualizar la copia del registro de posicion */
switch(ns) {
case 0: reg_pos_copy=(reg_pos_copy & 0x000000FF);
reg_pos_copy=(reg_pos_copy | pos_tic);
break;
case 1: reg_pos_copy=(reg_pos_copy & 0x0000FF00);
reg_pos_copy=(reg_pos_copy | (pos_tic<<8));
break;
case 2: reg_pos_copy=(reg_pos_copy & 0x00FF0000);
reg_pos_copy=(reg_pos_copy | (pos_tic<<16));
break;
case 3: reg_pos_copy=(reg_pos_copy & 0xFF000000);
reg_pos_copy=(reg_pos_copy | (pos_tic<<24));
break;
}
/* Enviar posiciones al registro de posicion "verdadero" */
*reg_pos=reg_pos_copy;
}
/*--------------*/
/* FUNCIONES PRIVADAS */
/*--------------*/
/**********************************************/
/* PROGRAMA DE PRUEBA PARA EL MODULO SERVO */
/**********************************************/
void play_sec1()
/*******************************************/
/* Reproducir una secuencia de movimiento */
/*******************************************/
{
int nv=5; /* Numero de vectores de posicion */
int sec1[][4]= { /* Vectores de posicion de la secuencia */
{-45,45,-45,-45},
{-45,-45,-45,45},
{45,-45,45,45},
{45,45,45,-45},
{0,0,0,0}
};
int i;
for (i=0; i<nv; i++) {
/* Posicionar */
servo_posicionar_all(sec1[i][0],sec1[i][1],sec1[i][2],sec1[i][3]);
/* Realizar una pausa */
sleep(1);
}
}
void play_sec2()
/*******************************************/
/* Reproducir una secuencia de movimiento */
/*******************************************/
{
int nv=3; /* Numero de vectores de posicion */
int sec[][4]= { /* Vectores de posicion de la secuencia */
{-90,-90,-90,-90},
{90,90,90,90},
{0,0,0,0}
};
int i;
for (i=0; i<nv; i++) {
/* Posicionar */
servo_posicionar_all(sec[i][0],sec[i][1],sec[i][2],sec[i][3]);
/* Realizar una pausa */
sleep(1);
}
}
void prueba1()
{
char buffer[100];
int posicion;
while (1) {
printf ("Servo1[-90,90]: ");
read_line(buffer,sizeof(buffer));
sscanf(buffer,"%d",&posicion);
servo_posicionar_all(posicion,posicion,posicion,posicion);
printf (" Copia Reg. Posicion: %x \n",reg_pos_copy);
}
}
void main_work(void)
{
char buffer[100];
int opcion;
/* Inicializar las posiciones de los servos */
servo_posicionar_all(0,0,0,0);
while (1) {
printf ("introduzca secuencia (1-2):\n");
read_line(buffer,sizeof(buffer));
sscanf(buffer,"%d",&opcion);
switch(opcion) {
case 1 : play_sec1();
break;
case 2 : play_sec2();
}
}
return;
}
Esta prueba está pensada para realizarse localmente. Se puede probar remotamente, sin embargo no tiene sentido porque no se puede comprobar que el sistema está funcionando. El programa de pruebas labobot.c realiza las impresiones en modo local. No obstante, sólo hay que cambiar las instrucciones printf por rprintf para poder ejecutarlo remotamente. Los pasos a seguir son los siguientes:
No olvidar alimentar la placa PLM-3003 con 5v (a través de una fuente de alimentación capaz de dar al menos 1.2A).
Este programa nos permite seleccionar entre dos posibles secuencias. Cada vez que pulsemos la tecla 1 ó 2, seguidas de Return, las minicámaras se posicionarán en las diferentes posiciones indicadas en el software y se volverá al menú principal. A partir de este ejemplo se pueden realizar aplicaciones mucho más complejas de control.
Una de los objetivos de este proyecto era el determinar cuántos servos se podrían llegar a controlar. Para probarlo se ha desarrollado una nueva versión, la 1.2, en la que se han ido añadiendo unidades de PWM hasta que se han agotado los recursos de la FPGA.
Como conclusiones podemos sacar las siguientes:
Por poner un ejemplo con números, sin con un sistema puramente software, basado en el microcontrolador 68hc11 de Motorola se podían controlar 4 servos, serán necesarios 7 microcontroladores conectados a uno maestro para poder controlar los 28 servos).
Como se desprende del ejemplo anterior, en el que es necesaria una red de microcontroladores para poder posicionar los servos de cualquier robot articulado con más de 4 articulaciones.
Incluso para el caso de controlar robots articulados con 4 ó menos servos, la solución HW/SW permite una mayor versatilidad, puesto que parte de los recursos de la FPGA se pueden emplear para implementar controladores necesarios para el robot: infrarrojos, ultrasonidos, sensores de contacto... En la solución SW se están desperdiciando recursos al tener que emplear un microcontrolador dedicado exclusivamente al control de los servos.
Cuando se emplea una solución software, es el microcontrolador el que genera mendiante interrupciones las señales PWM. Cuando se están controlando 4 servos y las posiciones de los 4 servos son similares, el ancho de la señal de PWM es casi igual, produciéndose las 4 interrupciones simultaneamente. Puesto que se atienden secuencialmente, por orden de llegada o en su caso por una prioridad preestablecida, ocurren pequeños retrasos que hacen que el servo ``vibre''. Sólo es apreciable en el caso particular antes mencionado: cuando dos o más servos están en posiciones muy cercanas.
Con la opción SW/HW esto no ocurre, consiguiéndose un movimiento fluido y sin vibraciones en ningún momento.
Conclusión final de esta trabajo:
Para el desarrollo de este proyecto se ha utilizado lo siguiente:
Tengo que agradecer a muchas personas su ayuda prestada para la realización de este trabajo:
This document was generated using the LaTeX2HTML translator Version 2K.1beta (1.48)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir543HbnXDh/lyx_tmpbuf5433VFIZk/labobot.tex
The translation was initiated by Juan Gonzalez on 2002-06-05