記事一覧

M5StickCで倒立振子2モータ化(I2CモータードライブDRV8830)その6 重心を下げてみた

M5StickCで倒立振子2モータ化(I2CモータードライブDRV8830)その6 重心を下げてみた



こんにちはRoboTakaoです。

引き続きM5StickCで作る倒立振子2モータ版です。

NX11_12.jpg

前回までの記事はこちらになります。
その1その2その3その4その5

今回は重心位置を下げるために鉄のプレートを数枚買ってきたので
ホットボンドで付けてみました

NX11_08.jpeg

NX11_09.jpg

これでホイール 軸の15mm上ぐらいに重心が来ました。

NX11_11.jpeg

バッテリも前回外付けにしましたので長く動かすことができて、PIDゲインを調整できます。



だいぶ安定してきたように思います。

スケッチ



#define BLYNK_PRINT Serial
#define BLYNK_USE_DIRECT_CONNECT

#include <M5StickC.h>
#include <BlynkSimpleEsp32_BLE.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <Wire.h>

#define MPU6886_AFS M5.MPU6886.AFS_2G // Ascale [g] (±2,4,8,16)
#define MPU6886_GFS M5.MPU6886.GFS_250DPS // Gscale [deg/s] (±250,500,1000,200)

char auth[] = "jZpGVzEJgw4g3SkzJkdpeAgdTGvRWAoW"; //メールで送られるAuth Token

int VMAX = 45;
int VMIN = 1;

float Kp = 3.5;
float Ki = 3.5;
float Kd = 0.05;
float target = -87.4;
float P, I, D, preP;

float power = 0;
float dt, preTime;
float TempTime;

bool started = false;

float roll, pitch, yaw;
float now;
int roll_initial;
int roll_initial_count = 100;

void drawScreen() {
M5.Lcd.setCursor(0, 0);
M5.Lcd.printf("T:%6.1f\n",target);
// 目標角度(オフセット込み)
M5.Lcd.printf("roll:%6.1f\n",roll);
// 現在のロール角
M5.Lcd.printf("now:%4.1f\n", target - roll);
// 現在の偏差
M5.Lcd.printf("Power:%5.1f\n", power);
// PID出力
M5.Lcd.printf("Kp:%5.1f\n", Kp);
M5.Lcd.printf("Ki:%5.1f\n", Ki);
M5.Lcd.printf("Kd:%5.1f\n", Kd);
M5.Lcd.printf("Bat:%5.1fV\n", M5.Axp.GetBatVoltage());// バッテリー電圧
}

const int motor_R = 0x60;
const int motor_L = 0x64;

//モータドライバ I2C制御 motor driver I2C
void writeMotorResister(byte vset_R, byte vset_L, byte data1){
int vdata_R = vset_R << 2 | data1;
int vdata_L = vset_L << 2 | data1;
Wire.beginTransmission(motor_R);
Wire.write(0x00);
Wire.write(vdata_R);
Wire.endTransmission(true);

Wire.beginTransmission(motor_L);
Wire.write(0x00);
Wire.write(vdata_L);
Wire.endTransmission(true);
}

//ヴァーチャルピンデータ受信
BLYNK_WRITE(V0) {
Kp = param[0].asFloat();
Serial.print("Kp:");
Serial.println(Kp);
}

BLYNK_WRITE(V1) {
Ki = param[0].asFloat();
Serial.print("Ki:");
Serial.println(Ki);
}

BLYNK_WRITE(V2) {
Kd = param[0].asFloat();
Serial.print("Kd:");
Serial.println(Kd);
}

BLYNK_WRITE(V3) {
VMAX = param[0].asInt();
Serial.print("VMAX");
Serial.println(VMAX);
}

BLYNK_WRITE(V4) {
target = param[0].asInt();
Serial.print("target");
Serial.println(target);
}

void Initial_Value(){
Blynk.virtualWrite(V0, Kp);
Blynk.virtualWrite(V1, Ki);
Blynk.virtualWrite(V2, Kd);
Blynk.virtualWrite(V3, VMAX);
Blynk.virtualWrite(V4, target);
}

//DRV8830 Fault時にリセット
uint8_t readMotorStatus(byte motor_adress) {
uint8_t result = 0x00;

Wire.beginTransmission(motor_adress);
Wire.write(0x01); // read register 0x01
Wire.endTransmission();

Wire.requestFrom(motor_adress, 1);
if (Wire.available()) {
result = Wire.read();
} else {
// Serial.println("No status data");
}

return result;
}

void resetMotorStatus(byte motor_adress) { //DRV8830リセット
Wire.beginTransmission(motor_adress);
Wire.write(0x01); // fault
Wire.write(0x80); // clear
Wire.endTransmission(true);
}

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

pinMode(GPIO_NUM_10, OUTPUT);
digitalWrite(GPIO_NUM_10, HIGH);

M5.begin();
M5.Lcd.setRotation(2);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(1);
M5.Axp.ScreenBreath(8);
M5.MPU6886.Init();
M5.MPU6886.SetGyroFsr(MPU6886_GFS);
M5.MPU6886.SetAccelFsr(MPU6886_AFS);
Blynk.setDeviceName("Blynk");
Blynk.begin(auth);

Wire.begin(0, 26, 400000); //SDA, SCL
writeMotorResister(0x00, 0x00, 0x00);

drawScreen();
}

void loop() {
M5.update();
Blynk.run();

int status_R = readMotorStatus(motor_R); //Motor Fault -> reset
if (status_R & 0x01) {
Serial.print("Right Motor Fault : ");
Serial.println(status_R, HEX);
resetMotorStatus(motor_R);
}

int status_L = readMotorStatus(motor_L); //Motor Fault -> reset
if (status_L & 0x01) {
Serial.print("Left Motor Fault : ");
Serial.println(status_L, HEX);
resetMotorStatus(motor_L);
}

if ( M5.BtnB.wasReleased() ) {
// Bボタンを押すと再起動
esp_restart();
}

if ( M5.BtnA.wasReleased() ) {
Serial.println("START");
digitalWrite(GPIO_NUM_10, LOW);
Initial_Value();

//roll_initail_count 分だけ捨てる
for(int j=1; j M5.MPU6886.getAhrsData(&pitch,&roll,&yaw);
delay(10);
}

//roll_initail_count 分だけ初期値として平均値を求める
for(int i=1; i<=roll_initial_count; i++){
M5.MPU6886.getAhrsData(&pitch,&roll,&yaw);
roll_initial += roll;
delay(10);
}
roll_initial /= roll_initial_count;
Serial.print("roll_initail:");
Serial.println(roll_initial);

digitalWrite(GPIO_NUM_10, HIGH);
preTime = micros();
started = true;
}

if (!started) {
return;
}

M5.MPU6886.getAhrsData(&pitch,&roll,&yaw);
roll = roll - (90 + roll_initial);
Serial.println(roll);

now = target - roll;
TempTime = micros();
dt = (TempTime - preTime) / 1000000; // 処理時間を求める
preTime = TempTime; // 処理時間を記録

// 目標角度から現在の角度を引いて偏差を求める
// -90~90を取るので90で割って-1.0~1.0にする
P = (target - roll) / 90;
// 偏差を積分する
I += P * dt;
// 偏差を微分する
D = (P - preP) / dt;
// 偏差を記録する
preP = P;
// 出力を計算する
power = Kp * P + Ki * I + Kd * D;
// 出力を-1.0~1.0の範囲に収める
if (power < -1.0) {
power = -1.0;
}
if (1.0 < power) {
power = 1.0;
}
// 積分部分が大きくなりすぎると出力が飽和するので大きくなり過ぎたら0に戻す(アンチワインドアップ)
if (1 < abs(I * Ki)) {
I = 0;
// anti windup
}

int Vdata_R = ((float)(VMAX - VMIN) * fabs(power)) + VMIN;
int Vdata_L = ((float)(VMAX - VMIN) * fabs(power)) + VMIN;

if(power > 0){
writeMotorResister(byte(Vdata_R), byte(Vdata_L), 0x01);
}else if(power < 0){
writeMotorResister(byte(Vdata_R), byte(Vdata_L), 0x02);
}

drawScreen();

delay(10);
}

スポンサードリンク

コメント

M5stickとSG90HVで倒立振子

極力ローコストで倒立振子を
M5stickとSG90HVで作る際、SG90HVの応答特性が良くないのでギヤモータとモータドライバを組み合わせた場合より難しいです。
SG90HVをいろいろ使われてますが動作特性で調べられてましたら教えて頂きたいです。
以上、よろしくお願い致します。

SG90HV応答性

残念ながらSG90HVの応答性を確認したことはありません。倒立振子を制御する際は、重心位置を高くした場合は、倒れる際のスピードに追いつくモータの最大スピードが見合うほどの速さが必要だと思っています。それぞれのモータスピードは上限があるのでモータありきの場合は重心位置を下げるなどの工夫が必要だと思います。ギアモータの減速比の調整でカバーできるものもあると思いますが、その場合トルクが見合わないかもしれません。SG90HVの場合は減速比は変えられないので、モータありきで構造設計するということになります。あとは制御(PIDゲイン調整)で工夫をすれば、立たせることはできると思います。動作電圧を上げて使用すると速度は速くなると思いますが定格以上で使う場合は自己責任となります。

コメントの投稿

非公開コメント

プロフィール

RoboTakao

Author:RoboTakao
みなさんご訪問ありがとうございます。ロボット作りたいけどお小遣いそんなにないし、簡単でローコストでロボットを作るための私のプロジェクトを紹介します。

ウェブサイトもありますのでそちらもよろしくお願いします。
http://robotakao.jp/

スポンサーリンク