Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rae default app #18

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
1 change: 1 addition & 0 deletions Working with RAE/RAE Default App/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":"0.2.0","configurations":[{"type":"robothub","request":"launch","name":"Run"}]}
8 changes: 8 additions & 0 deletions Working with RAE/RAE Default App/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"evenBetterToml.taplo.configFile.path": "/.vscode/taplo.toml",
"eslint.workingDirectories": [
"src"
],
"editor.tabSize": 2,
"ros.distro": "humble"
}
46 changes: 46 additions & 0 deletions Working with RAE/RAE Default App/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# RAE Default App

This is an example of Default RAE App source code. In this example, FollowMe Application is used as the default entry point, but others can be specified via `robotapp.toml` file.

## Features
- FollowMe Application - robot is put in a mode that follows a person standing in front or in the back of the robot
- A FrontEnd application is launched to publish images on LCD screen or control LED behavior
- Abstractions are provided for the robot class that allow customizing the App behavior, or creation of a whole different app
- It is possible to set up streams for both RobotHub using provided wrappers

## Requirements

- RAE

## Dependencies
- ROS2 Humble
- DepthAI libraries
- RobotHub

## Concepts of Robot classes

1. The Robot Class: An Integrated Approach to Robotics Control

The Robot class stands as a cornerstone in robotics control, encapsulating various functionalities such as movement, display, and LED control. This class serves as a bridge between hardware components and higher-level software commands, thereby streamlining robotics operations.

Key Functionalities:
- Movement Control: By integrating a MovementController, the Robot class allows for precise control over the robot's motion, enabling it to navigate its environment effectively.
- Visual Feedback: The incorporation of a DisplayController and LEDController provides visual feedback and communication capabilities, enhancing human-robot interaction.
- Battery Monitoring: The class also includes a method for battery state monitoring, ensuring the robot operates within its energy constraints.


2. The Camera Class: Enhancing Perception in Robotics

The Camera class is instrumental in augmenting a robot's perception of its surroundings. It leverages the depthai and robothub libraries to manage camera functionalities, which are vital for tasks like navigation, object recognition, and environmental interaction.

- Stream Management: It enables the creation and management of video streams through RobotHub and ROS, crucial for real-time visual data processing.
- Hardware Interface: The class interacts directly with the camera hardware, ensuring optimal utilization of the device's capabilities.
- Flexibility and Control: The inclusion of methods for starting and stopping the camera pipeline, and for publishing video data, provides flexibility and control over the camera operations.

3. The ROS2Manager Class: Streamlining ROS2 Operations

The ROS2Manager class encapsulates the management of ROS2 functionalities. ROS2 (Robot Operating System 2) is an essential component in modern robotics, providing tools for communication between different parts of a robotic system.

- Node Management: This class is responsible for creating and managing nodes in ROS2, facilitating communication between different components of a robot.
- Publisher and Subscriber System: It includes a comprehensive system for creating publishers, subscribers, clients etc. allowing the robot to interact with various data streams efficiently.
- Lifecycle Management: The class also manages the lifecycle of ROS2 processes, including starting and stopping nodes and context, ensuring robust and reliable operations.

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
172 changes: 172 additions & 0 deletions Working with RAE/RAE Default App/frontend/assets/main-638d2ebc.js

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Working with RAE/RAE Default App/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Rae - Robotics Access for Everyone</title>
<link rel="icon" type="image/x-icon" href="./assets/luxonis_logo-d1be00fb.svg">
<script type="module" crossorigin src="./assets/main-638d2ebc.js"></script>
<link rel="stylesheet" href="./assets/index-37243f35.css">
</head>
<body>
<div id="root"></div>

</body>
</html>
Empty file.
19 changes: 19 additions & 0 deletions Working with RAE/RAE Default App/robotapp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
config_version = "2.0"
configuration = []

[info]
name = "RAE Default Application"
description = "RAE Default Application"

[runtime]
application = "src/follow_me.py#Application"
workdir = "/app"
pre_launch = "export ROS_DOMAIN_ID=30\n. /opt/ros/$ROS_DISTRO/setup.sh\n. /ws/install/setup.sh && export LD_LIBRARY_PATH=/usr/local/lib/python3.10/dist-packages${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
permissions = ["rae-peripherals"]

[runtime.frontend]
redirectToIndex = true

[runtime.runs_on]
type = "image"
name = "luxonis/rae-ros-robot:dai_ros_py_sound"
Empty file.
Empty file.
127 changes: 127 additions & 0 deletions Working with RAE/RAE Default App/src/api/performance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import logging as log
import logging
import tracemalloc
from collections import defaultdict
from time import perf_counter
from typing import Any, Callable


def measure_performance(func: Callable[..., Any]) -> Callable[..., Any]:
def wrapper(*args: Any, **kwargs: Any) -> Any:
start_time = perf_counter()
value = func(*args, **kwargs)
end_time = perf_counter()
run_time = end_time - start_time
print(f"Execution of {func.__name__} took {run_time:.5f} seconds,")
return value

return wrapper


def default_value_10_000_000() -> float:
return 10_000_000.


# lock = Lock()
list_of_durations = defaultdict(list)
maximum_duration: dict[Any, float] = defaultdict(float)
minimum_duration: dict[Any, float] = defaultdict(default_value_10_000_000)
last_report_at: dict[Any, float] = defaultdict(float)
# maximum_duration = 0 # in seconds
# minimum_duration = 10_000_000 # in seconds
# last_report_at = perf_counter()


def measure_average_performance(func: Callable[..., Any]) -> Callable[..., Any]:
"""Report once every 5 minutes what the averages function duration is and what the max and min durations are."""

def wrapper(*args: Any, **kwargs: Any) -> Any:
global maximum_duration, minimum_duration, last_report_at
start_time = perf_counter()
value = func(*args, **kwargs)
end_time = perf_counter()
try:
run_time = end_time - start_time
if run_time > maximum_duration[func]:
maximum_duration[func] = run_time
if run_time < minimum_duration[func]:
minimum_duration[func] = run_time
list_of_durations[func].append(run_time)
list_of_durations[func].append(run_time)

now = perf_counter()
if now - last_report_at[func] > 1 * 60 * 5 and list_of_durations[func]:
average_duration = sum(list_of_durations[func]) / len(list_of_durations[func])
log.info("*" * 30)
log.info(f"Performance stats for {func.__name__}:")
log.info(f"Avarage duration: {average_duration * 1000} ms, Maximum duration: {maximum_duration[func] * 1000} ms, Minimal duration: {minimum_duration[func] * 1000} ms")
log.info("*" * 30)
list_of_durations[func].clear()
maximum_duration[func], minimum_duration[func] = 0, 10_000_000
last_report_at[func] = perf_counter()
except Exception:
pass
return value

return wrapper


last_call_at = perf_counter()
last_report_call_frequency_at = perf_counter()
call_frequency_memory = defaultdict(list)


def measure_call_frequency(func: Callable[..., Any]) -> Callable[..., Any]:
"""Measure how often a given function is called."""

def wrapper(*args: Any, **kwargs: Any) -> Any:
global call_frequency_memory, last_call_at, last_report_call_frequency_at
start_time = perf_counter()
# time from last call
time_from_last_call = start_time - last_call_at
last_call_at = start_time
call_frequency_memory[func].append(time_from_last_call)
if start_time - last_report_call_frequency_at > 1 * 60 * 1 and call_frequency_memory[func]:
average_call_time = sum(call_frequency_memory[func]) / len(call_frequency_memory[func])
maximal_call_time = max(call_frequency_memory[func])
print("*" * 50)
print(f"Calls per second stats for {func.__name__}:")
print(f"Average calls per second (CPS): {1 / average_call_time}, Minimal CPS: {1 / maximal_call_time}")
print("*" * 50)
call_frequency_memory[func].clear()
last_report_call_frequency_at = start_time

value = func(*args, **kwargs)
return value

return wrapper


def with_sql_exception_handling(func: Callable[..., Any]) -> Callable[..., Any]:
"""Try sql operation command, catch exception and finally close the cursor and the connection."""
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
return result
except Exception as e:
log.critical(f"Couldn't perform sql operation: {repr(e)}")
return None

return wrapper


def trace_memory(func: Callable[..., Any]) -> Callable[..., Any]:
"""Trace RAM usage."""
def wrapper(*args: Any, **kwargs: Any) -> Any:
tracemalloc.start()
value = func(*args, **kwargs)
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
log.info("*" * 50)
log.info(f"TOP 10 RAM usage for {func.__name__}:")
for stat in top_stats[:10]:
log.info(stat)
log.info("*" * 50)
return value

return wrapper
Empty file.
Loading