DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • The Data (Pipeline) Movement: A Guide to Real-Time Data Streaming and Future Proofing Through AI Automation and Vector Databases
  • Leveraging Apache Flink Dashboard for Real-Time Data Processing in AWS Apache Flink Managed Service
  • Real-Time Data Streaming on Cloud Platforms: Leveraging Cloud Features for Real-Time Insights
  • Stream Processing in the Serverless World

Trending

  • Software Delivery at Scale: Centralized Jenkins Pipeline for Optimal Efficiency
  • Developers Beware: Slopsquatting and Vibe Coding Can Increase Risk of AI-Powered Attacks
  • Implementing API Design First in .NET for Efficient Development, Testing, and CI/CD
  • How to Merge HTML Documents in Java
  1. DZone
  2. Data Engineering
  3. Big Data
  4. The Blackboard Pattern for Autonomous Navigation

The Blackboard Pattern for Autonomous Navigation

Learn more about the Blackboard design pattern in Java applications through an example autonomous car example.

By 
Nalla Senthilnathan user avatar
Nalla Senthilnathan
·
Aug. 07, 19 · Presentation
Likes (3)
Comment
Save
Tweet
Share
12.7K Views

Join the DZone community and get the full member experience.

Join For Free

The Blackboard pattern (ref#1) is an intriguing and esoteric design pattern. Searching the Internet for this pattern reveals that it is more popular among academic researchers than it is among developers (see ref#2 and ref#3, for instance). There are several online articles on the Blackboard pattern. However, most of them are describing the theory of the pattern. I found very few “worked examples” that provide deeper insight into this fascinating pattern. In this article, I present a Java example for the Blackboard pattern and a simple "Hello-World" style implementation for an autonomous navigation scenario. Let's get started.

Introduction

The Blackboard pattern is often applied to problem scenarios where a system gets data input in a continuous stream and the system needs to make some decisions or solve a problem (or, at least, partially solve the problem). The classic example that is often cited for the Blackboard pattern is speech recognition (ref#2). Another example where the Blackboard pattern is often used is the multi-sensor environment where the system responds autonomously to various sensor inputs (ref#5). With the advent of IoT ecosystems, it is easy to see the applicability of this pattern where data streams in from multiple sensors and the system need to react based on the characteristics of the input data. In an autonomous vehicle navigation scenario, the system gets input continuously from various sensors and need to make driving decisions in a continuous fashion. For simplicity of arguments, I consider an autonomous vehicle (AV) that needs to adjust its velocity based on velocities of a vehicle in the front of them and the velocity of a vehicle in the right lane.

Java and the Blackboard Pattern

The framework has three major abstractions – Blackboard, controller, and one or more knowledge sources.  The following diagram illustrates their interactions.

Blackboard Pattern Components

The Blackboard receives input (BlackBoardObjects) in a continuous stream. The Blackboard (an Observable) notifies the controller (an Observer) whenever it receives a Blackboard object. The controller enrolls a knowledge source — that can handle the task — whenever it is notified about a Blackboard object. The knowledge sources run in their own thread, process the Blackboard object, and update the Blackboard with a “partial solution Blackboard object.” The solution process continues with the Blackboard notifying the controller of the “partial solution Blackboard object” and so forth. When the controller receives a Blackboard object whose isReady flag is true, then the controller executes a solution step.

For brevity of discussion, I am using Java’s Observable and Observer classes to illustrate the pattern.

The Java classes for the framework include:

Blackboard.java

When a new BlackBoardObject is added, the BlackBoard notifies the controller:

public interface BlackBoard {

       public void addBlackBoardObject(BlackBoardObject bbo);

       public void notifyController(BlackBoardObject bbo);
}


AbstractBlackBoard.java

public abstract class AbstractBlackBoard extends Observable implements BlackBoard {
     public void addBlackBoardObject(BlackBoardObject bbo) {

          setChanged();
          notifyController(bbo);
     }

     public void notifyController(BlackBoardObject bbo) {
          notifyObservers(bbo);
     }
}


BlackBoardController.java

When the BlackBoardController is updated with a new BlackBoardObject, it queries the KnowledgeSource objects to see who might be interested in handling the task. It then assigns the BlackBoardObject to the KnowledgeSource and spawns a new thread for the task. When the controller receives a BlackBoardObject with the flag isReady set to true, it calls theexecOutcome(BlackBoardObject bbo) method:

public interface BlackBoardController extends Observer {

     public void setKnowledgeSourceList(List<KnowledgeSource> ksList);

     public void enrollKnowledgeSource(KnowledgeSource ks, ExecutorService exsvc);

     public void execOutcome(BlackBoardObject bbo);

}


AbstractBlackBoardController.java

public abstract class AbstractBlackBoardController implements BlackBoardController {

     protected List<KnowledgeSource> ksList = new ArrayList<KnowledgeSource>();

     public void update(Observable bb, Object bbo) {

          ExecutorService exsvc = Executors.newFixedThreadPool(1);

          if (((BlackBoardObject) bbo).isReady())
               execOutcome((BlackBoardObject) bbo);
          else {
               for (KnowledgeSource ks : ksList) {
                    if (ks.canHandle((BlackBoardObject) bbo, (AbstractBlackBoard) bb)) {
                         enrollKnowledgeSource(ks, exsvc);
                         break;
                    }
               }
          }

          execsvc.shutdown();
     }

     public void setKnowledgeSourceList(List<KnowledgeSource> ksList) {
          this.ksList = ksList;
     }

     public void enrollKnowledgeSource(KnowledgeSource ks, ExecutorService exsvc) {
          exsvc.execute(ks);
     }

}


BlackBoardObject.java

These are the basic data units that are placed on the Blackboard. The BlackBoardObject has a boolean flag isReady to indicate whether a decision point has been reached.

public interface BlackBoardObject {

     public boolean isReady();

}


AbstractBlackBoardObject.java

public abstract class AbstractBlackBoardObject implements BlackBoardObject {

     protected boolean isReady;

     public boolean isReady() {
          return isReady;
     }

     public void setReady(boolean isReady) {
          this.isReady = isReady;
     }

}


KnowledgeSource.java

The KnowledgeSource objects transform one kind of BlackBoardObject to another kind and update the BlackBoard. Optionally, the KnowledgeSource can set the isReady flag to true so the controller can execute a reaction to the input received so far. Keep in mind: A KnowledgeSource can operate in its own thread.

public interface KnowledgeSource extends Runnable {

     public boolean canHandle(BlackBoardObject bbo, BlackBoard bb);

     public BlackBoardObject process(BlackBoardObject bbo) throws Exception;

     public void updateBlackBoardObject(BlackBoardObject bbo);

}


AbstractKnowledgeSource.java

public abstract class AbstractKnowledgeSource implements KnowledgeSource {

     protected BlackBoardObject bbo;
     protected BlackBoard bb;

     public void run() {
          try {
              updateBlackBoardObject(process(bbo));
          }catch(Exception ex) {
              //TODO: log the exception
          }
     }

     public void updateBlackBoardObject(BlackBoardObject bbo) {
          bb.addBlackBoardObject(bbo);
     }
}


Autonomous Vehicle Navigation Example

The source can be downloaded from GitHub.  I have chosen a "Hello-World" style autonomous vehicle navigation scenario to illustrate the usage of the framework.   

Consider a simplified autonomous vehicle (AV) navigation scenario where the AV is at a steady speed but needs to be able to decide whether or not the current speed, at any given point in time, must be altered based on the velocity input of the front vehicle (FV) and velocity input of the right lane vehicle (RLV).  

  1. The AV gets the input data stream from two sensors – FV sensor and RLV sensor, which are added to the AutoNavBlackBoard as FrontVehicleDataBBO and RightVehicleDataBBO.
  2. Upon receiving the BBOs, Blackboard notifies the AutoNavBBController
  3. The controller then enrolls FrontVehicleDataKS and RightLaneVehicleDataKS to handle these BBOs
  4. These knowledge sources analyze the data, compute a delta velocity and place a DeltaVelocityDataBBO on the Blackboard.
  5. Blackboard notifies the controller of DeltaVelocityDataBBO
  6. The controller then enrolls a third knowledge source DeltaVelocityDataKS, which transforms DeltaVelocityDataBBO into a BrakePedalBBO, sets the isReady flag to true (steering wheel and accelerator pedal changes are ignored here for the brevity of this discussion), and updates the Blackboard.
  7. When the controller receives any BBO with the flagisReady set to true, it calls its execOutcome(BlackBoardObject bbo)  method, which then operates the brake pedal.

When the code is run, we get the following output:

==>> Blackboard received BBO rnd.pattern.blackboard.autonav.bbo.FrontVehicleDataBBO
==>> Blackboard received BBO rnd.pattern.blackboard.autonav.bbo.RightLaneVehicleDataBBO
==>> FrontVehicleDataKS processed FrontVehicleDataBBO
==>> Blackboard received BBO rnd.pattern.blackboard.autonav.bbo.DeltaVelocityDataBBO
==>> DeltaVelocityDataKS processed DeltaVelocityDataBBO
==>> Blackboard received BBO rnd.pattern.blackboard.autonav.bbo.BrakePedalBBO
==>> Operating brake pedal
==>> RightLaneVehicleDataKS processed RightLaneVehicleDataBBO
==>> Blackboard received BBO rnd.pattern.blackboard.autonav.bbo.DeltaVelocityDataBBO
==>> DeltaVelocityDataKS processed DeltaVelocityDataBBO
==>> Blackboard received BBO rnd.pattern.blackboard.autonav.bbo.BrakePedalBBO
==>> Operating brake pedal
. . .


In summary, here are the major takeaways from this demonstration:

  • The system can be easily extended by adding more knowledge sources like PotHoleDataKS, TrafficSignalDataKS, etc.
  • In the framework discussed above, I have used JDK’s Observer and Observable classes for the brevity of discussion. Since these classes are deprecated in Java 9, the reader may consider using alternatives like the Java 9 Flow API, etc.
  • Currently, the controller is not receiving any feedback from the knowledge sources. It would be interesting to modify the controller to receive feedback and then iterate on the task submissions to intelligently react (see, for instance, ref#4).
Data stream

Opinions expressed by DZone contributors are their own.

Related

  • The Data (Pipeline) Movement: A Guide to Real-Time Data Streaming and Future Proofing Through AI Automation and Vector Databases
  • Leveraging Apache Flink Dashboard for Real-Time Data Processing in AWS Apache Flink Managed Service
  • Real-Time Data Streaming on Cloud Platforms: Leveraging Cloud Features for Real-Time Insights
  • Stream Processing in the Serverless World

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!