Забавяне на извикването на програмата - C & Linux

Работя върху нов хардуер за активни протези на краката. Моята система има вграден компютър BeagleBone Black RevC и някои персонализирани платки. BeagleBone Black (BBB) ​​работи с Debian Linux.

Написах C конзолно приложение, за да говоря с другите ми дъски от Linux. От терминала мога да изпратя команда като "./plan execute_1 set_pid_gains 10 50 0", за да променя печалбите на контролна верига, работеща на моя двигателен драйвер. Функцията "план" е написана на C. Тя изпраща съобщението чрез SPI.

Програмата работи добре сама, мога да изпращам всички команди, които искам. Въпреки това, когато започнахме да го тестваме от Python (използвайки Popen за извикване на C програмата), осъзнахме, че не се изпълнява толкова бързо, колкото искахме.

За да репликирам и изолирам проблема, написах шел скрипт (fxa_test_z_1), който изпраща 3 команди в моята мрежа:

#!/bin/bash
# Places FlexSEA in Impedance control mode
# Use with care!

# Set control mode to 'z' (4)
./plan execute_1 set_control 4

# Current loop gains:
./plan execute_1 set_current_gains 10 50 0

# Choose from one of these:
./plan execute_1 set_z_gains 1 0 0
echo "FlexSEA in Stiffness mode"

Има 14ms забавяне между всяка функция (измерено с осцилоскоп). Проведох много малки експерименти, за да изолирам проблема. Отварянето и затварянето на SPI порта, изпращането на SPI команди и анализирането на agv[] не са проблем. Ако ги извикам няколко пъти в едно и също извикване на програма, забавянето е от порядъка на 700us между всеки сериен пакет.

Извикването на "nice -n -19 ./fxa_test_z_1" не промени нищо.

==

Какво мога да направя, за да направя тези извиквания на функции по-бързи? Има ли някаква надежда, че мога да ги накарам да минат под ms?

В момента се опитвам да избягвам да правя големи модификации на моя код, тъй като искаме да тестваме нашите контролни цикли утре.

Благодаря!

Джеф


person JFDuval    schedule 12.08.2014    source източник
comment
Това не са функции, които извиквате. Създавате чисто нов процес за изпълнение на самостоятелна програма, наречена plan. Създаването на процес е бавно. Това, което бихте искали да направите вместо това, е да имате plan цикъл на stdin или файл, който чете всяка команда една след друга, така че plan да не се налага да се събаря и стартира отново за всяка команда.   -  person indiv    schedule 13.08.2014
comment
Благодаря индивид! Наясно съм, че стартирам нов процес, а не функция, но целият този софтуер е нов за мен, така че не бях сигурен как да се справя с множество команди в едно извикване на програма. Това изглежда е това, от което се нуждая, нали? stackoverflow.com/questions/9157222/c-wait-for-stdin-read   -  person JFDuval    schedule 13.08.2014
comment
Друга възможност е да преобразувате вашата C програма в разширение на Python.   -  person Deduplicator    schedule 13.08.2014


Отговори (2)


Можете да го направите по-бърз, като накарате plan да свърши повече работа, преди да трябва да се стартира отново. Стартирането на процес е много работа, така че не искате да го правите по-често от необходимото.

За да накарате plan да върши повече работа, можете да му подадете списък с команди на stdin или от файл. След това трудната част е анализирането на всеки ред за извличане на отделни команди и параметри. Тъй като вашият съществуващ код вече чете команди от argv, е достатъчно лесно да анализирате командите в масив, подобен на argv, като използвате strtok:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_COMMAND_LEN 256
#define MAX_ARGS 64
char *fake_argv[MAX_ARGS];  /* For compatibility with existing parser. */
const char *delims = " \n";

int main(int argc, char *argv[])
{
    char command[MAX_COMMAND_LEN];

    while( fgets(command, sizeof(command), stdin) )
    {
        int fake_argc = 0;
        fake_argv[fake_argc] = strtok(command, delims);
        while( fake_argv[fake_argc] != NULL )
        {
            fake_argv[++fake_argc] = strtok(NULL, delims);      
        }

        { int i;  /* debug print... you can remove this block */
          printf("Handling command: [%s], argc %d\n", command, fake_argc);
          for( i = 0; i < fake_argc; ++i ) { printf("  arg: [%s]\n", fake_argv[i]); }
        }
        /* ... do the stuff you were already doing except now with fake_argv */
    }

    return 0;
}

Тогава вашият bash скрипт може да бъде просто набор от редове, които захранвате вашата програма:

./plan << END_COMMAND_LIST
execute_1 set_control 4
execute_1 set_current_gains 10 50 0
execute_1 set_z_gains 1 0 0
END_COMMAND_LIST

Сега процесът plan се създава веднъж и изпълнява 3 команди, преди да излезе.

person indiv    schedule 13.08.2014
comment
Дано помогне. Забравих обаче да попълня fake_argv[0], така че вашите индекси може да са отклонени с 1. - person indiv; 13.08.2014

Обединих кода на indiv с моя main() и той работи, мога да изпращам нови команди на всеки 760us (18 пъти по-бързо от преди!)

Моят код може да не е толкова елегантен, но други може да се възползват от цялостно решение, така че ето го:

//****************************************************************************
// main: FlexSEA Plan project: console app to control FlexSEA slaves
//****************************************************************************

//****************************************************************************
// Include(s)
//****************************************************************************

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../inc/flexsea_console.h"
#include "../inc/plan_spi.h"
#include "flexsea_local.h"

//****************************************************************************
// Local variable(s) & definitions
//****************************************************************************

int analog0 = 0;

//Choose between single multiple commands console app:
//#define SINGLE_COMMAND
#define MULTIPLE_COMMANDS

#ifdef SINGLE_COMMAND
    #ifdef MULTIPLE_COMMANDS
        #error "Pick one Command option!"
    #endif
#endif


#define MAX_COMMAND_LEN 256
#define MAX_ARGS 8
char *fake_argv[MAX_ARGS];
const char *delims = " \n";

//****************************************************************************
// External variable(s)
//****************************************************************************

extern unsigned char execute_1_data[];

//****************************************************************************
// Function(s)
//****************************************************************************

int main(int argc, char *argv[])
{
    #ifdef MULTIPLE_COMMANDS
    char command[MAX_COMMAND_LEN];
    char string1[20], string2[20] = "quit";
    char default_argv[] = "";
    int i = 0;
    #endif  //MULTIPLE_COMMANDS

    //Open SPI:
    flexsea_spi_open();

    #ifdef MULTIPLE_COMMANDS
    while(fgets(command, sizeof(command), stdin))
    {
        int fake_argc = 1;

        //Fills fake_argv with empty strings to avoid sending old values with new commands
        for(i = 0; i < MAX_ARGS; i++)
        {
            fake_argv[i] = default_argv;
        }

        //First argument
        fake_argv[fake_argc] = strtok(command, delims);

        //Other arguments
        while( fake_argv[fake_argc] != NULL )
        {
            fake_argv[++fake_argc] = strtok(NULL, delims);
        }

        //Enable for terminal debug only:
        /*
        for(i = 0; i < MAX_ARGS; i++)
        {
            printf("fake_argv[%i] = %s\n", i, fake_argv[i]);
        }
        */

        //Do we want to exit? (exit when "quit" is received)
        strcpy(string1, fake_argv[1]);
        if(!strcmp(string1, string2))
        {
            printf("Quitting.\n");
            break;
        }
        else
        {
            //Parser for console commands:
            flexsea_console_parser(fake_argc, fake_argv);

            //Can we decode what we received?
            decode_spi_rx();
        }
    }
    #endif  //MULTIPLE_COMMANDS

    #ifdef SINGLE_COMMAND

    //Parser for console commands:
    flexsea_console_parser(argc, argv);

    //Can we decode what we received?
    decode_spi_rx();

    #endif  //SINGLE_COMMAND

    //Close SPI:
    flexsea_spi_close();

    return 0;
}

Моят тестов шел скрипт е:

#!/bin/bash
# How quickly can we send serial commands?

./plan << END_COMMAND_LIST
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
execute_1 set_leds 0 255 0 0
END_COMMAND_LIST

Благодаря МНОГО за помощта!

Джеф

person JFDuval    schedule 13.08.2014