#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;
}
int rc_adc_init(void)
initializes the analog to digital converter for reading
double rc_adc_batt(void)
reads the voltage of the 2-cell Lithium battery
double rc_dsm_ch_normalized(int ch)
Returns a scaled value from -1 to 1 corresponding to the min and max values recorded during calibrati...
int rc_dsm_init(void)
Starts the DSM background service.
int rc_dsm_is_connection_active(void)
Easily check on the state of the DSM radio packets without checking timeouts yourself.
int rc_dsm_is_new_data(void)
This is a check to see if new data is available.
int rc_encoder_eqep_init(void)
Initializes the eQEP encoder counters for channels 1-3.
int rc_encoder_eqep_read(int ch)
Reads the current position of an encoder channel.
int rc_encoder_eqep_cleanup(void)
Stops the eQEP encoder counters and closes file descriptors. This is not strictly necessary but is re...
int rc_encoder_eqep_write(int ch, int pos)
Sets the current position of an eQEP encoder channel. Usually for resetting a counter to 0 but can se...
int rc_mpu_set_dmp_callback(void(*func)(void))
Sets the callback function that will be triggered when new DMP data is ready.
int rc_mpu_is_gyro_calibrated(void)
Checks if a gyro calibration file is saved to disk.
#define TB_PITCH_X
Index of the dmp_TaitBryan[] array corresponding to the Pitch (X) axis.
Definition: mpu.h:49
int rc_mpu_initialize_dmp(rc_mpu_data_t *data, rc_mpu_config_t conf)
Initializes the MPU in DMP mode, see rc_test_dmp example.
int rc_mpu_power_off(void)
Powers off the MPU.
rc_mpu_config_t rc_mpu_default_config(void)
Returns an rc_mpu_config_t struct with default settings.
int rc_mpu_calibrate_gyro_routine(rc_mpu_config_t conf)
Runs gyroscope calibration routine.
@ ORIENTATION_Y_UP
Definition: mpu.h:142
int rc_led_set(rc_led_t led, int value)
sets the state of an LED
int rc_led_blink(rc_led_t led, float hz, float duration)
blinks an led at specified frequency and duration.
void rc_led_cleanup(void)
closes file descriptors to all opened LEDs
@ RC_LED_GREEN
Definition: led.h:36
@ RC_LED_RED
Definition: led.h:37
int rc_motor_free_spin(int ch)
Puts a motor into a zero-throttle state allowing it to spin freely.
int rc_motor_init(void)
Initializes all 4 motors and leaves them in a free-spin (0 throttle) state.
int rc_motor_standby(int standby_en)
Toggle the H-bridges in and out of low-power standby mode.
int rc_motor_set(int ch, double duty)
Sets the bidirectional duty cycle (power) to a single motor or all motors if 0 is provided as a chann...
int rc_saturate_double(double *val, double min, double max)
Modifies val to be bounded between between min and max.
int rc_filter_pid(rc_filter_t *f, double kp, double ki, double kd, double Tf, double dt)
Creates a discrete-time implementation of a parallel PID controller with high-frequency rolloff using...
int rc_filter_alloc_from_arrays(rc_filter_t *f, double dt, double *num, int numlen, double *den, int denlen)
Like rc_filter_alloc(), but takes arrays for the numerator and denominator coefficients instead of ve...
int rc_filter_reset(rc_filter_t *f)
Resets all previous inputs and outputs to 0. Also resets the step counter & saturation flag.
double rc_filter_march(rc_filter_t *f, double new_input)
March a filter forward one step with new input provided as an argument.
int rc_filter_enable_soft_start(rc_filter_t *f, double seconds)
Enables soft start functionality where the output bound is gradually opened linearly from 0 to the no...
#define RC_FILTER_INITIALIZER
Definition: filter.h:82
int rc_filter_print(rc_filter_t f)
Prints the transfer function and other statistic of a filter to the screen.
int rc_filter_free(rc_filter_t *f)
Frees the memory allocated by a filter's buffers and coefficient vectors. Also resets all filter prop...
int rc_filter_enable_saturation(rc_filter_t *f, double min, double max)
Enables saturation between bounds min and max.
int rc_pthread_create(pthread_t *thread, void *(*func)(void *), void *arg, int policy, int priority)
starts a pthread with specified policy and priority
int rc_pthread_timed_join(pthread_t thread, void **retval, float timeout_sec)
Joins a thread with timeout given in seconds.
rc_state_t rc_get_state(void)
fetches the current process state as set by the user or signal handler
int rc_remove_pid_file(void)
Removes the PID file created by rc_make_pid_file().
void rc_set_state(rc_state_t new_state)
sets the current process state.
int rc_make_pid_file(void)
Makes a PID file RC_PID_FILE (/run/shm/robotcontrol.pid) containing the current PID of your process.
int rc_enable_signal_handler(void)
Enables a generic signal handler. Optional but recommended.
rc_state_t
process state variable, read and modify by rc_get_state, rc_set_state, and rc_print_state....
Definition: start_stop.h:60
int rc_kill_existing_process(float timeout_s)
This function is used to make sure any existing program using the PID file is stopped.
@ RUNNING
Definition: start_stop.h:62
@ EXITING
Definition: start_stop.h:64
@ PAUSED
Definition: start_stop.h:63
void rc_usleep(unsigned int us)
Sleep in microseconds.
Struct containing configuration and state of a SISO filter.
Definition: filter.h:43
double gain
Additional gain multiplier, usually 1.0.
Definition: filter.h:48
configuration of the mpu sensor
Definition: mpu.h:155
rc_mpu_orientation_t orient
DMP orientation matrix, see rc_mpu_orientation_t.
Definition: mpu.h:179
int dmp_sample_rate
sample rate in hertz, 200,100,50,40,25,20,10,8,5,4
Definition: mpu.h:176
data struct populated with new sensor data
Definition: mpu.h:199
double dmp_TaitBryan[3]
Tait-Bryan angles (roll pitch yaw) in radians from DMP based on ONLY Accel/Gyro.
Definition: mpu.h:219