#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "rc_balance_defs.h"
typedef enum drive_mode_t{
NOVICE,
ADVANCED
}drive_mode_t;
typedef enum arm_state_t{
ARMED,
DISARMED
}arm_state_t;
typedef struct setpoint_t{
arm_state_t arm_state;
drive_mode_t drive_mode;
double theta;
double phi;
double phi_dot;
double gamma;
double gamma_dot;
}setpoint_t;
typedef struct core_state_t{
double wheelAngleR;
double wheelAngleL;
double theta;
double phi;
double gamma;
double vBatt;
double d1_u;
double d2_u;
double d3_u;
double mot_drive;
} core_state_t;
typedef enum m_input_mode_t{
NONE,
DSM,
STDIN
} m_input_mode_t;
static void __print_usage(void);
static void __balance_controller(void);
static void* __setpoint_manager(void* ptr);
static void* __battery_checker(void* ptr);
static void* __printf_loop(void* ptr);
static int __zero_out_controller(void);
static int __disarm_controller(void);
static int __arm_controller(void);
static int __wait_for_starting_condition(void);
static void __on_pause_press(void);
static void __on_mode_release(void);
static core_state_t cstate;
static setpoint_t setpoint;
static m_input_mode_t m_input_mode = DSM;
static void __print_usage(void)
{
printf("\n");
printf("-i {dsm|stdin|none} specify input\n");
printf("-q Don't print diagnostic info\n");
printf("-h print this help message\n");
printf("\n");
}
int main(int argc, char *argv[])
{
int c;
pthread_t setpoint_thread = 0;
pthread_t battery_thread = 0;
pthread_t printf_thread = 0;
bool adc_ok = true;
bool quiet = false;
opterr = 0;
while ((c = getopt(argc, argv, "i:qh")) != -1){
switch (c){
case 'i':
if(!strcmp("dsm", optarg)) {
m_input_mode = DSM;
} else if(!strcmp("stdin", optarg)) {
m_input_mode = STDIN;
} else if(!strcmp("none", optarg)){
m_input_mode = NONE;
} else {
__print_usage();
return -1;
}
break;
case 'q':
quiet = true;
break;
case 'h':
__print_usage();
return -1;
break;
default:
__print_usage();
return -1;
break;
}
}
fprintf(stderr,"ERROR: failed to start signal handler\n");
return -1;
}
fprintf(stderr,"ERROR: failed to initialize pause button\n");
return -1;
}
fprintf(stderr,"ERROR: failed to initialize mode button\n");
return -1;
}
fprintf(stderr,"ERROR: failed to initialize eqep encoders\n");
return -1;
}
fprintf(stderr,"ERROR: failed to initialize motors\n");
return -1;
}
if(m_input_mode == DSM){
fprintf(stderr,"failed to start initialize DSM\n");
return -1;
}
}
fprintf(stderr, "failed to initialize adc\n");
adc_ok = false;
}
printf("\nPress and release MODE button to toggle DSM drive mode\n");
printf("Press and release PAUSE button to pause/start the motors\n");
printf("hold pause button down for 2 seconds to exit\n");
fprintf(stderr, "ERROR in rc_balance, failed to set RC_LED_GREEN\n");
return -1;
}
fprintf(stderr, "ERROR in rc_balance, failed to set RC_LED_RED\n");
return -1;
}
printf("Gyro not calibrated, automatically starting calibration routine\n");
printf("Let your MiP sit still on a firm surface\n");
}
setpoint.arm_state = DISARMED;
setpoint.drive_mode = NOVICE;
double D1_num[] = D1_NUM;
double D1_den[] = D1_DEN;
fprintf(stderr,"ERROR in rc_balance, failed to make filter D1\n");
return -1;
}
double D2_num[] = D2_NUM;
double D2_den[] = D2_DEN;
fprintf(stderr,"ERROR in rc_balance, failed to make filter D2\n");
return -1;
}
printf("Inner Loop controller D1:\n");
printf("\nOuter Loop controller D2:\n");
fprintf(stderr,"ERROR in rc_balance, failed to make steering controller\n");
return -1;
}
if (adc_ok) {
if(
rc_pthread_create(&battery_thread, __battery_checker, (
void*) NULL, SCHED_OTHER, 0)){
fprintf(stderr, "failed to start battery thread\n");
return -1;
}
}
else {
cstate.vBatt = V_NOMINAL;
}
if(isatty(fileno(stdout)) && (quiet == false)){
fprintf(stderr, "failed to start printf thread\n");
return -1;
}
}
fprintf(stderr,"ERROR: can't talk to IMU, all hope is lost\n");
return -1;
}
if(
rc_pthread_create(&setpoint_thread, __setpoint_manager, (
void*) NULL, SCHED_OTHER, 0)){
fprintf(stderr, "failed to start setpoint thread\n");
return -1;
}
printf("\nHold your MIP upright to begin balancing\n");
}
return 0;
}
void* __setpoint_manager(__attribute__ ((unused)) void* ptr)
{
double drive_stick, turn_stick;
int i, ch, chan, stdin_timeout = 0;
char in_str[11];
__disarm_controller();
if(m_input_mode == STDIN) fseek(stdin,0,SEEK_END);
if(setpoint.arm_state == DISARMED){
if(__wait_for_starting_condition()==0){
__zero_out_controller();
__arm_controller();
}
else continue;
}
switch(m_input_mode){
case NONE:
continue;
case DSM:
if(fabs(drive_stick)<DSM_DEAD_ZONE) drive_stick = 0.0;
if(fabs(turn_stick)<DSM_DEAD_ZONE) turn_stick = 0.0;
switch(setpoint.drive_mode){
case NOVICE:
setpoint.phi_dot = DRIVE_RATE_NOVICE * drive_stick;
setpoint.gamma_dot = TURN_RATE_NOVICE * turn_stick;
break;
case ADVANCED:
setpoint.phi_dot = DRIVE_RATE_ADVANCED * drive_stick;
setpoint.gamma_dot = TURN_RATE_ADVANCED * turn_stick;
break;
default: break;
}
}
setpoint.theta = 0;
setpoint.phi_dot = 0;
setpoint.gamma_dot = 0;
continue;
}
break;
case STDIN:
i = 0;
while ((ch = getchar()) != EOF && i < 10){
stdin_timeout = 0;
if(ch == 'n' || ch == '\n'){
if(i > 2){
in_str[i-2] = '\0';
if(chan == DSM_TURN_CH){
turn_stick = strtof(in_str, NULL) * DSM_TURN_POL;
setpoint.gamma_dot = turn_stick;
}
else if(chan == DSM_DRIVE_CH){
drive_stick = strtof(in_str, NULL) * DSM_DRIVE_POL;
setpoint.phi_dot = drive_stick;
}
}
if(ch == 'n') i = 1;
else i = 0;
}
else if(i == 1){
chan = ch - 0x30;
i = 2;
}
else{
in_str[i-2] = ch;
i++;
}
}
if(stdin_timeout >= SETPOINT_MANAGER_HZ){
setpoint.theta = 0;
setpoint.phi_dot = 0;
setpoint.gamma_dot = 0;
}
else{
stdin_timeout++;
}
continue;
break;
default:
fprintf(stderr,"ERROR in setpoint manager, invalid input mode\n");
break;
}
}
__disarm_controller();
return NULL;
}
static void __balance_controller(void)
{
static int inner_saturation_counter = 0;
double dutyL, dutyR;
/(ENCODER_POLARITY_R * GEARBOX * ENCODER_RES);
/(ENCODER_POLARITY_L * GEARBOX * ENCODER_RES);
cstate.phi = ((cstate.wheelAngleL+cstate.wheelAngleR)/2) + cstate.theta;
cstate.gamma = (cstate.wheelAngleR-cstate.wheelAngleL) \
* (WHEEL_RADIUS_M/TRACK_WIDTH_M);
return;
}
__disarm_controller();
return;
}
if(setpoint.arm_state==DISARMED){
return;
}
if(fabs(cstate.theta) > TIP_ANGLE){
__disarm_controller();
printf("tip detected \n");
return;
}
if(ENABLE_POSITION_HOLD){
if(fabs(setpoint.phi_dot) > 0.001) setpoint.phi += setpoint.phi_dot*DT;
setpoint.theta = cstate.d2_u;
}
else setpoint.theta = 0.0;
D1.
gain = D1_GAIN * V_NOMINAL/cstate.vBatt;
if(fabs(cstate.d1_u)>0.95) inner_saturation_counter++;
else inner_saturation_counter = 0;
if(inner_saturation_counter > (SAMPLE_RATE_HZ*D1_SATURATION_TIMEOUT)){
printf("inner loop controller saturated\n");
__disarm_controller();
inner_saturation_counter = 0;
return;
}
if(fabs(setpoint.gamma_dot)>0.0001) setpoint.gamma += setpoint.gamma_dot * DT;
dutyL = cstate.d1_u - cstate.d3_u;
dutyR = cstate.d1_u + cstate.d3_u;
return;
}
static int __zero_out_controller(void)
{
setpoint.theta = 0.0;
setpoint.phi = 0.0;
setpoint.gamma = 0.0;
return 0;
}
static int __disarm_controller(void)
{
setpoint.arm_state = DISARMED;
return 0;
}
static int __arm_controller(void)
{
__zero_out_controller();
setpoint.arm_state = ARMED;
return 0;
}
static int __wait_for_starting_condition(void)
{
int checks = 0;
const int check_hz = 20;
int checks_needed = round(START_DELAY*check_hz);
int wait_us = 1000000/check_hz;
if(fabs(cstate.theta) > START_ANGLE) checks++;
else checks = 0;
if(checks >= checks_needed) break;
}
checks = 0;
if(fabs(cstate.theta) < START_ANGLE) checks++;
else checks = 0;
if(checks >= checks_needed) return 0;
}
return -1;
}
static void* __battery_checker(__attribute__ ((unused)) void* ptr)
{
double new_v;
if (new_v>9.0 || new_v<5.0) new_v = V_NOMINAL;
cstate.vBatt = new_v;
}
return NULL;
}
static void* __printf_loop(__attribute__ ((unused)) void* ptr)
{
printf("\nRUNNING: Hold upright to balance.\n");
printf(" θ |");
printf(" θ_ref |");
printf(" φ |");
printf(" φ_ref |");
printf(" γ |");
printf(" D1_u |");
printf(" D3_u |");
printf(" vBatt |");
printf("arm_state|");
printf("\n");
}
printf("\nPAUSED: press pause again to start.\n");
}
last_rc_state = new_rc_state;
printf("\r");
printf("%7.3f |", cstate.theta);
printf("%7.3f |", setpoint.theta);
printf("%7.3f |", cstate.phi);
printf("%7.3f |", setpoint.phi);
printf("%7.3f |", cstate.gamma);
printf("%7.3f |", cstate.d1_u);
printf("%7.3f |", cstate.d3_u);
printf("%7.3f |", cstate.vBatt);
if(setpoint.arm_state == ARMED) printf(" ARMED |");
else printf("DISARMED |");
fflush(stdout);
}
}
return NULL;
}
static void __on_pause_press(void)
{
int i=0;
const int samples = 100;
const int us_wait = 2000000;
return;
__disarm_controller();
break;
__disarm_controller();
break;
default:
break;
}
while(i<samples){
return;
}
i++;
}
printf("long press detected, shutting down\n");
return;
}
static void __on_mode_release(void)
{
if(setpoint.drive_mode == NOVICE){
setpoint.drive_mode = ADVANCED;
printf("using drive_mode = ADVANCED\n");
}
else {
setpoint.drive_mode = NOVICE;
printf("using drive_mode = NOVICE\n");
}
return;
}