Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
aa7d527fbThis file is pinned to the selected release. Download the original to open it in your design tool.
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;
#define ABS(a) (((a) < 0) ? -(a) : (a))
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define DEG(a) ((a)*M_PI/180.0)
#define RAD(a) ((a)*180.0/M_PI)
#define RAD(a) ((a)*180.0/M_PI)
#define SIGN(a) (((a) < 0) ? (-1) : (((a) > 0) ? (1) : (0)))
double grand(){
double ret = 0;
for(int i = 0; i < 12; i++){
ret += (float)rand() / RAND_MAX;
}
return(ret - 6.0);
}
double prand(){
return(rand() * 2 * M_PI / RAND_MAX - M_PI);
}
// double mod(double pos){
// return(fmod(pos + M_PI, 2 * M_PI) - M_PI);
// }
double mod(double a){
while(a < -M_PI){
a += 2.0 * M_PI;
}
while(a > M_PI){
a -= 2.0 * M_PI;
}
return(a);
}
double minus_(double a, double b){
if(ABS(a - b) < M_PI){
return(a - b);
}
else if(a > b){
return(a - b - 2.0 * M_PI);
}
else{
return(a - b + 2.0 * M_PI);
}
}
class mot_c{
public:
struct {
double pos;
double vel;
double acc;
double u;
double v;
double w;
double t;
double cur;
double volt;
double ind;
double torq;
int res_polarity;
} state;
struct feedback_s{
enum{
ENC,
RES,
NONE
} type;
int count;
double res_offset;
int enc_offset;
} feedback;
struct {
double max_i;
double i;
double r;
double l;
double nm_a;
double v_rps;
double slip;
} elec_spec;
struct mech_spec_s{
double t2; // term. time const
double max_rps;
int pole_count;
enum{
AC_SYNC,
AC_ASYNC,
DC
} mot_type;
double friction;
double damping;
double inertia; // inertia
} mech_spec;
struct{
double var;
double sin_offset;
double sin_scale;
double cos_offset;
double cos_scale;
} noise;
struct {
double friction; // friction (torque)
double load; // asym. torque
double damping; // damping (torque / vel)
double inertia; // inertia (torque / acc)
} load;
void reset();
void step(double periode);
int get_enc();
double get_sin();
double get_cos();
int get_polarity();
void set_volt(double u, double v, double w);
};
void mot_c::reset(){
state.pos = 0.0;//prand();
state.vel = 0.0;
state.acc = 0.0;
state.u = 0.0;
state.v = 0.0;
state.w = 0.0;
state.t = 20.0;
state.cur = 0.0;
state.volt = 0.0;
state.ind = 0.0;
state.res_polarity = 1.0;
feedback.enc_offset = state.pos;
}
void mot_c::step(double periode){
double d;
double q;
state.res_polarity *= -1;
switch(mech_spec.mot_type){
case mech_spec_s::AC_SYNC:
d = 2/3 * (cos(state.pos * mech_spec.pole_count) * state.u + cos(state.pos * mech_spec.pole_count - 2/3 * M_PI) * state.v + cos(state.pos * mech_spec.pole_count + 2/3 * M_PI) * state.w);
q = 2/3 * (-sin(state.pos * mech_spec.pole_count) * state.u - sin(state.pos * mech_spec.pole_count - 2/3 * M_PI) * state.v - sin(state.pos * mech_spec.pole_count + 2/3 * M_PI) * state.w);
state.volt = q;
break;
case mech_spec_s::AC_ASYNC:
state.volt = 0.0;
break;
case mech_spec_s::DC:
state.volt = state.u - state.v;
break;
}
state.ind = state.vel * elec_spec.v_rps;
state.cur += (state.volt - state.ind - state.cur * elec_spec.r) / elec_spec.l * periode;
state.cur = CLAMP(state.cur, -elec_spec.max_i, elec_spec.max_i);
//state.cur = (state.volt - state.ind) / elec_spec.r;
state.torq = state.cur * elec_spec.nm_a - (mech_spec.damping + load.damping) * state.vel + load.load;
if(abs(state.torq) < mech_spec.friction + load.friction){
state.torq = 0.0;
}
else{
state.torq = state.torq - SIGN(state.torq) * (mech_spec.friction + load.friction);
}
state.acc = state.torq / (mech_spec.inertia + load.inertia);
state.vel += state.acc * periode;
state.pos += state.vel * periode;
state.pos = mod(state.pos);
if(state.cur > elec_spec.max_i){
cerr << "MOT: cur > max_i: " << state.cur << endl;
}
if(state.vel > mech_spec.max_rps){
cerr << "MOT: vel > max_v: " << state.vel << endl;
}
}
void mot_c::set_volt(double u, double v, double w){
state.u = u;
state.v = v;
state.w = w;
}
int mot_c::get_enc(){
return((int)((state.pos - feedback.enc_offset + 2 * M_PI) * feedback.count / 2 / M_PI) % feedback.count);
}
double mot_c::get_sin(){
return(sin((state.pos + feedback.res_offset) * feedback.count) * noise.sin_scale * state.res_polarity + noise.sin_offset + grand() * noise.var);
}
double mot_c::get_cos(){
return(cos((state.pos + feedback.res_offset) * feedback.count) * noise.cos_scale * state.res_polarity + noise.cos_offset + grand() * noise.var);
}
int mot_c::get_polarity(){
return(state.res_polarity);
}
class cmd_c{
public:
struct {
double time;
double pos;
double vel;
double acc;
} state;
enum{
POS,
POS_VEL,
POS_VEL_ACC,
VEL,
VEL_ACC
} type;
enum{
SINE,
SQUARE,
RAMP,
CONST
} wave;
double periode;
double amplitude;
double max_vel;
double max_acc;
double pos_res;
double vel_res;
double acc_res;
void reset();
void step(double periode);
double get_pos();
double get_vel();
double get_acc();
};
void cmd_c::reset(){
state.time = 0.0;
state.pos = 0.0;
state.vel = 0.0;
state.acc = 0.0;
}
void cmd_c::step(double s){
state.time += s;
switch(wave){
case SINE:
state.pos = mod(sin(state.time / periode * 2 * M_PI) * amplitude);
state.vel = cos(state.time / periode * 2 * M_PI) * amplitude;
state.acc = -sin(state.time / periode * 2 * M_PI) * amplitude;
break;
case SQUARE:
state.pos = mod(((int)(state.time / periode * 2) % 2) * amplitude);
state.vel = 0;
state.acc = 0;
break;
case RAMP:
state.pos = mod(state.time * amplitude);
state.vel = 0;
state.acc = 0;
break;
case CONST:
state.pos = mod(amplitude);
state.vel = 0;
state.acc = 0;
break;
}
}
double cmd_c::get_pos(){
return((int)(state.pos / pos_res) * pos_res);
}
double cmd_c::get_vel(){
return((int)(state.vel / vel_res) * vel_res);
}
double cmd_c::get_acc(){
return((int)(state.acc / acc_res) * acc_res);
}
class drive_c{
public:
struct cmd_s{
double pos;
double vel;
double acc;
} cmd;
double dc;
double pwm_scale;
int pwm_res;
double pid_periode;
struct {
double offset;
double sin_scale;
double sin_avg;
double cos_scale;
double cos_avg;
double res_var;
double pos;
double vel;
double acc;
double cur;
double friction;
double load;
double damping;
double inertia;
double p;
double v;
double a;
} est;
struct {
double mag_pos;
double mag_vel;
double ctr;
} state;
mot_c* mot;
cmd_c* in;
void reset();
void step(double periode);
void (*input_cmd)(drive_c* drv, double periode);
void (*input_feedback)(drive_c* drv, double periode);
void (*pid)(drive_c* drv, double periode);
void (*output)(drive_c* drv, double periode);
void set_pos(double pos);
void set_pos_vel(double pos, double vel);
void set_pos_vel_acc(double pos, double vel, double acc);
void set_vel(double pos);
void set_vel_acc(double pos, double acc);
};
void drive_c::reset(){
cmd.pos = 0.0;
cmd.vel = 0.0;
cmd.acc = 0.0;
est.offset = 0.0;
est.sin_scale = 1.0;
est.cos_scale = 1.0;
est.sin_avg = 0.0;
est.cos_avg = 0.0;
est.res_var = 0.0;
est.friction = mot->mech_spec.friction;
est.load = 0.0;
est.damping = mot->mech_spec.damping;
est.inertia = mot->mech_spec.inertia;
est.pos = 0.0;
est.vel = 0.0;
est.acc = 0.0;
est.cur = 0.0;
state.mag_pos = 0.0;
state.mag_vel = 0.0;
state.ctr = 0.0;
}
void drive_c::step(double periode){
input_cmd(this, periode);
input_feedback(this, periode);
pid(this, periode);
output(this, periode);
}
void input_cmd(drive_c* drv, double periode){
double tp, tv;
tp = drv->cmd.pos;
tv = drv->cmd.vel;
switch(drv->in->type){
case cmd_c::POS:
drv->cmd.pos = drv->in->get_pos();
drv->cmd.vel = (drv->cmd.pos - tp) / periode;
drv->cmd.acc = (drv->cmd.vel - tv) / periode;
break;
case cmd_c::POS_VEL:
drv->cmd.pos = drv->in->get_pos();
drv->cmd.vel = drv->in->get_vel();;
drv->cmd.acc = (drv->cmd.vel - tv) / periode;
break;
case cmd_c::POS_VEL_ACC:
drv->cmd.pos = drv->in->get_pos();
drv->cmd.vel = drv->in->get_vel();;
drv->cmd.acc = drv->in->get_acc();
break;
case cmd_c::VEL:
drv->cmd.pos = drv->cmd.pos + drv->cmd.vel * periode;
drv->cmd.vel = drv->in->get_vel();;
drv->cmd.acc = (drv->cmd.vel - tv) / periode;
break;
case cmd_c::VEL_ACC:
drv->cmd.pos = drv->cmd.pos + drv->cmd.vel * periode;
drv->cmd.vel = drv->in->get_vel();;
drv->cmd.acc = drv->in->get_acc();
break;
}
}
void input_feedback(drive_c* drv, double periode){
double t1, t2;
static double sin_avg_amp = 1.0;
static double cos_avg_amp = 1.0;
static double r = 1.0;
double dpos = 0.0;
double v = 0.0;
static double i_sum;
double p = 0.05;
double i = 1 * periode;
double vlo = 0.0 * periode;
static int point = 0;
const int hist_size = 10;
static double pos_hist[hist_size];
static double vel_hist[hist_size];
static double acc_hist[hist_size];
t1 = drv->est.pos;
switch(drv->mot->feedback.type){
case mot_c::feedback_s::ENC:
drv->est.pos = mod(drv->mot->get_enc() * 2 * M_PI / drv->mot->feedback.count - drv->est.offset);
break;
case mot_c::feedback_s::RES:
drv->est.sin_avg = 0.995 * drv->est.sin_avg + 0.005 * drv->mot->get_sin();
drv->est.cos_avg = 0.995 * drv->est.cos_avg + 0.005 * drv->mot->get_cos();
// sin_avg_amp = 0.5 * sin_avg_amp + 0.5 * (drv->mot->get_sin() - drv->est.sin_avg) / drv->est.sin_scale * drv->mot->get_polarity();
// cos_avg_amp = 0.5 * cos_avg_amp + 0.5 * (drv->mot->get_cos() - drv->est.cos_avg) / drv->est.cos_scale * drv->mot->get_polarity();
//
// r = 0.99 * r + 0.01 * (sin_avg_amp * sin_avg_amp + cos_avg_amp * cos_avg_amp);
//
// if(ABS(sin_avg_amp) < 0.01){
// drv->est.cos_scale -= 0.001 * SIGN(1 - cos_avg_amp);
// }
//
// if(ABS(cos_avg_amp) < 0.01){
// drv->est.sin_scale -= 0.001 * SIGN(1 - sin_avg_amp);
// }
//
// if(ABS(r - 1.0) > 0.6){
// //cerr << "res error r: " << r << endl;
// }
dpos = (drv->mot->get_sin() - drv->est.sin_avg) / drv->est.sin_scale * drv->mot->get_polarity() * cos(drv->est.pos + drv->est.offset) - (drv->mot->get_cos() - drv->est.cos_avg) / drv->est.cos_scale * drv->mot->get_polarity() * sin(drv->est.pos + drv->est.offset);
i_sum += dpos * i + vlo * (drv->state.ctr * drv->dc - drv->est.vel * drv->mot->elec_spec.v_rps) / drv->mot->elec_spec.r * drv->mot->elec_spec.nm_a * periode;
dpos = p * dpos + i_sum;
drv->est.pos += dpos;
drv->est.pos = mod(drv->est.pos);
pos_hist[point] = drv->est.pos;
vel_hist[point] = minus_(pos_hist[(point) % hist_size], pos_hist[(point + hist_size - 2) % hist_size]) / (2 * periode);
acc_hist[point] = minus_(vel_hist[(point) % hist_size], vel_hist[(point + hist_size - 2) % hist_size]) / (2 * periode);
point++;
point %= hist_size;
//drv->est.vel = i_sum / periode;
drv->est.pos = mod(atan2((drv->mot->get_sin() - drv->est.sin_avg) / drv->est.sin_scale * drv->mot->get_polarity(), (drv->mot->get_cos() - drv->est.cos_avg) / drv->est.cos_scale * drv->mot->get_polarity()) - drv->est.offset);
break;
case mot_c::feedback_s::NONE:
break;
}
double a = 0.0;
v = 0.0;
p = 0.0;
double x = 0.0;
for(int j = 0; j < hist_size; j++){
a += acc_hist[(point + j) % hist_size];
}
a /= hist_size;
a *= 0.5;
for(int j = 0; j < hist_size; j++){
x = (j - hist_size / 2.0) * periode;
v += vel_hist[(point + j) % hist_size] - (2 * a * x);
}
v /= hist_size;
for(int j = 0; j < hist_size; j++){
x = (j - hist_size / 2.0) * periode;
p += pos_hist[(point + j) % hist_size] - (a * x * x + v * x);
}
p /= hist_size;
x = hist_size / 2.0 * periode;
drv->est.p = (a * x * x + v * x + p);
drv->est.v = (2 * a * x + v);
drv->est.a = (2 * a);
drv->est.pos = drv->mot->state.pos;
//drv->est.pos = drv->est.p;
// t2 = drv->est.vel;
// drv->est.vel = (drv->est.pos - t1) / periode;
// drv->est.acc = (drv->est.vel - t2) / periode;
}
void input_feedback_real(drive_c* drv, double periode){
drv->est.pos = drv->mot->state.pos;
drv->est.vel = drv->mot->state.vel;
drv->est.acc = drv->mot->state.acc;
}
void pid(drive_c* drv, double periode){
double p = 1000; // kp
double i = 10000 * periode; // ki
double d = 0.6 / periode; // kd
double dd = 0.0 / periode; // rel. vel. kd
double in = 0.0; // ind. kp
double dc_scale = 1.0 / drv->dc; // dc voltage scale
double ind = drv->mot->state.vel * drv->mot->elec_spec.v_rps / drv->dc;
double e = minus_(drv->cmd.pos, drv->est.pos);
static double e_old = 0;
static double i_sum = 0;
i_sum += e;
//i_sum -=
drv->state.ctr = p * e;
drv->state.ctr += i * i_sum;
drv->state.ctr += d * (e - e_old);
if(abs(e) > 0.01){
drv->state.ctr += dd * (e - e_old) / e;
}
drv->state.ctr *= dc_scale;
drv->state.ctr += in * ind;
drv->state.ctr = CLAMP(drv->state.ctr, -1, 1);
// if(ABS(drv->est.vel) >= drv->mot->mech_spec.max_rps * 0.7){
// drv->state.ctr = 0.0;
// }
if(abs(drv->state.ctr) >= 0.99){
i_sum -= e;
}
i_sum = CLAMP(i_sum, -1/i, 1/i);
e_old = e;
}
void pid2(drive_c* drv, double periode){
double ind = drv->est.vel * drv->mot->elec_spec.v_rps;
double max_volt_pos = drv->dc * drv->pwm_scale - ind;
double max_cur_pos = max_volt_pos / drv->mot->elec_spec.r;
double max_torq_pos = max_cur_pos * drv->mot->elec_spec.nm_a;
max_torq_pos -= drv->est.friction;
max_torq_pos -= drv->est.load;
max_torq_pos -= drv->est.damping * drv->est.vel;
double max_acc_pos = max_torq_pos / drv->est.inertia;
double max_dvel_pos = max_acc_pos * periode;
double max_volt_neg = -drv->dc * drv->pwm_scale - ind;
double max_cur_neg = max_volt_neg / drv->mot->elec_spec.r;
double max_torq_neg = max_cur_neg * drv->mot->elec_spec.nm_a;
max_torq_neg -= drv->est.friction;
max_torq_neg += drv->est.load;
max_torq_neg -= drv->est.damping * drv->est.vel;
double max_acc_neg = max_torq_neg / drv->est.inertia;
double max_dvel_neg = max_acc_neg * periode;
drv->state.ctr = 0.0;
}
void output(drive_c* drv, double periode){
double u, v, w;
switch(drv->mot->mech_spec.mot_type){
case mot_c::mech_spec_s::AC_SYNC:
case mot_c::mech_spec_s::AC_ASYNC:
case mot_c::mech_spec_s::DC:
u = ((int)((drv->state.ctr * drv->pwm_scale + 1) / 2.0 * drv->pwm_res)) * drv->dc / drv->pwm_res;
v = ((int)((drv->state.ctr * drv->pwm_scale * (-1) + 1) / 2.0 * drv->pwm_res)) * drv->dc / drv->pwm_res;
w = 0;
break;
}
drv->mot->set_volt(u, v, w);
}