Jetson nano csi camera on ROS

There are some ROS packages for using CSI cameras on ROS

https://github.com/sfalexrog/jetson_camera

https://github.com/peter-moran/jetson_csi_cam

I try both but only get good results with the first one. So I forked it on the following addres:

https://github.com/inaciose/jetson_camera

 

jetson_camera

ROS package for csi camera video publisher

roslaunch jetson_camera jetson_camera.launch

There also a simple node to capture and publish the CSI camera image.

https://github.com/inaciose/ackerbot/blob/8d1851bf0a04c5f8a2c333b18d5b47b5c3486391/src/csicam_publisher.cpp

 

Camera calibration

Doing camera calibration with jetson_camera package publishing video from a IMX219 camera.

A simples utilização do ficheiro de calibração no driver não rectifica a imagem. Para efectuar a retificação da imagem usar o pacote image_proc.

Investigar melhor no futuro a optimização da calibração de cameras de olho de peixe.

http://wiki.ros.org/camera_calibration

http://wiki.ros.org/camera_calibration/Tutorials/MonocularCalibration

Commands

(terminal 1)
roslaunch jetson_camera jetson_camera.launch

(terminal 2)
rosrun camera_calibration cameracalibrator.py –size 9×6 –square 0.0265 image:=/main_camera/image_raw camera:=/main_camera

Problems running cameracalibrator.py

ImportError: No module named cv2

Python version error

The solution is changing the shebang to python3

alternative is direct runing

python3 /opt/ros/noetic/lib/camera_calibration/cameracalibrator.py –size 8×6 –square 0.108 image:=/camera/image_raw camera:=/camera

Service set_camera_info error

Waiting for service /main_camera/set_camera_info …
Service not found
QMutex: destroying locked mutex
QMutex: destroying locked mutex
Segmentation fault (core dumped)

O comando rosservice list não exibe nenhum serviço set_camera_info.

Execução com novo parametro

Globalmente, o processo de calibração seguido está descrito na wiki ros MonocularCalibration, no caso da camera csi com o jetson_camera é necessário incluir um parâmetro na chamada ao node cameracalibrator.py.

Conforme vi referido numa página web (noservice) , o node permite usar o parâmetro –no-service-check, que inibe a verificação dos serviços.

rosrun camera_calibration cameracalibrator.py –size 9×6 –square 0.0265 –no-service-check image:=/main_camera/image_raw camera:=/main_camera

Com este comando exibiu a seguinte janela.

Na janela é possível configurar o tipo de camera e a escala.

Como a camera tem um angulo de 160 graus penso que será uma camera olho de peixe.

Entretanto verifiquei no wiki ros camera calibration que a partir do melodic existem uma serie de parâmetros destinados a cameras olho de peixe.

–fisheye-recompute-extrinsicsts
–fisheye-fix-skew for fisheye
–fisheye-fix-principal-point
–fisheye-k-coefficients=NUM_COEFFS
–fisheye-check-conditions

De qualquer modo avancei com as seguintes configurações:

Pinhole = 1/1

Scale = 000/100

O interface tem 3 botões, Calibrate, Save e Commit.

Pelo que percebi o botão Commit só funciona bem se o serviço set_camera_info estiver disponível.

O botão Calibrate fica disponível depois de passarmos a imagem com os quadrados brancos e pretos, e os seus vertices em contacto serem detectados várias vezes.

Quando disponível cliquei no botão Calibrate e um conjunto de dados foram exibidos no terminal. Ver output de calibração.

De seguida cliquei em Save, e o seguinte ficheiro de arquivo ficou disponível

/tmp/calibrationdata.tar.gz

Deste arquivo destaco os seguintes ficheiros

ost.txt
ost.yaml

Estes ficheiros contêm os dados constantes no output de calibração. Em particular o ost.txt, é a segunda parte desse output.

O ficheiro ost.yaml contem os mesmos dados de calibração já no formato yaml que é usado no argumento camera_info_url nos drivers de camera para o ros.

Interrompi o funcionamento do node de captura de imagem e o node de calibração da camera.

Copiei o ficheiro para uma pasta, (~/.ros/camera_calibration)

Alterei o  parametro camera_info_url no ficheiro jetson_camera.launch para usar o esse ficheiro de calibração.

Quando lancei o jetson_camera.launch apareceu o seguinte warning.

[ WARN] [1630073534.594131929]: [camera] does not match name narrow_stereo in file /home/inaciose/catkin_ws/src/jetson_camera/camera_info/csi_cam_160_cal.yaml

A solução é trocar no ficheiro ost.yaml (antes de o copiar) o nome da camera para o nome certo. No caso:

# de
camera_name: narrow_stereo
# para
camera_name: camera

 

Still working on it

Usefull commands

Comandos uteis na calibração de uma camera IMX219 com o pacote jetson_camera.

roslaunch jetson_camera jetson_camera.launch

rosrun camera_calibration cameracalibrator.py –size 9×6 –square 0.0265 image:=/main_camera/image_raw camera:=/main_camera

rosrun camera_calibration cameracalibrator.py –size 9×6 –square 0.0265 –no-service-check image:=/main_camera/image_raw camera:=/main_camera

rosservice list

rosnode list

rostopic list

 

Extra urls

http://ros.org/wiki/camera_info_manager

http://ros.org/wiki/camera_calibration_parsers

http://wiki.ros.org/image_proc

Calibrar cameras olho de peixe
https://sites.google.com/site/scarabotix/ocamcalib-toolbox

Image pipeline alternativo
https://github.com/DavidTorresOcana/image_pipeline

Exemplos de mensagens nos topicos

rostopic echo /main_camera/camera_info

header: 
  seq: 5781
  stamp: 
    secs: 1630064629
    nsecs: 882128788
  frame_id: "main_camera_optical"
height: 480
width: 640
distortion_model: ''
D: []
K: [nan, 0.0, nan, 0.0, nan, nan, 0.0, 0.0, 0.0]
R: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
P: [nan, 0.0, nan, 0.0, 0.0, nan, nan, 0.0, 0.0, 0.0, 0.0, 0.0]
binning_x: 0
binning_y: 0
roi: 
  x_offset: 0
  y_offset: 0
  height: 0
  width: 0
  do_rectify: False

Output de calibração

**** Calibrating ****
mono fisheye calibration...
D = [-0.16411803209719655, 0.5323506420170566, -0.667575196157337, 0.3072524695696274]
K = [217.77569928178522, -3.0709848224418317, 315.6222686786177, 0.0, 285.57107620446374, 240.32213346777095, 0.0, 0.0, 1.0]
R = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
P = [217.77569928178522, -3.0709848224418317, 315.6222686786177, 0.0, 0.0, 285.57107620446374, 240.32213346777095, 0.0, 0.0, 0.0, 1.0, 0.0]
None
# oST version 5.0 parameters


[image]

width
640

height
480

[narrow_stereo]

camera matrix
217.775699 -3.070985 315.622269
0.000000 285.571076 240.322133
0.000000 0.000000 1.000000

distortion
-0.164118 0.532351 -0.667575 0.307252

rectification
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000

projection
217.775699 -3.070985 315.622269 0.000000
0.000000 285.571076 240.322133 0.000000
0.000000 0.000000 1.000000 0.000000

('Wrote calibration data to', '/tmp/calibrationdata.tar.gz')

Sources

noservices
https://answers.ros.org/question/62778/ros_camera_info-service-not-found-for-usb_cam/

 

No caso de uma camera usb (usb_cam), quando se clica no botão Commit, sai-se do programa e o node de captura da camera usb exibe a seguinte linha.

 

[ INFO] [1630076380.945883171]: writing calibration data to /home/inaciose/.ros/camera_info/head_camera.yaml

 

Condução autónoma em pista – ackerbot – primeiros testes

No dia 1 de Agosto de 2021 foram feitos os primeiros testes de condução autónoma do robot com direcção ackermann.

Os resultados foram promissores. Apesar do despiste inicial, a segunda tentativa foi bem sucedida.

A pista onde foram realizados os testes foi feita com cartolina preta. e fita cola amarela fina.

Os primeiros testes foram efectuados sem a linha central.  A faixa de rodagem tem 40 cm. O circuito está compreendido num espaço rectangular de 1.90M por 2.40M,

O resultado do primeiro teste pode ser observado no seguinte video.

O resultado da condução autónoma não foi propriamente bom mas foi promissor. Existiram despistes. Mas também sessões em que o robot conduziu várias voltas sem qualquer problema.

Na fase de recolha. o problema detectado foi a dificuldade de controlar o veiculo com precisão e suavidade.

O método de conversão da mensagem twist na velocidade aplicada ao motor e o angulo aplicado na direcção, extrai a velocidade linear, e computa o angulo com base na relação entre a velocidade angular, a velocidade linear e distancia entre eixos.

Este método faz com que a capacidade de virar o carro diminui com o aumento da velocidade, e que apenas a velocidades baixas se consegue virar de forma notável.

Estes teste foram realizados com o mcu ESP8266 e o usado nele subscrevia e descodificava a mensagem twist.

A mensagem twist era produzida por um joystick virtual na app ROS-Mobile.

 

ackerbot ms2 rms tests

Resumo dos testes

Depois de acertar com as modificações para o mcu passar a subscrever apenas o pub_dir e pub_vel, e com o circuito ainda com a linha ao centro foram efectuadas 9 sessões de condução com recolha de dados (write). Posteriormente foi treinado o modelo rrm01.h5 com uma selecção das sessões.

Os resultados da condução autónoma foram muito desanimadores.

Retirei a fita cola que fazia de linha central e fiz mais uma serie de sessões de condução para  recolha de dados,

Posteriormente com base nos dados de condução recolhidos foi treinados os modelos:

  • rrm02.h5 (set 12)
  • rrm02a.h5 (set 14)
  • rrm02b.h5 (set 11)
  • rrm02c.h5 (set 13)

A inexperiência de condução autonoma inicial com o modelo rrm02.h5, no mesmo sentido que foi efectuado o treino, também foi desanimadora.

No entanto, quando experimentei colocar o robot no sentido contrário os resultados melhoraram muito. O robot deu muitas voltas sem se despistar.

Com o modelo rrm02a.h5, o comportamento em sentido contrario foi bom, e no mesmo sentido foi ligeiramente melhor.

Os melhores resultados no mesmo sentido foram obtidos com o modelo rrm02c.h5, e não foram satisfatórios mas o desempenho no sentido contrário não foi o melhor.

A melhor situação será provavelmente a que foi obtida com o modelo rrm02a.h5.

Condições gerais dos testes

A camera estava inclinada o mais possível para baixo, a apontar para o mais perto do robot.

Velocidade de 0.15 M/s

Factor angular de 2

tamanho das imagens: 320 x 160

Software usado nos testes

https://github.com/inaciose/ackerbot_firmware/tree/a603cdecd182929d5878c34179876f9577fe9d89

https://github.com/inaciose/ackerbot/tree/8c3089886eb8cd15f8af7ec1eee050a3b7f1cb17

 

Automec-ad user commands v02

Os comandos básicos para o real e simulação são idênticos pois baseiam-se em ficheiros launch com os mesmos nomes, sendo assim, o que muda é o nome do pacote em variações com estes nomes de launch files (o numero e as letras finais identificam a cnn a usar) :

  • traxxas_free_drive.launch
  • traxxas_drive_write.launch
  • traxxas_training1.launch
  • traxxas_drive1.launch
  • traxxas_drive_signal1.launch

A estes nomes acrescem em alguns casos um ou dois parametros:

  • folder:=setX
  • model:=modelX.h5

Abaixo seguem exemplos para cada um dos casos (real e simulação).

Mais abaixo ainda existe um resumo dos nodes e dos respectivos parâmetros, e algumas notas.

Físico (real)

condução livre com o carro para testar
roslaunch physical_bringup traxxas_free_drive.launch

sessão para guardar dados de condução para treinar
roslaunch physical_bringup traxxas_drive_write.launch folder:=setX

treinar com os dados de uma sessão guardada
roslaunch physical_bringup traxxas_training1.launch folder:=setX model:=modelX.h5

condução autónoma com um modelo previamente treinado
roslaunch physical_bringup traxxas_drive1.launch model:=modelX.h5

condução autónoma com um modelo previamente treinado e reação aos sinais TODO
roslaunch physical_bringup traxxas_drive_signal1.launch model:=modelX.h5

 

Simulação

condução livre com o carro para testar
roslaunch simulation_bringup traxxas_free_drive.launch

sessão para guardar dados de condução para treinar
roslaunch simulation_bringup traxxas_drive_write.launch folder:=setX

treinar com os dados de uma sessão guardada
roslaunch simulation_bringup traxxas_training1.launch folder:=setX model:=modelX.h5

roslaunch simulation_bringup traxxas_training2a.launch folder:=setX model:=modelX.h5

roslaunch simulation_bringup traxxas_training2av.launch folder:=setX model:=modelX.h5

condução autónoma com um modelo previamente treinado
roslaunch simulation_bringup traxxas_drive1.launch model:=modelX.h5

roslaunch simulation_bringup traxxas_drive2a.launch model:=modelX.h5

roslaunch simulation_bringup traxxas_drive2av.launch model:=modelX.h5

condução autónoma com um modelo previamente treinado e reação aos sinais TODO
roslaunch simulation_bringup traxxas_drive_signal1.launch model:=modelX.h5

roslaunch simulation_bringup traxxas_drive_signal2a.launch model:=modelX.h5

roslaunch simulation_bringup traxxas_drive_signal2av.launch model:=modelX.h5

Notas sobre os ficheiros launch/modules que estão nos pacotes bringup
Tem launch files que incluem os launch files dos nodes nos respectivos pacotes. Estes launch files são incluídos pelos launch files finais.
O propósito é facilitar as alterações quando um node ou um pacote muda de nome. Neste caso basta alterar os nomes dos pacotes nestes launch files.
Além disso também contem os parâmetros básicos pré-definidos para o contexto, evitando que todos os launch files finais tenham essas definições.

 

cnn

Data, contem as pastas das diversas sessões de condução para recolha de dados para treino, uma pasta por cada conjunto de imagens, e respectivo ficheiro csv

Models, contem os modelos resultantes dos treinos efectuados com os dados recolhidos

Launch, contem os ficheiros de lançamento para cada um dos nodes (devem ser incluidos nos ficheiros de pacotes bringup

  • write.launch
    • driving1.launch
    • training1.launch

Scripts

Contem os scripts de uso global  e uma pasta por cada conjunto de scripts associados a um modelo de machine learning especifico

  • write.py
  • cnn1/
  • cnn2

write.py

‘~image_raw_topic‘, ‘/ackermann_vehicle/camera/rgb/image_raw’
‘~twist_cmd_topic‘, ‘/cmd_vel’
‘~folder‘, ‘set1’
‘~rate‘, 30
‘~width‘, 320
‘~height‘, 160

cnn1/training1.py

‘~base_folder‘, ‘set1’)- nos argumentos dos launchs está abreviado para folder
‘~modelname‘, ‘model1.h5’) – nos argumentos dos launchs está abreviado para model
‘~epochs‘, 20)
‘~steps_per_epoch‘, 100)
‘~batch_xtrain‘, 20)
‘~batch_ytrain‘, 1)
‘~batch_xval‘, 25)
‘~batch_yval‘, 0)
‘~validation_steps‘, 50)

cnn1/driving1.py

‘~image_raw_topic‘, ‘/ackermann_vehicle/camera/rgb/image_raw’
‘~twist_cmd_topic‘, ‘/cmd_vel’
‘~vel_cmd_topic‘, ” – se não for vazio publica  o booleano no tópico
‘~twist_linear_x‘, 1
‘~float_cmd_topic‘, ” – se não for vazio subscreve o tópico para obter a velocidade a aplicar (modo real), se não aplica a velocidade explicita em twist_linear_x (modo simulação)
‘~modelname‘, ‘model1.h5’ – nos argumentos dos launchs está abreviado para model

physical_bringup

Launch files para o carro real. Acho que me falta lançar o rosserial.
Ver acima os comandos.

robot_driving

  • physical_camera.launch – Lança o node camera2topic.py
  • physical_robot_control.launch – Lança o node androidConversor.py
  • physical_robot_driving.launch – Lança por inclusão o conversor e a camera

camera2topic.py

‘~camera_topic‘,’/real_camera’
‘~int_camera_id‘, 2

androidConversor.py

‘~twist_dir_topic‘, ‘/android_input_dir’
‘~bool_vel_topic‘, ‘/android_input_vel’
‘~int_dir_topic‘, ‘/pub_dir’
‘~int_vel_topic‘, ‘/pub_vel’
‘~int_vel_max‘, 108

signal_recognition

TODO: fazer apenas um launch, e colocar as diferenças para os pacotes bringup

  • physical_signal_recognition.launch
  • signal_panel_recognition.launch

signal_panel_recognition.py

‘~image_raw_topic‘, ‘/ackermann_vehicle/camera2/rgb/image_raw’
‘~float_cmd_topic‘, ‘/flt_cmd’
‘~twist_linear_x‘, 0.5

simulation_bringup

Launch files para a simulação.
Ver acima os comandos.

simulation_environment

Contem a definição dos mundos (pistas)

  • arena.launch (world:=nome)
  • signal_panel.launch

gazebo_signalling_panel_controller

  • “topic_monitor1”, topic_monitor1, “/monitor1/image1”
  • “topic_monitor2”, topic_monitor2, “/monitor2/image2”
  • “source_package”, source_package, “simspace”
  • “source_folder”, source_folder, “semaphores_pics”
  • “name_pic0”, name_pic0, “left”
  • “name_pic1”, name_pic1, “right”
  • “name_pic2”, name_pic2, “up”
  • “name_pic3”, name_pic3, “stop”
  • “name_pic4”, name_pic4, “parking”
  • “name_pic5”, name_pic5, “chess”
  • “default_pic”, default_pic, 3

traxxas_description

Contem os modelos do carro, configurações e controller para o gazebo.
Tem mais dois nodes para implementação da conversão das mensagens twist para ackermann ou para o modelo usado pelo carro real. (eventualmente passar estes nodes para o pacote robot_driving)