Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions gaia_slam/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
logs/
.gaia_slam_db
cmake-*
.idea
.clang-tidy
.clang-format
74 changes: 74 additions & 0 deletions gaia_slam/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
###################################################
# Copyright (c) Gaia Platform LLC
#
# Use of this source code is governed by the MIT
# license that can be found in the LICENSE.txt file
# or at https://opensource.org/licenses/MIT.
###################################################

cmake_minimum_required(VERSION 3.16)

project(gaia_slam)

set(CMAKE_CXX_STANDARD 17)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why C++17? Do we use C++17 features in this app?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but I don't see a reason not to use it. (Well actually i do since our DAC headers use C++17 features, it's a known problem, better to proactively avoid warnings).


# We need pthreads support.
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED)

include("/opt/gaia/cmake/gaia.cmake")

# --- Generate Direct Access classes from DDL---
process_schema(
DDL_FILE ${PROJECT_SOURCE_DIR}/gaia/gaia_slam.ddl
DATABASE_NAME gaia_slam
)

# -- Translate ruleset into CPP --
translate_ruleset(
RULESET_FILE ${PROJECT_SOURCE_DIR}/gaia/gaia_slam.ruleset
DATABASE_NAME gaia_slam
CLANG_PARAMS
-I ${PROJECT_SOURCE_DIR}/include
)

#
# Direct Access Example
#
add_executable(gaia_slam_direct_access
src/main_direct_access.cpp
src/graph.cpp
)

target_add_gaia_generated_sources(gaia_slam_direct_access)

target_include_directories(gaia_slam_direct_access PRIVATE
${GAIA_INC}
${PROJECT_SOURCE_DIR}/include
)

target_link_libraries(gaia_slam_direct_access PRIVATE
${GAIA_LIB}
Threads::Threads
)

#
# Rules Example
#
add_executable(gaia_slam_rules
src/main_rules.cpp
src/graph.cpp
)

target_add_gaia_generated_sources(gaia_slam_rules)

target_include_directories(gaia_slam_rules PRIVATE
${GAIA_INC}
${PROJECT_SOURCE_DIR}/include
)

target_link_libraries(gaia_slam_rules PRIVATE
${GAIA_LIB}
Threads::Threads
)
11 changes: 11 additions & 0 deletions gaia_slam/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
This example aims at teaching Gaia concepts using a real-world use-case scenario (SLAM). This is only for demonstration
purposes and does not aim to be a real solution for SLAM.

Still, you can appreciate some elements of Graph-based SLAM such as the graph data structure (`graph`, `vertex`, `edge`),
observation of the external world (`incoming_data_event`), and building of the graph elements when new observations come in.

The data model (the SLAM graph) is defined in `gaia/gaia_slam.ddl`, while the rules are defined in `gaia/gaia_slam.ruleset`.

There are two executables:
1. `src/main_rules.cpp`: Shows how mutating the database triggers the rules in `gaia/gaia_slam.ruleset`.
2. `src/main_direct_access.cpp`: Show how to manipulate the Graph using the direct_access API. This code does not use rules.
69 changes: 69 additions & 0 deletions gaia_slam/gaia/gaia_slam.ddl
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
----------------------------------------------------
-- Copyright (c) Gaia Platform LLC
--
-- Use of this source code is governed by the MIT
-- license that can be found in the LICENSE.txt file
-- or at https://opensource.org/licenses/MIT.
----------------------------------------------------

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tables need an explainer - how do these get used?
I saw that there are comments in the ruleset that talks about hooking rules up to table events,
but "what is this table representing" should be described here as clearly as possible.
Especially the incoming_data_event which won't make sense reading here and is tough to follow from the ruleset.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a little explanation but not too detail since I'm no SLAM expert.

database gaia_slam

-- SLAM graph.
table graph

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to what bill said. plus a good part in the readme.md detailing why, what, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added readme with pretty much same code content as the description of this PR.

(
id string unique,
vertices references vertex[],
edges references edge[]
)

-- A new vertex is created every time a new observation from a sensor is done.
table vertex
(
id uint64 unique,
type uint8,
data uint8[],
pose_x double,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an obvious candidate for a nested struct when we support complex types...

pose_y double,

-- Relationships
graph_id string,
graph references graph
using vertices
where vertex.graph_id = graph.id,

in_edges references edge[],
out_edges references edge[]
)

-- A new edge is created between every new created vertex and the previous one.
table edge
(
id uint64 unique,

-- Relationships
graph_id string,
graph references graph
where edge.graph_id = graph.id,

dest_id uint64,
dest references vertex
using in_edges
where edge.dest_id = vertex.id,

src_id uint64,
src references vertex
using out_edges
where edge.src_id = vertex.id
)

-- This table represents a generic input from a sensor.
-- i.e. Lidar sensor, image, etc..
-- Note: this is just an example.
table incoming_data_event
(
id uint64 unique,
type uint8,
data uint8[],
pose_x double,
pose_y double
)
91 changes: 91 additions & 0 deletions gaia_slam/gaia/gaia_slam.ruleset
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
////////////////////////////////////////////////////
// Copyright (c) Gaia Platform LLC
//
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE.txt file
// or at https://opensource.org/licenses/MIT.
////////////////////////////////////////////////////

#include <atomic>

#include <gaia/logger.hpp>

constexpr uint64_t c_first_vertex_id = 0;
std::atomic<uint64_t> c_last_edge_id = 0;

// The serial_group() keyword makes the rules in the ruleset run serially:
// only one rule is executed at a time. This prevents transaction
// conflicts when modifying the same data.
ruleset data_processing : serial_group()
{

// Process an incoming data event by creating a new vertex in the graph.
on_insert(incoming_data_event)
{
gaia_log::app().info("Processing incoming_data_event(id: {})", id);

// The loop below finds the maximum id value for existing edge ids.
// In practice, we wouldn't search the table each time to look for
// a new ID. This is only done to provide an example of searching
// a table.
// The leading slash '/' means to search through all records in the
// specified table.
uint64_t max_vertex_id = c_first_vertex_id;
for (/v:vertex)
{
if (v.id > max_vertex_id)
{
max_vertex_id = v.id;
}
}

uint64_t new_vertex_id = max_vertex_id + 1;
gaia_log::app().info("Creating new vertex(id: {})", new_vertex_id);

// Create a vertex record
std::string graph_id_string("graph_1");
vertex.insert(
id: new_vertex_id,
type: incoming_data_event.type,
data: incoming_data_event.data,
pose_x: incoming_data_event.pose_x,
pose_y: incoming_data_event.pose_y,
graph_id: graph_id_string.c_str()
);
}

// Process each new vertex by creating an edge with the previous existing vertex.
on_insert(vertex)
{
gaia_log::app().info("Processing vertex(id: {})", id);

if (vertex.id == c_first_vertex_id)
{
return;
}

uint64_t prev_vertex_id = vertex.id - 1;
uint64_t new_edge_id = c_last_edge_id.fetch_add(1);
gaia_log::app().info(
"Creating edge(id: {}) with between vertex(id: {}) and vertex(id: {})",
new_edge_id, prev_vertex_id, vertex.id);

edge.insert(
id: new_edge_id,
graph_id: vertex.graph_id,
src_id: prev_vertex_id,
dest_id: vertex.id);
}
}

ruleset graph_processing
{
// Reacts to the creation of an edge by just printing it.
// This rule could contain part of the SALM algorithm.
on_insert(edge)
{
gaia_log::app().info(
"Created edge(id: {}) vertex(id: {}) -> vertex(id: {})",
edge.id, edge.src_id, edge.dest_id);
}
}
24 changes: 24 additions & 0 deletions gaia_slam/include/graph.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
////////////////////////////////////////////////////
// Copyright (c) Gaia Platform LLC
//
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE.txt file
// or at https://opensource.org/licenses/MIT.
////////////////////////////////////////////////////

#pragma once

#include <gaia_gaia_slam.h>

namespace gaia::gaia_slam::graph
{

graph_t create_graph(const std::string& uuid);

vertex_t create_vertex(const graph_t& graph, int64_t id, int64_t vertex_type);

edge_t create_edge(int64_t id, const vertex_t& src, const vertex_t& dest);

void clear_data();

} // namespace gaia::gaia_slam::graph
20 changes: 20 additions & 0 deletions gaia_slam/include/vertex_types.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
////////////////////////////////////////////////////
// Copyright (c) Gaia Platform LLC
//
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE.txt file
// or at https://opensource.org/licenses/MIT.
////////////////////////////////////////////////////

#pragma once

#include <cstdint>

namespace gaia::gaia_slam::vertex_type
{

static constexpr uint8_t c_lidar_scan = 0;
static constexpr uint8_t c_image_keyframe = 1;
static constexpr uint8_t c_visual_landmark = 2;

} // namespace gaia::gaia_slam::vertex_type
64 changes: 64 additions & 0 deletions gaia_slam/src/graph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
////////////////////////////////////////////////////
// Copyright (c) Gaia Platform LLC
//
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE.txt file
// or at https://opensource.org/licenses/MIT.
////////////////////////////////////////////////////

#include "graph.hpp"

namespace gaia::gaia_slam::graph
{

graph_t create_graph(const std::string& id)
{
return graph_t::get(graph_t::insert_row(id.c_str()));
}

vertex_t create_vertex(const graph_t& graph, int64_t id, int64_t vertex_type)
{
vertex_writer vertex_w;
vertex_w.id = id;
vertex_w.type = vertex_type;
vertex_w.graph_id = graph.id();

return vertex_t::get(vertex_w.insert_row());
}

edge_t create_edge(int64_t id, const vertex_t& src, const vertex_t& dest)
{
if (src.graph_id() == dest.graph_id())
{
throw std::runtime_error("You cannot create an edge from vertices in different graphs!");
}

edge_writer edge_w;
edge_w.id = id;
edge_w.src_id = src.id();
edge_w.dest_id = dest.id();
edge_w.graph_id = src.graph_id();

return edge_t::get(edge_w.insert_row());
}

template <typename T_type>
void clear_table()
{
for (auto obj_it = T_type::list().begin();
obj_it != T_type::list().end();)
{
auto next_obj_it = obj_it++;
next_obj_it->delete_row();
}
}

void clear_data()
{
clear_table<edge_t>();
clear_table<vertex_t>();
clear_table<graph_t>();
clear_table<incoming_data_event_t>();
}

} // namespace gaia::gaia_slam::graph
Loading