Droneforge SDK

repository·main·Indexed 42 days ago

https://github.com/droneforge/nimbusos-desktop

An 'App Experience' for drone development providing a visual and expression-based environment to build, deploy, and monitor autonomous drone agents. Includes the DroneForge CRSF Service for telemetry via shared memory IPC, a C++ TelemetryClient API for accessing AttitudeData, BatteryData, and LinkData, and a system for defining agent behavior using expressions, PID controllers, and sensor inputs.

Tokens
7.9K
Snippets
23
Records
33
Agent score
53%

What's inside Droneforge

  1. Validate Telemetry Data Freshness

    main

    Because telemetry is provided via shared memory, you should validate the age of the data using the timestamp_us field (microseconds since epoch) provided in every data structure. This ensures you are not acting on stale data if the service or hardware stops updating.

    Example logic to check if data is less than 100ms old:

    auto battery = client.getBattery();
    if (battery) {
        auto age_ms = (getCurrentTimestamp() - battery->timestamp_us) / 1000;
        if (age_ms < 100) {
            // Data is fresh
        }
    }
    auto battery = client.getBattery();
    if (battery) {
        auto age_ms = (getCurrentTimestamp() - battery->timestamp_us) / 1000;
        if (age_ms < 100) {  // Data is fresh (< 100ms old)
            // Use battery data
        }
    }
  2. How expressions work in Droneforge

    main

    Expressions are the primary way to define agent behavior by connecting inputs (sensors/parameters) to outputs (controls).

    Workflow:

    1. Create a node (building block).
    2. Connect nodes via lines (connecting an output of one block to an input of another).
    3. Write an expression in the block to describe the logic.

    Syntax Rules:

    • Single Formulas: An expression can be a simple math formula like input * 0.5.
    • Multiple Assignments: Use semicolons to assign multiple outputs in one block, e.g., roll = 992; throttle = 200.
    • Order of Operations: Use parentheses to control evaluation, e.g., (a + b) * 2.
    • Operators: Supports math (+, -, *, /), comparisons (==, !=, <, <=, >, >=), and logic (&&, ||).
    // Example: Multiple assignments
    roll = 992; pitch = 992; yaw = 992; throttle = 200
  3. Run the DroneForge CRSF Service

    main

    The service requires a serial port and a baud rate as command line arguments.

    Common Baud Rates:

    • 420000 (ExpressLRS/ELRS)
    • 400000 (ELRS alternative)
    • 115200 (Standard CRSF)

    Execution Examples:

    Linux:

    ./droneforge-crsf-service /dev/ttyUSB0 420000

    Windows:

    droneforge-crsf-service.exe COM3 420000
    # Linux
    ./droneforge-crsf-service /dev/ttyUSB0 420000
    
    # Windows
    droneforge-crsf-service.exe COM3 420000
  4. Build the DroneForge CRSF Service

    main

    To build the service from source, you need a C++17 compatible compiler (GCC 8+, Clang 7+, or MSVC 2019+), CMake 3.16+, and the fmt library.

    Linux/macOS

    # Clone and enter directory
    git clone https://github.com/your-org/droneforge-crsf-service.git
    cd droneforge-crsf-service
    
    # Build
    mkdir build && cd build
    cmake ..
    make -j$(nproc)

    Windows (Visual Studio)

    # Clone and enter directory
    git clone https://github.com/your-org/droneforge-crsf-service.git
    cd droneforge-crsf-service
    
    # Build
    mkdir build && cd build
    cmake .. -G "Visual Studio 16 2019"
    cmake --build . --config Release
    mkdir build && cd build
    cmake ..
    make -j$(nproc)
  5. Integrate the TelemetryClient in C++

    main

    The Client Interface is a header-only MIT-licensed library. To use it, include client/telemetry_client.h and use the droneforge::telemetry namespace.

    Basic Usage Pattern

    1. Instantiate TelemetryClient.
    2. Call .connect() to establish the shared memory IPC connection.
    3. Use .isConnected() to monitor the connection state.
    4. Use getter methods like .getAttitude(), .getBattery(), and .getLink() which return std::optional objects containing the data.

    Singleton Pattern for Global Access

    For access throughout your application without passing instances, use TelemetryStore::getInstance():

    TelemetryStore::getInstance().connect();
    auto attitude = TelemetryStore::getInstance().getAttitude();
    #include "client/telemetry_client.h"
    using namespace droneforge::telemetry;
    
    int main() {
        TelemetryClient client;
        
        if (!client.connect()) {
            std::cerr << "Failed to connect to CRSF service" << std::endl;
            return 1;
        }
        
        while (client.isConnected()) {
            auto attitude = client.getAttitude();
            if (attitude) {
                std::cout << "Pitch: " << attitude->pitch << "°" << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        return 0;
    }
  6. Deploy an agent to the drone

    main

    To run your agent on a drone:

    1. Select Agent: In the Build Tab, ensure the correct agent is selected in the left sidebar.
    2. Connect (VRX Controls):
      • Bind: Put your drone into bind mode (unplug/plug 3 times until flashes change from quick to single). Click Bind in the Deploy tab.
      • Scan: If the video feed is black, use Scan to cycle 5.8 GHz bands until the transmission is found.
    3. Configure Profile: In the Deploy Tab, set the drone's mass and KV for accurate physics.
    4. Execute: Click Play to deploy the agent. Click Pause to stop it.

    Warning: Always keep a hand on your safety switch or emergency stop when playing.

  7. Use the Telemetry Shared Memory interface

    main

    The CRSF Service exposes real-time telemetry data through a shared memory segment named droneforge_crsf_telemetry. Consumers can map this memory to access high-frequency updates without the overhead of network or socket-based IPC.

    The shared memory structure TelemetrySharedMemory contains the following data groups:

    Control & Status

    • sequence_number: An atomic uint32_t that increments every 10ms. Consumers should monitor this to detect new data updates.
    • service_running: Atomic bool indicating if the service is active.
    • error_count: Atomic uint32_t tracking the number of errors.
    • last_error: A char[256] buffer containing the most recent error string.

    Attitude Data

    Accessible via attitude struct. Validated by attitude_valid (atomic bool).

    • pitch: float
    • roll: float
    • yaw: float
    • timestamp_us: uint64_t (microseconds)

    Battery Data

    Accessible via battery struct. Validated by battery_valid (atomic bool).

    • voltage: float
    • current: float
    • capacity: float
    • remaining_percent: uint8_t
    • timestamp_us: uint64_t (microseconds)

    Accessible via link struct. Validated by link_valid (atomic bool).

    • uplink_rssi: float
    • downlink_rssi: float
    • link_quality: uint8_t
    • snr: int8_t
    • timestamp_us: uint64_t (microseconds)
  8. Use the TelemetryStore singleton for global telemetry access

    main

    For most applications, use the TelemetryStore singleton to access real-time telemetry data. It provides a convenient, globally accessible interface that manages an internal TelemetryClient instance.

    To use it, call getInstance() to get the singleton reference, then call connect() to establish the shared memory connection before attempting to read data.

    #include "telemetry_client.h"
    
    void monitor_drone() {
        auto& telemetry = droneforge::telemetry::TelemetryStore::getInstance();
    
        if (telemetry.connect()) {
            while (telemetry.isConnected()) {
                if (telemetry.hasNewData()) {
                    auto attitude = telemetry.getAttitude();
                    if (attitude) {
                        // Use attitude->pitch, attitude->roll, etc.
                    }
                }
            }
        }
    }
  9. Troubleshoot DroneForge CRSF Service

    main

    Service fails to start

    • Linux Permissions: Ensure the user has serial port access: sudo usermod -a -G dialout $USER.
    • Device Path: Verify the device exists using ls /dev/ttyUSB* or ls /dev/ttyACM*.
    • Baud Rate: Ensure the baud rate matches your CRSF device configuration (e.g., 420000 for ELRS).

    No telemetry data

    • Verify the CRSF device is actually transmitting telemetry.
    • Check flight controller CRSF telemetry settings.
    • Monitor the service output for CRSF frame reception messages.

    Client connection fails

    • Service Status: Ensure the service is running before starting your client application.
    • Shared Memory Permissions: On Linux, check access to /dev/shm/.
    • Conflicts: Ensure no other processes are using the shared memory segment.
  10. Examples of common agent behaviors

    main

    Practical expression snippets for common tasks:

    PID Control for Yaw: Center on a target when it is visible:

    yaw = pid(320, target_x, 0.8, 0.0, 0.1, 300)

    Smooth Ramping: Smoothly ramp throttle over 2 seconds:

    throttle = lin(172, 600, 2.0)

    Latching/Memory: Latch a flag (aux1) the first time a target appears:

    aux1 = mem(1800, target_visible, 0, 1)

    One-time Randomization: Apply a randomized hover nudge that is evaluated only once:

    roll = once(randrange(50)) - 25
    // Center on a target when it is visible
    yaw = pid(320, target_x, 0.8, 0.0, 0.1, 300)
  11. Reference: TelemetryClient API

    main

    The TelemetryClient class provides the primary interface for accessing telemetry via shared memory IPC.

    MethodReturn TypeDescription
    bool connect()boolConnect to shared memory
    void disconnect()voidDisconnect from shared memory
    bool isConnected()boolCheck connection status
    std::optional<AttitudeData> getAttitude()std::optional<AttitudeData>Get attitude data
    std::optional<BatteryData> getBattery()std::optional<BatteryData>Get battery data
    std::optional<LinkData> getLink()std::optional<LinkData>Get link statistics
    bool hasNewData()boolCheck for new data since last call
  12. Use Droneforge expression functions

    main

    The following functions are available for complex logic within expressions:

    FunctionDescription
    if(condition, true_val, false_val)Standard conditional logic
    lin(start, end, duration)Ramps from start to end over a specified duration
    pid(setpoint, pv, kp, ki, kd [, max_output])PID controller (setpoint, process variable, gains)
    mem(value [, condition [, reset [, edge_once]]])Stores and holds a value
    once(expr)Evaluates an expression once and caches the result
    acceleration(desired_accel [, motor_efficiency])Returns throttle needed for vertical acceleration
    rand()Random float 0..1
    randn()Random normal (mean 0, std 1)
    randint(max)Integer 0..max-1
    randrange(max)Float 0..max
    sin, cos, tan, sqrt, abs, floor, ceil, signStandard math functions
    min(a,b), max(a,b)Comparison functions