ev3dev – webcam

This post is part 2 of 9 of  AD4M4ST0R - um rover LEGO

A webcam USB utilizada no AD4M4ST0R é uma Logitech C170.

Depois de ligada são reconhecidos dois devices: uma webcam propriamente dita do tipo UVC (USB Video Class) gerida pelo v4l2 e um microfone gerido como uma placa de som pelo ALSA:

root@ev3dev:~# dmesg
(...)
usb 1-1.3: new full-speed USB device number 5 using ohci
Linux video capture interface: v2.00
uvcvideo: Found UVC 1.00 device Webcam C170 (046d:082b)
input: Webcam C170 as /devices/platform/ohci.0/usb1/1-1/1-1.3/1-1.3:1.0/input/input3
usbcore: registered new interface driver uvcvideo
USB Video Class driver (1.1.1)
usbcore: registered new interface driver snd-usb-audio

O microfone não aparece como dispositivo USB, apenas a webcam:

root@ev3dev:~# lsusb
Bus 001 Device 005: ID 046d:082b Logitech, Inc.
(...)

mas se formos ver ao alsamixer  temos lá uma nova placa de som: “Webcam C170”.

A parte mais crítica, a criação de um dispositivo video, também funcionou:

root@ev3dev:~# ls /dev

mostra entre outros “video0”.

Como a webcam foi também reconhecida como um dispositivo OHCI temos ainda:

root@ev3dev:~# ls /dev/input/by-id/ -la
(...)
lrwxrwxrwx 1 root root   9 Aug 18 12:13 usb-_Webcam_C170-event-if00 -> ../event3
(...)

 

Para capturar imagens da webcam optei pelo fswebcam

apt-get install fswebcam

Como já o tinha experimentado com esta mesma webcam no meu Ubuntu, avanço para um comando que sei que funciona:

fswebcam -d /dev/video0 -p YUYV -r 640x480 teste.jpg

E efectivamente funciona só que quando fui ver a imagem esta tinha apenas 176×144 e não 640×480 (e o próprio comando devolve isso: “Adjusting resolution from 640×480 to 176×144”). Mas que raio…?

Perdi algum tempo nisto por isso vou alongar-me um pouco mais para que outros que passem pelo mesmo possam beneficiar também.

Descobri que a câmara permite despejar video em dois formatos diferentes: YUYV e MJPG:

root@ev3dev:~# v4l2-ctl --list-formats
ioctl: VIDIOC_ENUM_FMT
    Index       : 0
    Type        : Video Capture
    Pixel Format: 'YUYV'
    Name        : YUV 4:2:2 (YUYV)

    Index       : 1
    Type        : Video Capture
    Pixel Format: 'MJPG' (compressed)
    Name        : MJPEG

MJPG (Motion JPEG) é um formato com compressão (e alguma perda também) e por isso consome menos largura de banda que o YUYV. A minha teoria é de que sendo o bus USB do EV3 apenas 1.1 (11 Mbps) em vez de USB 2.0 (480 Mbps) como no meu Ubuntu a webcam de algum modo se autoconfigura para a largura de banda disponível. Por isso no Ubuntu ao pedir com o fswebcam uma foto 640×480 extraída de video gerado em formato YUV ele consiga mas no EV3 seja obrigado a reduzir para 176×144.

A solução foi indicar ao fswebcam para lidar com MJPG:

fswebcam -d /dev/video0 -p MJPEG -r 640x480 teste.jpg

Agora já funciona, excepto que nalgumas situações ocorre um erro semelhante a este (os dois números hexadecimais do final variam):

GD Error: gd-jpeg: JPEG library reports unrecoverable error: Not a JPEG file: starts with 0x92 0x7

Aparentemente algumas webcams (e pelos vistos a maioria são da Logitech?) demora a estabilizar a captura e por isso produz lixo nas primeiras frames. As soluções passam invariavelmente por descartar umas quantas frames inicais… só ainda não encontrem o número mágico:

fswebcam -d /dev/video0 -p MJPEG -r 640x480 -S 20 teste.jpg

Descartar 20 frames funciona sempre mas parece-me um exagero. Além disso descobri que à medida que se vai utilizando podemos baixar o número, até mesmo deixar de descartar qualquer frame… Parece que a câmara “aquece” ou se vai auto-ajustando, será?

Para facilitar a utilização a partir de outros programas criei um script ‘saycheese.sh’ que ao ser invocado tira uma fotografia e guarda-a com um nome correspondendo à data:

#!/bin/sh
 filename=$(date +"%d-%m-%y_%Hh%Mm%Ss")
 fswebcam -d /dev/video0 -p MJPEG -r 640x480 -q -D 1 -S 10 -s brightness=8 -s contrast=10  $filename.jpg

Utilizo alguns outros parâmetros:

  • “-D 1” força um atraso de 1 segundo entre a activação da câmara e o íncio da captura
  • -s brightness=8 aumenta ligeiramente o brilho
  • -s contrast=10 diminui ligeiramente o contraste

Os valores de brightness e contrast, bem como outros aceites pela webcam, podem ser questionados à própria câmara:

root@ev3dev:~# v4l2-ctl --all
Driver Info (not using libv4l2):
    Driver name   : uvcvideo
    Card type     : Webcam C170
    Bus info      : usb-ohci.0-1.3
    Driver version: 3.14.7
    Capabilities  : 0x84000001
        Video Capture
        Streaming
        Device Capabilities
    Device Caps   : 0x04000001
        Video Capture
        Streaming
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
    Width/Height  : 640/480
    Pixel Format  : 'MJPG'
    Field         : None
    Bytes per Line: 0
    Size Image    : 921600
    Colorspace    : SRGB
Crop Capability Video Capture:
    Bounds      : Left 0, Top 0, Width 640, Height 480
    Default     : Left 0, Top 0, Width 640, Height 480
    Pixel Aspect: 1/1
Streaming Parameters Video Capture:
    Capabilities     : timeperframe
    Frames per second: 30.000 (30/1)
    Read buffers     : 0
                     brightness (int)    : min=-64 max=64 step=1 default=0 value=0
                       contrast (int)    : min=0 max=30 step=1 default=13 value=13
                     saturation (int)    : min=0 max=127 step=1 default=38 value=38
                            hue (int)    : min=-16000 max=16000 step=1 default=0 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=20 max=250 step=1 default=100 value=100
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=5000 value=5000 flags=inactive
                      sharpness (int)    : min=0 max=100 step=1 default=35 value=35
         backlight_compensation (int)    : min=0 max=1 step=1 default=0 value=0
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=2 max=5000 step=1 default=312 value=10000 flags=inactive
         exposure_auto_priority (bool)   : default=0 value=1
                   pan_absolute (int)    : min=-72000 max=72000 step=3600 default=0 value=0
                  tilt_absolute (int)    : min=-54000 max=54000 step=3600 default=0 value=0
                  zoom_absolute (int)    : min=1 max=2 step=1 default=1 value=1

(embora nem todos os comandos aceites pela câmara sejam perceptíveis, só experimentando vários valores para ver a diferença)

AD4M4ST0R – um rover LEGO

This post is part 1 of 9 of  AD4M4ST0R - um rover LEGO

Na continuação das experiências com o ev3dev, dois dispositivos USB um pouco mais complicados:

  • wireless gamepad
  • webcam

Desta vez mostro primeiro o resultado e preocupo-me depois com as explicações 🙂

ad4m4st0r-01

O gamepad é um gamepad genérico para Sony Playstation 3 e PC, penso que a maioria dos gamepads USB serão igualmente reconhecidos. Já com as webcams não será bem assim, será necessário compatibilidade com v4l2 (webcams) ou gPhoto2 (máquinas fotográficas). Neste exemplo estou a usar uma webcam Logitech C170 que funciona com o v4l2 através do uvcvideo. Também utilizei com sucesso uma Canon Digital IXUS 500 com o gPhoto2. E também tentei com 3 webcams antigas sem sucesso (apesar de 2 delas funcionarem no meu Ubuntu… pelos vistos funcionam em modo de compatibilidade v4l1 que não parece existir no ev3dev).

A foto abaixo (não tocada) foi tirada pelo AD4M4ST0R durante o decorrer do video anterior:19-08-14_09h53m54s

[Actualizado a 20 de Agosto]

Para quem estiver interessado, publiquei em artigos distintos os detalhes quanto:

ev3dev – backup e boot time

Nesta primeira semana de experiências com o ev3dev utilizei sempre o mesmo cartão microSD (um Kingston microSD 2GB penso que classe 4). Os tempos de arranque (desde o carregar no botão de power até ao primeiro ‘ping’) foram sempre de 60 segundos, não é muito mau comparado com o tempo de arranque do EV3 com o firmware LEGO por isso não me preocupei em experimentar outros cartões.

Mas hoje lembrei-me de ter visto alguma utilização de swap e já que as operações de update são longas (mais de 10 minutos só para actualizar o catálogo com ‘apt-get update’) podia ser que a coisa melhorasse com um cartão mais rápido.

Trocar de cartão é simples e faz-se em 5 minutos:

  1. colocar o cartão no meu portátil Ubuntu
  2. criar uma imagem do cartão e guardar como backup
  3. tirar o cartão e colocar outro qualquer
  4. repor a imagem anterior neste novo cartão

Como novo cartão utilizei um SanDisk Ultra de 4 GB (microSDHC classe UHS-I).

Os tempos de arranque desceram de 60 segundos para 50 segundos, nada mau. E a actualização do catálogo (‘apt-get update’) passou para ligeiramente menos de 8 minutos. Não é um ganho espantoso mas se estiver a trabalhar com pilhas ou baterias em vez de regulador DC já é qualquer coisa.

Como o novo cartão é de 4 GB em vez de 2 GB posso aproveitar o novo espaço extendendo o file system. Podia tê-lo feito de dentro do próprio ev3dev mas ainda não domino muito a linha de comando por isso fi-lo em 3 fases:

  1. Colocando o cartão no meu Ubuntu e utilizando o Gparted para criar uma nova partição do tipo ‘lvm2 pv’ com os 1906 MB livres no cartão
  2. Ainda no Ubuntu usando o system-config-lvm para adicionar a nova partição ao Volume Group já existente
  3. Voltando ao ev3dev e extendendo o Logical Volume ‘root’:
root@ev3dev:~# lvextend --extents +100%FREE /dev/ev3devVG/root
  Extending logical volume root to 3.59 GiB
  Logical volume root successfully resized

[editado posteriormente]

faltou indicar como expandir o file system:

resize2fs  /dev/ev3devVG/root

[se não der  no EV3 pode-se fazer no Ubuntu, precedendo o comando com ‘sudo’]

 

ev3dev – controlando motores

A utilização básica do LEGO Mindstorms EV3 com ev3dev fica completa com a utilização de motores.

Ligando um motor (45503 EV3 Medium Servo Motor) na ficha A, o sistema operativo acusa o evento:

root@ev3dev:~# dmesg
(...)
tacho-motor tacho-motor0: Tacho motor registered.
ev3-tacho-motor outA:motor: A Tacho Motor connected to port outA gpio 91 irq 192

Uma vez mais é necessário consultar a wiki do projecto ev3dev para entender como aceder aos motores. Estes também são mapeados pelo sistema operativo, desta vez em “/sys/bus/legoev3/devices/” e em ” /sys/class/tacho-motor/”:

root@ev3dev:~# ls /sys/bus/legoev3/devices/
in2  in3  in4  outA  outA:motor  outB  outC  outD
root@ev3dev:~# ls /sys/class/tacho-motor/
tacho-motor0
root@ev3dev:~# ls /sys/class/tacho-motor/tacho-motor0
device           port_name      pulses_per_second     reset        speed_regulation_K  subsystem
duty_cycle     position       pulses_per_second_sp  run            speed_regulation_P  time_sp
duty_cycle_sp  position_mode  ramp_down_sp        run_mode        state            type
estop           position_sp    ramp_up_sp        speed_regulation_D    stop_mode        uevent
polarity_mode  power          regulation_mode        speed_regulation_I    stop_modes

Muito informação para processar mas para começar podemos confirmar apenas o tipo de motor:

root@ev3dev:~# cat /sys/class/tacho-motor/tacho-motor0/type
minitacho

Os motores Mindstorms são inicializados no modo “run forever”, podemos também confirmar isso:

root@ev3dev:~# cat /sys/class/tacho-motor/tacho-motor0/run_mode
forever

Neste modo podemos controlar o motor controlando 2 parâmetros:

  • /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
  • /sys/class/tacho-motor/tacho-motor0/run

o shell script abaixo faz o motor rodar 5 segundos num sentido e 5 segundos no sentido inverso, com um duty factor de 50%:

 #!/bin/bash
 echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
 echo   1 > /sys/class/tacho-motor/tacho-motor0/run
 echo   50 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
 sleep 5s
 echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
 sleep 1s
 echo   -50 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
 sleep 5s
 echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
 echo   0 > /sys/class/tacho-motor/tacho-motor0/run

 

 

ev3dev – lendo sensores

Começar a trabalhar com sensores Mindstorms, mais concretamente Touch Sensores. Tenho duas versões: NXT e EV3.

Adicionei touch sensor (NXT) no porto #2. O sistema operativo detecta o acontecimento:

root@ev3dev:~# dmesg
...
msensor sensor0: Mindstorms sensor registered.
touch-sensor in2:nxt-analog-sensor: Touch sensor connected to port in2

Alguma informação útil na wiki do projecto ev3dev:

De acordo com a wiki, os sensores Mindstorms são mapeados em “/sys/class/msensor”:

root@ev3dev:~# ls /sys/class/msensor
 sensor0

Confirma-se a presença de um sensor “sensor0”. Que podemos saber acerca dele?

root@ev3dev:~# ls /sys/class/msensor/sensor0
 bin_data     dp    num_values  subsystem  units   value2  value5
 bin_data_format  mode    port_name   type_id    value0  value3  value6
 device         modes    power        uevent     value1  value4  value7

Muita informação, alguma apenas genérica. Fiquemos por “mode” e “value0”:

root@ev3dev:~# cat /sys/class/msensor/sensor0/mode
 TOUCH

Sim, é um Touch Sensor. E como saber o estado?

root@ev3dev:~# cat /sys/class/msensor/sensor0/value0
 0

O sensor estava em repouso. Vamos repetir enquanto premimos o sensor:

root@ev3dev:~# cat /sys/class/msensor/sensor0/value0
 1

Então e se trocarmos por um sensor EV3?

root@ev3dev:~# dmesg
 ...
 msensor sensor0: Mindstorms sensor unregistered.
 msensor sensor1: Mindstorms sensor registered.
 touch-sensor in2:ev3-analog-sensor: Touch sensor connected to port in2

E qual é o device?

root@ev3dev:~# ls /sys/class/msensor/
 sensor1

Agora é ‘sensor1’ em vez de ‘sensor0’. Talvez a troca tenha sido demasiado rápida ou talvez o sistema operativo nunca liberte os mapeamentos efectuados [até ao próximo boot].

A informação disponível é semelhante:

root@ev3dev:~# ls /sys/class/msensor/sensor1
 bin_data     dp    num_values  subsystem  units   value2  value5
 bin_data_format  mode    port_name   type_id    value0  value3  value6
 device         modes    power        uevent     value1  value4  value7
root@ev3dev:~# cat /sys/class/msensor/sensor1/mode
 TOUCH

E a leitura do estado do sensor funciona da mesma maneira através de ‘value0’.

Este forma do sistema operativo mapear todos os dispositivos no file system dá-nos uma enorme flexibilidade – todas as linguagens de programação permitem aceder a ficheiros… até a própria shell! Eis shell script que se limita a esperar que o sensor seja premido:

#!/bin/bash
 BOTAO=0
 while [ $BOTAO -ne "1" ]; do
 BOTAO=$(cat /sys/class/msensor/sensor0/value0)
 done
 echo "Ouch!" > /dev/tty0

se o ficheiro contendo este script se chamar ’touch.sh’ e tiver permissões de execução podemos invocá-lo directamente da linha de comando:

root@ev3dev:~# chmod +x touch.sh
root@ev3dev:~# ./touch.sh

ou de dentro de outros scripts.

Nota: usei aqui ‘sensor0’, assumo que há apenas um sensor e excepto na situação acima em que retirei um sensor e adicionei outro que por isso ficou ‘sensor1’, será sempre ‘sensor0’.

EV3MP3 – Um MP3 Player em LEGO

Encontrei quase por total acaso um projecto muito interessante: ev3dev.

Trata-se de uma versão de Debian para o LEGO Mindstorms EV3. Não há qualquer alteração ao firmware do EV3 – o bootloader do EV3 arranca a imagem do ev3dev instalada num cartão micro-SD.

Alguras horas depois de começar (a maior parte delas apenas a fazer actualizações de sistema já que o EV3 é cerca de 4x mais lento que um Raspberry Pi e como a versão do chipset USB é apenas 1.1 por a ligação Wi-Fi nunca fará mais que 11 mbps) descobri que o ev3dev se dá muito bem com muito do hardware que utilizo com o Raspberry Pi, nomeadamente:

  • Hub USB
  • ThePiHut Wi-Fi USB card
  • Audio USB card

Com meia duzia de linhas temos um shell script que nos saúda num português macarrónico e toca uma música em formato MP3 após ser premido um touch sensor:

#!/bin/bash

# play deve usar USB sound card
export AUDIODEV=hw:1,0

# saudar utilizador

tput clear > /dev/tty0
figlet -f small "Carregue" > /dev/tty0
figlet -f small "para" > /dev/tty0
figlet -f small "ouvir" > /dev/tty0
figlet -f small "musica" > /dev/tty0

espeak -v pt-pt "olá  queres ouvir uma música?" --stdout | play -t wav -

BOTAO=0
while [ $BOTAO -ne "1" ]; do
  BOTAO=$(cat /sys/class/msensor/sensor0/value0)
done

tput clear > /dev/tty0
figlet -f small "No ar:" > /dev/tty0
figlet -f mini "Highway To Hell" > /dev/tty0
figlet -f small "(AC/DC)" > /dev/tty0

mpg321 -a hw:1,0 HighwayToHell.mp3 -g 20

tput clear > /dev/tty0

Para quem tmabém possa estar interessado, após os updates de sistema a versão do kernel é a 3.14.7:

root@ev3dev:~# uname -a
Linux ev3dev 3.14.7-2-ev3dev-pre1 #2 PREEMPT Tue Jul 15 22:29:55 CDT 2014 armv5tejl GNU/Linux

e o htop diz que estou a usar 25 dos 57 MB de RAM disponíveis além de 7 dos 63 MB de swap.

O processador é reconhecido como um ARM926EJ-S rev 5 (v5l):

root@ev3dev:~# cat /proc/cpuinfo
processor    : 0
model name    : ARM926EJ-S rev 5 (v5l)
Features    : swp half thumb fastmult edsp java
CPU implementer    : 0x41
CPU architecture: 5TEJ
CPU variant    : 0x0
CPU part    : 0x926
CPU revision    : 5

Hardware    : LEGO MINDSTORMS EV3 Programmable Brick
Revision    : 0000
Serial        : 0000000000000000