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;
}
}
答案 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到255将需要更改其余代码。例如,将0到255范围内的值x
转换为伺服脉冲宽度:
pulsew = (x * 1000 / 256) + 1000 ;