3.2.2. Component Development (SmartKeyboardNavigation)

In this section the SmartKeyboardNavigation component will be modeled and implemented. To do so, a new SmartSoft Component has to be created. The name of this new component should be SmartKeyboardNavigation.

After creating the project the component itself has to be modeled. The component should be able to catch the keyboard input and send the current velocity of the robot. The keyboard input will be received and processed with a few lines of C++ code. This code will run in an user thread, therefore the SmartTask "KeyboardInputTask" has to be added to the component. The thread should be executed approx. every 500ms, therefor the KeyboardInputTask should be modeled periodically with a period of 500ms (see figure 3.2).

To control the velocity of the robot a SmartSendClient "navVelSendClient" is modelled. The velocity is sent with the "CommNavigationVelocity" Communication Object. This Communication Object can be found in the CommBasicObjects repository for importing. The serverName and serviceName the new SmartSendClient has to connect to can be left blank. They will be set in the System Configuration in the next step. Finally the SmartComponentMetadata has to be added to the component.

In summary the SmartKeyboardNavigation component consists of:

Figure 3.1 shows the modeled component.

SmartKeyboardNavigation

Figure 3.1. SmartKeyboardNavigation


KeyboardInputTask task with its timing parameters.

Figure 3.2. KeyboardInputTask task with its timing parameters.


After the component is modeled the c++ code of the component hull with its inner and outer interfaces has to be generated to start the implementation of the keyboard navigation component. To do so, the user code has to be written in the generated "KeyboardInputTask.cc" and "KeyboardInputTask.hh" user code files.

First of all it has to be possible to detect a keyboard input. To do so, the function kbhit() [1] is implemented in the file "KeyboardInputTask.cc":


#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

int kbhit(void)
{
	struct termios oldt, newt;
	int ch;
	int oldf;

	tcgetattr(STDIN_FILENO, &oldt);
	newt = oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
	oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
	fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

	ch = getchar();

	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
	fcntl(STDIN_FILENO, F_SETFL, oldf);

	if(ch != EOF)
	{
		ungetc(ch, stdin);
		return 1;
	}

	return 0;
}
 
				

If a key is pressed the velocity of the robot has to be adjusted. The current speed control values are stored in task member variables "x" and "omega". These variables are defined in the "KeyboardInputTask.hh" file:

class KeyboardInputTask  : public KeyboardInputTaskCore
{

public:
	KeyboardInputTask(CHS::SmartComponent *comp);
	virtual ~KeyboardInputTask();
	
	virtual int on_entry();
	virtual int on_execute();
	virtual int on_exit();

	double x;
	double omega;
};
				

Depending on the pressed key the speed or the heading velocity has to be increased or decreased. To do so, the following code has to be executed each time the task is executed.


int KeyboardInputTask::on_execute()
{
	if(kbhit()) {
		char c = getchar();

		/*Arrow keys*/
		if (c == '\033') { // if the first value is esc
			getchar(); // skip the [
			switch(getchar()) { // the real value
				case 'A':
					std::cout << "Accelerating" << std::endl;
					x += 150;
					break;
				case 'B':
					std::cout << "Decreasing speed" << std::endl;
					x -= 150;
					break;
				case 'C':
					std::cout << "Shifting steering to right" << std::endl;
					omega -= 0.2;
					break;
				case 'D':
					std::cout << "Shifting steering to left" << std::endl;
					omega += 0.2;
					break;
			}
		/*WASD*/
		}else if(c == 'w') {
			std::cout << "Accelerating" << std::endl;
			x += 150;
		} else if (c == 'd') {
			std::cout << "Shifting steering to right" << std::endl;
			omega -= 0.2;
		} else if (c == 'a') {
			std::cout << "Shifting steering to left" << std::endl;
			omega += 0.2;
		} else if (c == 's') {
			std::cout << "Decreasing speed" << std::endl;
			x -= 150;
		} else if (c == 'q') {
			std::cout << "Emergency fullstop" << std::endl;
			x = 0;
			omega = 0;
		}
	}

	CommBasicObjects::CommNavigationVelocity vel;
	vel.set_vX(x);
	vel.set_omega(omega);

	CHS::StatusCode status = COMP->navVelSendClient->send(vel);
}
 
				

If a key is pressed the x or omega variable will be adjusted. The velocity values are then stored in the CommNavigationObject "vel" and send via the SmartSendClient "navVelSendClient".

In the example above the speed of the robot is increased or decreased by 150[mm/s] and the heading velocity is increased or decreased by 0.2[rad/s]. To make those values configurable store them in SmartComponentParameters. For this purpose a SmartComponentParameter has to be added to the component model. Figure 3.3 shows the SmartKeyboardNavigation component with an added SmartComponentParameter.

Adding parameters

Figure 3.3. Adding parameters


The internal parameters speedAcceleration and angularAcceleration are added:


InternalParam Settings{
	speedAcceleration : Double = 150.0
	angularAcceleration : Double = 0.2
}

				

After adding the parameters the code has to be regenerated. Additionally, the code of the "KeyboardInputTask.cc" has to be adapted. The values 150 and 0.2 have to be exchanged by the corresponding parameters:

int KeyboardInputTask::on_execute()
{
	ParameterStateStruct::SettingsType localstate = COMP->getGlobalState().getSettings();
	if(kbhit()) {
		char c = getchar();

		/*Arrow keys*/
		if (c == '\033') { // if the first value is esc
			getchar(); // skip the [
			switch(getchar()) { // the real value
				case 'A':
					std::cout << "Accelerating" << std::endl;
					x += localstate.getSpeedAcceleration();
					break;
				case 'B':
					std::cout << "Decreasing speed" << std::endl;
					x -= localstate.getSpeedAcceleration();
					break;
				case 'C':
					std::cout << "Shifting steering to right" << std::endl;
					omega -= localstate.getAngularAcceleration();
					break;
				case 'D':
					std::cout << "Shifting steering to left" << std::endl;
					omega += localstate.getAngularAcceleration();
					break;
			}
		/*WASD*/
		}else if(c == 'w') {
			std::cout << "Accelerating" << std::endl;
			x += localstate.getSpeedAcceleration();
		} else if (c == 'd') {
			std::cout << "Shifting steering to right" << std::endl;
			omega -= localstate.getAngularAcceleration();
		} else if (c == 'a') {
			std::cout << "Shifting steering to left" << std::endl;
			omega += localstate.getAngularAcceleration();
		} else if (c == 's') {
			std::cout << "Decreasing speed" << std::endl;
			x -= localstate.getSpeedAcceleration();
		} else if (c == 'q') {
			std::cout << "Emergency fullstop" << std::endl;
			x = 0;
			omega = 0;
		}
	}
	vel.set_vX(x);
	vel.set_omega(omega);

	CHS::StatusCode status = COMP->navVelSendClient->send(vel);
}
				

With this the component modeling and implementation is done and the new component can be build (cmake).



[1] Implementation on kbhit taken from: http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html

WordPress Appliance - Powered by TurnKey Linux