[PYTHON] Contrôlez le panneau LED Matrix depuis ROS

introduction

J'ai acheté une Matrix LED (64x64) chez Shigezone à Akihabara, mais j'ai décidé de l'exécuter sur ROS. Dans cet article, nous allons créer un "nœud ROS qui s'abonne à la rubrique Image et affiche l'image sur la LED Matrix".

Préparation environnementale

Préparer le Raspberry Pi

Cette fois, nous utiliserons Raspberry Pi 3 avec Ubuntu installé pour gérer ROS. Flashez l'image de ici sur la carte SD et installez ROS. J'ai installé Ubuntu 18.04.3. De arm64. ROS a installé ros-melodic-desktop en se référant à ce site.

Préparation de la carte de connexion du panneau LED Matrix

Afin d'utiliser ce logiciel comme pilote pour MatrixLED, nous avons préparé une carte pilote correspondante. La carte a été [obtenue] de electrodragon (https://www.electrodragon.com/product/rgb-matrix-panel-drive-board-raspberry-pi/), mais vous pouvez la préparer vous-même. La carte que vous avez achetée dispose d'un convertisseur de niveau intégré de 3,3 V à 5 V, vous pouvez donc vous attendre à un fonctionnement stable. Puisque la LED Matrix utilisée est de 64x64 et a des lignes d'adresse A à E, il est nécessaire de souder le côté de la carte de commande en un seul endroit pour connecter «E». Pour le panneau LED Matrix que j'ai acheté cette fois, j'ai défini un cavalier pour connecter la broche 8 à E. Il est nécessaire d'alimenter 5V (je pense que c'est au moins 3A) séparément au corps du panneau LED.

Préparation du pilote de panneau LED

Pour voir si le matériel fonctionne correctement, clonez à partir du référentiel, puis compilez l'exemple de programme.

rpi-rgb-led-matrix$ make -C examples-api-use
rpi-rgb-led-matrix$ sudo examples-api-use/demo -D0 --led-rows=64 --led-cols=64 --led-panel-type=FM6126A

Les cubes colorés devraient tourner. En fonction du type de panneau LED Matrix, le FM6126A peut être monté sous forme de puce, auquel cas il est nécessaire de spécifier une option car le signal requis pour l'initialisation est différent.

Si cela fonctionne, installez la bibliothèque pour Python selon les Procédures du même référentiel. Je vais.

$ sudo apt-get update && sudo apt-get install python2.7-dev python-pillow -y
rpi-rgb-led-matrix/bindings/python$ make build-python
rpi-rgb-led-matrix/bindings/python$ sudo make install-python

À ce stade, vous serez en mesure de faire "à partir de rgbmatrix importer RGBMatrix, RGBMatrixOptions".

Pour utiliser ROS avec sudo

Le logiciel du pilote Matrix LED utilisé cette fois doit être exécuté par sudo. Avec la combinaison Ubuntu18.04 + Raspberry pi3, je ne pouvais pas accéder aux ressources matérielles (GPIO, etc.) requises par les utilisateurs généraux, j'ai donc décidé de lancer le côté ROS avec sudo. (Je pense qu'il devrait être configuré pour que les utilisateurs généraux puissent accéder aux ressources matérielles nécessaires.)

Généralement, ROS ne fonctionne pas bien car les variables d'environnement sont effacées lorsque sudo est exécuté. J'ai donc décidé d'ajouter les paramètres au fichier sudoers.

Defaults        env_keep += "PATH PKG_CONFIG_PATH PYTHONPATH ROS_ROOT ROS_ETC_DIR ROS_MASTER_URI ROS_VERSION ROS_PYTHON_VERSION ROS_PACKAGE_PATH ROS_DISTRO CMAKE_PREFIX_PATH LD_LIBRARY_PATH"

En ajoutant cette ligne avec la commande "sudo visudo", les variables d'environnement requises pour ROS sont héritées lorsque sudo est exécuté. Cependant, LD_LIBRARY_PATH n'est pas hérité par les spécifications.

Par conséquent, j'ai décidé de lier les chemins nécessaires dans "/etc/ld.so.conf.d/ros.conf". Dans le cas du mérodique ROS, cela ressemble à ceci.

/home/user/catkin_ws/devel/lib
/opt/ros/melodic/lib

Après avoir terminé les réglages, exécutez "sudo ld config".

En outre, définissez des autorisations pour que les utilisateurs du démon puissent accéder à /home/user/.ros/log. Sans cela, il s'arrêtera avec une erreur d'autorisation lors de l'exécution d'un nœud ROS. (Lors de l'exécution d'un nœud ROS avec sudo, le fichier journal semble être généré en tant qu'utilisateur du démon.) À ce stade, je pense que vous pouvez accéder aux ressources ROS même avec sudo.

Implémentation du nœud ROS

Commencez par créer un package ROS.

~/catkin_ws/src$ catkin_create_pkg matrix_led_ros roscpp rospy std_msgs
~/catkin_ws$ catkin_make

À partir de là, nous allons implémenter le script sous le package matrix_led_ros créé.

Créer un nœud pour publier des sujets d'image à partir de GIF

Afin de tester le programme à créer ensuite, j'ai implémenté un nœud qui lit GIF et le publie en tant que sujet Image.

#!/usr/bin/env python
from __future__ import print_function

import roslib
import sys
import rospy
import cv2
from std_msgs.msg import String
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError
import argparse

FILE_NAME = "test.gif"
REFRESH_RATE = 1
parser = argparse.ArgumentParser(
            prog='gif-publisher.py',
            usage='python gif-publisher --filename inputfilename',
            description='publish Image message from gif file',
            epilog='end',
            add_help=True,
            )
parser.add_argument('-f', '--filename', help='input file name',
                    required=True)
parser.add_argument('-r', '--rate', help='refresh rate',type=int)
args = parser.parse_args()

if args.filename:
  FILE_NAME = args.filename
if args.rate:
  REFRESH_RATE = args.rate
  
class image_publisher:
  def __init__(self):
    self.image_pub = rospy.Publisher("/imagetopic",Image)
    self.bridge = CvBridge()

  def readGif(self, filename, hz=1):
    gif = cv2.VideoCapture(filename)
    r = rospy.Rate(hz)
    while not rospy.is_shutdown():
        try:
            stat, frame = gif.read()
            if not stat:
                gif = cv2.VideoCapture(filename)
            else:
              try:
                self.image_pub.publish(self.bridge.cv2_to_imgmsg(frame, "bgr8"))
                r.sleep()
              except CvBridgeError as e:
                  print(e)
        except KeyboardInterrupt:
            break

def main(args):
  ip = image_publisher()
  rospy.init_node('gif_image_publisher', anonymous=True)
  ip.readGif(FILE_NAME, REFRESH_RATE)
  try:
    rospy.spin()
  except KeyboardInterrupt:
    print("Shutting down")

if __name__ == '__main__':
    main(sys.argv)

Utilisez-le comme "python gif-publisher.py -f test.gif -r 10". Cet exemple publie test.gif à 10 ips avec le nom / imagetopic. Si rien n'est spécifié, l'image sera publiée à une vitesse explosive, donc je règle la fréquence d'images avec "r = rospy.Rate (hz)" et "r.sleep ()". Dans le cas du Raspberry pi, cela fonctionnait jusqu'à moins de 30 images par seconde.

Créez un nœud ROS qui reçoit le sujet Image

C'est finalement le sujet principal. Un script qui s'abonne à / imagetopic et l'affiche sur le panneau LED Matrix.

#!/usr/bin/env python
from __future__ import print_function

import roslib
import sys, time
import rospy
import cv2
from std_msgs.msg import String
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError
from rgbmatrix import RGBMatrix, RGBMatrixOptions
from PIL import Image as pilI

BRIGHTNESS = 0.5

class image_viewer:
  def __init__(self):
    self.bridge = CvBridge()
    self.image_sub = rospy.Subscriber("imagetopic",Image,self.callback)

    # Configuration for the matrix
    self.options = RGBMatrixOptions()
    self.options.rows = 64
    self.options.cols = 64
    self.options.chain_length = 1
    self.options.parallel = 1
    self.options.hardware_mapping = 'regular'
    self.matrix = RGBMatrix(options = self.options)
    self.max_brightness = self.matrix.brightness
    self.matrix.brightness = self.max_brightness * BRIGHTNESS
    self.double_buffer = self.matrix.CreateFrameCanvas()

  def toSquare(self, img):
    w, h =img.size
    if w == h:
        return img
    elif w > h:
        result = pilI.new(img.mode, (w, w), (0,0,0))
        result.paste(img, (0, (w - h) // 2))
        return result
    else:
        result = pilI.new(img.mode, (h, h), (0,0,0))
        result.paste(img, ((h - w) // 2, 0))
        return result
      
  def callback(self,data):
    try:
      cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")
    except CvBridgeError as e:
      print(e)
    pilImage = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
    pilImage = pilI.fromarray(pilImage)
    pilImage.thumbnail((self.matrix.width, self.matrix.height), pilI.ANTIALIAS)
    offset = (int)(self.matrix.height - pilImage.height)/2
    
    self.double_buffer.SetImage(self.toSquare(pilImage), 0)
    self.double_buffer = self.matrix.SwapOnVSync(self.double_buffer)
    
    
def main(args):
  iv = image_viewer()
  rospy.init_node('image_viewer')
  try:
    rospy.spin()
  except KeyboardInterrupt:
    print("Shutting down")

if __name__ == '__main__':
    main(sys.argv)

Démarrez-le comme "sudo python image-viewer-ros.py". Ce script ne fonctionnera pas correctement à moins que vous n'ayez les privilèges root, mais vous pouvez l'exécuter avec ROS dans sudo par le contenu défini dans "Pour utiliser ROS avec sudo" plus tôt. Ça ressemble à ça quand ça bouge. J'ai emprunté cette animation GIF. P_20191109_160055 (2) .gif

Dans la partie "self.matrix = RGBMatrix (options = self.options)", vous pouvez spécifier les options à passer à la LED de la matrice. Pour les images plus grandes que 64x64, les côtés longs ont été réduits à 64 pixels et les marges ont été remplies de noir.

À la fin

Qu'as-tu pensé. Dans cette implémentation, il y a une partie où le scintillement est un peu inquiétant, il semble donc y avoir place à l'amélioration. Si j'ai une chance, je listerai également celui implémenté en C ++. De plus, la carte pilote utilisée cette fois-ci peut utiliser 3 canaux en même temps, et en tant que LED Matrix, elle peut être étendue en connectant des perles, donc si vous pouvez obtenir plusieurs panneaux LED, j'aimerais essayer une résolution plus élevée.

Recommended Posts

Contrôlez le panneau LED Matrix depuis ROS
Réduction d'image (panneau matriciel LED 16 x 32)
Panneau de contrôle Pymel
Contrôlez les ampoules LED depuis un micro-ordinateur (Intel Edison) (1)
Contrôle des ampoules LED à partir d'un micro-ordinateur (Intel Edison) (2)
Contrôler à distance le climatiseur de l'extérieur (par MQTT)
Génération de fichiers PPM du panneau matriciel LED (compatible RVB japonais)
Appelez votre propre module python à partir du package ROS
Contrôler les trackpads Linux
Contrôle à domicile de Cortana.
À propos de la matrice de confusion
Contrôlez la lumière intelligente «Yeelight» de Python sans utiliser le cloud
Écouter de la musique à partir d'un haut-parleur USB à l'aide du package ROS (gx_sound_player)