This week I’ve been digging into Robot Operating System (ROS) and how our project will make use of functional components of ROS. The eGreenhouse that we are working on will use ROS to coordinate the end effector movement and data collection, following instructions received from the user interface. The portion that I’ve been working on sends a series of instructions to the Grbl CNC controller. A second node monitors the position of the end effector and triggers data collection actions. Once the destination is reached, an image or set of environmental sensor readings are collected and the next destination is sent to the CNC controller. This process is relatively straight forward – go to a location, collect some data, go to the next point, and repeat. But it gets interesting in the context of the whole machine and the mechanisms that ROS provides to make it work.
ROS nodes are processes which run independently of one another and communicate through topics and services. As a quick 10 cent description, topics are unidirectional communication channels which nodes may either publish or subscribe to in a one to one, one to many, or many to many relationship. A topic is effectively a queue of messages sent from the publisher to the subscriber. Each node which subscribes to a given topic receives all messages published to that topic. Services are a bidirectional communication channel where individual requests and responses sent between nodes in a one to one relationship. Topics and services provide a convenient means for nodes to execute their respective series of functions or run a microcontroller asynchronous of one another but still pass data among them when any events of interest occur. It has been particularly fun to learn about the parallelism that is created through this organization. When a node receives a request through a service, it spins off a new thread to handle the request through the specified handler function. The sending node blocks until the response to the request is received. Each topic subscriber runs its own thread. When messages queue up on a topic, each is processed in the order they are received. Each message received on a topic is processed in the order that it was received, but if a node subscribes to multiple topics, they operate concurrently.
As I’ve been working on this project it has been thought provoking to design the interactions for each node around the implications of the built in thread structure. I’ve also incorporated some additional multithreading to handle executing each set of way points. This week I’ve been working on the node which receives requests from the front end, the PathPlanner node. The requests will contain a unique id of the program to run. Our database contains a table of waypoints, each assigned to a given program id. Each waypoint is associated with a location in the study plot and an instruction to collect either an image or a set of sensor readings needs to be collect. When the node is started, it spins off a new thread which watches for program ids in a queue. As program ids are removed from the queue, the watching thread of the PathPlanner node queries the database for the set of waypoints for that program and sends them to the CNC controller, one at a time so that data can be collected. Meanwhile, the main thread is watching the service for additional requests. This way, if the user wants to execute another program before the current one is complete, the id can be added to the queue and executed when the time comes. To make this work, this node manages two concurrently running threads, processes incoming service requests, and publishes instructions to an outbound topic.