Kubo: Hindbrain Firmware - olinrobotics/gravl GitHub Wiki
Kubo's hindbrain is made up of a Teensy 3.5 and its associated Roboclaw motor controllers, sensors, and safety systems. The hindbrain has several functions:
- Monitor safety system in real time, stop the tractor if the system is activated.
- Receive commands from the midbrain, translate them, and forward them to the motor controllers.
- Read current tractor state from sensor values and send that data to the midbrain.
- Monitor link between self and midbrain for connectivity
The hindbrain firmware is (currently) composed of six files contained in gravl/src/hindBrain
. These files are comprised of the main hindbrain script hindBrain.ino
and corresponding header file hindBrain.h
, as well as two OAK classes for an eStop and a softSwitch.
The eStop and softSwitch classes were born from an old lab project attempting to created a generalized framework for ROS robots at Olin. Each class consists of a .cpp
file, which contains the implementation of the class, and a .h
file which should be included in any script using the class.
The OAK Estop class is designed to monitor both a hardware pin and a ROS topic /softestop
, and stop the tractor if it receives an estop state.
- If bool
True
is published to/softestop
, the class registers a "Software Estop" and calls the attached OnStop() function. - If bool
False
is published tosoftestop
, the class calls the attached OnStart() function. - If the hardware pin is pulled low, the class registers a "Hardware Estop" and calls the OnStop() function.
- Any state changes are published to the
/hardestop
topic.
Creating an eStop to manage a vehicle safety system is done using the Estop::Estop class. A short example is shown below:
Note: This code is non-functional, and is solely intended to provide a reference example. For a working example, please see Kubo's hindbrain code.
#include "estop.h"
#include <ros.h>
Estop *eStop;
ros::NodeHandle nh;
const byte ESTOP_SENSE_PIN = 1;
const byte ESTOP_DEBOUNCE_TIME = 50; // ms
#define SERIAL_BAUD_RATE 115200 // hz
boolean isEStopped = false;
void setup() {
eStop = new Estop(&nh, ESTOP_SENSE_PIN, ESTOP_DEBOUNCE_TIME);
eStop->onStop(eStopVehicle);
eStop->offStop(eStartVehicle);
nh.getHardware()->setBaud(SERIAL_BAUD_RATE);
nh.initNode();
}
void loop() {
// Vehicle runtime code
}
void eStopVehicle() {
// Enter estop state
isEStopped = true;
// Stop Vehicle
}
void eStartVehicle() {
// Leave estop state
isEStopped = false;
// Start Vehicle
}
#include "estop.h"
#include <ros.h>
In order to use the estop class, it must be added to the local directory and included. Using quotation marks tells the Arduino compiler to check the local directory, then the system library directory. In addition, the Arduino ros library needs to be included in order to create a NodeHandle and generally set a Rosserial environment.
Estop *eStop;
ros::NodeHandle nh;
const byte ESTOP_SENSE_PIN = 1;
const byte ESTOP_DEBOUNCE_TIME = 50; // ms
#define SERIAL_BAUD_RATE 115200 // hz
boolean isEStopped = false;
Outside of either the setup()
or loop()
functions, the nodehandle and the Estop need to be defined. Additionally, constants for the pin number, the button debounce time in milliseconds, and the baud rate for the serial communication between the hindbrain and the midbrain are all defined.
void setup() {
eStop = new Estop(&nh, ESTOP_SENSE_PIN, ESTOP_DEBOUNCE_TIME);
eStop->onStop(eStopVehicle);
eStop->offStop(eStartVehicle);
nh.getHardware()->setBaud(SERIAL_BAUD_RATE);
nh.initNode();
}
void loop() {
// Vehicle runtime code
}
Once the Estop class is setup in the setup()
function, no work need be done in the loop()
function for it to continue functioning. First step is to instantiate the class with the address of the nodehandle (which is why the ampersand is used), the pin for sensing hardware estops, and the pin debounce time. Secondly, the onStop and onStart functions need to be defined - these are discussed in more detail below. Finally, the NodeHandle needs to be initialized to begin the serial communication to the midbrain.
void eStopVehicle() {
// Enter estop state
isEStopped = true;
// Stop Vehicle
}
void eStartVehicle() {
// Leave estop state
isEStopped = false;
// Start Vehicle
}
Lastly, the functions for onStart()
and onStop()
need to be defined. The onStop()
function should set any estop-related variables to the estopped state and perform any operations needed to emergency stop the vehicle. This may include turning on warning lights, shutting off the engine, and moving actuators to stopped positions.
The onStart()
function should set any estop-related variables to the normal state and perform any operations needed to restart the vehicle, This may include starting the engine, turning on running lights, etc.
The OAK SoftSwitch class is designed to monitor a ROS topic and update a corresponding hardware pin based on received messages.
- If bool
True
is published to/<topic>
, the class pulls the pin HIGH. - If bool
False
is published to/<topic>
, the class pulls the pin LOW.
Creating an softSwitch to manage a DigitalPin is done using the OAKSoftSwitch::OAKSoftSwitch class. A short example is shown below:
Note: This code is non-functional, and is solely intended to provide a reference example. For a working example, please see Kubo's hindbrain code.
#include "soft_switch.h"
#include <ros.h>
OAKSoftSwitch *switch;
ros::NodeHandle nh;
const byte SWITCH_PIN = 1;
#define SERIAL_BAUD_RATE 115200 // hz
void setup() {
switch = new OAKSoftSwitch(&nh, "/switch", SWITCH_PIN);
nh.getHardware()->setBaud(SERIAL_BAUD_RATE);
nh.initNode();
}
void loop() {
// Vehicle runtime code
}
#include "estop.h"
#include <ros.h>
In order to use the softSwitch class, it must be added to the local directory and included. Using quotation marks tells the Arduino compiler to check the local directory, then the system library directory. In addition, the Arduino ros library needs to be included in order to create a NodeHandle and generally set a Rosserial environment.
OAKSoftSwitch *switch;
ros::NodeHandle nh;
const byte SWITCH_PIN = 1;
#define SERIAL_BAUD_RATE 115200 // hz
Outside of either the setup()
or loop()
functions, the nodehandle and the softSwitch need to be defined. Additionally, constants for the pin number and the baud rate for the serial communication between the hindbrain and the midbrain are all defined.
void setup() {
switch = new OAKSoftSwitch(&nh, "/switch", SWITCH_PIN);
nh.getHardware()->setBaud(SERIAL_BAUD_RATE);
nh.initNode();
}
void loop() {
// Vehicle runtime code
}
Once the softSwitch class is setup in the setup()
function, no work need be done in the loop()
function for it to continue functioning. First step is to instantiate the class with the address of the nodehandle (which is why the ampersand is used), the name of the topic for subscription, and the pin number. Finally, the NodeHandle needs to be initialized to begin the serial communication to the midbrain.