PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem bei SingleShunt Strommessung für BLDC



EOS400DMAN
21.01.2015, 20:17
Hallo zusammen,

der Beitrag ist recht lang geworden, es würde mich trotzdem freuen, wenn sich jemand die Zeit nimmt ihn zu lesen.
Ich versuche gerade einen BLDC-Regler mit feldorientierter Regelung und
singleshunt Strommessung aufzubauen. Allerdings stellen mich die
Messergebnisse nicht ganz zufrieden. Vielleicht hat ja jemand Erfahrung
damit und kann mir dabei etwas helfen. Für die Strommessung verwende ich
einen ACS709 Hall-Effekt-Stromsensor. Im Anhang mal ein Screenshot, wie
die Ströme aktuell aussehen.
29648

Die Ströme werden zweimal pro PWM-Periode gemessen und über das
Kirchhoffsches Gesetz der jeweils fehlende dritte Strom berechnet.
Des Prinzip wird auch hier nochmals erklärt: http://ww1.microchip.com/downloads/en/AppNotes/01299A.pdf
Ich weiß nun nicht, ob der Fehler bereits in der Hardware liegt oder in meiner Software.

Als Mikrocontroller verwende ich einen STM32F103C8.
Timer 1 verwende ich zu PWM Erzeugung. Dieser zählt von 0 bis 2048 und
dann wieder runter auf 0. Timer 3 läuft synchron zählt aber von 0 bis
4095. Damit triggere ich den ADC. Dabei wird in der ISR von Timer 3 der
neue Vergleichswert für die Zweite triggerung des ADCs geladen. In der
ADC-ISR wird dann wieder der Wert für die erste Triggerung geladen.

Init des Timer 3:


void ADC_Timer_ini()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 4095;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_UpdateRequestConfig(TIM3, TIM_UpdateSource_Global);
TIM_UpdateDisableConfig(TIM3,DISABLE);
TIM_Cmd(TIM3, ENABLE);
}

void ADC_Timer_Compare_ini()
{
TIM_OCInitTypeDef TIM_OCInitStructure;

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_Pulse = 2048;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);

TIM_OCInitStructure.TIM_Pulse = 1024;

TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = 3072;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_DMACmd(TIM3, TIM_DMA_CC1, ENABLE);
TIM_DMACmd(TIM3, TIM_DMA_CC3, ENABLE);
}

void ADC_Timer_Interrupt_ini()
{
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriori ty = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);

TIM_ITConfig(TIM3, TIM_IT_CC4, ENABLE);
}


Ini des Timer 1:


void PWMTimer_ini()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
TIM_TimeBaseStructure.TIM_Period = 2048;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM1->CR1 |= TIM_CR1_URS; // Nur Over- bzw. Underflow erzeugt Update event
TIM_UpdateRequestConfig(TIM1, TIM_UpdateSource_Regular);
TIM_UpdateDisableConfig(TIM1,DISABLE);
TIM_Cmd(TIM1, ENABLE);

TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_3Transfers);
}

void PWMChannel_ini()
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;

TIM_OCInitStructure.TIM_Pulse = 0;

TIM_OC1Init(TIM1, &TIM_OCInitStructure);
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
TIM_OC3Init(TIM1, &TIM_OCInitStructure);

TIM_CtrlPWMOutputs(TIM1, ENABLE);

TIM1->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // Compare-Werte erst beim Update-Event übernehmen
TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Compare-Werte erst beim Update-Event übernehmen
}


ADC Ini:


void ADC_2_ini(void)
{
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC Takt max 14MHz. 72MHz/6 = 12MHz
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);

ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_Init(ADC2, &ADC_InitStructure);

ADC_InjectedSequencerLengthConfig(ADC2, 2);
ADC_InjectedChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5);
ADC_InjectedChannelConfig(ADC2, ADC_Channel_1, 2, ADC_SampleTime_7Cycles5);

ADC_InjectedDiscModeCmd(ADC2, ENABLE);
ADC_ExternalTrigInjectedConvCmd(ADC2, ENABLE);
ADC_ExternalTrigInjectedConvConfig(ADC2, ADC_ExternalTrigInjecConv_T3_CC4);

ADC_Cmd(ADC2, ENABLE);

// Starte Kalibierung
ADC_ResetCalibration(ADC2);
while(ADC_GetResetCalibrationStatus(ADC2));
ADC_StartCalibration(ADC2);
while(ADC_GetCalibrationStatus(ADC2));
}

void ADC_2_Interrupt_ini()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriori ty = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
ADC_ITConfig(ADC2, ADC_IT_JEOC, ENABLE);
}


ISR Timer 3:


void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC4))
{
GPIOB->ODR ^= GPIO_Pin_6;
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
TIM3->CCR4 = pwm_aktuell.adc_comp.adc_triggerung_2;
}
}


ISR ADC:


void ADC1_2_IRQHandler(void)
{
ADC_ClearITPendingBit(ADC2, ADC_IT_JEOC);


ADC_Strom_1 = ADC_GetInjectedConversionValue(ADC2, ADC_InjectedChannel_1);
ADC_Strom_2 = ADC_GetInjectedConversionValue(ADC2, ADC_InjectedChannel_2);

TIM3->CCR4 = pwm_aktuell.adc_comp.adc_triggerung_1;
flags |= adc_messung_fertig;

strom_auswerten();
}


Funktion zum Auswerten der Strommesswerte:


void strom_auswerten(void)
{
ACS_ref = gleitender_mittelwert(ACS_ref, ADCBuffer[0], 3);

ADC_Strom_1 -= ACS_ref + ACS_0_Strom_offset;
ADC_Strom_2 -= ACS_ref + ACS_0_Strom_offset;

ADC_Strom_1 = ADC_Strom_1 << 3;
ADC_Strom_2 = ADC_Strom_2 << 3;

if(flags & erste_adc_messung )
{
flags &= ~erste_adc_messung;
flags &= ~adc_messung_fertig;
}
else
{
flags &= ~adc_messung_fertig;

if(counter==2)
{
if(array_counter==2000)
{
asm("nop");
}
else
{
counter = 0;
I_U_array[array_counter] = I_U_flt;//I_U_flt;
I_V_array[array_counter] = I_V_flt;//I_V_flt;
I_W_array[array_counter] = I_W_flt;

//I_U_array[array_counter] = ADC_Strom_1;//I_U_flt;
//I_V_array[array_counter] = - ADC_Strom_2;//I_V_flt;
//I_W_array[array_counter] = -ADC_Strom_1 + ADC_Strom_2;

teta_array[array_counter++] = teta;
}
}
else
{
counter += 1;
}

switch (sektor)
{
case 1: // 0- 60°
{
I_W_raw = - ADC_Strom_2;
I_V_raw = ADC_Strom_1;
I_U_raw = - I_V_raw - I_W_raw;
break;
}
case 2: // 60-120°
{
I_U_raw = -ADC_Strom_2;
I_W_raw = ADC_Strom_1;
I_V_raw = (- I_W_raw - I_U_raw);
break;
}
case 3: // 120-180°
{
I_U_raw = - ADC_Strom_2;
I_W_raw = ADC_Strom_1;
I_V_raw = - I_U_raw - I_W_raw;
break;
}
case 4: //180-240°
{
I_U_raw = ADC_Strom_1;
I_W_raw = - ADC_Strom_2;
I_V_raw = - I_W_raw - I_U_raw;
break;
}
case 5: //240-300°
{
I_U_raw = ADC_Strom_1;
I_W_raw = - ADC_Strom_2;
I_V_raw = - I_W_raw - I_U_raw;
break;
}
case 6: //300-360°
{
I_V_raw = ADC_Strom_1;
I_W_raw = - ADC_Strom_2;
I_U_raw = - I_V_raw - I_W_raw;
break;
}
default: break;
}
}

I_U_flt = gleitender_mittelwert(I_U_flt, I_U_raw, filter_gewichtung_strom);
I_V_flt = gleitender_mittelwert(I_V_flt, I_V_raw, filter_gewichtung_strom);
I_W_flt = gleitender_mittelwert(I_W_flt, I_W_raw, filter_gewichtung_strom);
return;
}


Ich habe nun echt schon viel versucht und gemessen. Wenn ich zum Beispiel nur einen Wechselstrom durch den Hall-Sensor schicken, wird dieser auch korrekt gemessen:
29649
Nur die Messung in Kombination mit dem SVPWM Modul funktioniert überhaupt nicht Mir fällt nichts mehr ein was ich noch versuchen kann. Deshalb wäre ich über eure Hilfe sehr dankbar.

Viele Grüße und vielen Dank im Voraus Michael

damfino
22.01.2015, 09:58
2 Ideen:
1) Sind die Timer immer synchron, oder driften sie auseinander?
2) Der Hallsensor wird durch den Motor und dessen Magnetfeld gestört.

LG!

EOS400DMAN
22.01.2015, 14:43
Hallo,

zuerst danke für die Antwort.
Zu 1. ich habe das getestet in dem ich mit beiden Timern ein PWM erzeugt habe und die Ausgänge mit dem Oszi gemessen habe. Diese sind synchron und driften auch nicht.
Zu 2. Der Sensor ist recht weit Entfernt vom Motor, deshalb kann ich mir das nicht so recht vorstellen.

Viele Grüße Michael

shedepe
24.01.2015, 09:21
Hey mit welcher Frequenz läuft deine PWM ?
http://www.allegromicro.com/~/media/Files/Datasheets/ACS709-Datasheet.ashx

Nachdem Datenblatt ist die maximale ungedämpfte Frequenz die damit gemessen werden kann 120 kHz. Je nach Kondensator zwischen dem Filter Pin und GND kann das auch erheblich weniger sein.

Ich würde vorschlagen du misst testweise mal mit einem herkömmlichen Messshunt. Damit kannst du deinen Quellcode überprüfen und Fehler die durch den Stromsensor entstehen ausschließen.

EOS400DMAN
24.01.2015, 11:38
Hallo,

mein PWM läuft mit 17,6kHz. Ich habe auch schon mit einem Shunt getestet. Dies kann ich aber mit dem aktuellen Code nochmals versuchen.

Viele Grüße Michael

EDIT: Ich habe nun nochmal mit dem ACS709 und einem Shunt gemessen. Sieht leider beides gleich schlecht aus :/
2965529656

Viele Grüße Michael

shedepe
25.01.2015, 12:03
Könntest du zu dem Diagramm bitte die Beschriftung noch mal extra dazu schreiben. Die ist leider so klein geworden, dass man sie kaum lesen kann.
Zu den Messwerten. Wenn ich grade keinen Denkfehler drin habe würde ich die Messwerte so als realistisch einschätzen. Es wird nacheinander ein Stromfluss durch die einzelnen Motorspulen festgestellt der auch umgetastet worden ist.

PICture
25.01.2015, 12:34
Hallo!

Sorry, dass ich kurz unterbreche, aber ich bewundere oft zahlreiche Disskusionen über undefinierte Sachen. Beispielweise hier: um was für ein Strom (Spitze, Mittel, RMS, usw.) es genau geht ? :confused:

EOS400DMAN
25.01.2015, 13:32
Guten Tag,

es geht um die Momentanwerte des Stroms.
Mittlerweile bin ich ganz zufrieden mit den Ergebnissen. Baue ich die Sternschaltung des Motors aus drei Leistungswiderständen nach, dann bekomme ich folgenden Verlauf:
29659
Das sieht schon sehr nach Sinus Verläufen aus.

Messe ich am Motor bekomme ich folgende Ergebnisse:
29660
Diese sehen zwar nicht so nach Sinus aus, dies liegt aber vermutlich daran, dass der Motor zwangskommutiert läuft. Da sehen die Ströme meines Wissens nach nicht so schön aus. Das ist ja auch der Grund warum man den Aufwand einer FOC betreibt.

Viele Grüße Michael