NXT GT

 

NXT GT is a Bluetooth based R/C car. OSEK was originally designed for embedded real-time control systems which are used in real cars, so this would be a suitable application for nxtOSEK!

    

     

NXT GT uses the following Sensors/Servo Motors and other hardware devices:

  • Servo Motor on Port A for steering control
  • Servo Motor on Port B for rear left wheel
  • Servo Motor on Port C for rear right wheel
  • Light Sensors on Port 1(left) and Port 3(right)
  • Ultrasonic Sensor on Port 2
  • Touch Sensor on Port 4
  • Abe UB 22S Bluetooth dongle
  • BUFFALO digital/analog game pad for PC

This version of NXT GT does not use all NXT Sensors, however these Sensors can be used to develop an intelligent car which may have line tracing, Ultrasonic Sensor based obstruction avoidance.
On the PC side, we developed a Bluetooth based R/C application program that is called NXT GamePad. NXT GamePad enables users to remotely control a NXT GT by using an analog stick game pad. Since NXT GamePad V1.01, NXT GamePad has also supported data logging feature. In case of NXT GT and NXT GamePad, NXT GT sends logging data every 10msec and the logged data can be save as a CSV file. So it is easy to analyze the logged data in MATLAB, Microsoft Excel...

 

Custom plastic wheels:

It is difficult to make a NXT based R/C Car Drifting due to rubber tires and less motor speed compared to real R/C Drift Cars. Hence, custom plastic wheels were needed to reduce the tire-road friction.

    

 

Building instructions:

LDraw/ML CAD file of NXT GT is available to see the detailed building instructions: nxtGT.ldr
To open nxtGT.ldr, you also need to have LEGO Mindstorms NXT LDraw Parts Library which is available at Philo's Home Page.

 

Specifications:

Specifications (with custom plastic wheels)
Weight [g]
922 (with rechargeable battery)
Moment of inertia [kgm^2]
3.1x10^(-3)
Maximum steering angle [deg]
-40 to 40

Minimum rotation radius [cm]

47.5 (with rubber tyres)
Maximum speed [m/s]
1.3 (with rubber tyres @8V)
Maximum motor torque [N.cm]
16.7
Maximum motor power [W]
3.10
Coefficient of static tyre friction

Forward: 0.096 Backward: 0.110 Side: 0.146 (custom plastic wheels)
Forward: 0.577 Backward: 0.677 Side: 0.805 (rubber tyres)

 

Source code:

NXT GT has the following three main control logics:

  • Proportional steering angle control
  • Wheels speed control with Electronic Differential Control that can be switched ON/OFF by the Touch Sensor
  • Bluetooth communication handler includes NXT GamePad data logger API

TaskControl is the core Task of NXT GT and it is activated every 10msec. In TaskControl, steering control logic, wheels speed control logic with Electronic Differential Control, and Bluetooth communication handler are implemented. Beside with these core logics, distance measurement by the Ultrasonic Sensor (for data logging) is activated every 50msec and LCD monitor is activated every 500msec. So totally, there are 4 Tasks (TaskInitialize, TaskControl, TaskSonar and TaskLCD) are running in the NXT. A unique point of nxtOSEK application is that users do not need to use time wait API which is frequently used in other NXT programming languages to execute control algorithm at desired timing. nxtOSEK provides accurate preemptive and periodical/event driven Task scheduling.

samples\nxtgt\nxtgt.c

/* nxtgt.c */
#include "kernel.h"
#include "kernel_id.h"
#include "ecrobot_interface.h"

/* OSEK declarations */
DeclareCounter(SysTimerCnt);
DeclareTask(TaskInitialize);
DeclareTask(TaskControl);
DeclareTask(TaskSonar);
DeclareTask(TaskLCD);

/* Definitions */
#define MOTOR_STEERING     NXT_PORT_A
#define MOTOR_LEFT         NXT_PORT_B
#define MOTOR_RIGHT        NXT_PORT_C
#define STEERING_LIMIT             40 /* degree */
#define STEERING_P_GAIN             2 /* proportinal gain */
#define DIFF_GAIN_MAX            0.7F /* 1.0-DIFF_GAIN_MAX: max. differential effect */
#define NEUTRAL_DEAD_ZONE           2 /* degree */
#define PWM_OFFSET                 10 /* friction compensator */
#define EDC_ON                     -1 /* Electronic Differential Control: ON */
#define EDC_OFF                     1 /* Electronic Differential Control: OFF */

static S8 EDC_flag;                   /* EDC flag */

/* Prototypes */
S32 FrictionComp(S32 ratio, S32 offset);

/* nxtOSEK hooks */
void ecrobot_device_initialize(void)
{
  ecrobot_set_light_sensor_active(NXT_PORT_S1);
  ecrobot_set_light_sensor_active(NXT_PORT_S3);
  ecrobot_init_sonar_sensor(NXT_PORT_S2);
  ecrobot_init_bt_connection();
}

void ecrobot_device_terminate(void)
{
  nxt_motor_set_speed(MOTOR_STEERING, 0, 1);
  nxt_motor_set_speed(MOTOR_RIGHT, 0, 1);
  nxt_motor_set_speed(MOTOR_LEFT, 0, 1);
  ecrobot_set_light_sensor_inactive(NXT_PORT_S1);
  ecrobot_set_light_sensor_inactive(NXT_PORT_S3);
  ecrobot_term_sonar_sensor(NXT_PORT_S2);
  ecrobot_term_bt_connection();
}

void user_1ms_isr_type2(void)
{
  StatusType ercd;

  ercd = SignalCounter(SysTimerCnt); /* Increment OSEK Alarm Counter */
  if (ercd != E_OK)
  {
    ShutdownOS(ercd);
  }
}

/* TaskInitialize */
TASK(TaskInitialize)
{
  nxt_motor_set_speed(MOTOR_STEERING, 0, 1);
  nxt_motor_set_speed(MOTOR_RIGHT, 0, 1);
  nxt_motor_set_speed(MOTOR_LEFT, 0, 1);
  nxt_motor_set_count(MOTOR_STEERING, 0);
  nxt_motor_set_count(MOTOR_RIGHT, 0);
  nxt_motor_set_count(MOTOR_LEFT, 0);

  EDC_flag = EDC_OFF;

  TerminateTask();
}

/* TaskControl executed every 10msec */
TASK(TaskControl)
{
  S32 analog_stick_left;  /* speed command data from GamePad */
  S32 analog_stick_right; /* steering command data from GamePad */
  S32 steering_angle;
  S32 steering_err;
  S32 steering_speed;
  S32 diff_gain;
  U8 touch_sensor;
  static U8 touch_sensor_state = 0;
  static U8 bt_receive_buf[32]; /* buffer size is fixed as 32 */

  /* receive NXTGamePad command
   * byte0 speed_data -100(forward max.) to 100(backward max.)
   * byte1 steering_data -100(left max.) to 100(right max.)
   */

  ecrobot_read_bt_packet(bt_receive_buf, 32);
  analog_stick_left = -(S32)(*(S8 *)(&bt_receive_buf[0])); /* reverse the direction */
  analog_stick_right = (S32)(*(S8 *)(&bt_receive_buf[1]));

  /* read Touch Sensor to switch Electronic Differential Control */
  touch_sensor = ecrobot_get_touch_sensor(NXT_PORT_S4);
  if (touch_sensor == 1 && touch_sensor_state == 0)
  {
    EDC_flag = ~EDC_flag + 1; /* toggle */
  }
  touch_sensor_state = touch_sensor;

  /* steering control */
  steering_angle = nxt_motor_get_count(MOTOR_STEERING);
  steering_err = (STEERING_LIMIT*analog_stick_right)/100 - steering_angle;
  steering_speed = STEERING_P_GAIN*steering_err;
  nxt_motor_set_speed(MOTOR_STEERING, FrictionComp(steering_speed,PWM_OFFSET), 1);

  /* wheel motors control with Electric Differential Control */
  
diff_gain = 10;
  if (steering_angle > NEUTRAL_DEAD_ZONE) /* turn right */
  {
    if (EDC_flag == EDC_ON)
    {
      diff_gain = (S32)((1.0F - (float)steering_angle*DIFF_GAIN_MAX/STEERING_LIMIT)*10);
    }
    nxt_motor_set_speed(MOTOR_RIGHT,
      FrictionComp((analog_stick_left*diff_gain)/10,PWM_OFFSET), 1);
    nxt_motor_set_speed(MOTOR_LEFT, FrictionComp(analog_stick_left,PWM_OFFSET), 1);
  }
  else if (steering_angle < -NEUTRAL_DEAD_ZONE) /* turn left */
  {
    if (EDC_flag == EDC_ON)
    {
      diff_gain = (S32)((1.0F + (float)steering_angle*DIFF_GAIN_MAX/STEERING_LIMIT)*10);
    }
    nxt_motor_set_speed(MOTOR_RIGHT, FrictionComp(analog_stick_left,PWM_OFFSET), 1);
    nxt_motor_set_speed(MOTOR_LEFT,
      FrictionComp((analog_stick_left*diff_gain)/10,PWM_OFFSET), 1);
  }
  else /* go straight */
  {
    nxt_motor_set_speed(MOTOR_RIGHT, FrictionComp(analog_stick_left,PWM_OFFSET), 1);
    nxt_motor_set_speed(MOTOR_LEFT, FrictionComp(analog_stick_left,PWM_OFFSET), 1);
  }

  /* send NXT status data to NXT GamePad */
  ecrobot_bt_data_logger((S8)analog_stick_left, (S8)analog_stick_right);

  TerminateTask();
}

/* TaskSonar executed every 50msec */
TASK(TaskSonar)
{
  S32 sonar;

  /* Sonar Sensor is invoked just for data logging */
  sonar = ecrobot_get_sonar_sensor(NXT_PORT_S2);

  TerminateTask();
}

/* TaskLCD executed every 500msec */
TASK(TaskLCD)
{
  ecrobot_status_monitor("NXT GT");

  TerminateTask();
}

/* Sub functions */
S32 FrictionComp(S32 ratio, S32 offset)
{
  if (ratio > 0)
  {
    return ((100-offset)*ratio/100 + offset);
  }
  else if (ratio < 0)
  {
    return ((100-offset)*ratio/100 - offset);
  }
  else
  {
    return ratio;
  }
}

samples\nxtgt\nxtgt.oil

#include "implementation.oil"

CPU ATMEL_AT91SAM7S256
{
  OS LEJOS_OSEK
  {
    STATUS = EXTENDED;
    STARTUPHOOK = FALSE;
    ERRORHOOK = FALSE;
    SHUTDOWNHOOK = FALSE;
    PRETASKHOOK = FALSE;
    POSTTASKHOOK = FALSE;
    USEGETSERVICEID = FALSE;
    USEPARAMETERACCESS = FALSE;
    USERESSCHEDULER = FALSE;
  };

  /* Definition of application mode */
  APPMODE appmode1{};

  /* Definition of TaskInitialize */
  TASK TaskInitialize
  {
    AUTOSTART = TRUE
    {
      APPMODE = appmode1;
    };
    PRIORITY = 4;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* Stack size */
  };

  /* Definition of TaskControl */
  TASK TaskControl
  {
    AUTOSTART = FALSE;
    PRIORITY = 3;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* Stack size */
  };

  /* Definition of TaskSonar */
  TASK TaskSonar
  {
    AUTOSTART = FALSE;
    PRIORITY = 2;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* Stack size */
  };

  /* Definition of TaskLCD */
  TASK TaskLCD
  {
    AUTOSTART = FALSE;
    PRIORITY = 1;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* Stack size */
  };

  /* Definition of OSEK Alarm Counter */
  COUNTER SysTimerCnt
  {
    MINCYCLE = 1;
    MAXALLOWEDVALUE = 10000;
    TICKSPERBASE = 1; /* One tick is equal to 1msec */
  };

  /* Definition of TaskControl execution timing */
  ALARM cyclic_alarm_TaskControl
  {
    COUNTER = SysTimerCnt;
    ACTION = ACTIVATETASK
    {
      TASK = TaskControl;
    };
    AUTOSTART = TRUE
    {
      ALARMTIME = 1;
      CYCLETIME = 10; /* executed every 10msec */
      APPMODE = appmode1;
    };
  };

  /* Definition of TaskSonar execution timing */
  ALARM cyclic_alarm_TaskSonar
  {
    COUNTER = SysTimerCnt;
    ACTION = ACTIVATETASK
    {
      TASK = TaskSonar;
    };
    AUTOSTART = TRUE
    {
      ALARMTIME = 1;
      CYCLETIME = 50; /* executed every 50msec */
      APPMODE = appmode1;
    };
  };

  /* Definition of TaskLCD execution timing */
  ALARM cyclic_alarm_TaskLCD
  {
    COUNTER = SysTimerCnt;
    ACTION = ACTIVATETASK
    {
      TASK = TaskLCD;
    };
    AUTOSTART = TRUE
    {
      ALARMTIME = 1;
      CYCLETIME = 500; /* executed every 500msec */
      APPMODE = appmode1;
    };
  };
};

 

 

 

Back to Samples