[Release] Gogo🦍🦍 Keyboard | The keyboard input package I always wanted for ROS 2

Gogo​:gorilla::gorilla: Keyboard

I made gogo_keyboard to simply get keyboard events in ROS 2 (… and asyncio, and Zenoh). gogo_keyboard creates a new independent SDL2 window (with a :gorilla:) that captures the key presses and releases. Then sends the key information onto a ROS topic.

Try it out

pip install gogo_keyboard[dll]
python3 -m gogo_keyboard.ros_node

ROS 2 default String message

$ ros2 topic echo /key_press
data: '{"symbol": "Y", "code": 28, "modifiers": 0, "is_pressed": true}'
---
data: '{"symbol": "Y", "code": 28, "modifiers": 0, "is_pressed": false}'
---

Interactive Gorilla window

Why?

I needed something very safe and simple. Very importantly those feature are critical for me:

  • Capture not only key presses, but also key releases.
  • Have an independent window that the user clicks when he wants to send keys.
  • No sudo required.
  • Key press + release on the same reliable topic.

And those were important but not required features:

  • Give feedback to user when pressing a key (the gorilla moves, and color changes).
  • Easy to customize using python+asyncio.
  • Have an easy to use message type with good amount of data. Message is a json String, yes not a fancy custom message but:
    • This way it uses a default message type, so all nodes, systems and humans can parse it.
    • If you want another message type, just tweak the – very simple – gogo_keyboard.ros_node.
    • I think most user don’t need a lightweight and optimized message serialization for key presses.

I would gladly improve the message type to something better than json String if you know a suitable type.

Compared to other packages

  • ros2-keyboard I was using this in the past, it’s very good. So I took heavy inspiration from it. Problems are:
    • There is one topic for presses and one for releases. So message ordering can get messed-up.
    • Uses custom message type that are very lightweight, but harder to use (type is just two uint16).
    • sudo to install sdl.
  • ros2-keyboard-driver :
    • No key releases event.
    • Uses the terminal to capture input, not an independent window.
  • teleop_twist_keyboard Many libraries are like this: It does not send keys, only sends Twists messages.
4 Likes

Oh, but the same can happen even on a single topic (and it actually does quite often when using DDS (not sure if also with these small messages)).

I strongly suggest adding a timestamp to the messages so that the receivers can cache and order the messages accordingly (bringing in a small latency).

Otherwise, creating a custom and suitable message type should not be felt as a disadvantage in the ROS world. I understand your strive to get it as simple as possible, but if it were for me, I’d go with a custom message.

In some cases, RViz can also be used as a source of keyboard events. Not sure if it matches your use-case, but if it does, you can have a look into it.

1 Like

Thanks, I will add this, I’ve been thinking about TCP too much lately. This is critical even though I never used it. I will also add a counter, so the user can know when something went wrong.

Okay ROS messages are now like this:

$ ros2 topic echo /key_press
---
data: '{"header": {"time_ns": 1770108818789964619, "count": 7}, "symbol": "Y", "code": 28, "modifiers": 0, "is_pressed": true}'
---
data: '{"header": {"time_ns": 1770108818856609766, "count": 8}, "symbol": "Y", "code": 28, "modifiers": 0, "is_pressed": false}'
---

Wouldn’t reliable QoS with reasonably deep history do a similar job?

That’s what I am using actually (or at least what I think I am doing), correct me if I am wrong:

# gogo_keyboard/ros_node.py

pub = node.create_publisher(
        String,
        topic,
        QoSProfile(  # no message lost (hopefuly)
            reliability=ReliabilityPolicy.RELIABLE,
            history=HistoryPolicy.KEEP_ALL,
            durability=DurabilityPolicy.VOLATILE,
        ),
    )

My main worry is about the ordering (losing messages is also bad, but DDS should not), I want to avoid key pressed and key released being out of order. This can have terrible consequences in a setup where the robot moves while the key pressed, then stops once the key is released.

That should be done using a Time Sequencer . If you’re looking at sequence numbers, you’re basically reimplementing what the time sequencer does (or not? maybe you want a more sophisticated logic). But I see that TimeSequencer introduces a constant delay, whereas sequence numbers allow you to process the event immediately if you know that the incoming message has the expected sequence number… So maybe I convinced myself that sequence numbers are a good thing here :slight_smile:

Wow I didn’t know about this little guy. Cool. In the end I will let the user do whatever he needs to not mess this up on the receiving side. He should have all the necessary data to make a decision using the timestamp and the sequence number. (And I think most user will never think of this for their keyboard).

Later I might make a ros package with this lib and a custom message so the Time Sequencer and such tools work. But right now, I don’t need it and I have other things to work on.

1 Like