This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BalaC balancing robot (IMU:MPU6886) | |
// by Kiraku Labo | |
// | |
// 1. Lay the robot flat, and power on. | |
// 2. Wait until Gal-1 (Pitch Gyro calibration) completes. | |
// 3. Hold still the robot upright in balance until Cal-2 (Accel & Yaw Gyro cal) completes. | |
// | |
// short push of power button: Gyro calibration | |
// long push (>1sec) of power button: switch mode between standig and demo(circle) | |
// | |
#include <M5StickC.h> | |
#define LED 10 | |
#define N_CAL1 100 | |
#define N_CAL2 100 | |
#define LCDV_MID 60 | |
boolean serialMonitor=true; | |
boolean standing=false; | |
int16_t counter=0; | |
uint32_t time0=0, time1=0; | |
int16_t counterOverPwr=0, maxOvp=20; | |
float power, powerR, powerL, yawPower; | |
float varAng, varOmg, varSpd, varDst, varIang; | |
float gyroXoffset, gyroYoffset, gyroZoffset, accXoffset; | |
float gyroXdata, gyroYdata, gyroZdata, accXdata, accZdata; | |
float aveAccX=0.0, aveAccZ=0.0, aveAbsOmg=0.0; | |
float cutoff=0.1; //~=2 * pi * f (Hz) | |
const float clk = 0.01; // in sec, | |
const uint32_t interval = (uint32_t) (clk*1000); // in msec | |
float Kang, Komg, KIang, Kyaw, Kdst, Kspd; | |
int16_t maxPwr; | |
float yawAngle=0.0; | |
float moveDestination, moveTarget; | |
float moveRate=0.0; | |
const float moveStep=0.2*clk; | |
int16_t fbBalance=0; | |
int16_t motorDeadband=0; | |
float mechFactR, mechFactL; | |
int8_t motorRDir=0, motorLDir=0; | |
bool spinContinuous=false; | |
float spinDest, spinTarget, spinFact=1.0; | |
float spinStep=0.0; //deg per 10msec | |
int16_t ipowerL=0, ipowerR=0; | |
int16_t motorLdir=0, motorRdir=0; //0:stop 1:+ -1:- | |
float vBatt, voltAve=3.7; | |
int16_t punchPwr, punchPwr2, punchDur, punchCountL=0, punchCountR=0; | |
byte demoMode=0; | |
void setup() { | |
pinMode(LED, OUTPUT); | |
digitalWrite(LED, HIGH); | |
Serial.begin(115200); | |
M5.begin(); | |
Wire.begin(0, 26); //SDA,SCL | |
imuInit(); | |
M5.Axp.ScreenBreath(11); | |
M5.Lcd.setRotation(2); | |
M5.Lcd.setTextFont(4); | |
M5.Lcd.fillScreen(BLACK); | |
M5.Lcd.setTextSize(1); | |
resetMotor(); | |
resetPara(); | |
resetVar(); | |
calib1(); | |
#ifdef DEBUG | |
debugSetup(); | |
#else | |
setMode(false); | |
#endif | |
} | |
void loop() { | |
checkButtonP(); | |
#ifdef DEBUG | |
if (debugLoop1()) return; | |
#endif | |
getGyro(); | |
if (!standing) { | |
dispBatVolt(); | |
aveAbsOmg = aveAbsOmg * 0.9 + abs(varOmg) * 0.1; | |
aveAccZ = aveAccZ * 0.9 + accZdata * 0.1; | |
M5.Lcd.setCursor(10,130); | |
M5.Lcd.printf("%5.2f ", -aveAccZ); | |
if (abs(aveAccZ)>0.9 && aveAbsOmg<1.5) { | |
calib2(); | |
if (demoMode==1) startDemo(); | |
standing=true; | |
} | |
} | |
else { | |
if (abs(varAng)>30.0 || counterOverPwr>maxOvp) { | |
resetMotor(); | |
resetVar(); | |
standing=false; | |
setMode(false); | |
} | |
else { | |
drive(); | |
} | |
} | |
counter += 1; | |
if (counter >= 100) { | |
counter = 0; | |
dispBatVolt(); | |
if (serialMonitor) sendStatus(); | |
} | |
do time1 = millis(); | |
while (time1 - time0 < interval); | |
time0 = time1; | |
} | |
void calib1() { | |
calDelay(30); | |
digitalWrite(LED, LOW); | |
calDelay(80); | |
M5.Lcd.fillScreen(BLACK); | |
M5.Lcd.setCursor(0, LCDV_MID); | |
M5.Lcd.print(" Cal-1 "); | |
gyroYoffset=0.0; | |
for (int i=0; i <N_CAL1; i++){ | |
readGyro(); | |
gyroYoffset += gyroYdata; | |
delay(9); | |
} | |
gyroYoffset /= (float)N_CAL1; | |
M5.Lcd.fillScreen(BLACK); | |
digitalWrite(LED, HIGH); | |
} | |
void calib2() { | |
resetVar(); | |
resetMotor(); | |
digitalWrite(LED, LOW); | |
calDelay(80); | |
M5.Lcd.setCursor(0, LCDV_MID); | |
M5.Lcd.println(" Cal-2 "); | |
accXoffset=0.0; | |
gyroZoffset=0.0; | |
for (int i=0; i <N_CAL2; i++){ | |
readGyro(); | |
accXoffset += accXdata; | |
gyroZoffset += gyroZdata; | |
delay(9); | |
} | |
accXoffset /= (float)N_CAL2; | |
gyroZoffset /= (float)N_CAL2; | |
M5.Lcd.fillScreen(BLACK); | |
digitalWrite(LED, HIGH); | |
} | |
void checkButtonP() { | |
byte pbtn=M5.Axp.GetBtnPress(); | |
if (pbtn==2) calib1(); //short push | |
else if (pbtn==1) setMode(true); //long push | |
} | |
void calDelay(int n) { | |
for (int i=0; i<n; i++) { | |
getGyro(); | |
delay(9); | |
} | |
} | |
void setMode(bool inc) { | |
if (inc) demoMode=++demoMode%2; | |
M5.Lcd.fillScreen(BLACK); | |
M5.Lcd.setCursor(5, 5); | |
if (demoMode==0) M5.Lcd.print("Stand "); | |
else if (demoMode==1) M5.Lcd.print("Demo "); | |
} | |
void startDemo() { | |
moveRate=1.0; | |
spinContinuous=true; | |
spinStep=-40.0*clk; | |
} | |
void resetPara() { | |
Kang=37.0; | |
Komg=0.84; | |
KIang=800.0; | |
Kyaw=4.0; | |
Kdst=85.0; | |
Kspd=2.7; | |
mechFactL=0.45; | |
mechFactR=0.45; | |
punchPwr=20; | |
punchDur=1; | |
fbBalance=-3; | |
motorDeadband=10; | |
maxPwr=120; | |
punchPwr2=max(punchPwr, motorDeadband); | |
} | |
void getGyro() { | |
readGyro(); | |
varOmg = (gyroYdata-gyroYoffset); //unit:deg/sec | |
yawAngle += (gyroZdata-gyroZoffset) * clk; //unit:g | |
varAng += (varOmg + ((accXdata-accXoffset) * 57.3 - varAng) * cutoff ) * clk; //complementary filter | |
} | |
void readGyro() { | |
float gX, gY, gZ, aX, aY, aZ; | |
M5.Imu.getGyroData(&gX,&gY,&gZ); | |
M5.Imu.getAccelData(&aX,&aY,&aZ); | |
gyroYdata=gX; | |
gyroZdata=-gY; | |
gyroXdata=-gZ; | |
accXdata=aZ; | |
accZdata=aY; | |
} | |
void drive() { | |
#ifdef DEBUG | |
debugDrive(); | |
#endif | |
if (abs(moveRate)>0.1) spinFact=constrain(-(powerR+powerL)/10.0, -1.0, 1.0); //moving | |
else spinFact=1.0; //standing | |
if (spinContinuous) spinTarget += spinStep * spinFact; | |
else { | |
if (spinTarget < spinDest) spinTarget += spinStep; | |
if (spinTarget > spinDest) spinTarget -= spinStep; | |
} | |
moveTarget += moveStep * (moveRate +(float)fbBalance/100.0); | |
varSpd += power * clk; | |
varDst += Kdst * (varSpd * clk -moveTarget); | |
varIang += KIang * varAng * clk; | |
power = varIang + varDst + (Kspd * varSpd) + (Kang * varAng) + (Komg * varOmg); | |
if (abs(power) > 1000.0) counterOverPwr += 1; | |
else counterOverPwr =0; | |
if (counterOverPwr > maxOvp) return; | |
power = constrain(power, -maxPwr, maxPwr); | |
yawPower = (yawAngle - spinTarget) * Kyaw; | |
powerR = power - yawPower; | |
powerL = power + yawPower; | |
ipowerL = (int16_t) constrain(powerL * mechFactL, -maxPwr, maxPwr); | |
int16_t mdbn=-motorDeadband; | |
int16_t pp2n=-punchPwr2; | |
if (ipowerL > 0) { | |
if (motorLdir == 1) punchCountL = constrain(++punchCountL, 0, 100); | |
else punchCountL=0; | |
motorLdir = 1; | |
if (punchCountL<punchDur) drvMotorL(max(ipowerL, punchPwr2)); | |
else drvMotorL(max(ipowerL, motorDeadband)); | |
} | |
else if (ipowerL < 0) { | |
if (motorLdir == -1) punchCountL = constrain(++punchCountL, 0, 100); | |
else punchCountL=0; | |
motorLdir = -1; | |
if (punchCountL<punchDur) drvMotorL(min(ipowerL, pp2n)); | |
else drvMotorL(min(ipowerL, mdbn)); | |
} | |
else { | |
drvMotorL(0); | |
motorLdir = 0; | |
} | |
ipowerR = (int16_t) constrain(powerR * mechFactR, -maxPwr, maxPwr); | |
if (ipowerR > 0) { | |
if (motorRdir == 1) punchCountR = constrain(++punchCountR, 0, 100); | |
else punchCountR=0; | |
motorRdir = 1; | |
if (punchCountR<punchDur) drvMotorR(max(ipowerR, punchPwr2)); | |
else drvMotorR(max(ipowerR, motorDeadband)); | |
} | |
else if (ipowerR < 0) { | |
if (motorRdir == -1) punchCountR = constrain(++punchCountR, 0, 100); | |
else punchCountR=0; | |
motorRdir = -1; | |
if (punchCountR<punchDur) drvMotorR(min(ipowerR, pp2n)); | |
else drvMotorR(min(ipowerR, mdbn)); | |
} | |
else { | |
drvMotorR(0); | |
motorRdir = 0; | |
} | |
} | |
void drvMotorL(int16_t pwm) { | |
drvMotor(0, (int8_t)constrain(pwm, -127, 127)); | |
} | |
void drvMotorR(int16_t pwm) { | |
drvMotor(1, (int8_t)constrain(-pwm, -127, 127)); | |
} | |
void drvMotor(byte ch, int8_t sp) { | |
Wire.beginTransmission(0x38); | |
Wire.write(ch); | |
Wire.write(sp); | |
Wire.endTransmission(); | |
} | |
void resetMotor() { | |
drvMotorR(0); | |
drvMotorL(0); | |
counterOverPwr=0; | |
} | |
void resetVar() { | |
power=0.0; | |
moveTarget=0.0; | |
moveRate=0.0; | |
spinContinuous=false; | |
spinDest=0.0; | |
spinTarget=0.0; | |
spinStep=0.0; | |
yawAngle=0.0; | |
varAng=0.0; | |
varOmg=0.0; | |
varDst=0.0; | |
varSpd=0.0; | |
varIang=0.0; | |
} | |
void sendStatus () { | |
Serial.print(millis()-time0); | |
Serial.print(" stand="); Serial.print(standing); | |
Serial.print(" accX="); Serial.print(accXdata); | |
Serial.print(" power="); Serial.print(power); | |
Serial.print(" ang=");Serial.print(varAng); | |
Serial.print(", "); | |
Serial.print(millis()-time0); | |
Serial.println(); | |
} | |
void imuInit() { | |
M5.Imu.Init(); | |
if (M5.Imu.imuType=M5.Imu.IMU_MPU6886) { | |
M5.Mpu6886.SetGyroFsr(M5.Mpu6886.GFS_250DPS); //250DPS 500DPS 1000DPS 2000DPS | |
M5.Mpu6886.SetAccelFsr(M5.Mpu6886.AFS_4G); //2G 4G 8G 16G | |
if (serialMonitor) Serial.println("MPU6886 found"); | |
} | |
else if (serialMonitor) Serial.println("MPU6886 not found"); | |
} | |
void dispBatVolt() { | |
M5.Lcd.setCursor(5, LCDV_MID); | |
vBatt= M5.Axp.GetBatVoltage(); | |
M5.Lcd.printf("%4.2fv ", vBatt); | |
} |
0 件のコメント:
コメントを投稿