Posted on by

Welcome to the next installment in my series on the ROS transform system (ROS tf). In my previous article I explained how BLUEsat’s Rovers all run on the Robotics Operating System (ROS), and what the ROS tf system was all about. In this article I will introduce a number of useful tools and packages that allow us to take advantage of ROS transforms, with very little code involved.

First we’ll look at the Robot State Publisher – a simple tool for publishing basic transforms of robots joints using a model of your robot. Then I will demonstrate visualising TF trees in RQT, a useful way to identify problems with your transform system. Finally I will explain how to visualise the movement of your robot using RViz.

Using Robot State Publisher

The standard ROS packages include a number of ways to to publish transforms with little or no code.  One of the most useful of these is the robot state publisher, which takes in a URDF file modelling your robot, and outputs static transforms for fixed joints. It also listens for joint messages to publish the position of other joints. Creating a URDF file is outside the scope of this article, but I recommend the SolidWorks to URDF converter, although it is no longer maintained.

The robot state publisher package is effective because it abstracts away a lot of the work you would otherwise need to do. Developers only need to publish message describing an angular rotation, velocity or extension of a joint (depending on the type – more later). There’s even a UI you can use for testing provided by the joint state publisher ROS package.

Starting the publisher is reasonably simple. First you need to define the location of your urdf file. If you are using a ROS launch file then you can simply add the following line to your launch file.

<param name="robot_description" command="$(find xacro)/xacro '$(find owr_bluetounge2_tf)/robots/owr_bluetounge2_tf.URDF'" />

Replacing owr_bluetounge2_tf with the the name of the ROS package your urdf file is in and /robots/owr_bluetounge2_tf.URDF with the path to your urdf file within the the package folder. If you want, you can also get rid of the $(find ... ) section completely and just use an absolute path.

Then to run the node itself, add the following line to the file and then run it using the roslaunch command.

<node name="robot_state_publisher" pkg="robot_state_publisher" type="state_publisher" />

A complete file can be found on our GitHub.

By itself this will only publish the “static transforms.” These are the ones not associated with moving joints. To get ROS to publish transforms for joints that move we need to provide it with either an “extension”, “rotation” or “velocity” for the joint. The type of value depends on what type of joint you defined in your urdf. In terms of units ROS standardises measurements in meters, radians, and m/s for distance, rotation and speed respectively.

Now that you have your robot model set up, you probably want to test it. The easiest way to do this is with the “joint_state_publisher”. This provides a simple GUI to publish those topics.

A gui generated by ROS's robot_state_publisher tool that shows a range of sliders used to control ROS transforms
You can use the joint_state_publisher in parallel with the robot_state_publisher to control transforms.

To run the “joint_state_publisher” simply type:

rosrun joint_state_publisher joint_state_publisher _use_gui:=true

This is useful for testing, but for the actual operation of your robot you probably want to automate this. To do this you need to publish one or more “sensor_msgs/joint_states” message on the /joint_states topic describing the states of your joints.

As well as a standard ROS header describing time information, the message contains four arrays. The first is “names” and corresponds to the names of your joints in the urdf file. This defines the order for the values in the other three arrays; “position”, “velocity”, “effort”. To preserve ordering, you need to define each named joint in all three other arrays, but the joint state publisher will only consider position or velocity depending on the type of joint.

Visualising TF Trees in RQT

Once you have your transform tree up and running, you probably want to check that it is working as expected – this is where RQT comes in. If you haven’t encountered RQT before, I recommend you take a look at our previous tutorial on debugging ROS systems. As I mentioned in that article, RQT is basically a Swiss army knife full of handy ROS tools. You start it by typing rqt in the terminal, with roscore running.

In this case the plugin we are looking for is simply called the “TF Tree” tool. You can find it in the “Plugins” menu under “Visualisation”. It allows you to see the connections in your TF tree, when they last updated, and most importantly, any gaps in the tree.

The image below is a great example of this. We can see that there are several disjoint trees. This is a good indication that something is wrong with our ROS system. In order for us to be able to transform between all of the robot’s co-ordinate systems we need to be able to transform freely.

A visualisation of the BLUEtongue Rover's TF Tree in RQT. Showing several disjointed trees.
In this RQT screenshot the the BLUEtongue Rover’s TF Tree is shown as several disconnected graphs. This means that some of the transforms we were expecting are missing.

In the case above this is because the robot state publisher has not received states for all of the joints it is expecting. But other causes may include a node not running or a localisation system that hasn’t collected sufficient sensor data yet.

3D Representations of your Robot in RViz

Finally let’s visualise the transforms we have been working. This is where RViz comes in.

RViz contains method for visualising a range of different transformed data. First let’s see our robot in its transformed state.

The RViz window is split into three columns. From left to right. The first displays information about the data you are visualising, the second displays a 3D scene that will contain the data and the third contains information about the camera.

To add your robot, make sure you have joint state publisher running as described above. Then click “Add” in the left hand column. A list of data types should appear in a new window. Select “Robot Model.” You should get something like the below image.

The BLUEtongue 2.0 Rover visualised on a 3D Plane in ROS's RViz tool.
The BLUEtongue 2.0 Rover fully visualised in RViz using the joint state publisher.

Now try moving some of the joints using the joint state publisher. The corresponding part of your robot should move!

Have a play around in RViz and see what else you can visualise.

Thanks for reading this installation of my series on the ROS transform (TF) system. Please, stay tuned for the next instalment where I will cover using these transforms to process data in code!



Posted on by

At BLUEsat UNSW, the Off-World Robotics Software Team uses the Robotics Operating System (ROS) as the basis for much of our code. The modularity, communications framework, and existing packages it provides are all factors in this, but another key benefit is its “transform” system. The transform library, which is often referred to as “tf”, provides a standardised way of managing the position of your robot[s] in a ROS system, as well as the moving components of said robots and features in the environment. This article is the first of a series that will introduce you to ROS tf.

The tf system operates by providing a tree of co-ordinate systems, called “frames”. Each frame is an independent co-ordinate system, with an origin that can move relative to its parent in the tree. A common example of this is localising a robot to its environment. To do this you would need at least two frames: one frame for your robot’s local area – lets call it “map”, and another for your robot itself – lets call it “base_link”. In order for ROS tf to localise your robot on the map we need to publish a transform between the “map” and “base_link” frames. Such a transform will often be published by a SLAM package such as hector_slam or an EKF package such as that provided in the ROS navigation packages.

BLUEsat's BLUEtongue Rover represented as a 3D model in RViz with each transform being marked by a set of axes.
ROS’s RViz tool can be used to display 3D representations of your transform tree. Here we see the BLUEtongue Rover with each set of axes representing a transform in our tree.

For every link in the tree we need to provide ROS with a “transform”, that defines the relative position of the two frames. This is where ROS’s modularity kicks in, as you are not restricted to a single node publishing all of your transforms. Instead, as with normal ROS topics, you can have many nodes providing different transforms! This means that you could have one node publishing your robot’s position in your map co-ordinate system, and another node handling the rotation of your rover’s arm. ROS tf then puts these together for you, so that you could, for example, calculate the location for the end of your arm in the map co-ordinate system.

Transforms are time stamped which means that nodes can deal with temporary data loss or lag and do accurate mapping calculations. It also means that they can be recorded with rosbag for future playback. However the time-stamping can also create some issues, which I shall talk about later in the article.

The Justification for ROS TF

So why is this useful? Say I have a LIDAR on a gimbal, and I need to know the positions of its point cloud relative to my rover’s centre of mass. But the LIDAR only publishes a plain of point information relative to its own position. Furthermore the angle of the gimbal is controlled by a separate system, to the one publishing the LIDAR data. Sound familiar?

In a traditional solution the code that reads the LIDAR data, must be aware of the position of the gimbal and manually transform its output to its desired co-ordinate system. This means that the gimbal must know to provide that data, and your system must have a way of syncing the timing of the inputs.

In ROS all of this work is done for you: the LIDAR driver publishes a header indicating that it is in a co-ordinate system who’s parent is the top of the gimbal, and the instant the data was recorded at. The system responsible for controlling the gimbal publishes a transform, indicating its position at that instant. And any system that needs the two pieces of data in a common co-ordinate system, such as that of the base of the rover, can simply run the data through a standard filter provided by ROS to get the information it needs. The video below shows our original BLUEtongue 1.0 Mars Rover doing exactly that.

 

Admittedly if only one system is using those two pieces of data there may not be a massive advantage, but imagine if you had many sensors on top of the gimbal, or many separate systems controlling moving parts…

The Transform Tree

As mentioned previously ROS transforms exist in a tree structure. This has some important implications for how we can structure our graph. The first of these is that a transform can only have one parent. So ROS tf won’t solve more complex graphs. This is especially relevant if you are using something like ROS’s Joint State Publisher to publish the position of joints on your robot as ROS won’t do calculations for multi-link joints. You’ll need to do that yourself.

It also means that if one link in your tree fails you won’t be able to do transforms across that gap as there is no redundancy. However, the system is reasonably resilient. You can do a transform between any two connected points, even if the rest of the graph is not fully connected.

Part of the BLUEtongue 2.0 Rover's Transform (TF) Tree in ROS's RQT tool. ROS tf
Part of the BLUEtongue 2.0 Mars Rover’s Transform (TF) Tree displayed in ROS’s RQT tool.

As well as resilience, the tf tree does offer several advantages. As it’s a tree, each node only needs to know about the two co-ordinate frames it is publishing between; dramatically reducing the complexity of any publisher. This is especially useful in a distributed system with many moving parts, or even a ROS environment with multiple robots!  Furthermore if you follow ROS conventions for naming your frames you can easily combine 3rd party tools, such as one of the many ROS packages used for localisation or calculating the position of your robots joints, without having to modify them in any way.

The Timing Problem

I’d be amiss if I didn’t mention that the ROS tf system is not without its issues and difficulties. Foremost of these is ensuring that you have the transforms you need when you need them. Normally any data you want to transform will have a standard ROS header as part of its message. This header not only identifies the frame, but also the time the data was received. Lets look at our example with the LIDAR gimbal to see why this is important. In that scenario, by the time the LIDAR data  is ready to publish on our ROS network, the gimbal would have moved on. However, we want to do our transforms using the position at the time the data was collected. The timestamp allows us to do this.

But, unsurprisingly, ROS only keeps a limited buffer of transforms in memory. This can cause problems if one of your transform publishers suffers from lag, or your data takes a long time to process before a transformation is applied. Usually your system will need to be able to cope with dropping frames of data, although there are other ways to handle this that I will discuss later in the series.

Next Steps

Well that’s it for now. I hope the first part of this guide to the ROS tf system has proven useful to you! Keep an eye on our blog for my next article, where I’ll be looking at tools and libraries provided by ROS that take advantage of the tf system. In the meantime you may want to take a look at our guide to ROS debugging, which introduces some of the tools we are going to be looking at, or if you are feeling impatient you can skip ahead and look at the official tutorials. If you are interested in learning more about the robotics we do at BLUEsat UNSW you could also take a look at the Off-World Robotics page.


Posted on by

Modular designs are not a new concept, in fact they are used just about everywhere from your humble desktop computer to scaffolding on the side of a building. But have you ever seen a modular Mars rover? Well BLUEsat’s new NUMBAT Rover is just that.

The chasis of the NUMBAT Mars Rover ready to be wired.

CAD Render of the NUMBAT robot's chasis. CAD render of the NUMBAT Rover, showing modules being inserted into the chasis.
As you can see in the images above, the NUMBAT rover has a series of “Module Slots” which allow for different systems to be easily placed inside the robot.  These modules can be placed at any point inside the rover, there are no limitations stating that the power supply or on-board computer has to be in a specific location.

It should not be understated how useful this is from a design perspective. The modular system allows for all rover systems to be developed independently, without worrying whether they will interfere with other parts.  As long as the components can fit within a standard size module box, which come in a range of lengths, it can be assembled into the rover.  This also opens up the possibility of having “hot swap” modules, which can be rapidly taken out and replaced depending on the rovers needs.  For example between competition tasks the modules for scientific testing and core drilling could be swapped for additional battery modules and a manipulator arm.

But that’s not all this modular design has to offer, it also means that the rover can fulfil a large range of different operating modes just by swapping out systems. Instead of having to design multiple Mars rovers, one modular platform can fit a variety of tasks. Some examples of this include:

  • Stationary science mission – If the wheel modules and arm modules are not installed, their places may be filled with different scientific modules, greatly enhancing the capabilities of the Rover and allowing it to serve as a scientific hub for different experiments.
  • Assembly line worker – If scientific modules and drilling modules are replaced with additional manipulator arms, potentially with inbuilt tools, the Rover can be repurposed as an assembly line machine. The addition of autonomous software and drive capabilities would allow the Rover to function in most situations.
  • Telecommunications relay – addition of multiple antenna could allow the Rover to become a relay for a mars based telecommunications network.

Overall, the BLUEsat Off-World Robotics Team is very proud of our modular Rover and can’t wait to see how it will perform at this years European Rover Challenge.


Posted on by

So you’ve written the ultimate ROS program: after thousands of lines of code your robot will finally achieve sentience and bring about the singularity!

One by one you launch your nodes. Each one bringing the Apocalypse ever closer. You hit enter on the last command. And. And, nothing happens. What went wrong? How will you find, and forever squash that bug that prevented your moment of triumph? This blog attempts to answer those questions, and more*.

At BLUEsat we’ve had our share of complicated ROS debugging problems. The best ones happen when you are half-way through a competition task, with time ticking on the clock. Although this article will also look at the more common situation of debugging in a less time pressured, and fire prone environment**.

Below are several tools and techniques that we have successfully deployed to debug our ROS environment.

Keep Calm And … FIRE!

You’ve probably heard this before but its very important when debugging to not jump to conclusions or apply fixes you haven’t tested properly. Google for example has a policy of rolling back changes on its services rather than trying to push a fix. A similar idea applies in a competition or time pressured situation: make sure you have thought through that patch that removes the “don’t kill humans” safety from your robot!  That being said, unfortunately a roll back is unlikely to be applicable in a competition situation, nor is it likely to be able to put out that fire you just started on your robot. So we can’t just copy Google, but we should think about what we are doing before we do it.

Basically any patches or configuration fixes you apply during such a situation is a calculated risk, and you should make sure you understand those risks before you do something. During the European Rover Challenge last year I found it was possible to tweak small settings, restart certain nodes, and re-calibrate systems; but it was too risky to power cycle the rover during a task due to the time it took to establish communication. Likewise restarting our drive systems or cameras was very disruptive to our pilot, so could only be done in certain situations where the damage done by not fixing the system could be worse. That being said, after a critical camera failed we did attempt to programmatically power cycle that device – the decision being that the camera was important enough to attempt such a risky move. (In the end we weren’t able to do this during the task, and our pilot managed to navigate the rover without the camera in question.)

In a non time pressured situation you can be more flexible. It is possible to test different options and see if they work. That is provided they don’t damage your robot. However a structured approach is often beneficial for those more complicated bugs. I often find that when I’m debugging an intermittent or difficult to detect problem that it is easy to end up lose track of what I’ve tried, or get results mixed up. A technique I’ve found to be very useful was to record what I was doing as I did it, especially if the problem includes sensor data. We had a number of problems with our Rover‘s steering system when we first implemented our swerve drive and I found writing down ADC values and rotation readings in different situations really helped debug it (You can read more about how we use ADC’s in our steering system in one of our previous articles).

Basically the main point is too keep your head clear, and think through the consequences before you act. Know the risks and have your E-Stop button ready! Now lets look at some tools you can use to aid you in your debugging.

The BLUEtongue 2.0 Rover being debugged during the 2016 ERC, with Team Members Assisting. (LTR): Timothy Chin, Sebastian Holzapfel, Simon Ireland, Nuno Das Neves, Harry J.E Day.
Debugging is often a team effort.

(more…)


Posted on by

It’s one thing to design a satellite or rover, but without manufacturing you’re dead in the water. Over the years at BLUEsat the problem, or more specifically the cost, of manufacturing has been recurring issue for our mechanical engineering teams. It’s not unusual for the bill for manufacturing our designs to come in at several times our material costs, not to mention long lead times, lack of quality control and no second chances once the part comes in.

Late last year the society decided that enough was enough and purchased a CNC router. A CNC is a simple machine at its core. It consists of a rapidly spinning tool that cuts away at material, which is then mounted on driven guide rails, controlling its position in space. Using this system in combination with computer controls, a CNC router can cut out almost any geometry that we choose.

BLUEsat's CNC Router
BLUEsat’s CNC Router

The process for making a part on the CNC has three stages:

  1. Model the part in CAD, we use Autodesk Inventor.
  2. Create a tool path using CAM software, we use HSM
  3. Secure your material to the CNC router, load the tool path, and begin cutting.

One of the parts that we made recently was an aluminium work holding jig. The model is shown below. This part has some complex features such as bottom rails, notched sides, counterbored holes and raised supports. To make this part by hand would take days and a very competent machinist, and we don’t have access to either.

Jig Plate CAD model
Jig Plate CAD model

Using this model, a tool path was developed with CAM software. The program does most of the heavy lifting, but the user must define the positions of each feature, the speed the machine moves at and how fast it will spin. These speeds are very important to the quality of the final piece and must be tailored to each feature. Below is an example of what the tool path looks like on the computer, red lines indicate that the machine is moving, blue lines show it is cutting.

 

Jig Plate CAM operations
Jig Plate CAM operations

Finally, with our tool path created, we were ready to set up the CNC itself. The material needs to be secured to the surface of the bed to prevent any movement during the cutting operation. This can be done in a number of ways, such as using a machine vice or work holding clamps. For this piece, we started with work holding clamps and then secured it using holes drilled into the material itself.

Now onto the fun part, the cutting. The tool path is loaded onto the CNC and the machine is set to run. Generally, we do a single operation at a time. This gives us time to clean up after each cut and inspect if it was successful. Here are a few videos of cutting.

 

 

 

 

All up, this part took 6hrs to machine. That included the setup, cutting and cleaning up of the part. Below is the final part:

Completed Jig Plate
Completed Jig Plate
Bottom View
Bottom View

 

 

 

 

 

 

 

 

 

 

 

Using our CNC has allowed for rapid prototyping of parts, drastically reduced lead times and most importantly, cut manufacturing costs by an order of magnitude.


Posted on by


 

 

At the start of semester we ran a number of seminars on different skills that we use at BLUEsat. In the first of these videos, former Rover Software Team Lead, Harry J.E Day went through an introduction to “the Robotics Operating System” (ROS) covering setting up ROS catkin workspaces, and basic publishers and subscribers.

You will need the vm image from here: http://bluesat.com.au/vm-image/

The slides from this presentation can be found here: http://bluesat.com.au/an-introduction…

Errata:

  • Some slides in the recording refer to “std_msg” there should be an ‘s’ on the end (i.e “std_msgs”)
  • On the slide “A Basic Node – CMakeLists.txt (cont)” there should only be one ‘o’ in “node.”
  • In step 4 of the “Publisher (cont)” section there should be an e on the end of “pub_node”
  • The person on the last slide was the first robotics COO at BLUEsat not CTO.

These have all been corrected in the slides.


Posted on by

One of the most surprising things about our experience at the European Rover Challenge last year was how incredibly close to total failure we came. Two days before the competition began, while we were in Poland, our control board failed. In addition to having to port all of our embedded codebase to Arduino in two days, we had to fix our overcurrent protection mechanism on the rover’s claw. This was a critical system since it prevents the claw servo from overheating when try to pick up objects. Before we developed our original software solution, a large number of servos had been destroyed due to overheating. Due to errors we’d made in calibration during the port to Arduino, our original software solution didn’t work and we had to think of something else.

Seb Holzapfel and I realised that a hardware solution would also solve this problem. We designed the circuit shown below. It consists of an Op-Amp, a diode, a mosfet and a few resistors. It was designed such that when a large amount of current flows through the 100m Ohm resistor, the PWM signals will be cut off from the claw. This causes the servo motor in the claw to stop drawing current, and therefore prevents overheating.

But why was this worth writing about? Well, we had to build this in a very short period of time and we didn’t really have the correct spare parts on hand. We only had a few op-amps, some jumper cables, some veroboard and a few resistors. This wasn’t enough to build the circuit shown above. We had to improvise. I realised that since our control boards had all failed, we could, in fact, harvest them for the parts we needed. Fortunately, after doing a quick stocktake of the parts on the old control boards, I determined that all the parts we would need were present. We just had to salvage them.

 

Seb is shown above trying, ultimately unsuccessfully, to fix one of our control boards.

 

 

While everyone else was out testing the rover and after we ported the code to Arduino successfully, Seb and I found a bit of spare time on our hands. About 2 hours. We got to work, I desoldered parts from the dead control boards with the hot air gun, while Seb put those parts together into the monstrosity you see below.

It isn't pretty, but it worked.

We then tested it using a coil of wire as a load and verified that it worked. It was then deployed onto the rover. Despite being built in just an afternoon, it actually worked better than the previous software solution when we tested it with the rover. And with this “solution”, we came 9th.

And that’s how we built a critical system in just 2 hours from parts we salvaged from dead control boards.

 

BLUEsat OWR ERC Team.
Back row (left to right): myself, Timothy Chin, Denis Wang, Simon Ireland, Nuno Das Neves, Helena Kertesz.
Front row (left to right): Harry J.E. Day, Seb Holzapfel

Posted on by

In our last article, as part of our investigation into different Graphical User Interface (GUI) options for the next European Rover Challenge (ERC) we looked at a proof of concept for using QML and Qt5 with ROS. In this article we will continue with that proof of concept by creating a custom QML component that streams a ros sensor_msgs/Video topic, and adding it to the window we created in the previous article.

Setting up our Catkin Packages

  1. In qt-creator reopen the workspace project you used for the last tutorial.
  2. For this project we need an additional ROS package for our shared library that will contain our custom QML Video Component. We need this so the qt-creator design view can deal with our custom component. In the project window, right click on the “src” folder, and select “add new”.
  3. Select “ROS>Package” and then fill in the details so they match the screenshot below. We’ll call this package “ros_video_components” and  the Catkin dependencies are “qt_build roscpp sensor_msgs image_transport” The QT Creator Create Ros Package Window
  4. Click “next” and then “finish”
  5. Open up the CMakeLists.txt file for the ros_video_components package, and replace it with the following file.
    ##############################################################################
    # CMake
    ##############################################################################
    
    cmake_minimum_required(VERSION 2.8.3)
    project(ros_video_components)
    
    ##############################################################################
    # Catkin
    ##############################################################################
    
    # qt_build provides the qt cmake glue, roscpp the comms for a default talker
    find_package(catkin REQUIRED COMPONENTS qt_build roscpp sensor_msgs image_transport)
    include_directories(include ${catkin_INCLUDE_DIRS})
    # Use this to define what the package will export (e.g. libs, headers).
    # Since the default here is to produce only a binary, we don't worry about
    # exporting anything.
    catkin_package(
        CATKIN_DEPENDS qt_build roscpp sensor_msgs image_transport
        INCLUDE_DIRS include
        LIBRARIES RosVideoComponents
    )
    
    ##############################################################################
    # Qt Environment
    ##############################################################################
    
    # this comes from qt_build's qt-ros.cmake which is automatically
    # included via the dependency ca ll in package.xml
    find_package(Qt5 COMPONENTS Core Qml Quick REQUIRED)
    
    ##############################################################################
    # Sections
    ##############################################################################
    
    file(GLOB QT_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} resources/*.qrc)
    file(GLOB_RECURSE QT_MOC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS include/ros_video_components/*.hpp)
    
    QT5_ADD_RESOURCES(QT_RESOURCES_CPP ${QT_RESOURCES})
    QT5_WRAP_CPP(QT_MOC_HPP ${QT_MOC})
    
    ##############################################################################
    # Sources
    ##############################################################################
    
    file(GLOB_RECURSE QT_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS src/*.cpp)
    
    ##############################################################################
    # Binaries
    ##############################################################################
    add_library(RosVideoComponents ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
    qt5_use_modules(RosVideoComponents Quick Core)
    target_link_libraries(RosVideoComponents ${QT_LIBRARIES} ${catkin_LIBRARIES})
    target_include_directories(RosVideoComponents PUBLIC include)
    
    

    Note: This code is based on the auto generated CMakeList.txt file provided by the qt-create ROS package.
    This is similar to what we did for the last example, but with a few key differences

    catkin_package(
        CATKIN_DEPENDS qt_build roscpp sensor_msgs image_transport
        INCLUDE_DIRS include
        LIBRARIES RosVideoComponents
    )
    

    This tells catkin to export the RosVideoComponents build target as a library to all dependencies of this package.

    Then in this section we tell catkin to make a shared library target called “RosVideoComponents” that links the C++ source files with the Qt MOC/Header files, and the qt resources. Rather than a ROS node.

    add_library(RosVideoComponents ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
    qt5_use_modules(RosVideoComponents Quick Core)
    target_link_libraries(RosVideoComponents ${QT_LIBRARIES} ${catkin_LIBRARIES})
    target_include_directories(RosVideoComponents PUBLIC include)
    
  6. Next we need to fix our package.xml file, the qt-creator plugin has a bug where it puts all the ROS dependecies in one build_depends and run_depends tag, rather than putting them seperatly. You need to seperate them like so:
      <buildtool_depend>catkin</buildtool_depend>
      <buildtool_depend>catkin</buildtool_depend>
      <build_depend>qt_build</build_depend>
      <build_depend>roscpp</build_depend>
      <build_depend>image_transport</build_depend>
      <build_depend>sensor_msgs</build_depend>
      <build_depend>libqt4-dev</build_depend>
      <run_depend>qt_build</run_depend>
      <run_depend>image_transport</run_depend>
      <run_depend>sensor_msgs</run_depend>
      <run_depend>roscpp</run_depend>
      <run_depend>libqt4-dev</run_depend>
    
  7. Again we need to create src/ resources/ and include/ros_video_components folders in the package folder.
  8. We also need to make some changes to our gui project to depend on the library we generate. Open up the CMakeLists.txt file for the gui package and replace the following line:
    find_package(catkin REQUIRED COMPONENTS qt_build roscpp sensor_msgs image_transport)

    with

    find_package(catkin REQUIRED COMPONENTS qt_build roscpp sensor_msgs image_transport ros_video_components)
  9. Then add the following lines to the gui package’s package.xml,
    <build_depend>ros_video_components</build_depend>
    <run_depend>ros_video_components</run_depend>
    
  10. In the file browser create src and include/ros_video_components folders in the ros_video_components folder.

Building the Video Streaming Component

When we are using the rover the primary purpose the GUI serves in most ERC tasks is displaying camera feed information to users. Thus it felt appropriate to use streaming video from ROS as a proof of concept to determine if QML and Qt5 would be an appropriate technology choice.

We will now look at building a QML component that subscribes to a ROS image topic, and displays the data on screen.

  1. Right click on the src folder of the ros_video_components folder, and select “Add New.”
  2. We first need to create a class for our qt component so select,  “C++>C++ Class”
  3. We’ll call our class “ROSVideoComponent” and it has the custom base class “QQuickPaintedItem.” We’ll also need to select that we want to “Include QObject” and adjust the path of the header file so the compiler can find it. Make sure your settings match those in this screenshot:
    Qt Creator C++ Class Creation Dialouge
  4. Open up the header file you just created and update it to match the following
     
    #ifndef ROSVIDEOCOMPONENT_H
    #define ROSVIDEOCOMPONENT_H
    
    #include <QQuickPaintedItem>
    #include <ros/ros.h>
    #include <image_transport/image_transport.h>
    #include <sensor_msgs/Image.h>
    #include <QImage>
    #include <QPainter>
    
    class ROSVideoComponent : public QQuickPaintedItem {
        // this marks the component as a Qt Widget
        Q_OBJECT
        
        public:
            ROSVideoComponent(QQuickItem *parent = 0);
    
            void paint(QPainter *painter);
            void setup(ros::NodeHandle * nh);
    
        private:
            void receiveImage(const sensor_msgs::Image::ConstPtr & msg);
    
            ros::NodeHandle * nh;
            image_transport::Subscriber imageSub;
            // these are used to store our image buffer
            QImage * currentImage;
            uchar * currentBuffer;
            
            
    };
    
    #endif // ROSVIDEOCOMPONENT_H
    

    Here, QQuickPaintedItem is a Qt class that we can override to provide a QML component with a custom paint method. This will allow us to render our ROS video frames.
    Also in the header file we have a setup function which we use to initialise our ROS subscriptions since we don’t control where the constructor of this class is called, and our conventional ROS subscriber callback.

  5. Open up the ROSVideoComponent.cpp  file add change it so it looks like this:
     
    #include <ros_video_components/ROSVideoComponent.hpp>
    
    ROSVideoComponent::ROSVideoComponent(QQuickItem * parent) : QQuickPaintedItem(parent), currentImage(NULL), currentBuffer(NULL) {
    
    }
    

    Here we use an intialiser list to call our parent constructor, and then initialise our currentImage and currentBuffer pointers to NULL. The latter is very important as we use it to check if we have received any ROS messages.

  6. Next add a “setup” function:
    void ROSVideoComponent::setup(ros::NodeHandle *nh) {
        image_transport::ImageTransport imgTrans(*nh);
        imageSub = imgTrans.subscribe("/cam0", 1, &ROSVideoComponent::receiveImage, this, image_transport::TransportHints("compressed"));
        ROS_INFO("setup");
    }
    

    This function takes in a pointer to our ROS NodeHandle, and uses it to create a subscription to the “/cam0” topic.  We use image transport, as is recomended by ROS for video streams, and direct it to call the recieveImage callback.

  7. And now we implement  said callback:
    void ROSVideoComponent::receiveImage(const sensor_msgs::Image::ConstPtr &msg) {
        // check to see if we already have an image frame, if we do then we need to delete it
        // to avoid memory leaks
        if(currentImage) {
            delete currentImage;
        }
    
        // allocate a buffer of sufficient size to contain our video frame
        uchar * tempBuffer = (uchar *) malloc(sizeof(uchar) * msg->data.size());
        
        // and copy the message into the buffer
        // we need to do this because the QImage api requires the buffer we pass in to continue to exist
        // whilst the image is in use, but the msg and it's data will be lost once we leave this context.
        memcpy(tempBuffer, msg->data.data(), msg->data.size());
        
        // we then create a new QImage, this code below matches the spec of an image produced by the ros gscam module
        currentImage = new QImage(tempBuffer, msg->width, msg->height, QImage::Format_RGB888);
        
        ROS_INFO("Recieved");
        
        // We then swap out of buffer to avoid memory leaks
        if(currentBuffer) {
            delete currentBuffer;
            currentBuffer = tempBuffer;
        }
        // And re-render the component to display the new image.
        update();
    }
    
  8. Finally we override the paint method
    
    void ROSVideoComponent::paint(QPainter *painter) {
        if(currentImage) {
            painter->drawImage(QPoint(0,0), *(this->currentImage));
        }
    }
    
  9. We now have our QML component, and you can check that everything is working as intended by building the project (hammer icon in the bottom right or the IDE or using catkin_make). In order to use it we must add it to our qml file, but first since we want to be able to use it in qt-creator’s design view we need to add a plugin class.
  10. Right click on the src folder and select “Add New” again.
  11. Then select “C++>C++ Class.”
  12. We’ll call this class OwrROSComponents, and use the following settings:OwrROSCOmponents class creation dialouge
  13. Replace the header file so it looks like this
    #ifndef OWRROSCOMPONENTS_H
    #define OWRROSCOMPONENTS_H
    
    #include <QQmlExtensionPlugin>
    
    class OWRRosComponents : public QQmlExtensionPlugin {
        Q_OBJECT
        Q_PLUGIN_METADATA(IID "bluesat.owr")
    
        public:
            void registerTypes(const char * uri);
    };
    
    #endif // OWRROSCOMPONENTS_H
    
  14. Finally make the OwrROSComponents.cpp file look like this
    #include "ros_video_components/OwrROSComponents.hpp"
    #include "ros_video_components/ROSVideoComponent.hpp"
    
    void OWRRosComponents::registerTypes(const char *uri) {
        qmlRegisterType<ROSVideoComponent>("bluesat.owr",1,0,"ROSVideoComponent");
    }
    
  15. And now we just need to add it our QML and application code. Lets do the QML first. At the top of the file (in edit view) add the following line:
    import bluesat.owr 1.0
    
  16. And just before the final closing bracket add this code to place the video component below the other image
    ROSVideoComponent {
       // @disable-check M16
       objectName: "videoStream"
       id: videoStream
       // @disable-check M16
       anchors.bottom: parent.bottom
       // @disable-check M16
       anchors.bottomMargin: 0
       // @disable-check M16
       anchors.top: image1.bottom
       // @disable-check M16
       anchors.topMargin: 0
       // @disable-check M16
       width: 320
       // @disable-check M16
       height: 240
    }
    

    This adds our custom “ROSVideoComponent” who’s type we just registered in the previous steps to our window.

    Note: the @disable-check M16 prevents qt-creator from getting confused about our custom component, which it doesn’t detect properly. This is an unfortunate limit of using cmake (catkin) rather than qt’s own build system.

  17. Then because Qt’s runtime and qt-creator use different search paths we also need to register the type on the first line of our MainApplication::run() function
    qmlRegisterType<ROSVideoComponent>("bluesat.owr",1,0,"ROSVideoComponent");
    
  18. Finally we need to add the following lines to the end of our run function in main application to connect our video component to our NodeHandle
    ROSVideoComponent * video = this->rootObjects()[0]->findChild<ROSVideoComponent*>(QString("videoStream"));
    video->setup(&nh);
    

    And the relevant #include

    #include <ros_video_components/ROSVideoComponent.hpp>
    
  19. To test it publish a video stream using your preferred ros video library.
    For example if you have the ROS gscam library setup and installed you could run the following to stream video from a webcam:

    export GSCAM_CONFIG="v4l2src device=/dev/video0 ! videoscale ! video/x-raw,width=320,height=240 ! videoconvert"
    rosrun gscam gscam __name:=camera_1 /camera/image_raw:=/cam0

Conclusion

So in our previous post we learnt how to setup Qt and QML in ROS’s build system, and get that all working with the Qt-Creator IDE. Then this time we built on that system to develop a widget that takes ROS video data and renders it to the screen, demonstrating how to integrate ROS’s message system into a Qt/QML environment.

The code in this tutorial forms the basis of BLUEsat’s new rover user interface, which is currently in active development. You can see the current progress on our github, where there should be a number of additional widgets being added in the near future.  If you want to learn more about the kind of development we do at BLUEsat or are a UNSW student interested in joining feel free to send an email to info@bluesat.com.au.

Acknowledgements

Some of the code above is based on a Stack Overflow answer by Kornava, about how to created a custom image rendering component, which can be found here.


Posted on by

The BLUEsat Off World Robotics Software team is rebuilding our user interface, in an effort to address maintenance and learning curve problems we have with our existing glut/opengl based gui. After trying out a few different options, we’ve settled on a combination of QT and QML. We liked this option as it allowed us easy maintenance, with a reasonable amount of power and flexibility. We decided to share with you a simple tutorial we made for working with QT and ROS.

In part one of this article we go through the process of setting up a ROS package with QT5 dependencies and building a basic QML application. In the next instalment we will then look at streaming a ROS sensor_msgs/Image video into a custom QML component. The article assumes that you have ROS kinetic setup, and some understanding of how ROS works. It does not assume any prior knowledge of QT.

Full sources for this tutorial can be found on BLUEsat’s github.

Setting up the Environment

First things first, we need to setup Qt, and because one of our criteria for GUI solutions is ease of use, we also need to setup qt-creator so we can take advantage of its visual editor features.

Fortunately there is a ROS plugin for qt-creator (which you can find more about here). To setup we do the following (instructions for Ubuntu 16.04, for other platforms see the source here):


sudo add-apt-repository ppa:beineri/opt-qt571-xenial
sudo add-apt-repository ppa:levi-armstrong/ppa
sudo apt-get update && sudo apt-get install qt57creator-plugin-ros

We also need to install the ROS QT packages, these allow us to easily setup some of the catkin dependencies we will need later (note: unfortunately these packages are currently designed for QT4, so we can’t take full advantage of them)


sudo apt-get install ros-kinetic-qt-build

Setting up our ROS workspace

  1. We will use qt-creator to create our workspace, so start by opening qt-creator.
  2. On the welcome screen select “New Project”. Then chose “Import Project>Import ROS Workspace”.
    The QT-Creator new project dialogue display the correct selection for creating a new ros project.
  3. Name the project “qt-gui” and set the workspace path to a new folder of the same name. An error dialogue will appear, because we are not using an existing workspace, but that is fine.
  4. Then click “Generate Project File”The QT Import Existing ROS Project Window
  5. Click “Next”, choose your version control settings then click “Finish”
  6. For this project we need a ROS package that contains our gui node. In the project window, right click on the “src” folder, and select “add new”.
  7. Select “ROS>Package” and then fill in the details so they match the screenshot below. We’ll call it “gui” and  the Catkin dependencies are “qt_build roscpp sensor_msgs image_transport” QT Creator Create Ros Package Window
  8. Click “next” and then “finish”
  9. Open up the CMakeLists.txt file for the gui package, and replace it with the following file.
    
    ##############################################################################
    # CMake
    ##############################################################################
    
    cmake_minimum_required(VERSION 2.8.0)
    project(gui)
    
    ##############################################################################
    # Catkin
    ##############################################################################
    
    # qt_build provides the qt cmake glue, roscpp the comms for a default talker
    find_package(catkin REQUIRED COMPONENTS qt_build roscpp sensor_msgs image_transport)
    set(QML_IMPORT_PATH "${QML_IMPORT_PATH};${CATKIN_GLOBAL_LIB_DESTINATION}" )
    set(QML_IMPORT_PATH2 "${QML_IMPORT_PATH};${CATKIN_GLOBAL_LIB_DESTINATION}" )
    include_directories(${catkin_INCLUDE_DIRS})
    # Use this to define what the package will export (e.g. libs, headers).
    # Since the default here is to produce only a binary, we don't worry about
    # exporting anything. 
    catkin_package()
    
    ##############################################################################
    # Qt Environment
    ##############################################################################
    
    # this comes from qt_build's qt-ros.cmake which is automatically 
    # included via the dependency ca ll in package.xml
    #rosbuild_prepare_qt4(QtCore QtGui QtQml QtQuick) # Add the appropriate components to the component list here
    find_package(Qt5 COMPONENTS Core Gui Qml Quick REQUIRED)
    
    ##############################################################################
    # Sections
    ##############################################################################
    
    file(GLOB QT_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} resources/*.qrc)
    file(GLOB_RECURSE QT_MOC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS include/gui/*.hpp)
    
    QT5_ADD_RESOURCES(QT_RESOURCES_CPP ${QT_RESOURCES})
    QT5_WRAP_CPP(QT_MOC_HPP ${QT_MOC})
    
    ##############################################################################
    # Sources
    ##############################################################################
    
    file(GLOB_RECURSE QT_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS src/*.cpp)
    
    ##############################################################################
    # Binaries
    ##############################################################################
    
    add_executable(gui ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
    qt5_use_modules(gui Quick Core)
    target_link_libraries(gui ${QT_LIBRARIES} ${catkin_LIBRARIES})
    target_include_directories(gui PUBLIC include)
    install(TARGETS gui RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
    

    Note: This code is based on the auto generated CMakeList.txt file provided by the qt-create ROS package.
    Lets have a look at what this file is doing:

    cmake_minimum_required(VERSION 2.8.3)
    project(gui)
    
    ...
    
    find_package(catkin REQUIRED COMPONENTS qt_build roscpp sensor_msgs image_transport)
    include_directories(${catkin_INCLUDE_DIRS})
    

    This part is simply setting up the ROS package, as you would expect in a normal CMakeLists.txt file.

    find_package(Qt5 COMPONENTS Core Qml Quick REQUIRED)
    

    In this section we setup catkin to include Qt5, and tell it we need the Core, QML, and Quick components. This differs from a normal qt-build CMakeList.txt, because we need QT5 rather than QT4.

    file(GLOB QT_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} resources/*.qrc)
    file(GLOB_RECURSE QT_MOC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS include/ros_video_components/*.hpp)
    
    QT5_ADD_RESOURCES(QT_RESOURCES_CPP ${QT_RESOURCES})
    QT5_WRAP_CPP(QT_MOC_HPP ${QT_MOC}
    

    This section tells cmake where to find the QT resource files, and where to find the QT header files so we can compile them using the QT precompiler.

    file(GLOB_RECURSE QT_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS src/*.cpp)
    

    And this tells cmake where to find all the QT (and ROS) source files for the project

    add_executable(gui ${QT_SOURCES} ${QT_RESOURCES_CPP} ${QT_FORMS_HPP} ${QT_MOC_HPP})
    qt5_use_modules(gui Quick Core)
    target_link_libraries(gui ${QT_LIBRARIES} ${catkin_LIBRARIES})
    target_include_directories(gui PUBLIC include)
    install(TARGETS gui RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION})
    

    Finally we setup a ROS node (executable) target called “gui” that links the C++ source files with the Qt MOC/Header files, and the QT resources.

  10. Next we need to fix our package.xml file, the qt-creator plugin has a bug where it puts all the ROS dependencies in one build_depends and run_depends tag, rather than putting them separately. You need to separate them like so:
     
    <buildtool_depend>catkin</buildtool_depend>
      <buildtool_depend>catkin</buildtool_depend>
      <build_depend>qt_build</build_depend>
      <build_depend>roscpp</build_depend>
      <build_depend>image_transport</build_depend>
      <build_depend>sensor_msgs</build_depend>
      <build_depend>libqt4-dev</build_depend>
      <run_depend>qt_build</run_depend>
      <run_depend>image_transport</run_depend>
      <run_depend>sensor_msgs</run_depend>
      <run_depend>roscpp</run_depend>
      <run_depend>libqt4-dev</run_depend> 
    
  11. Create src/, include/<package name> and resources/ folders in the package. (Note: it doesn’t seem possible to do this in qt-creator you have to do it from the terminal or file browser).

Our ROS workspace is now setup and ready to go. We can start on our actual code.

Creating a Basic QML Application using the Catkin Build System

We’ll start by creating a basic ROS node, that displays a simple qml file.

  1. Right click on the src folder in the gui package, and select “Add New”
  2. Select “ROS>Basic Node” and then click choose
  3. Call it “guiMain” and click Finish. You should end up with a new file open in the editor that looks like this:Qt creator window displaying a main function with a basic ros "Hello World" in it
  4. We’ll come back to this file later, but first we need to add our QML Application. In order to call ros::spinOnce, without having to implement threading we need to subclass the QQmlApplicationEngine class so we can a Qt ‘slot’ to trigger it in the applications main loop  (more on slots later). So to start we need to create a new class: right click on the src directory again and select “Add New.”
  5. Select “C++>C++ Class “, then click “Choose”
  6. Set the “Class Name” to “MainApplication”, and the “Base Class” to “QQmlApplicationEngine.”
  7. Rename the header file so it has the path “../include/gui/MainApplication.hpp” This allows the CMakeLists.txt file we setup earlier to find it, a run the MOC compiler on it.
  8. Rename the source file so that it is called “MainApplication.cpp”. Your dialog should now look like this:Qt Creator "C++ Class" dialogue showing the settings described above.
  9. Click “Next”, then “Finish”.
  10. Change you MainApplication.hpp file to match this one:
    #ifndef MAINAPPLICATION_H
    #define MAINAPPLICATION_H
    
    #include <ros/ros.h>
    #include <QQmlApplicationEngine>
    
    class MainApplication : public QQmlApplicationEngine {
        Q_OBJECT
        public:
            MainApplication();
            //this method is used to setup all the ROS functionality we need, before the application starts running
            void run();
            
        //this defines a slot that will be called when the application is idle.
        public slots:
            void mainLoop();
    
       private:
            ros::NodeHandle nh;
    };
    
    #endif // MAINAPPLICATION_H
    

    Two important parts here, we add the line “Q_OBJECT” below the class declaration. This tells the QT MOC compiler to do QT magic here in order to make this into a valid QT Object.
    Secondly, we add the following lines:

    public slots:
        void mainLoop();
    

    What does this mean? Well QT uses a system of “slots” and “signals” rather than the more conventional “listener” system used by many other gui frameworks. In layman’s terms a slot acts similarly to a callback, when an event its been “connected” to occurs the function gets called.

  11. Now we want to update the MainApplication.cpp file. Edit it so it looks like the following:
    #include "gui/MainApplication.hpp"
    #include <QTimer>
    
    MainApplication::MainApplication() {
        
    }
    
    void MainApplication::run() {
        
        //this loads the qml file we are about to create
        this->load(QUrl(QStringLiteral("qrc:/window1.qml"))); 
        
        //Setup a timer to get the application's idle loop
        QTimer *timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(mainLoop()));
        timer->start(0);
    }
    

    The main things here are we load a qml file, at the resource path “qrc:/window1.qml”. In a moment we will create this file. The other important thing is the set of timer code. Basically how this works is we create a timer object, we create a connection between the timer object’s “timeout” event (signal), and our “mainLoop” slot which we will create in a moment. We then set the timeout to 0, causing this event to trigger whenever the application is idle.

  12. Finally we want to add the the mainLoop function to the end of our MainApplication code, it simply calls ros::SpinOnce to get the latest ROS messages whenever the application is idle.
    void MainApplication::mainLoop() {
        ros::spinOnce();
    }
    
  13. In our guiMain.cpp we need to add the following lines at the end of our main function:
        QGuiApplication app(argc, argv);
        MainApplication engine;
        engine.run();
        
        return app.exec();
    

    This initialises our QML application, calls our run function, then enters QT’s main loop.

  14. You will also need to add these two #includes, to the top of the guiMain.cpp file
    #include <QGuiApplication>
    #include <gui/MainApplication.hpp>
    
  15. We now have all the C++ code we need to run our first demo. All that remains is writing the actual QT code. Right click on the “resources” folder, and select “Add New.”
  16. In the New File window, select “Qt” and then “QML File (Qt Quick 2)”, and click “Choose.”
  17.  Call the file “window1” and finish.
  18. We want to create a window rather than an item, so change the qml file so it looks like this:
    import QtQuick 2.0
    import QtQuick.Window 2.2
    
    Window {
        id: window1
        visible: true
    
    }
    
  19. Now we will use the visual editor to add a simple image to the window. With the QML file open, click on the far left menu and select “Design” mode. You should get a view like this:
    QT Creator QML Design View
  20. From the left hand “QML Types” toolbox drag an image widget onto the canvas. You should see rectangle with “Image” written in it on your canvas.
    Qt Creator Design Window with an image added
  21. We need to add an image for it to use. To do this we need a resource file, so switch back to edit mode. Right click on the resources folder and select “Add New.”
  22. Select “Qt>Qt Resource File” and then click “Choose”
  23. Call the resource file “images,” and finish.
  24. This should open the resource file editor, first you need to add a new prefix. Select “add>New Prefix”
    QT Creator Resource File Editor: Select Add>Add Prefix
  25. Change the “prefix” to “/image”.
  26. We now want to add an image. Find an image file on your computer than is appropriate then click “Add Files” and navigate to it. If the file is outside your project, qt-creator will prompt you to save it to your resources folder, which is good. You should now have a view that looks like this:
    QT Creator Resource File Editor with an image added
  27. Switch back to the qml file and design view.
  28. Click the image, on the right hand side will be a drop down marked “source”, select the image you just added from it. (Note if the designer has auto filled this box but the image preview is not appearing you may need to select another image, and then reselect the one you want). I used the BLUEsat logo as my image:
    I used the BLUEsat logo for my example
  29. Now we just need to put the qml somewhere we can find it. As in steps 21 to 26, create a new resource file in the resources folder called qml and add the qml window1.qml to it under the “/” prefix.
  30. At this point you should be able to build your project. You can build using catkin_make as you normally would, or by clicking the build button in the bottom left corner of the ide.
  31. To run your ROS node you can either run it as you would normally from the command line using “rosrun”, or you can run it from the ide. To setup running it from the ide select “Project” from the left hand menu, then under “desktop” select run.
  32. Under the run heading, select “Add Run Step>rosrun step.” You should get a screen like this.new/prefix1QT Creator - Project settings screen
  33. Set the package to “gui”, and the target should auto fill with “gui” as well.
  34. Press run in the bottom left corner. You should get something like this (note: depending on where you places the image you may need to resize the window to see it). Important: as always with ROS, roscore needs to be running for nodes to start correctly. Window Displaying BLUEsat Logo
  35. You have a working gui application now, compiled with catkin and running a ROS Spin Loop at the appropriate loop, but this is a bit useless without using some information from other ROS nodes in the gui. In our next article, we will look at streaming video from ROS into Qt so stay tuned!

 


Next Page »