サンプル

Simulator SDK デモ

Simulator SDKを使って、独自のドメイン特化シミュレータを高速構築できます。

最小構成

2つのコールバック関数だけでシミュレータを実装:

// my_simulator.c
#include "lcvm_sim.h"

typedef struct {
    double robot_x, robot_y;
    double target_x, target_y;
} RobotState;

// FFI: ロボットの目標位置設定
static void ffi_set_target(LcvmState *vm) {
    double y = lcvm_pop_double(vm);
    double x = lcvm_pop_double(vm);
    RobotState *state = (RobotState *)vm->user_data;
    state->target_x = x;
    state->target_y = y;
}

// FFI: 現在位置 X 取得
static void ffi_get_position_x(LcvmState *vm) {
    RobotState *state = (RobotState *)vm->user_data;
    lcvm_push_double(vm, state->robot_x);
}

// FFI: 現在位置 Y 取得
static void ffi_get_position_y(LcvmState *vm) {
    RobotState *state = (RobotState *)vm->user_data;
    lcvm_push_double(vm, state->robot_y);
}

// 初期化コールバック
void on_init(LcvmState *vm, void *user) {
    lcvm_register_function_ex("set_target",     ffi_set_target,     2, "dd");
    lcvm_register_function_ex("get_position_x", ffi_get_position_x, 0, "");
    lcvm_register_function_ex("get_position_y", ffi_get_position_y, 0, "");
}

// フレーム更新コールバック
void on_frame(LcvmState *vm, float dt, void *user) {
    RobotState *state = (RobotState *)user;

    // 簡易的な移動シミュレーション
    double dx = state->target_x - state->robot_x;
    double dy = state->target_y - state->robot_y;
    state->robot_x += dx * dt * 0.5;
    state->robot_y += dy * dt * 0.5;

    // 描画(RayLibなど)
    // draw_robot(state->robot_x, state->robot_y);
}

int main() {
    RobotState state = {0, 0, 0, 0};

    lcvm_sim_config_t config = {
        .script_path       = "robot.c",
        .enable_hot_reload = 1,
        .on_init           = on_init,
        .on_frame          = on_frame,
        .user_data         = &state,
    };

    lcvm_sim_ctx_t *sim = lcvm_sim_create(&config);
    if (!sim) return 1;

    while (!should_quit()) {
        lcvm_sim_update(sim, get_frame_dt());
    }
    lcvm_sim_destroy(sim);
    return 0;
}

制御スクリプト

Lambda C は C 言語サブセットであり多値戻りをサポートしないため、座標取得は軸ごとの関数に分けます。

// robot.c
void   set_target(double x, double y);
double get_position_x();
double get_position_y();

int main() {
    // 目標位置を設定
    set_target(10.0, 5.0);

    // 到着するまで待機
    while (1) {
        double x = get_position_x();
        double y = get_position_y();

        double dx = 10.0 - x;
        double dy = 5.0 - y;
        if (dx * dx + dy * dy < 0.01) break;  // 到着判定
    }
    return 0;
}

ドローン配送デモ

Lambda C パッケージには、Lambda C スクリプトで制御される完全なドローン配送シミュレーション (demos/raylib_drone/) が含まれています。地点 A で荷物をピックアップし、B に配送、A に戻るミッションを、11 状態のステートマシンと簡易 PID 制御で実装。R キーで飛行中に 2 つの経路スクリプト — 単純往復 delivery.c (A→B→A) と巡回 patrol.c (A→B→C→A) — を切り替えられます。

設計思想、スクリプトとホストの責務分担、Simulator SDK との関係、操作キーやビルド手順については ドローンデモ ページで詳しく解説しています。


関数ポインタ

関数ポインタを使ったコールバックパターン:

// callback.c

// 関数ポインタ型
typedef int (*operation_t)(int, int);

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }

// 関数ポインタを引数に取る
int apply(operation_t op, int x, int y) {
    return op(x, y);
}

int main() {
    int result;

    result = apply(add, 10, 5);  // 15
    printf("add: %d\n", result);

    result = apply(sub, 10, 5);  // 5
    printf("sub: %d\n", result);

    result = apply(mul, 10, 5);  // 50
    printf("mul: %d\n", result);

    return 0;
}

状態遷移テーブル

// state_table.c
typedef void (*state_handler_t)();

void state_idle()   { printf("IDLE\n"); }
void state_run()    { printf("RUN\n"); }
void state_stop()   { printf("STOP\n"); }

state_handler_t handlers[3] = { state_idle, state_run, state_stop };

int main() {
    int state = 0;
    int i;

    for (i = 0; i < 3; i++) {
        handlers[state]();
        state = state + 1;
    }

    return 0;
}

enumとunion

enum(列挙型)

// enum_example.c

// 自動インクリメント
enum Color {
    RED,      // 0
    GREEN,    // 1
    BLUE      // 2
};

// 明示的な値指定
enum Status {
    OK = 0,
    ERROR = -1,
    TIMEOUT = -2
};

int main() {
    enum Color c = GREEN;
    enum Status s = OK;

    printf("Color: %d\n", c);   // 1
    printf("Status: %d\n", s);  // 0

    return 0;
}

union(共用体)

// union_example.c

union Value {
    int i;
    double d;
    char *s;
};

struct Variant {
    int type;  // 0=int, 1=double, 2=string
    union Value val;
};

void print_variant(struct Variant *v) {
    switch (v->type) {
        case 0: printf("int: %d\n", v->val.i); break;
        case 1: printf("double: %f\n", v->val.d); break;
        case 2: printf("string: %s\n", v->val.s); break;
    }
}

int main() {
    struct Variant v;

    v.type = 0;
    v.val.i = 42;
    print_variant(&v);

    v.type = 1;
    v.val.d = 3.14;
    print_variant(&v);

    return 0;
}

Heap Watermark活用

一時メモリの効率的な管理:

// heap_watermark.c

int process_data() {
    int mark = heap_mark();  // 現在位置を記録

    // 大量の一時メモリを確保
    char *buffer = malloc(1024);
    int *array = malloc(100 * sizeof(int));

    // ... 処理 ...

    heap_release(mark);  // 一括解放(O(1))
    return 0;
}

int main() {
    int i;
    for (i = 0; i < 1000; i++) {
        process_data();  // メモリリークなし
    }
    return 0;
}

GPIO制御

// gpio_blink.c
void gpio_write(int pin, int value);
void delay_ms(int ms);

int main() {
    int i;
    for (i = 0; i < 10; i++) {
        gpio_write(13, 1);  // LED点灯
        delay_ms(500);
        gpio_write(13, 0);  // LED消灯
        delay_ms(500);
    }
    return 0;
}

ホストFFI実装

void ffi_gpio_write(LcvmState *vm) {
    int value = lcvm_pop_int(vm);
    int pin = lcvm_pop_int(vm);

    // ピン番号の妥当性チェック
    if (pin < 0 || pin >= NUM_GPIO_PINS) {
        lcvm_record_error(vm, "Invalid GPIO pin", 0);
        return;
    }

    // ハードウェアアクセス
    HAL_GPIO_WritePin(GPIO_PORT, pin, value);
}

// 型シグネチャ付きで登録
lcvm_register_function_ex("gpio_write", ffi_gpio_write, 2, "ii");

センサー読み取り

// temperature_monitor.c
double read_temperature();
void send_alert(double temp);

int main() {
    double temp;
    int i;

    for (i = 0; i < 100; i++) {
        temp = read_temperature();
        printf("Temperature: %.2f C\n", temp);

        if (temp > 85.0) {
            send_alert(temp);
            break;
        }

        delay_ms(1000);
    }

    return 0;
}

PIDコントローラー

// pid_control.c
double setpoint = 100.0;
double kp = 1.0, ki = 0.1, kd = 0.05;
double integral = 0.0, prev_error = 0.0;

double get_sensor_value();
void set_actuator(double value);
void delay_ms(int ms);

double pid_update(double measured) {
    double error = setpoint - measured;
    double proportional = kp * error;

    integral = integral + error;
    double integral_term = ki * integral;

    double derivative = error - prev_error;
    double derivative_term = kd * derivative;

    prev_error = error;

    return proportional + integral_term + derivative_term;
}

int main() {
    int i;
    for (i = 0; i < 1000; i++) {
        double measured = get_sensor_value();
        double output = pid_update(measured);
        set_actuator(output);
        delay_ms(10);
    }
    return 0;
}

ステートマシン

// state_machine.c
int state = 0;
int counter = 0;

void led_on();
void led_off();
int button_pressed();
void delay_ms(int ms);

int main() {
    while (1) {
        switch (state) {
            case 0:  // IDLE
                led_off();
                if (button_pressed()) {
                    state = 1;
                    counter = 0;
                }
                break;

            case 1:  // RUNNING
                led_on();
                counter = counter + 1;
                if (counter >= 100) {
                    state = 2;
                }
                break;

            case 2:  // DONE
                led_off();
                if (button_pressed()) {
                    state = 0;
                }
                break;
        }
        delay_ms(10);
    }
    return 0;
}