Setting up the BeagleY-AI for Robotics

Introduction

In this article, we’ll recount the steps taken for getting the BeagleY-AI (2820-102991834-ND) to control an industrial robot arm (igus ReBeL 6DOF; 4903-REBEL-6DOF-02-ND). Concepts and softwares used, such as ROS2 and iRC, will be explained briefly and problems encountered will be documented. This article will consist of three parts: project planning, using ROS2 and using CRI-Python-Lib. For those who want to follow along and accomplish something similar, instructions will be given throughout.

Part I: Picking the Right Pieces

The first immediate goal of the project was to attain programmatic control of the robot arm via code, ensuring that the robot could execute movements autonomously. For that, choices of electronics and software needed to be made first. We made use of the BeagleY-AI, initially chosen for its supposed AI inference capability. As the control center of the system, we expected the Beagle to interface with the robot arm’s embedded computer (which we’ll refer to as “the robot arm” for simplicity), an external camera module (Arducam; 4679-B0292-ND) and perform AI inference. As far as software to control the robot, we had three options: iRC (igus Robot Control), ROS2 and CRI-Python-Lib. iRC is the official software developed by igus for its robots. This is where you can create programs in their custom scripting language and run paths.

ROS2 is an open-source robotics framework developed by academics and used widely in both academia and industry.

CRI-Python-Lib is a Python library created for communicating to the robot arm’s computer via an application-layer protocol known as CRI, which runs on TCP. Since iRC requires a Windows PC, and we wanted to use a Beagle, we couldn’t use it in our project. ROS2 and its advertised simplicity through abstraction seemed to be a good fit. We just needed to be able to tell the robot, in the simplest way possible, to move the arm to a certain position. CRI-Python-Lib would not be needed if ROS2 functioned correctly. So with that, we had our tech stack.

Part II: Building with ROS

ROS2 is an open-source framework for building complex software for robotics. If your robot has ROS2 support, you can program complex behaviors on it without touching concepts like multithreading and servo stall current. We will be referencing the instructions in the ROS2 library by TruPhysics

Now we take our idea and move on to implementation, where assumptions are tested and decisions are reevaluated. To get ROS2 running on the Beagle, there are a few crucial steps:

  1. Pull and Build the ROS2 Docker image
  2. Run a container from this build
  3. Test out the ROS2 example scripts

These steps will all be executed from the Beagle in the Terminal.

First, we’ll clone the TruPhysics git repository. Make sure that the board is connected to the Internet.

git clone https://bitbucket.org/truphysics/igus_rebel_ros2.git

Navigate into this repository and build the image. This will take some time.

sudo docker compose build

This command will pull the base image and apply layers on top, forming our ideal Docker container.

Next, we’ll need to run the container. Docker Compose will create and start it. We can also run docker execute to enter the container.

sudo docker compose up -d
sudo docker exec -it ros2_jazzy_rebel_dev bash

Before we try out the example code, we need to connect the Beagle to the robot arm. The robot requires connection over Ethernet, so get an Ethernet cable and connect the Beagle to the robot. We also need to make sure that the Beagle has a valid IP address over Ethernet (read pg. 2 of CPR_RobotInterfaceCRI.pdf). Since the robot arm looks for an IP address in the same subnet, 192.168.3, we can set the last value of the address to anything but 11 (the robot arm already took 192.168.3.11).

You can view your current IP addresses with ip a.

If you’ve turned the robot on and physically connected it to the Beagle, you should see an IPv4 address listed next to eth0. We’ll want to delete that and put in our own custom address (I used 192.168.3.100, but it can be anything other than 192.168.3.11).

sudo ip addr del 192.168.X.X/X dev eth0
sudo ip addr add 192.168.3.100/X dev eth0`

You should also disable Wi-Fi just in case any weird network conflicts happen.

Now we’ll try running the launch script.

ros2 launch igus_rebel rebel.launch.py

Here, we try to launch rebel.launch.py in the igus_rebel package (for reference, there are 4 packages defined in the igus_rebel_ros2/src directory: igus_rebel, igus_rebel_description, igus_rebel_moveit_config and igus_rebel_msgs). You may get an error. The first time we ran it, the console was flooded with an error message regarding the parsing of the robot status message. While the robot is enabled, it periodically sends the states of its Digital Inputs, Digital Outputs, E-Stop and more, all of which make up the status message. The error message specified that the stoi() function to convert the strings containing these status values into integer values failed. After looking through the codebase, I determined that the file of interest is igus_rebel_ros2/src/igus_rebel/src/CriMessages.cpp. It defines how messages are parsed. It contains a try-catch block that attempts to do the stoi() conversions and sends an error message on an exception. Printing these strings out individually revealed the issue: the Digital Output string contained hexadecimal characters that the stoi() call was unable to understand.

A quick look at cppreference.com helped to find out that the string’s base can be configured to hexadecimal, so I changed the stoi() call in line 303.

dout = std::stoi(doutString);
dout = std::stoi(doutString, nullptr, 16);

It is worth noting that every time a change is made to the source code, the project must be rebuilt. The TruPhysics ROS2 code uses a tool called colcon, which was installed when the Docker image was built. To use colcon, we’ll navigate to the top-level src folder, igus_rebel_ros2/src and run the following command:

colcon build --symlink-install --cmake-clean-cache --packages-select igus_rebel --cmake-args -DCMAKE_BUILD_TYPE=Release

This line uses colcon to build the project. It first remove the CMake cache file, then processes the specified package in Release mode (Debug mode adds 10 more things to process).

Now we can finally try to run the code properly. To run the robot from the Beagle via ROS2, three scripts must be run in parallel (I like to open up new terminal windows, spin up new Docker containers in each of them and then run the scripts from that environment).

Run the robot launch script in one instance.

ros2 launch igus_rebel rebel.launch.py

Run the MoveIt Motion Planner script in another.

ros2 launch igus_rebel_moveit_config igus_rebel_motion_planner.launch.py

Finally, run the Teleoperation script in a third instance.

ros2 run igus_rebel_moveit_config rebel_servo_teleop_keyboard

From here, the robot arm can be operated in twist (‘t’) mode or joint (‘j’) mode. Type “j” into the console in the Teleoperation script window to move via joints. Have your E-Stop button close to you in case the arm tries to push through the table. You can control the joints A1-A6 by pressing or holding down the number keys 1-6. If there is a need to reverse the direction of joint movement, press the “r” key and move the joint of interest.

Again, be careful when doing this, as it is incredibly easy to let the joints go past their limits. Your space should be resilient to unpredictable robot movement. One telltale sign of a joint nearing its limit is a sudden acceleration towards the point until it stops, which resembles an asymptote and discontinuity. We found that the fix to getting a joint back to its normal range of position is by plugging the robot arm into a Windows PC with iRC installed, running the software, connecting to the robot and continuously enabling and jogging the desired joint back into range. iRC is also very useful in debugging. Whenever the robot mysteriously stopped functioning properly, iRC would display the exact errors. But when iRC failed, we used a software called ModuleCtrl that offers unsafe joint manipulation, perfect for bypassing software motor limits and putting joints back on track. This software allowed us to pick a joint by CAN ID and move it to a set point. Similarly to working with iRC, we had to go back-and-forth with enabling and moving.

One problem that became evident in teleoperation testing was performance. Movement would become jittery after moving the joints back and forth a couple of times. Spinning up btop revealed that running all three scripts in parallel + X display server was using ~95% CPU. Looking deeper, the processes using the most resources were the ros_control_node (center of the whole ROS operation) at ~29% CPU and the MoveIt script at ~19%.

We needed to run not only robot control via ROS2, but also camera control and AI inference. After reverse-engineering the call to the service for setting digital outputs, it became clear that the worst problem was with setting the digital outputs. Since the digital outputs would need to be set frequently (for opening/closing the gripper), we couldn’t have ROS2 take ~5 seconds per action, we needed quicker feedback. ROS2 promised abstraction and ease-of-use, but we only saw complexity and a large learning curve. With a priority for timely completion of the project, the decision was made to move away from ROS2 rather than to peel back all the layers of abstraction and optimize.

Part III: Switching to CRI-Python-Lib

And so, we’ve come back to an old friend: CRI-Python-Lib. We know that this library is much more barebones than ROS2. It wraps the messaging and robot control in a layer of Python. This is helpful for the developer, as they can reference the CRI Documentation, send CRI messages manually and maintain absolute control over the robot arm. The setup for using this library was straightforward, as I’ll show below. The lack of need for Docker containers makes the process much simpler. The only kind of software isolation we’ll need will be achieved through Python virtual environments.

The first step is to get the library on the Beagle.

git clone https://github.com/CommonplaceRobotics/CRI-Python-Lib.git

Navigate into this cloned repository and create a Python virtual environment. This is done to prevent dependency conflicts and maintain a clean environment.

python -m venv venv

A new folder called venv will be created. Activate this virtual environment (which you’ll need to do every time you want to run code from this library).

source venv/bin/activate

Since we are starting this for the first time, we need to populate the environment with the right dependencies.

pip install .

This will look at the requirements.txt file and install the listed dependencies (both of which are used for testing, which we do not need)

Now comes the part where we had the most trouble. The example scripts didn’t work right out of the box. The Ethernet connection was fine, but the robot would enable and not move when told to. When plugging the robot into a PC for iRC, we were able to jog the robot joints just fine after hitting a couple of random buttons. We tried plugging it back into the Beagle, ran the example scripts and they worked. Somehow, iRC was able to move the robot while the scripts weren’t and the scripts were only able to work after messing around with iRC. We suspected that the pressed buttons were important, so we plugged the arm back into the PC, loaded up iRC and took note of what buttons we pressed to start the robot. We compared them to the in-app Robot Control Log and developed an initialization sequence.

After adding it to the example scripts, the robot arm still didn’t move. Using the Python logging utility, we printed out the joints’ reference statuses and somehow, the function call to reference_all_joints() didn’t end up referencing everything. It got through two or three joints, then stopped. I wondered if the reference function call was being stopped prematurely, so I added delays between function calls to enable, reset and reference. As I systematically adjusted and deleted these time values, I found that the delays between enabling and referencing and referencing and reenabling were the most crucial.

Below is example code that can be used to properly initialize, move and exit the robot arm:

import logging
from time import sleep
from cri_lib import CRIController

# constants for arm orientation
A, B, C = -179, 0, 179

# configure logger to INFO level - change to DEBUG to see CRI Messages
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

controller = CRIController()

# connect Beagle to Robot Arm (192.168.3.11) via Ethernet
if not controller.connect("192.168.3.11"):
    logger.info("Unable to connect to robot!")
    quit()

# request the passive connection to be an active one
controller.set_active_control(True)
logger.info("Acquired active control...")

# motors must be enabled to reference
controller.enable()
logger.info("Enabling Motors...")
sleep(2)

# wait for referencing to finish
ref_state = controller.reference_all_joints()
if not ref_state:
    logger.info("Error with referencing, exiting...")
    controller.disable()
    controller.close()
    quit()
sleep(2)

#controller.get_referencing_info() # uncomment to view reference status in DEBUG mode

# reenable because referencing disables the robot
controller.enable()

# wait for kinematics to be ready
kine_state = controller.wait_for_kinematics_ready()
if not ref_state:
    logger.info("Error with kinematics, exiting...")
    controller.disable()
    controller.close()
    quit()

# moving the arm to a certain XYZ coord at speed 100 in "base" mode
controller.move_cartesian(300, 0, 100, A, B, C, 0,0,0, 100, '#base', True)

# exit the robot
logger.info("Disabling motors and Disconnecting...")
controller.disable()
controller.close()

logger.info("Script execution completed successfully.")

robot_example.py (1.7 KB)

One other issue we ran into concerned the robot firmware. Out-of-the-box, the igus ReBeL robot arm came with version 14.6. We found that by using the Updater CPRog, we could update our robot firmware to version 15, so we did. We temporarily switched over from iRC v14 to iRC v15 and were able to jog the arm as intended. The issue came up when we tried using it with CRI-Python-Lib. Its GitHub mentions its compatibility with “robots with iRC V14-004-1 and newer”.

We took this to mean that if we upgraded the robot firmware to V15, we could still work with the latest version of CRI-Python-Lib. Running the normally error-free CRI-Python-Lib scripts on this new firmware failed. We were able to connect the robot, but the console got flooded with error messages. Fixing these compatibility issues would’ve been a tremendous time sink, so we decided to downgrade back to version 14 robot firmware.

From this point, we were able to create scripts that moved the arm to specific points and opened/closed the end-effector (which we 3D-printed fingers for) to pick and place small objects.

Conclusion

After choosing the hardware for the project, we explored different softwares and became aware of their limitations. Complexity and performance were the leading constraints in our system because our project needed to integrate different subsystems and perform many tasks. The most challenging, but rewarding, part of this entire initial stage of the project was learning new tools. Using ROS2 forced me to learn Docker. Using CRI-Python-Lib forced me to understand asynchronous IO. Every new thing introduced other new things. Since I had to fully understand a given software in order to debug, I was always tripping over bugs and mechanics unknown to me in pursuit of sufficient depth. Overall, this article aimed to record progress as well as provide solutions and clarity for people who want to create a similar project. With that, the hardware-heavy portion is done.