Implementing world

Writing logic and physics for the world

Creating the node

Most nodes will share basic boilerplate which can be copied from the examples ROS gives:

$ cp /opt/ros/$ROS_DISTRO/share/launch_testing_ros/examples/talker.py src/world/world/world.py

Now rename your node World and add the following lines:

class World(Node):
    def __init__(self):
      super().__init__('world')
      self.generate_waypoints()

    def generate_waypoints(self):
        self.waypoints = []

        WIDTH = 200 
        HEIGHT = 100 

        for i in range(30):
            angle = i * 2 * math.pi / 30
            x = math.cos(angle) * WIDTH
            y = math.sin(angle) * HEIGHT
            self.waypoints.append(Waypoint(x=x, y=y, idx=i))

This code generates the waypoints that the car will follow. As you can see, we are just drawing an ellipse (that you can resize by tweaking with the WIDTH and HEIGHT values), but the shape can be any shape you want.

However, if you tried to execute this code, you’d face an error because Waypoint is a custom message that we haven’t defined yet.

Defining the Waypoint message

We need to create a message to represent a point in space. If you have read the previous chapters, you know that messages are defined in the um_msgs directory. In said package, create a directory msg and a file called Waypoint.msg inside that directory, such that the structure of the package is:

src/um_msgs/
├── CMakeLists.txt
├── include
│   └── um_msgs
├── msg
│   └── Waypoint.msg
├── package.xml
└── src

If you’re not sure how to implement this, check the reference .

A waypoint is defined as such:

float64 x
float64 y
uint16 idx

And since we’re at it, we can also define Waypoints.msg like this:

Waypoint[] waypoints

Publishing the waypoints

After importing the message (from um_msgs.msg import Waypoint), we can already execute our world node. All it does is generate the waypoints, but we don’t do anything with them. Let’s publish them.

In the __init__ method, include the following lines:

def __init__(self):
    ...
    self.waypoints_publisher = self.create_publisher(Waypoints, '/world_waypoints', 10)
    self.waypoints_publisher_timer = self.create_timer(3.0, self.publish_waypoints)

These lines of code create a ROS publisher object and a timer object that periodically calls a callback function to publish messages to a specific ROS topic. Note that you can’t rename this topic because qt_interface reads from it.

Next, define the callback function:

def publish_waypoints(self):
    msg = Waypoints(msg=self.waypoints)
    self.waypoints_publisher.publish(msg)

Note that this is also a method in our node and should be indented to the same height as __init__.

Executing the code

Finally, if we execute this code and the interface, we should already see our waypoints being rendered into the canvas.

world showing waypoints

Last modified March 18, 2023: Fix link to image (62e4b8e)