资讯详情

Linux(ubuntu 18)上编译 及修改 INAV 飞控代码(混控功能)

出于个人模型爱好,我一直想尝试VTOL垂直起落,夏天试了几种离题太远就不说了。在地铁冥想之前,一个计划突然闪现。与鱼鹰基本相似V22, 双发垂直起飞,然后电机转90度提供前进推力,固定翼提供升力。有一个问题:电机转90度后飞控板XYZ方向传感器没有相应的旋转,不能发挥自稳定作用。当两个电机的速差在垂直起落时控制左右翼的平衡,但当转向水平方向时,应转向前进方向来控制差异,并发挥类似方向舵的作用。我想到了几个方案,包括机械旋转飞控板,这并非不可能,但最终我觉得更改代码是最简单的。

使用的飞行控制是F4 开源INAV 2.0.0.第一个问题是编译。我去了。网上搜索的信息不多。简书的连接写得很清楚,https://www.jianshu.com/p/9a13eee41b48 我应该感谢你,但我真的很想给作者几次,因为这些实际操作远远不够,其他的CSDN还有一篇关于上面的文章inav但是写的太笼统了,不懂linux中间有太多的坑:Linxu的版本选择、编译飞控代码需要依赖的软件包。编译命令。前后花了将近2个星期的个人时间。中间还放弃过,

有人说可以用docker编译,实际试了下我靠,不是不行,docker下载有多恶心,安装有多恶心docker要先安装windows server,如此不可操作的建议...你知道我以前安装过windows server迷失了多少次自我?

这本书的解释是平时不怎么写的Linux还有一些修改INAV需要的人,对Linxu熟客会觉得很常识,可以直接跳过。

第一部分 编译 第一步:安装虚拟机。我用的版本是VMware? Workstation 14 Pro。配置虚拟硬盘至少20G空间,其他默认情况就足够了

第二步:网上下载Ubuntu 18AMD64.我想在这里这么简单的信息, 没人提到我前后下载了不少于5、6个版本linux安装包,包Ubuntu包的大小没那么吓人。更多的用户。然后想当然地使用它X在86版本中,编译时出现了32位error,找了几天,发现没有对应的。X86-64位版本, 或者包的尺寸有几个G大,最后尝试的态度是AMD64版,其实是可以用的。

第三步:更新Ubuntu软件,命令 sudo apt-get update (这个命令写起来也很简单,但是中国还涉及到更新源的问题。修改更新源本身就是一个猜测和盲目测试的过程,费时费力。我参考了一下CSDN里面有很多文章,具体自己搜索,这里没必要展开)

安装make, gcc, git等 sudo apt install git sudo apt install gcc sudo apt install ruby

第四步:github下载INAV2.0.0, 耐心多试两次还是可以直接从github成功下载。 git clone https://github.com/iNavFlight/inav.git

请注意,最新代码是通过这种方式获得的。如果你想得到指定版本,你应该先在左边选择tag如果使用标签,最简单的是直接下载标签版本git命令要checkout指定版本。

事实上,你可以直接尝试编译INAV但确保你几乎肯定会遇到各种各样的编辑。包括这个:

第五步: 安装gcc-arm-none-eabi, 我使用INAV2.0.0 下载的arm编译器是gcc-arm-none-eabi-7-2018-q2-update 仅供参考的版本。 这个包比较大,远远大于编译inav代码确定下载成功,然后继续下一步 可以用inav脚本在目录中 ./install-toolchain.sh

也可手动下载, 参考文章:https://blog.csdn.net/yk150915/article/details/80117082 搜索gcc arm eabi, 同时参考多篇文章,这里不展开,否则又是一大坨。

第六步: 在代码目录下编译: make TARGET=OMNIBUSF4PRO 这里Tareget 参数是你的飞控板型号,即使是F4飞控也分V3版本和pro但现在淘宝基本上都是山寨pro版本。具体参考inav/docs 以下说明,以及Makefile。

如果没有编译,并且安装在上面列出的版本中,现在应该只有缺乏支持包。看看缺少什么,根据提示在线搜索,一般可以给出方法。

若通过,在 inav/obj应该有目录.hex文件编译成功。可以和解release的hex确认字节大小。然后上板测试。

第二部分, 修改INAV代码(混控部分) 我以为需要很长时间,但当我坐下来打开它时,我发现INAV写得很清楚,没有太多炫目的句子技巧,个人非常厌恶使用非常罕见的句子,看起来很困难,也没有看到提高任何可读性。 我的目的是暂时调整混合控制通道,因为它是直接的inav 该功能在配置器中无法实现。 跳过驱动什么的,直奔主题,有与混控通道相关的文件src/main/flight/mixer.c src/main/flight/servos.c

舵机和电机可见PWM控制的混合控制是分开处理的。INAV配置器的混控界面里一样,分成电机混控和舵机混控

存储混合控制信息的数据结构:

static servoMixer_t currentServoMixer[MAX_SERVO_RULES]; static motorMixer_t currentMixer[MAX_SUPPORTED_MOTORS];

typedef struct servoMixer_s { uint8_t targetChannel; // servo that receives the output of the rule uint8_t inputSource; // input channel for this rule int8_t rate; // range [-125; 125] ; can be used to adjust a rate 0-125% and a direction uint8_t speed; // reduces the speed of the rule, 0=unlimited speed } servoMixer_t;

typedef struct motorMixer_s { float throttle; float roll; float pitch; float yaw; } motorMixer_t;

存储混合控制信息的数据结构并不复杂,例如motormix里边的throttle,roll,pitch,yaw,自然是相应配置界面中的相应项。

如果获得遥控辅助通道,那就简单了。PWM信息,即代表电机倾斜角度的信息RC通道, 重新设置相应的混合控制规则的通道值。 这里说说我做的。VT0L, 只有两个电机 两个舵机和两个舵机不仅负责垂直起落时电机的前后控制,还负责水平飞行时左右副翼的操作和类似水平尾翼的俯仰操作。 垂直/水平飞行的转换是通过新设置的辅助通道实现的,我使用CH7,CH7混合控制电机的角度,扳动或旋转,看看预期的操作模式。 说起来难,可以参考视频演示

https://www.bilibili.com/video/av70709777?from=search&seid=16415844640983981290

双发固定翼 VTOL 操纵转换演示

其实关于身体本身有很多说法,可以参考5imx相关帖子。

http://bbs.5imx.com/forum.php?mod=viewthread&tid=1551588&extra=page=1

2轴VTOL垂直起降固定翼 INAV 修改编译飞控代码

这里还有一个细节,就是开始想从角度不断调整混控比例,然后改为直接在45度以上调整几个通道的混控值。因为在45度的方向上,无论是电机转速还是舵机的滚动偏转,都有水平和垂直的重量力,所以没有必要继续调整比例。

继续找到通道输入量的存储位置servos.c的 servoMixer()函数中放置遥控器通道输入量 rcData[AUX3]里边的,

这个值是从0 ~ 500的PWM因此,我设置了连续变化值 260基本上是45度以上的位置。

添加代码在----VTOL Custom Code----- 其他的都是注释INAV原有代码, 修改后编译,最后要解释的是,由于个人使用,通道和混合控制值都是写死的INAV配置器还应配置相应的初始混合控制规则,并注意。

如下: servos.c:

void servoMixer(float dT) {   int16_t input[INPUT_SOURCE_COUNT]; // Range [-500: 500]    if (FLIGHT_MODE(MANUAL_MODE)) {       input[INPUT_STABILIZED_ROLL] = rcCommand[ROLL];
        input[INPUT_STABILIZED_PITCH] = rcCommand[PITCH];
        input[INPUT_STABILIZED_YAW] = rcCommand[YAW];
    } else {
        // Assisted modes (gyro only or gyro+acc according to AUX configuration in Gui
        input[INPUT_STABILIZED_ROLL] = axisPID[ROLL];
        input[INPUT_STABILIZED_PITCH] = axisPID[PITCH];
        input[INPUT_STABILIZED_YAW] = axisPID[YAW];

        // Reverse yaw servo when inverted in 3D mode only for multirotor and tricopter
        if (feature(FEATURE_3D) && (rcData[THROTTLE] < PWM_RANGE_MIDDLE) &&
        (mixerConfig()->platformType == PLATFORM_MULTIROTOR || mixerConfig()->platformType == PLATFORM_TRICOPTER)) {
            input[INPUT_STABILIZED_YAW] *= -1;
        }
    }

    input[INPUT_FEATURE_FLAPS] = FLIGHT_MODE(FLAPERON) ? servoConfig()->flaperon_throw_offset : 0;

    if (IS_RC_MODE_ACTIVE(BOXCAMSTAB)) {
        input[INPUT_GIMBAL_PITCH] = scaleRange(attitude.values.pitch, -1800, 1800, -360, +360);
        input[INPUT_GIMBAL_ROLL] = scaleRange(attitude.values.roll, -1800, 1800, -360, +360);
    } else {
        input[INPUT_GIMBAL_PITCH] = 0;
        input[INPUT_GIMBAL_ROLL] = 0;
    }

    input[INPUT_STABILIZED_THROTTLE] = motor[0] - 1000 - 500;  // Since it derives from rcCommand or mincommand and must be [-500:+500]

    // center the RC input value around the RC middle value
    // by subtracting the RC middle value from the RC input value, we get:
    // data - middle = input
    // 2000 - 1500 = +500
    // 1500 - 1500 = 0
    // 1000 - 1500 = -500
    input[INPUT_RC_ROLL]     = rcData[ROLL]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_PITCH]    = rcData[PITCH]    - PWM_RANGE_MIDDLE;
    input[INPUT_RC_YAW]      = rcData[YAW]      - PWM_RANGE_MIDDLE;
    input[INPUT_RC_THROTTLE] = rcData[THROTTLE] - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH5]      = rcData[AUX1]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH6]      = rcData[AUX2]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH7]      = rcData[AUX3]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH8]      = rcData[AUX4]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH9]      = rcData[AUX5]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH10]     = rcData[AUX6]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH11]     = rcData[AUX7]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH12]     = rcData[AUX8]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH13]     = rcData[AUX9]     - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH14]     = rcData[AUX10]    - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH15]     = rcData[AUX11]    - PWM_RANGE_MIDDLE;
    input[INPUT_RC_CH16]     = rcData[AUX12]    - PWM_RANGE_MIDDLE;

    //---------------VTOL Custom Code--------------------
    if(input[INPUT_RC_CH7] > 260)  {
        //swap roll and Yaw channel
        //currentServoMixer[1].targetChannel = 3;
        currentServoMixer[1].inputSource = INPUT_STABILIZED_ROLL ;
        currentServoMixer[1].rate = 50;
        //currentServoMixer[3].targetChannel = 4;
        currentServoMixer[3].inputSource = INPUT_STABILIZED_ROLL ;
        currentServoMixer[3].rate = 50;
    }
    else {
        //currentServoMixer[1].targetChannel = 3;
        currentServoMixer[1].inputSource = INPUT_STABILIZED_YAW ;
        currentServoMixer[1].rate = 50;
        //currentServoMixer[3].targetChannel = 4;
        currentServoMixer[3].inputSource = INPUT_STABILIZED_YAW ;
        currentServoMixer[3].rate = 50;
    }
    //---------------VTOL Custom Code--------------------

    for (int i = 0; i < MAX_SUPPORTED_SERVOS; i++) {
        servo[i] = 0;
    }

    // mix servos according to rules
    for (int i = 0; i < servoRuleCount; i++) {
        const uint8_t target = currentServoMixer[i].targetChannel;
        const uint8_t from = currentServoMixer[i].inputSource;

        /*
         * Apply mixer speed limit. 1 [one] speed unit is defined as 10us/s:
         * 0 = no limiting
         * 1 = 10us/s -> full servo sweep (from 1000 to 2000) is performed in 100s
         * 10 = 100us/s -> full sweep (from 1000 to 2000)  is performed in 10s
         * 100 = 1000us/s -> full sweep in 1s
         */
        int16_t inputLimited = (int16_t) rateLimitFilterApply4(&servoSpeedLimitFilter[i], input[from], currentServoMixer[i].speed * 10, dT);

        servo[target] += servoDirection(target, from) * ((int32_t)inputLimited * currentServoMixer[i].rate) / 100;
    }

    for (int i = 0; i < MAX_SUPPORTED_SERVOS; i++) {

        /*
         * Apply servo rate
         */
        servo[i] = ((int32_t)servoParams(i)->rate * servo[i]) / 100L;

        /*
         * Perform acumulated servo output scaling to match servo min and max values
         * Important: is servo rate is > 100%, total servo output might be bigger than
         * min/max
         */
        if (servo[i] > 0) {
            servo[i] = (int16_t) (servo[i] * servoMetadata[i].scaleMax);
        } else {
            servo[i] = (int16_t) (servo[i] * servoMetadata[i].scaleMin);
        }

        /*
         * Add a servo midpoint to the calculation
         */
        servo[i] += servoParams(i)->middle;

        /*
         * Constrain servo position to min/max to prevent servo damage
         * If servo was saturated above min/max, that means that user most probably
         * allowed the situation when smix weight sum for an output was above 100
         */
        servo[i] = constrain(servo[i], servoParams(i)->min, servoParams(i)->max);
    }
} 

mixer.c

void mixTable(const float dT)
{
    int16_t input[3];   // RPY, range [-500:+500]
    // Allow direct stick input to motors in passthrough mode on airplanes
    if (STATE(FIXED_WING) && FLIGHT_MODE(MANUAL_MODE)) {
        // Direct passthru from RX
        input[ROLL] = rcCommand[ROLL];
        input[PITCH] = rcCommand[PITCH];
        input[YAW] = rcCommand[YAW];
    }
    else {
        input[ROLL] = axisPID[ROLL];
        input[PITCH] = axisPID[PITCH];
        input[YAW] = axisPID[YAW];

        if (motorCount >= 4 && mixerConfig()->yaw_jump_prevention_limit < YAW_JUMP_PREVENTION_LIMIT_HIGH) {
            // prevent "yaw jump" during yaw correction
            input[YAW] = constrain(input[YAW], -mixerConfig()->yaw_jump_prevention_limit - ABS(rcCommand[YAW]), mixerConfig()->yaw_jump_prevention_limit + ABS(rcCommand[YAW]));
        }
    }

    // Initial mixer concept by bdoiron74 reused and optimized for Air Mode
    int16_t rpyMix[MAX_SUPPORTED_MOTORS];
    int16_t rpyMixMax = 0; // assumption: symetrical about zero.
    int16_t rpyMixMin = 0;

    //---------------VTOL Custom Code--------------------
    if(rcData[AUX3] - PWM_RANGE_MIDDLE < 260)  {
        //swap roll and Yaw channel
        currentMixer[0].roll = 1;
        currentMixer[1].roll = -1;
        currentMixer[0].yaw = 0;
        currentMixer[1].yaw = 0;
    }
    else {
        currentMixer[0].roll = 0;
        currentMixer[1].roll = 0;
        currentMixer[0].yaw = 1;
        currentMixer[1].yaw = -1;
    }
    //---------------VTOL Custom Code--------------------


    // motors for non-servo mixes
    for (int i = 0; i < motorCount; i++) {
        rpyMix[i] =
            input[PITCH] * currentMixer[i].pitch +
            input[ROLL] * currentMixer[i].roll +
            -mixerConfig()->yaw_motor_direction * input[YAW] * currentMixer[i].yaw;

        if (rpyMix[i] > rpyMixMax) rpyMixMax = rpyMix[i];
        if (rpyMix[i] < rpyMixMin) rpyMixMin = rpyMix[i];
    }

    int16_t rpyMixRange = rpyMixMax - rpyMixMin;
    int16_t throttleRange, throttleCommand;
    int16_t throttleMin, throttleMax;
    static int16_t throttlePrevious = 0;   // Store the last throttle direction for deadband transitions

    // Find min and max throttle based on condition.
    if (feature(FEATURE_3D)) {
        if (!ARMING_FLAG(ARMED)) throttlePrevious = PWM_RANGE_MIDDLE; // When disarmed set to mid_rc. It always results in positive direction after arming.

        if ((rcCommand[THROTTLE] <= (PWM_RANGE_MIDDLE - rcControlsConfig()->deadband3d_throttle))) { // Out of band handling
            throttleMax = flight3DConfig()->deadband3d_low;
            throttleMin = motorConfig()->minthrottle;
            throttlePrevious = throttleCommand = rcCommand[THROTTLE];
        } else if (rcCommand[THROTTLE] >= (PWM_RANGE_MIDDLE + rcControlsConfig()->deadband3d_throttle)) { // Positive handling
            throttleMax = motorConfig()->maxthrottle;
            throttleMin = flight3DConfig()->deadband3d_high;
            throttlePrevious = throttleCommand = rcCommand[THROTTLE];
        } else if ((throttlePrevious <= (PWM_RANGE_MIDDLE - rcControlsConfig()->deadband3d_throttle)))  { // Deadband handling from negative to positive
            throttleCommand = throttleMax = flight3DConfig()->deadband3d_low;
            throttleMin = motorConfig()->minthrottle;
        } else {  // Deadband handling from positive to negative
            throttleMax = motorConfig()->maxthrottle;
            throttleCommand = throttleMin = flight3DConfig()->deadband3d_high;
        }
    } else {
        throttleCommand = rcCommand[THROTTLE];
        throttleMin = motorConfig()->minthrottle;
        throttleMax = motorConfig()->maxthrottle;

        // Throttle compensation based on battery voltage
        if (feature(FEATURE_THR_VBAT_COMP) && feature(FEATURE_VBAT) && isAmperageConfigured())
            throttleCommand = MIN(throttleMin + (throttleCommand - throttleMin) * calculateThrottleCompensationFactor(), throttleMax);
    }

    throttleRange = throttleMax - throttleMin;

    #define THROTTLE_CLIPPING_FACTOR    0.33f
    motorMixRange = (float)rpyMixRange / (float)throttleRange;
    if (motorMixRange > 1.0f) {
        for (int i = 0; i < motorCount; i++) {
            rpyMix[i] /= motorMixRange;
        }

        // Allow some clipping on edges to soften correction response
        throttleMin = throttleMin + (throttleRange / 2) - (throttleRange * THROTTLE_CLIPPING_FACTOR / 2);
        throttleMax = throttleMin + (throttleRange / 2) + (throttleRange * THROTTLE_CLIPPING_FACTOR / 2);
    } else {
        throttleMin = MIN(throttleMin + (rpyMixRange / 2), throttleMin + (throttleRange / 2) - (throttleRange * THROTTLE_CLIPPING_FACTOR / 2));
        throttleMax = MAX(throttleMax - (rpyMixRange / 2), throttleMin + (throttleRange / 2) + (throttleRange * THROTTLE_CLIPPING_FACTOR / 2));
    }

    // Now add in the desired throttle, but keep in a range that doesn't clip adjusted
    // roll/pitch/yaw. This could move throttle down, but also up for those low throttle flips.
    if (ARMING_FLAG(ARMED)) {
        for (int i = 0; i < motorCount; i++) {
            motor[i] = rpyMix[i] + constrain(throttleCommand * currentMixer[i].throttle, throttleMin, throttleMax);

            if (failsafeIsActive()) {
                motor[i] = constrain(motor[i], motorConfig()->mincommand, motorConfig()->maxthrottle);
            } else if (feature(FEATURE_3D)) {
                if (throttlePrevious <= (PWM_RANGE_MIDDLE - rcControlsConfig()->deadband3d_throttle)) {
                    motor[i] = constrain(motor[i], motorConfig()->minthrottle, flight3DConfig()->deadband3d_low);
                } else {
                    motor[i] = constrain(motor[i], flight3DConfig()->deadband3d_high, motorConfig()->maxthrottle);
                }
            } else {
                motor[i] = constrain(motor[i], motorConfig()->minthrottle, motorConfig()->maxthrottle);
            }

            // Motor stop handling
            if (ARMING_FLAG(ARMED) && (getMotorStatus() != MOTOR_RUNNING)) {
                if (feature(FEATURE_MOTOR_STOP)) {
                    motor[i] = (feature(FEATURE_3D) ? PWM_RANGE_MIDDLE : motorConfig()->mincommand);
                } else {
                    motor[i] = motorConfig()->minthrottle;
                }
            }
        }
    } else {
        for (int i = 0; i < motorCount; i++) {
            motor[i] = motor_disarmed[i];
        }
    }

    /* Apply motor acceleration/deceleration limit */
    applyMotorRateLimiting(dT);
}

 

标签: ch10传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台