Motion Loop
Motion Control contains the main Finite State Machine, with these states:
The main loop of the motion controller. It is a FSM (Finite State Machine) which governs the state transitions.
There are following states present:
motion_control.hpp:
72typedef enum
73{
74 Unknown,
75 Idle,
76 Sitting, // Sitting, torque off
77 Standing_up,
78 Standing,
79 Walking,
80 Sitting_down
81} tState;
State: Unknown
Default state. Turn off servo motors. This is a save state that should never be reached.
motion_control.cpp:
166// Loop in which the motions are coordinated
167void MotionControl::motion_loop(void)
168{
169 switch (hex_state) {
170 case Unknown: // check in which state the robot is and go to idle
171 servo_driver.free_servos();
172 break;
State: Idle
Idle state is the default starting state.
State transitions
The robot waits for an input in form of the “start_cmd”. If the command is received the state changes to Sitting while the servos are turned on.
motion_control.cpp:
174 case Idle:
175 if (start_cmd) {
176 next_hex_state = Sitting;
177 servo_driver.turn_on_servos();
178 }
179 break;
State: Sitting
In the sitting state the servo motors move to the neutral sitting position with a reduced speed. This enables a smooth transition from unknown servo positions to the neutral sitting position. (In the visualization this step is unconstrained, meaning it will take 0 time to execute).
State transitions
The stand_up_cmd resets the servos to full speed again, while transitioning to the standing up state. If the start_cmd is reset the robot goes back to the idle state.
motion_control.cpp:
181 case Sitting:
182 current_height = sitting_height;
183 servo_driver.set_servo_speed(0.2); // reduce speed to prevent jerking motion
184 for (int c = 0; c < NUMBER_OF_LEGS; c++) {
185 feet.foot[c].position.x = neutral_foot_pos_x[c];
186 feet.foot[c].position.y = neutral_foot_pos_y[c];
187 feet.foot[c].position.z = current_height;
188 }
189
190 if (stand_up_cmd) {
191 servo_driver.set_servo_speed(0); // max speed
192 next_hex_state = Standing_up;
193 } else if (!start_cmd) {
194 next_hex_state = Idle;
195 servo_driver.free_servos();
196 }
197 break;
State: Standing_up
In the standing up state the height of the body is increased until it reaches the desired standing height.
State transitions
As soon as the standing height is reached the FSM transitions to the walking state.
motion_control.cpp:
199 case Standing_up:
200 current_height -= sit_step_height;
201 if (current_height <= -walking_height) {
202 next_hex_state = Walking;
203 current_height = -walking_height;
204 }
205
206 for (int c = 0; c < NUMBER_OF_LEGS; c++) {
207 // feet.foot[c].position.x = neutral_foot_pos_x[c];
208 // feet.foot[c].position.y = neutral_foot_pos_y[c];
209 feet.foot[c].position.z = current_height;
210 }
211 break;
State: Standing
In the standing state the robot only moves its body without moving the feet positions. This also serves as a proof of the inverse kinematics.
State transitions
If the stand_up_cmd is reset, the robot will sit down again. If the walking_cmd is set the robot goes to the Walking state.
motion_control.cpp:
213 case Standing:
214 body.orientation.pitch = 0.4 * cmd_vel_.linear.x;
215 body.orientation.roll = 0.5 * cmd_vel_.linear.y;
216 body.orientation.yaw = 0.5 * cmd_vel_.angular.z;
217
218 if (!stand_up_cmd) {
219 if (gait.is_standing()) {
220 next_hex_state = Sitting_down;
221 }
222 }
223
224 if (walking_cmd) {
225 next_hex_state = Walking;
226 }
227 break;
State: Walking
This state simply invokes the gait_cycle, which is where it starts walking according to the user input.
State transitions
If the stand_up_cmd is reset, the robot will sit down again. If the walking_cmd is reset the robot goes to the Standing state.
motion_control.cpp:
229 case Walking:
230 gait.gait_cycle(cmd_vel_, &feet);
231 /*
232 std::cerr << "[ ] feet(x,y,z) G1: (" <<
233 feet.foot[0].position.x << ", " <<
234 feet.foot[0].position.y << ", " <<
235 feet.foot[0].position.z << ")" <<
236 std::endl;
237 */
238
239 if (!stand_up_cmd) {
240 if (gait.is_standing()) {
241 next_hex_state = Sitting_down;
242 }
243 }
244 if (!walking_cmd) {
245 if (gait.is_standing()) {
246 next_hex_state = Standing;
247 }
248 }
249 break;
State: Sitting_down
This is the reversed motion from standing up.
motion_control.cpp:
251 case Sitting_down:
252 current_height += sit_step_height;
253 if (current_height >= sitting_height) {
254 current_height = sitting_height;
255 next_hex_state = Sitting;
256 }
257
258 for (int c = 0; c < NUMBER_OF_LEGS; c++) {
259 // feet.foot[c].position.x = neutral_foot_pos_x[c];
260 // feet.foot[c].position.y = neutral_foot_pos_y[c];
261 feet.foot[c].position.z = current_height;
262 }
263 break;
Transmitting the Results
After the State logic the new states are assigned:
motion_control.cpp:
271hex_state = next_hex_state;
And the leg joint angles are translated into joint_states which are then transmitted to the servo driver:
motion_control.cpp:
275// transmit the servo positions
276if (hex_state != Idle) {
277 if (inverse_kinematics.calculate_ik(feet, body, &legs)) {
278 int i = 0;
279 for (int leg_index = 0; leg_index < NUMBER_OF_LEGS; leg_index++) {
280 joint_state_.position[i] = legs.leg[leg_index].coxa;
281 i++;
282 joint_state_.position[i] = legs.leg[leg_index].femur;
283 i++;
284 joint_state_.position[i] = legs.leg[leg_index].tibia;
285 i++;
286 }
287 servo_driver.transmit_servo_positions(joint_state_);
288 } else {
289 RCLCPP_WARN(rclcpp::get_logger("Motion Control"), "Out of Range");
290 feet = old_feet; // Do not move any foot if one is out of range
291 }
292}
293
294old_feet = feet;
Functions
Not all functions are listed here, there are some more in the code, some of which are unused, obsolete or used for testing.
Sit Down/Stand Up
Commands used to trigger transitions. Only works if the start_cmd flag is set.
motion_control.cpp:
126void MotionControl::stand_up(void)
127{
128 if (start_cmd) {
129 RCLCPP_INFO(rclcpp::get_logger("Motion Control"), "Stand up");
130 stand_up_cmd = true;
131 }
132}
133
134void MotionControl::sit_down(void)
135{
136 if (start_cmd) {
137 RCLCPP_INFO(rclcpp::get_logger("Motion Control"), "Sit down");
138 stand_up_cmd = false;
139 }
140}
Start & Stop Command
This command first checks if the hexapod is sitting, if yes it sets the flag to either turn the servo motors on or off.
motion_control.cpp:
143void MotionControl::start_stop(void)
144{
145 if (stand_up_cmd) {return;}
146 if (start_cmd) {
147 RCLCPP_INFO(rclcpp::get_logger("Motion Control"), "Stop");
148 start_cmd = false;
149 } else {
150 RCLCPP_INFO(rclcpp::get_logger("Motion Control"), "Start");
151 start_cmd = true;
152 }
153}
Stand/Walk Command
This command sets the flag to either walk or move the body while standing.
motion_control.cpp:
155void MotionControl::stand_walk(void)
156{
157 if (!stand_up_cmd) {return;}
158 if (walking_cmd) {
159 RCLCPP_INFO(rclcpp::get_logger("Motion Control"), "Stand");
160 walking_cmd = false;
161 } else {
162 RCLCPP_INFO(rclcpp::get_logger("Motion Control"), "Walk");
163 walking_cmd = true;
164 }
165}
Update Velocity Command
Sets a new velocity if the robot is in the standing position.
motion_control.cpp:
93void MotionControl::update_vel(geometry_msgs::msg::Twist cmd_vel)
94{
95 // if the sitdown commands arrives the velocity is set to 0.
96 // So the Hexapod can enter a standing state
97 if (stand_up_cmd) {
98 cmd_vel_ = cmd_vel;
99 } else {
100 cmd_vel_.linear.x = 0;
101 cmd_vel_.linear.y = 0;
102 cmd_vel_.angular.z = 0;
103 }
104}