Управление квадрокоптером с контроллера Трик

Необходимое оборудование

Для тестирования работы протокола Mailbox не обязательно иметь реальный контроллер Трик, так как среда разработки TrikStudio имеет встроенный симулятор, который поддерживает передачу данных по Mailbox, поэтому, список необходимого оборудования и ПО выглядит следующим образом:

  1. TrikStudio.

  2. PioneerStation.

  3. Квадрокоптер Геоскан Пионер Мини.

  4. Контроллер Трик (опционально).

Подготовка к работе

  1. Ознакомиться с описанием протокола mailbox,

  2. Ознакомиться с описанием api объекта mailbox.

  3. Произвести настройку оборудования в соответсвии с выбранными устройствами по инструкциям

Примечание

Если у вас нет контроллера Трик и вы используете симуляцию в TrikStudio, то в LUA коде при подключении ( ф-я mailbox.connect() ) будет использоваться IP адрес вашего компьютера.

Основные сведения по работе с протоколом Mailbox

Для понимания работы проекта следует ознакомиться с документацией по протоколу mailbox, а также с описанием api объекта mailbox.

Код для квадрокоптера на Lua

-- Simplification and caching table.unpack calls
local unpack = table.unpack

-- Base pcb number of RGB LEDs
local ledNumber = 4
-- RGB LED control port initialize
local leds = Ledbar.new(ledNumber)

-- Function changes color on all LEDs
local function changeColor(color)
    -- Changing color on each LED one after another
    for i=0, ledNumber - 1, 1 do
        leds:set(i, unpack(color))
    end
end

-- Table of colors in RGB form for changeColor function
local colors = {
        {1, 0, 0}, -- r
        {0, 1, 0}, -- g
        {0, 0, 1}, -- b
        {1, 1, 1}, -- w
}


-- Event processing function called automatically by autopilot
function callback(event)

end

function lshift(x, by)
  return x * 2 ^ by
end

function rshift(x, by)
  return math.floor(x / 2 ^ by)
end

function BitAND(a,b)
    local p,c=1,0
    while a>0 and b>0 do
        local ra,rb=a%2,b%2
        if ra+rb>1 then c=c+p end
        a,b,p=(a-ra)/2,(b-rb)/2,p*2
    end
    return c
end

mailbox.setHullNumber(45);

changeColor(colors[lshift(1, 2)]);

-- example of message 0b 001 00000

while(true)
do
    hull, msg = mailbox.receive(true);

    cmd = rshift(BitAND(tonumber(msg), 224), 5);

    if(msg=="0") then
        changeColor({0,0,0});
        break;
    end;

    if(cmd == 1) then
        local r = rshift(BitAND(tonumber(msg), 4), 2);
        local g = rshift(BitAND(tonumber(msg), 2), 1);
        local b = BitAND(tonumber(msg), 1);
        changeColor({r,g,b});
    end
    if(cmd == 2) then
        local val = BitAND(tonumber(msg), 3);

        if(val == 0) then
            data,_,_,_,_ = Sensors.range();
            mailbox.send(hull, data);
        end
        if(val == 1) then
            data = Sensors.altitude();
            mailbox.send(hull, data);
        end
        if(val == 2) then
            roll,pitch,yaw = Sensors.orientation();
            mailbox.send(hull, math.floor(roll));
            mailbox.send(hull, math.floor(pitch));
            mailbox.send(hull, math.floor(yaw));
        end
        if(val == 3) then
            gx,gy,gz = Sensors.gyro();
            mailbox.send(hull, math.floor(gx));
            mailbox.send(hull, math.floor(gy));
            mailbox.send(hull, math.floor(gz));
        end
    end
    if(cmd == 3) then
        local val = BitAND(tonumber(msg), 7);
        if(val == 0) then
            ap.push(Ev.MCE_PREFLIGHT);
        end
        if(val == 4) then
            ap.push(Ev.ENGINES_DISARM);
        end
    end
end

Код для контроллера Трик

// глобальная переменаая-флаг состояния работы программы
var stop = false;

// callback функция, вызываемая, при нажатии на любую кнопку контроллера
// принимает code - код кнопки, и value - значение
// (1 - нажатие, 0 - отпускание, 2 - зажатие)
var keys_handler = function(code, value){
    switch(code){
        case KeysEnum.Up:
            code = 1;
            if(value == 1)  Menu.pointer_l--;
            break;
        case KeysEnum.Left:
            code = 2;
            break;
        case KeysEnum.Down:
            code = 3;
            if(value == 1)  Menu.pointer_l++;
            break;
        case KeysEnum.Power:
            code = 4;
            break;
        case KeysEnum.Esc:
            code = 5;
            if (Menu.deph != 0 && value == 1){
                Menu.deph--;
                Menu.pointer_l = Menu.pointer_h;
            }
            break;
        case KeysEnum.Right:
            code = 6;
            break;
        case KeysEnum.Enter:
            code = 7;
            if(value == 1)  menu_enter_handler();
            break;
    }

    if(code != 7)   draw_menu();
}

var Menu = {
    // главные заголовки меню
    heads: ["RGB LEDS", "SENSORS", "FLYING", "EXIT"],
    // второй уровень меню, каждый элемент массива соответствует главным заголовкам
    lables: [
        ["|-Red", "|-Green", "|-Blue"],
        ["|-Distance", "|-Altitude", "|-Orientation", "|-Gyro"],
        ["|-Arm", "|-Takeoff", "|-Go forward", "|-Go backward", "|-Go left", "|-Go right", "|-Land", "|-Disarm"],
        ["|-Sure exit?"]
    ],
    // действия при нажатии на каждый заголовок второго уровня
    actions: [
        [
            function() {Menu.colors[0] = !Menu.colors[0]; mailbox.send( 45, (1<<5) + Menu.convert_color_to_int() )},
            function() {Menu.colors[1] = !Menu.colors[1]; mailbox.send( 45, (1<<5) + Menu.convert_color_to_int() )},
            function() {Menu.colors[2] = !Menu.colors[2]; mailbox.send( 45, (1<<5) + Menu.convert_color_to_int() )},
        ],
        [
            function() {mailbox.send( 45, (2<<5) + 0); process_dst_parser()},
            function() {mailbox.send( 45, (2<<5) + 1); process_alt_parser()},
            function() {mailbox.send( 45, (2<<5) + 2); process_orientation_parser()},
            function() {mailbox.send( 45, (2<<5) + 3); process_accel_parser()},
        ],
        [
            function() {mailbox.send( 45, (3<<5) + 0)},
            function() {mailbox.send( 45, (3<<5) + 1)},
            function() {mailbox.send( 45, (3<<5) + 2)},
            function() {mailbox.send( 45, (3<<5) + 3)},
            function() {mailbox.send( 45, (3<<5) + 4)},
            function() {mailbox.send( 45, (3<<5) + 5)},
            function() {mailbox.send( 45, (3<<5) + 6)},
            function() {mailbox.send( 45, (3<<5) + 7)},
        ],
        [
            function() {mailbox.send(45, 0); stop=true;}
        ]

    ],
    // указатель текущего уровня погружения в меню (0 - заголовки первого уровня, 1 - второго)
    deph: 0,
    // расстояние в пикселях между элементами меню
    space_y: 20,
    // текущее положение курсора на заголовках первого уровня
    pointer_h: 0,
    // текущее положение курсора на заголовках второго уровня
    pointer_l: 0,
    // состояния RGB светодиодов соответственно
    colors: [false,false,false],
    convert_color_to_int: function(){
        return ((Menu.colors[0]<<2) + (Menu.colors[1]<<1) + (Menu.colors[2]<<0))
    }
}

// отрисовка меню
var draw_menu = function(){
    brick.display().clear()

    if(Menu.deph == 0){
        if(Menu.pointer_l < 0)      Menu.pointer_l = 0;
        if(Menu.pointer_l > Menu.heads.length-1)    Menu.pointer_l = Menu.heads.length-1;

        for (var i=0; i<Menu.heads.length; i++){
            brick.display().addLabel(Menu.heads[i], 25, (i+1)*Menu.space_y)
        }
    }
    else {
        if(Menu.pointer_l < 0)      Menu.pointer_l = 0;
        if(Menu.pointer_l > Menu.lables[Menu.pointer_h].length-1)   Menu.pointer_l = Menu.lables[Menu.pointer_h].length-1;

        for (var i=0; i<Menu.lables[Menu.pointer_h].length; i++){
            brick.display().addLabel(Menu.lables[Menu.pointer_h][i], 25, (i+1)*Menu.space_y)
        }
    }

    brick.display().addLabel("->", 0, (Menu.pointer_l+1)*Menu.space_y)

    brick.display().redraw()
}

//обработка нажатия на Enter, вызывается внутри keys_handler
var menu_enter_handler = function() {
    if(Menu.deph == 0){
        Menu.pointer_h = Menu.pointer_l;
        Menu.pointer_l = 0;
        Menu.deph++;
    }
    else {
        Menu.actions[Menu.pointer_h][Menu.pointer_l]();
    }
    draw_menu()
}

// выводит всплывающее окно с показаниями датчиков (ответ от квадрокоптера)
// принимает массив с именами показаний, которые ожидаются
var process_sensors_parser = function(names) {
    brick.display().clear();
    for(var j=0; j < names.length; j++){
        msg = mailbox.receive();
        brick.display().addLabel(names[j] + Math.round(Number(msg)*100)/100, 25, 70 + (j+1)*20)
    }
    brick.display().redraw();
    script.wait(2000);
    draw_menu();
}

var process_dst_parser = function() {
    var names = ["Distance: "]
    process_sensors_parser(names);
}

var process_alt_parser = function() {
    var names = ["Altitude: "]
    process_sensors_parser(names);
}

var process_orientation_parser = function() {
    var names = ["Roll: ", "Pitch: ", "Azimuth: "]
    process_sensors_parser(names);
}

var process_accel_parser = function() {
    var names = ["Gx: ", "Gy: ", "Gz: "]
    process_sensors_parser(names);
}

var main = function() {
    brick.keys().buttonPressed.connect(keys_handler)
    draw_menu()

    // УКАЖИТЕ ПРАВИЛЬНЫЙ IP КВАДРОКОПТЕРА В ВАШЕЙ СЕТИ
    // если точкой доступа является сам квадрокоптера, ip удрес будет 192.168.4.1
    mailbox.connect("192.168.43.182", 8889)
    script.wait(1000)
    print('Connection should be done')

    // пустой бесконечный цикл с одной лишь командой задержки
    // поскольку все нажатия и команды обрабатываются как callback-функции
    while(!stop){
        script.wait(10)
    }
}