WAVEGO Custom Action Development Tutorial
WAVEGO Custom Action Development Tutorial
In the ServoCtrl.h example, we provide six functions for custom actions:
void functionStayLow(); void functionHandshake(); void functionJump(); void functionActionA(); void functionActionB(); void functionActionC();
The first three are pre‑programmed actions: crouch low, handshake, and jump.
The last three can be used to write your own custom actions. You can also add more functions and modify the HTML code in WebPage.h to add more buttons.The code for calling functions is in app_httpd.cpp.
In this tutorial, we will explain how to program actions.
Take the crouch‑stand action as an example:
void functionStayLow(){
// Robot crouches from normal standing height WALK_HEIGHT to the minimum height WALK_HEIGHT_MIN
for(float i = 0; i<=1; i+=0.02){
standUp(besselCtrl(WALK_HEIGHT, WALK_HEIGHT_MIN, i));
GoalPosAll();
delay(1);
}
delay(300);
// Robot rises from minimum height WALK_HEIGHT_MIN to maximum height WALK_HEIGHT_MAX
for(float i = 0; i<=1; i+=0.02){
standUp(besselCtrl(WALK_HEIGHT_MIN, WALK_HEIGHT_MAX, i));
GoalPosAll();
delay(1);
}
// Robot lowers from maximum height WALK_HEIGHT_MAX back to normal height WALK_HEIGHT
for(float i = 0; i<=1; i+=0.02){
standUp(besselCtrl(WALK_HEIGHT_MAX, WALK_HEIGHT, i));
GoalPosAll();
delay(1);
}
}
The complete code is shown above. When this function is called, the robot first crouches to the lowest point, then stands up to the highest point, and finally returns to the normal standing height.
The standUp() function controls the robot’s standing height.
For example, to make the robot stand at 70mm, you can write:
standUp(70); // call this function to calculate the angles each joint must rotate to achieve a standing height of 70mm, // and store the target positions in GoalPWN[]. // Calling this function alone does not start the servos. GoalPosAll(); // after calling this function, all servos of the robot will move to the corresponding positions in GoalPWM[].
The purpose of GoalPosAll(): each leg joint calculation takes time, especially for complex actions. The calculation load is high, and all servos need to start moving simultaneously to make the action look more coordinated.
If you want a linear action, you can write the crouch‑stand action like this:
for(int i = 0;i <= 10;i++){
standUp(70-i);
GoalPosAll();
delay(10);
}
for(int i = 0;i <= 10;i++){
standUp(60+i);
GoalPosAll();
delay(10);
}
If you want the motion to be smooth, you can use the following functions to achieve a compliant motion effect.
besselCtrl(VAL_START, VAL_END, i); // VAL_START: initial value // VAL_END: final value // i: linear interpolation point (0 to 1) // The return value of this function is a nonlinearly varying quantity calculated based on VAL_START, VAL_END, and the linearly varying setpoint i. Below we will explain how this is used.

As shown in the figure above, the horizontal axis represents the variable i, a linear interpolation point. The value of i changes linearly between 0 and 1 (with a constant increment over equal time intervals).
The vertical axis shows VAL_START and VAL_END, which are the start value and the end value respectively. However, note that the start value does not necessarily have to be smaller than the end value.
The output value lies between VAL_START and VAL_END. When the value of i changes linearly from 0 to 1, the rate of change of the output value varies: it starts slow, then becomes fast, and finally slows down.
This makes the robot’s movements appear more natural.
For example, to lower the robot from WALK_HEIGHT to WALK_HEIGHT_MIN, you can write:
for(float i = 0; i<=1; i+=0.02){
standUp(besselCtrl(WALK_HEIGHT, WALK_HEIGHT_MIN, i));
GoalPosAll();
delay(1);
}
besselCtrl() can also be used for other more complex purposes. Here is an example of the handshake action:
void functionHandshake(){
// Each leg can be edited individually, then call GoalPosAll() to move them together.
for(float i = 0; i<=1; i+=0.02){
// Control the front two legs to retract slightly and rise, making the robot look up.
singleLegCtrl(1, besselCtrl(WALK_EXTENDED_X, 0, i), besselCtrl(WALK_HEIGHT,
WALK_HEIGHT_MAX, i), besselCtrl(WALK_EXTENDED_Z, -15, i));
singleLegCtrl(3, besselCtrl(WALK_EXTENDED_X, 0, i), besselCtrl(WALK_HEIGHT,
WALK_HEIGHT_MAX, i), WALK_EXTENDED_Z);
// Control the rear two legs to retract and lower, making the robot sit while looking up.
singleLegCtrl(2, -WALK_EXTENDED_X, besselCtrl(WALK_HEIGHT, WALK_HEIGHT_MIN10, i), besselCtrl(WALK_EXTENDED_Z, 2*WALK_EXTENDED_Z, i));
singleLegCtrl(4, -WALK_EXTENDED_X, besselCtrl(WALK_HEIGHT, WALK_HEIGHT_MIN10, i), besselCtrl(WALK_EXTENDED_Z, 2*WALK_EXTENDED_Z, i));
// After all calculations, move together – otherwise moving step by step looks uncoordinated.
GoalPosAll();
// A short delay; the leg calculations also take some time.
delay(1);
}
// Handshake action part.
for(float i = 0; i<=1; i+=0.02){
singleLegCtrl(3, besselCtrl(0, WALK_RANGE/2+WALK_EXTENDED_X, i),
besselCtrl(WALK_HEIGHT_MAX, WALK_HEIGHT_MIN, i), besselCtrl(WALK_EXTENDED_Z, 0,
i));
GoalPosAll();
delay(1);
}
for(int shakeTimes = 0; shakeTimes < 3; shakeTimes++){
for(float i = 0; i<=1; i+=0.03){
singleLegCtrl(3, WALK_RANGE/2+WALK_EXTENDED_X,
besselCtrl(WALK_HEIGHT_MIN, WALK_HEIGHT_MIN+30, i), 0);
GoalPosAll();
delay(1);
}
for(float i = 0; i<=1; i+=0.03){
singleLegCtrl(3, WALK_RANGE/2+WALK_EXTENDED_X,
besselCtrl(WALK_HEIGHT_MIN+30, WALK_HEIGHT_MIN, i), 0);
GoalPosAll();
delay(1);
}
}
// Opposite of the first part: return to standing posture.
for(float i = 0; i<=1; i+=0.02){
singleLegCtrl(1, besselCtrl(0, WALK_EXTENDED_X, i),
besselCtrl(WALK_HEIGHT_MAX, WALK_HEIGHT, i), besselCtrl(-15, WALK_EXTENDED_Z,
i));
singleLegCtrl(3, besselCtrl(WALK_RANGE/2+WALK_EXTENDED_X, WALK_EXTENDED_X,
i), besselCtrl(WALK_HEIGHT_MIN, WALK_HEIGHT, i), besselCtrl(0, WALK_EXTENDED_Z,
i));
singleLegCtrl(2, -WALK_EXTENDED_X, besselCtrl(WALK_HEIGHT_MIN-10,
WALK_HEIGHT, i), besselCtrl(2*WALK_EXTENDED_Z, WALK_EXTENDED_Z, i));
singleLegCtrl(4, -WALK_EXTENDED_X, besselCtrl(WALK_HEIGHT_MIN-10,
WALK_HEIGHT, i), besselCtrl(2*WALK_EXTENDED_Z, WALK_EXTENDED_Z, i));
GoalPosAll();
delay(1);
}
}