NXTway-GS(平衡机器人) 通用C API函数

 

NXTway (一个平衡机器人)是一个具有挑战性的NXT应用。 最麻烦的当然是—— "如何使机器人平衡"并且它需要复杂的数学方程式和物理/控制理论知识。不过,在这个例子中,你用HiTechnic Gyro Sensor(陀螺仪传感器)做一个平衡机器人,就不需要深入了解很多的知识。 所有你所需要做的只是:依照搭建指导搭建一个机器人,然后使用NXTway-GS通用平衡C API函数编写程序。 此外,这个样例可以视作一个基础,以便未来更加复杂的机器人开发。

 

搭建指导(英文): NXTway-GS_Building_Instructions.pdf

这个NXTway-GS新版本更加容易搭建,需要更少的部件,比先前的版本更加严格,并且支持许多变量,如下所示:
- RCX摩托轮胎或者是NXT标准轮胎
- 狭宽两种轮子间距行走
NXTway-GS 的摩托轮胎版本更加的强劲,快速,反应敏捷并且好看! 但是摩托轮胎并不包含在NXT零售套件中。

 

NXTway-GS 通用平衡C API函数:

NXTway-GS 通用平衡C API函数有一个简单的API结构(一个控制函数和一个初始化函数)。这个控制函数(balance_control)必须每4msec调用一次并且初始化函数(balance_init) 要初始化内部状态变量以更加有效的重置平衡控制。
这些C函数不是手写的,而是用设计为一个Simulink model并且用叫做Real-Time Workshop Embedded Coder的C/C++代码产生器自动产生那些函数。如果你是Simulink用户,你可以下载它,从The MathWorks File Exchange site。 它包含了有价值的文档来理解这个API函数中使用的控制理论(伺服器控制: 状态空间 + 积分反馈)。
NXTway-GS 通用平衡C API函数作为一个C函数库被提供出来,并且源代码被保存nxtOSEK\ecrobot\nxtway_gs_balancer目录下面。这个Simulink模型同样也被保存在html文件中(Web浏览器必须兼容SVG)。使用这些函数,你需要include balancer.h 头文件在你的源代码中。

通用平衡C API函数
描述

void
balance_control(
    F32 args_cmd_forward,
    F32 args_cmd_turn,
    F32 args_gyro,
    F32 args_gyro_offset,
    F32 args_theta_m_l,
    F32 args_theta_m_r,
    F32 args_battery,
    S8 *ret_pwm_l,
    S8 *ret_pwm_r)

控制机器人平衡。这个API必须每4msec被调用一次并且机器人必须按照指南里面搭建。 陀螺仪传感器的校准数值很可能是取决于产品,并且当它启动的时候会浮动。所以这些东东是要考虑到的。左右马达的转动可能不同,即便有相同的PWM输入。在这种情况下,用户需要添加转动补偿。

参数:
    args_cmd_forward: 机器人控制指令 前进/后退。
       100.0F(最大前进速度)至  -100.0F (最大后退速度)
    args_cmd_turn: 机器人控制命令 右转/左转。
       100.0F(最大右转速度) 至  -100.0F (最大左转速度)
    args_gyro: HiTechnic Gyro Sensor原始数据用来测量机器人的主体倾角速度。
    args_gyro_offset: HiTechnic Gyro Sensor 原始数据,当机器人主体倾斜角速度是0。
    args_theta_m_l: 左马达的转角原始数据
    args_theta_m_r: 右马达的转角原始数据
    args_battery: 电池电压(mV)
返回值:
    ret_pwm_l: 左马达的PWM输入
    ret_pwm_r: 右马达的PWM输入

void
balance_init(void)

初始化内部状态变量。这个API能够有效重置平衡控制。使用这个函数,左右马达转动角度必须重置为0。

参数:
    
返回值:
    无

 

样例程序:Sample program:

NXTway-GS 样例程序是手工编写的并且你可以通过蓝牙和PC HID 手柄控制它(有analog输入)。 这个样例程序假设使用的是 NXT GamePad PC实用工具软件。 超声波传感器(声纳)被用来探测障碍物并且当机器人接近障碍(25cm内), 机器人会后退以避障,这样就不会再去理会手柄的输入了。
默认情况下,样例程序的设置是为摩托轮胎版本设定的。并且它提供了宽/窄两种轮子间距的行走。当使用NXT标准轮胎时,需要在用户Makefile(在下面的样例Makefile中,USER_DEF宏被注释掉了)中添加编译宏(NXT_STD_TIRE)到USER_DEF宏定义。NXTway-GS的平衡控制变现也由控制参数决定(定义在balancer_param.c中)。这些参数是根据搭建指南里面的样例优化的。不过,你可能需要打开机器人调整下这些参数。

samples\nxtway_gs\Makefile

# Target specific macros
TARGET = NXTway_GS

NXTOSEK_ROOT = ../..

# nxtway_gs_balancer library desiged for NXTway-GS two wheeled self-balancing robot
USER_INC_PATH = $(NXTOSEK_ROOT)/ecrobot/nxtway_gs_balancer
USER_LIB = nxtway_gs_balancer

# using NXT standard tires (not Motorcycle tires)
#USER_DEF = NXT_STD_TIRE <-This macro has to be enabled for NXT standard tires

# User application source
TARGET_SOURCES := \
    balancer_param.c \
    nxtway_gs_main.c

# OSEK OIL file
TOPPERS_OSEK_OIL_SOURCE := ./nxtway_gs.oil

# below part should not be modified
O_PATH ?= build
include $(NXTOSEK_ROOT)/ecrobot/lejos_osek.tmf

samples\nxtway_gs\nxt_config.h

/**
 *******************************************************************************
 ** FILE NAME : nxt_config.h
 **
 ** ABSTRUCT : NXT device configration
 *******************************************************************************
 **/

#ifndef _NXT_CONFIG_H_
#define _NXT_CONFIG_H_

#include "ecrobot_interface.h"

/* NXT motor port configuration */
#define PORT_MOTOR_R NXT_PORT_B
#define PORT_MOTOR_L NXT_PORT_C
/* NXT sensor port configuration */
#define PORT_SONAR   NXT_PORT_S2
#define PORT_GYRO    NXT_PORT_S4
/* NXT Bluetooth configuration */
#define BT_PASS_KEY  "1234" <-Bluetooth pass key which needs to be set in PC
#endif

samples\nxtway_gs\nxtway_gs_main.c

/**
 ******************************************************************************
 ** FILE NAME : nxtway_gs_main.c
 **
 ** ABSTRUCT  : NXTway-GS (with a HiTechnic Gyro Sensor)
 **             Two wheeled self-balancing R/C robot controlled via Bluetooth.
 ******************************************************************************
 **/

#include "kernel.h"
#include "kernel_id.h"
#include "ecrobot_interface.h"

#include "balancer.h" /* NXTway-GS C API header file */ <-This header file has to be included
#include "nxt_config.h"

/*============================================================================
 * MACRO DEFINITIONS
 *===========================================================================*/
typedef enum {
  INIT_MODE,   /* system initialize mode */
  CAL_MODE,    /* gyro sensor offset calibration mode */
  CONTROL_MODE /* balance and RC control mode */
} MODE;

#define MIN_DISTANCE (25)    /* minimum distance in cm for obstacle avoidance */
#define BT_RCV_BUF_SIZE (32) /* it must be 32bytes with NXT GamePad */

/*============================================================================
 * DATA DEFINITIONS
 *===========================================================================*/

MODE nxtway_gs_mode = INIT_MODE; /* NXTway-GS mode */
volatile U8 obstacle_flag;       /* 1(obstacle detected)/0(no obstacle) */

/*============================================================================
 * FUNCTIONS
 *===========================================================================*/
/*============================================================================
 * Embedded Coder Robot hook functions
 *===========================================================================*/
//*****************************************************************************
// FUNCTION    : ecrobot_device_initialize
// ARGUMENT    : none
// RETURN      : none
// DESCRIPTION : ECRobot device init hook function
//*****************************************************************************

void ecrobot_device_initialize(void)
{
  ecrobot_init_sonar_sensor(PORT_SONAR);
  ecrobot_init_bt_slave(BT_PASS_KEY);
}

//*****************************************************************************
// FUNCTION    : ecrobot_device_terminate
// ARGUMENT    : none
// RETURN      : none
// DESCRIPTION : ECRobot device term hook function
//*****************************************************************************

void ecrobot_device_terminate(void)
{
  nxt_motor_set_speed(PORT_MOTOR_L, 0, 1);
  nxt_motor_set_speed(PORT_MOTOR_R, 0, 1);
  ecrobot_term_sonar_sensor(PORT_SONAR);
  ecrobot_term_bt_connection();
}

/*============================================================================
 * TOPPERS ATK specific Function/Tasks
 *===========================================================================*/

DeclareCounter(SysTimerCnt);

//*****************************************************************************
// FUNCTION    : user_1ms_isr_type2
// ARGUMENT    : none
// RETURN      : none
// DESCRIPTION : 1msec periodical OSEK type 2 ISR
//*****************************************************************************

void user_1ms_isr_type2(void)
{
  /* Increment System Timer Count to activate periodical Tasks */
  (void)SignalCounter(SysTimerCnt);
}

//*****************************************************************************
// TASK        : OSEK_Task_ts1
// ARGUMENT    : none
// RETURN      : none
// DESCRIPTION : 4msec periodical Task and controls NXTway-GS
//               INIT_MODE
//               ↓
//               CAL_MODE
//               ↓
//               CONTROL_MODE
//*****************************************************************************

TASK(OSEK_Task_ts1)
{
  static U32 gyro_offset;    /* gyro sensor offset value */
  static U32 avg_cnt;        /* average counter to calc gyro offset */
  static U32 cal_start_time; /* calibration start time */
  static U8 bt_receive_buf[BT_RCV_BUF_SIZE]; /* Bluetooth receive buffer(32bytes) */
  SINT i;
  S8 cmd_forward,cmd_turn;
  S8 pwm_l,pwm_r;

  switch(nxtway_gs_mode){
    case(INIT_MODE):
      gyro_offset = 0;
      avg_cnt = 0;
      for (i = 0; i < BT_RCV_BUF_SIZE; i++){
        bt_receive_buf[i] = 0;
      }
      balance_init(); /* NXTway-GS C API initialize function */ <-Call balance init function
      nxt_motor_set_count(PORT_MOTOR_L, 0); /* reset left motor count */
      nxt_motor_set_count(PORT_MOTOR_R, 0); /* reset right motor count */
      cal_start_time = ecrobot_get_systick_ms();
      nxtway_gs_mode = CAL_MODE;
      break;

    case(CAL_MODE):
      gyro_offset += (U32)ecrobot_get_gyro_sensor(PORT_GYRO);
      avg_cnt++;
      /* 1000msec later, start balancing */
      if ((ecrobot_get_systick_ms() - cal_start_time) >= 1000U){
        gyro_offset /= avg_cnt;
        ecrobot_sound_tone(440U, 500U, 30); /* beep a tone */
        nxtway_gs_mode = CONTROL_MODE;
      }
      break;

    case(CONTROL_MODE):
      /*
       * R/C command from NXT GamePad
       * buf[0]: -100(forward max.) to 100(backward max.)
       * buf[1]: -100(turn left max.) to 100(turn right max.)
       */
      (void)ecrobot_read_bt_packet(bt_receive_buf, BT_RCV_BUF_SIZE);
      cmd_forward = -(S8)bt_receive_buf[0]; /* reverse the direction */
      cmd_turn = (S8)bt_receive_buf[1];
      if (obstacle_flag == 1){
        /* make NXTway-GS move backward to avoid obstacle */
        cmd_forward = -100;
        cmd_turn = 0;
      }

      /* NXTway-GS C API balance control function (has to be invoked in 4msec period) */
      
balance_control( <-Call balance control function
        (F32)cmd_forward,
        (F32)cmd_turn,
        (F32)ecrobot_get_gyro_sensor(PORT_GYRO),
        (F32)gyro_offset,
        (F32)nxt_motor_get_count(PORT_MOTOR_L),
        (F32)nxt_motor_get_count(PORT_MOTOR_R),
        (F32)ecrobot_get_battery_voltage(),
        &pwm_l,
        &pwm_r);
      nxt_motor_set_speed(PORT_MOTOR_L, pwm_l, 1);
      nxt_motor_set_speed(PORT_MOTOR_R, pwm_r, 1);

      ecrobot_bt_data_logger(cmd_forward, cmd_turn); /* Bluetooth data logger */
      break;

    default:
      /* unexpected mode */
      nxt_motor_set_speed(PORT_MOTOR_L, 0, 1);
      nxt_motor_set_speed(PORT_MOTOR_R, 0, 1);
      break;
  }

  TerminateTask(); /* terminates this task and executed by System Timer Count */
}

//*****************************************************************************
// TASK        : OSEK_Task_ts2
// ARGUMENT    : none
// RETURN      : none
// DESCRIPTION : 40msec periodical Task and detects obstacle in front of
//               NXTway-GS by using a sonar sensor
//*****************************************************************************

TASK(OSEK_Task_ts2)
{
  obstacle_flag = 0; /* no obstacles */
  if ((nxtway_gs_mode == CONTROL_MODE) &&
    (ecrobot_get_sonar_sensor(PORT_SONAR) <= MIN_DISTANCE)){
     obstacle_flag = 1; /* obstacle detected */
  }

  TerminateTask(); /* terminates this task and executed by System Timer Count */
}

//*****************************************************************************
// TASK        : OSEK_Task_Background
// ARGUMENT    : none
// RETURN      : none
// DESCRIPTION : Background(never terminated) Task
//*****************************************************************************

TASK(OSEK_Task_Background)
{
  while(1){
    ecrobot_status_monitor("NXTway-GS"); /* LCD display */
    systick_wait_ms(500); /* 500msec wait */
  }
}

/******************************** END OF FILE ********************************/

samples\nxtway_gs\balancer_param.c

/**
 *******************************************************************************
 ** FILE NAME : balancer_param.c
 **
 ** ABSTRUCT  : NXTway-GS balance control parameters
 **
 ** NOTE: These parameters definitely affect the balance control of NXTway-GS.
 *******************************************************************************
 **/
#include "ecrobot_interface.h"

/*============================================================================
 * DATA DEFINITIONS
 *===========================================================================*/

F32 A_D = 0.8F;   /* low pass filter gain for motors average count */
F32 A_R = 0.996F; /* low pass filter gain for motors target count */

/*
 * NOTE: When NXT standard tires are used for NXTway-GS, a compiler macro (NXT_STD_TIRE)
 * has to be added to USER_DEF macro definition in user Makefile
 * E.g. USER_DEF = NXT_STD_TIRE
 */

#ifdef NXT_STD_TIRE
  /* state feedback gain for NXT standard tire */
  F32 K_F[4] = {-0.834434F, -38.1749F, -1.0985F, -3.55477F};
#else
  /* state feedback gain for RCX Motorcycle tire */
  F32 K_F[4] = {-0.870303F, -31.9978F, -1.1566F, -2.78873F};
#endif

F32 K_I = -0.44721F;   /* servo control integral gain */
F32 K_PHIDOT = 25.0F;  /* turn target speed gain */
F32 K_THETADOT = 7.5F; /* forward target speed gain */

const
F32 BATTERY_GAIN = 0.001089F; /* battery voltage gain for motor PWM outputs */
const
F32 BATTERY_OFFSET = 0.625F;  /* battery voltage offset for motor PWM outputs */

/******************************** END OF FILE ********************************/

samples\nxtway_gs\nxtway_gs.oil

/**
 ******************************************************************************
 ** FILE NAME : nxtway_gs.oil
 **
 ** ABSTRUCT : OSEK OIL(OSEK Implementation Language) file for NXTway-GS
 ******************************************************************************
 **/
#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{};

  /* Definitions of a periodical task: OSEK_Task_ts1 */
  TASK OSEK_Task_ts1
  {
    AUTOSTART = FALSE;
    PRIORITY = 3;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* bytes */
  };
  ALARM OSEK_Alarm_task_ts1
  {
    COUNTER = SysTimerCnt;
    ACTION = ACTIVATETASK
    {
      TASK = OSEK_Task_ts1;
    };
    AUTOSTART = TRUE
    {
      APPMODE = appmode1;
      ALARMTIME = 1;
      CYCLETIME = 4; /* executed every 4msec */ <-Configure Task execution period to 4msec
    };
  };

  /* Definitions of a periodical task: OSEK_Task_ts2 */
  TASK OSEK_Task_ts2
  {
    AUTOSTART = FALSE;
    PRIORITY = 2;
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* bytes */
  };
  ALARM OSEK_Alarm_task_ts2
  {
    COUNTER = SysTimerCnt;
    ACTION = ACTIVATETASK
    {
      TASK = OSEK_Task_ts2;
    };
    AUTOSTART = TRUE
    {
      APPMODE = appmode1;
      ALARMTIME = 1;
      CYCLETIME = 40; /* executed every 40msec */
    };
  };

  /* Definition of background task: OSEK_Task_Background */
  TASK OSEK_Task_Background
  {
    AUTOSTART = TRUE
    {
      APPMODE = appmode1;
    };
    PRIORITY = 1; /* lowest priority */
    ACTIVATION = 1;
    SCHEDULE = FULL;
    STACKSIZE = 512; /* bytes */
  };

  /* Definition of OSEK Alarm Counter: SysTimerCnt */
  COUNTER SysTimerCnt
  {
    MINCYCLE = 1;
    MAXALLOWEDVALUE = 10000;
    TICKSPERBASE = 1;
  };

 

 

 

返回 示例程序