Arduino Drone项目,当输出“加载”(甚至上限为gnd)时,输入“steering”命令开始出现故障

时间:2016-06-07 05:06:27

标签: c arduino embedded arduino-uno arduino-ide

Arduino无人机项目,每当输出“加载”(“开路”信号输入引脚到ESC或甚至是接地到地)时,输入“转向”命令开始出现故障(转到非常低的值< < 1000)。

电机速度是转向命令和油门的函数。 (在这个只有一个电机的测试案例中,如下面的代码所示,unMotorSpeed = unThrottleIn +/- unSteeringIn)

当连接到示波器时,物理输入信号(来自接收器的转向和油门)很棒,我甚至交换输入引脚以确保接收器和arduino之间没有问题。问题似乎来自软件,但它没有意义,因为当没有附加“负载”时,输入和输出值都很好并且干净。 (我把“负载”放在引号中,因为有时候它实际上是一个开路 - >超高阻抗输入信号到电子速度控制器(ESC),我甚至没有接地完成电路。

是否有人能够查看代码并查看我是否遗漏了某些内容?

此时,一个稍微快速的解决方法就是不要将新的毛刺值写入电机,并在新速度显着低于旧速度时保持旧的速度值(这些值超过50khz,很明显,只需一步就可以获得巨大的跳跃感。但

注意:在代码中,unMotorSpeed的总输出量来自servoThrottle引脚。只是我最终没有改变的原始命名......如果您阅读代码并查看所有变量,则很清楚。

更新:奇怪的是,我只是运行我的设置而没有任何改变,一切工作......我多次重新调整arduino,以确保它不是一些幸运的故障,它继续工作,一切都设置好了。然后我将遥控器放在地上,并将面板上的一些电线移动到周围,然后在将设置重新组合后,事情又回到了他们不可思议的方式。 Idk怎么办!

// --> starting code found at: rcarduino.blogspot.com
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
with.html


#include <Servo.h>

// Assign your channel in pins
#define THROTTLE_IN_PIN 3
#define STEERING_IN_PIN 2


// Assign your channel out pins
#define THROTTLE_OUT_PIN 9
//#define STEERING_OUT_PIN 9


// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoThrottle;
//Servo servoSteering;


// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2

// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;

// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;

// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulThrottleStart;
uint32_t ulSteeringStart;
//uint32_t ulAuxStart;

void setup()
{
  Serial.begin(9600);

  // attach servo objects, these will generate the correct
  // pulses for driving Electronic speed controllers, servos or other devices
  // designed to interface directly with RC Receivers 
  servoThrottle.attach(THROTTLE_OUT_PIN);

  // using the PinChangeInt library, attach the interrupts
  // used to read the channels
  attachInterrupt(digitalPinToInterrupt(THROTTLE_IN_PIN), calcThrottle,CHANGE);
  attachInterrupt(digitalPinToInterrupt(STEERING_IN_PIN), calcSteering,CHANGE);
}

void loop()
{
  // create local variables to hold a local copies of the channel inputs
  // these are declared static so that thier values will be retained
  // between calls to loop.
  static uint16_t unThrottleIn;
  static uint16_t unSteeringIn;
  static uint16_t difference;
  static uint16_t unMotorSpeed; // variable that stores overall motor speed
  static uint8_t bUpdateFlags; // local copy of update flags

  // check shared update flags to see if any channels have a new signal
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;

    // in the current code, the shared values are always populated
    // so we could copy them without testing the flags
    // however in the future this could change, so lets
    // only copy when the flags tell us we can.

    if(bUpdateFlags & THROTTLE_FLAG)
    {
      unThrottleIn = unThrottleInShared;
    }

    if(bUpdateFlags & STEERING_FLAG)
    {
      unSteeringIn = unSteeringInShared;
    }

    // clear shared copy of updated flags as we have already taken the updates
    // we still have a local copy if we need to use it in bUpdateFlags
    bUpdateFlagsShared = 0;

    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
    // as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
    // service routines own these and could update them at any time. During the update, the
    // shared copies may contain junk. Luckily we have our local copies to work with :-)
  }

  //Serial.println(unSteeringIn);

  // do any processing from here onwards
  // only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
  // variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
  // the interrupt routines and should not be used in loop

  // the following code provides simple pass through
  // this is a good initial test, the Arduino will pass through
  // receiver input as if the Arduino is not there.
  // This should be used to confirm the circuit and power
  // before attempting any custom processing in a project.

  // we are checking to see if the channel value has changed, this is indicated 
  // by the flags. For the simple pass through we don't really need this     check,
  // but for a more complex project where a new signal requires significant processing
  // this allows us to only calculate new values when we have new inputs, rather than
  // on every cycle.

///// if-else chain commented out to determine/prove problem with steering signal --> buggy! 


  if(unSteeringIn < 1400) // if steering joystick moved left
  {
  difference = 1400 - unSteeringIn;
  if(unThrottleIn - difference >= 0)
    unMotorSpeed = unThrottleIn - difference;
  }
  else if(unSteeringIn > 1550) //if steering joystick moved right (needs to be tweaked, but works for now)
  {
    difference = unSteeringIn - 1600;
    if(unThrottleIn + difference < 2000)
      unMotorSpeed = unThrottleIn + difference;
  }
  else
  {
    unMotorSpeed = unThrottleIn;  
  }

  //Serial.println(unMotorSpeed);
  //Serial.println(unSteeringIn);
  //Serial.println(unThrottleIn);

  if(bUpdateFlags)
  {
    //Serial.println(servoThrottle.readMicroseconds());
    if(servoThrottle.readMicroseconds() != unMotorSpeed)
    {
      servoThrottle.writeMicroseconds(unMotorSpeed);
      Serial.println(unMotorSpeed);
    }
  }

  bUpdateFlags = 0;
}

// simple interrupt service routine
void calcThrottle()
{
  // if the pin is high, its a rising edge of the signal pulse, so lets record its value
  if(digitalRead(THROTTLE_IN_PIN) == HIGH)
  {
    ulThrottleStart = micros();
  }
  else
  {
    // else it must be a falling edge, so lets get the time and subtract the time of the rising edge
    // this gives use the time between the rising and falling edges i.e. the pulse duration.
    unThrottleInShared = (uint16_t)(micros() - ulThrottleStart); // pulse duration

    // use set the throttle flag to indicate that a new throttle signal has been received
    bUpdateFlagsShared |= THROTTLE_FLAG;
  }
}

void calcSteering()
{
  if(digitalRead(STEERING_IN_PIN) == HIGH)
  {
    ulSteeringStart = micros();
  }
  else
  {
    unSteeringInShared = (uint16_t)(micros() - ulSteeringStart); // pulse duration
    bUpdateFlagsShared |= STEERING_FLAG;
  }
}

1 个答案:

答案 0 :(得分:0)

您应该阅读AttachInterrupt()的文档 - 在“关于中断服务程序”一节中,它提供了有关某些函数在从中断调用时的行为方式的信息。对于micros(),它声明:

  

micros()最初工作,但在1-2毫秒后会开始表现不规律。

我认为这意味着在ISR运行超过1ms后,而不是一般只运行1 ms,因此在这种情况下可能不适用,但您可能需要考虑如何在ISR中进行计时。这是Arduino的一个问题 - 可怕的文档!

可能导致一个明确的问题是unSteeringInShared是非原子的。它是8位硬件上的16位值,因此需要多个指令进行读写,并且可以中断该过程。因此,在读取第二个字节之前,可以读取loop()上下文中值的一个字节,然后通过中断上下文更改这两个字节,这样就可以得到两个不同值的两半。

要解决此问题,您可以在阅读时禁用中断:

noInterrupts() ;
unSteeringIn = unSteeringInShared ;
interrupts() ;

或者您可以旋转锁定读取:

do
{
    unSteeringIn = unSteeringInShared ;

} while( unSteeringIn != unSteeringInShared ) ;

你也应该为unThrottleInShared做同样的事情,虽然为什么你没有看到任何问题也不清楚 - 这可能不是你目前观察到的问题,但无论如何肯定是一个问题。 / p>

或者,如果8位分辨率足够,则可以将输入编码为原子8位值,因此:

uint8_t unSteeringInShared ;


...


int32_t timeus = micros() - ulSteeringStart - 1000 ;
if( timeus < 0 )
{
    unSteeringInShared = 0 ;
}
else if( timeus > 1000 )
{
    unSteeringInShared = 255;
}
else
{
    unSteeringInShared = (uint8_t)(time * 255 / 1000) ;
}

当然,将您的比例从1000更改为2000到0到25​​5将需要更改其余代码。例如,将0到255范围内的值x转换为伺服脉冲宽度:

 pulsew = (x * 1000 / 256) + 1000 ;