Writing a Basic Teleop - VTAstrobotics/Documentation GitHub Wiki


Contents

Prerequisites

To understand the content in this page, you should know

and (of course), you should have completed Software Setup.

Big picture

To drive a robot, we need some way of telling it what we want it to do (go forward, turn, etc.). So the driver will hold a controller to direct the robot, whose commands are transmitted by a router from the device the controller is connected to --> the device controlling the robot. In practice, this may look like this.

I'll break down what we need to do in the context of the driver side vs. the robot side. The driver side handles the input from the driver and sends it to the robot, and the robot handles the rest.

Driver side

Connect the controller

If you have set up your computer using a VMWare Ubuntu virtual machine (as recommended in Software Setup), begin by launching this VM. After it has finished launching, plug in the controller. You should see a prompt to allow you to connect it to your virtual machine.

Run the builtin joy publisher node

In a terminal with ros2 sourced, run ros2 run joy joy_node.

Note: soon, I hope this will mean you simply must be in the developer container. For now, you need ros2 installed and sourced on your VM.

After a few seconds, you should see a message in that terminal indicating the joy node has identified your controller.

Next, open another terminal and run ros2 topic echo joy. This will show us what the joy topic is currently transmitting. Move the joysticks and press the buttons on the controller to verify everything is working properly.

The driver side is now set up.

Robot side

It is expected this will take longer than the driver side setup. You may kill your nodes in the meantime (ctrl + c) and relaunch them as needed.

Setup

Begin by replicating the simple subscriber given in the ROS2 tutorials. Then, confirm you are receiving the controller's messages (of type Joy).

Note: thus far, you should not use the robot or its OBC. Just try to pass the messages back and forth between two terminals.

Talk to the microcontroller

Now we're going to modify your subscriber node to take the messages being sent and tell the robot's microcontroller about them. If your setup differs from mine (in that the motors/motor controllers are directly connected to your OBC), you may skip this step.

First, ensure you have some copy of Controller.py. This is an enum that will interpret the Joy-typed message for us.

Next, in your callback function check every input we care about. For tank drive, this would be LS_Y and RS_Y. For arcade drive, this would be LS_Y and RS_X.

Then send your microcontroller the value and indicate which motor it belongs to. I've chosen to number my motors [1-inf) followed by its value, separated by a colon, followed by a terminating semicolon. This looks like X:Y; where X is my motor id and Y is its value. An example might be 1:82.35;, where 1 means my left drive motor and 82.35 is the desired value.

A simple example:

# teleop is my package name
from teleop.Controller import Controller as ctrl
from teleop.UART import UART
...
def listener_callback(self, msg):
  # get both joystick y-axis values
  lY = msg.axes[ctrl.LS_Y.value]
  rY = msg.axes[ctrl.RS_Y.value]

  # adjust values for my motor controller's range
  lY = (-1 * lY * 90) + 90
  rY = (rY * 90) + 90

  # round to 2 decimal places
  lY = round(lY, 2)
  rY = round(rY, 2)

  # send each value to the motor controller over uart
  self.uart.send(("1:" + str(lY) + ";").encode())
  resp = self.uart.getAllFeedback()
  self.uart.send(("2:" + str(rY) + ";").encode())
  resp = self.uart.getAllFeedback()
...

Configure the microcontroller

Now we have the controller's message going to the robot's main computer (i.e. Jetson Nano), but the Jetson does not (atm) directly interact with motor controllers. So, in my example, I send messages to the microcontroller encoding which motor I want and the value to give to that motor. So now we have to tell the microcontroller how to interpret these messages and tell the motor controllers what we want.

In my case, I used an Arduino RP2040 Connect board, so I just went to the Arduino IDE and wrote some basic code. Here's the important bit:

...
void loop() {
  if (Serial1.available() > 0) {
    String message = Serial1.readStringUntil(';');

    const char* cid = message.c_str();
    char* separator = strchr(cid, ':');
    *separator++ = '\0';  // separate c strings, then move to the next

    const char* cval = separator;
    separator = strchr(cval, ';');
    *separator = '\0';

    const int id = atoi(cid);
    const float val = atof(cval);

    switch (id) {
      case LD:
        moveServo(leftDrive, val);
        break;
      case RD:
        moveServo(rightDrive, val);
        break;
...

For more help on Jetson <--> Arduino communication over UART, see Communication with UART.

Run it on test bot

Run the joy node on your VM. Get test bot and power its OBC. Either connect a monitor, keyboard, and mouse to its HDMI and USB ports or ssh over our network. Then, run your ROS2 subscriber node on the OBC. Ensure that both computers are connected to our Wi-Fi network (not eduroam).

Congratulations!

Networking troubleshooting

If you are unable to see the joy topic being published on the robot using ros2 topic list, before we begin may I first extend to you a very warm welcome to hell.

Start by confirming the firewall is down on both machines. You may try sudo ufw disable. Kill all processes and containers. Now, try to publish the message across the network again.

If still no joy topic shows up, consult the Router Troubleshooting page to view the router settings, and confirm that the router's date and time are synced by navigating to Advanced -> Security -> Schedule.

If not, you must connect the router to the Internet for a few seconds to let it automatically sync the date and time. It appears to reset to December 1969 after a power cycle. Plug an ethernet cable into the router's input port.