Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions src/Lite3_sdk_deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,15 @@ source install/setup.bash
python3 src/Lite3_sdk_deploy/interface/robot/simulation/mujoco_simulation_ros2.py
```

### Control (Terminal 2)
### Control (Terminal 1)

<span style="color: red;">**Note:**</span>
> - Right click simulator window and select "always on top"
> - When the robot dog stands up, it may become stuck due to self-collision in the simulation. This is not a bug; please try again.
> - z: default position
> - z: default position / stand up from lie down
> - c: rl control default position
> - x: lie down
> - r: joint damping
> - wasd:forward/leftward/backward/rightward
> - qe:clockwise/counter clockwise

Expand Down Expand Up @@ -166,14 +168,18 @@ source install/setup.bash
ros2 run lite3_sdk_deploy rl_deploy
```
<span style="color: red;">**keyboard control:**</span>
> - z: default position
> - z: default position / stand up from lie down
> - c: rl control default position
> - x: lie down
> - r: joint damping
> - wasd:forward/leftward/backward/rightward
> - qe:clockwise/counter clockwise

<span style="color: red;">**gamepad control:**</span>
> - Y: default position
> - Y: default position / stand up from lie down
> - A: rl control default position
> - X: lie down
> - Press both joystick buttons:joint damping
> - Left joystick:forward/leftward/backward/rightward
> - Right joystick:clockwise/counter clockwise

Expand All @@ -196,4 +202,4 @@ Verified cross-host cases:

<span style="color: red;">**Warning: The ROS 2 version in Lite3 is foxy (Compatible with Ubuntu 20.04). If the ROS 2 version in your host computer is not foxy, communication will not work properly (ROS 2 does not officially support cross-version communication).**</span>
We add an additional shadow subscriber to solve this problem temporarily.
If you know how to solve it better, welcome to submit an issue or a pull request.
If you know how to solve it better, welcome to submit an issue or a pull request.
2 changes: 2 additions & 0 deletions src/Lite3_sdk_deploy/include/types/custom_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace types{
WaitingForStand = 0,
StandingUp = 1,
JointDamping = 2,
LieDown = 3,
RLControlMode = 6,
};

Expand All @@ -23,6 +24,7 @@ namespace types{
kIdle = 0,
kStandUp = 1,
kJointDamping = 2,
kLieDown = 3,
kRLControl = 6,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def __init__(self,
self.viewer = None
if USE_VIEWER:
self.viewer = mujoco.viewer.launch_passive(self.model, self.data)
self._configure_viewer_camera()

def _set_initial_pose(self, key: str):
"""关节位置设置为与 PyBullet 脚本一致的初始角度"""
Expand All @@ -112,6 +113,13 @@ def _set_initial_pose(self, key: str):
self.data.qpos[:] = qpos0
mujoco.mj_forward(self.model, self.data)

def _configure_viewer_camera(self):
self.viewer.cam.type = mujoco.mjtCamera.mjCAMERA_FREE
self.viewer.cam.lookat[:] = np.array([0.4, 0.0, 0.45])
self.viewer.cam.distance = 2.4
self.viewer.cam.azimuth = 135.0
self.viewer.cam.elevation = -25.0

def _cmd_callback(self, msg: JointsDataCmd):
"""Convert received (published) positions/velocities to internal (raw)"""
if len(msg.data.joints_data) not in (12, 16):
Expand Down Expand Up @@ -274,4 +282,4 @@ def _publish_robot_state(self, step: int):
sim_node = MuJoCoSimulationNode()
sim_node.start()
sim_node.destroy_node()
rclpy.shutdown()
rclpy.shutdown()
15 changes: 12 additions & 3 deletions src/Lite3_sdk_deploy/interface/user_command/keyboard_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,19 @@ class KeyboardInterface : public UserCommandInterface
usr_cmd_->target_mode = uint8_t(RobotMotionState::StandingUp);
std::cout << "[MODE] Standing Up\n";
}
else if (k == 'z' && msfb_->GetCurrentState() == RobotMotionState::LieDown) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::StandingUp);
std::cout << "[MODE] Standing Up\n";
}
else if (k == 'c' && msfb_->GetCurrentState() == RobotMotionState::StandingUp) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::RLControlMode);
std::cout << "[MODE] RL Control\n";
}
else if (k == 'x' && (msfb_->GetCurrentState() == RobotMotionState::StandingUp
|| msfb_->GetCurrentState() == RobotMotionState::RLControlMode)) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::LieDown);
std::cout << "[MODE] Lie Down\n";
}
}

void keyboard_loop()
Expand All @@ -116,7 +125,7 @@ class KeyboardInterface : public UserCommandInterface
<< "╚════════════════════════════════════════════════╝\n"
<< " Movement: W/S (forward/back) A/D (left/right)\n"
<< " Rotation: Q (CCW) E (CW)\n"
<< " Mode: R (damping) Z (stand) C (control)\n"
<< " Mode: R (damping) Z (stand) C (control) X (lie down)\n"
<< "\n";

char ch;
Expand All @@ -130,7 +139,7 @@ class KeyboardInterface : public UserCommandInterface
char k = std::tolower(static_cast<unsigned char>(ch));

// Handle mode commands
if (k == 'r' || k == 'z' || k == 'c') {
if (k == 'r' || k == 'z' || k == 'c' || k == 'x') {
process_mode_command(k);
continue;
}
Expand Down Expand Up @@ -227,4 +236,4 @@ class KeyboardInterface : public UserCommandInterface
<< " side=" << max_side_
<< " yaw=" << max_yaw_ << "\n";
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,20 @@ class KeyboardInterface : public UserCommandInterface
usr_cmd_->target_mode = uint8_t(RobotMotionState::JointDamping);
std::cout << "[MODE] Joint Damping\n";
}
else if (keycode == KEY_Z && msfb_->GetCurrentState() == RobotMotionState::WaitingForStand) {
else if (keycode == KEY_Z && (msfb_->GetCurrentState() == RobotMotionState::WaitingForStand
|| msfb_->GetCurrentState() == RobotMotionState::LieDown)) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::StandingUp);
std::cout << "[MODE] Standing Up\n";
}
else if (keycode == KEY_C && msfb_->GetCurrentState() == RobotMotionState::StandingUp) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::RLControlMode);
std::cout << "[MODE] RL Control\n";
}
else if (keycode == KEY_X && (msfb_->GetCurrentState() == RobotMotionState::StandingUp
|| msfb_->GetCurrentState() == RobotMotionState::RLControlMode)) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::LieDown);
std::cout << "[MODE] Lie Down\n";
}
}

bool init_all_keyboards()
Expand Down Expand Up @@ -170,7 +176,7 @@ class KeyboardInterface : public UserCommandInterface
<< "╚════════════════════════════════════════════════╝\n"
<< " Hold W/S/A/D/Q/E → velocity ramps up\n"
<< " Release key → instant stop on that axis\n"
<< " Modes: R (damping) Z (stand) C (RL control)\n\n";
<< " Modes: R (damping) Z (stand) C (RL control) X (lie down)\n\n";

struct input_event ev;

Expand Down Expand Up @@ -210,7 +216,7 @@ class KeyboardInterface : public UserCommandInterface

// Mode keys (only on real press)
if (ev.value == 1) {
if (ev.code == KEY_R || ev.code == KEY_Z || ev.code == KEY_C) {
if (ev.code == KEY_R || ev.code == KEY_Z || ev.code == KEY_C || ev.code == KEY_X) {
process_mode_key(ev.code);
}
if (ev.code == KEY_ESC) {
Expand Down Expand Up @@ -281,4 +287,4 @@ class KeyboardInterface : public UserCommandInterface
<< " side:" << max_side_
<< " yaw:" << max_yaw_ << "\n";
}
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class RetroidGamepadInterface : public UserCommandInterface {
bool Y_last = (last_buttons_ & BIT_Y) != 0;
bool A_pressed = (msg->buttons & BIT_A) != 0;
bool A_last = (last_buttons_ & BIT_A) != 0;
bool X_pressed = (msg->buttons & BIT_X) != 0;
bool X_last = (last_buttons_ & BIT_X) != 0;
bool left_axis_button = (msg->buttons & BIT_LEFT_AXIS_BUTTON) != 0;
bool right_axis_button = (msg->buttons & BIT_RIGHT_AXIS_BUTTON) != 0;
bool left_axis_button_last = (last_buttons_ & BIT_LEFT_AXIS_BUTTON) != 0;
Expand All @@ -111,6 +113,21 @@ class RetroidGamepadInterface : public UserCommandInterface {
if (A_pressed && !A_last) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::RLControlMode);
RCLCPP_INFO(node_->get_logger(), "Mode: RL Control");
} else if (X_pressed && !X_last) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::LieDown);
RCLCPP_INFO(node_->get_logger(), "Mode: Lie Down");
}
break;
case RobotMotionState::RLControlMode:
if (X_pressed && !X_last) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::LieDown);
RCLCPP_INFO(node_->get_logger(), "Mode: Lie Down");
}
break;
case RobotMotionState::LieDown:
if (Y_pressed && !Y_last) {
usr_cmd_->target_mode = uint8_t(RobotMotionState::StandingUp);
RCLCPP_INFO(node_->get_logger(), "Mode: Standing Up");
}
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ class ControlParameters
*/
float stand_duration_ = 1.5;

/**
* @brief lie down duration
*/
float liedown_duration_ = 2.0;

/**
* @brief policy path
*/
std::string common_policy_path_;
Vec3f common_policy_p_gain_, common_policy_d_gain_;
};

155 changes: 155 additions & 0 deletions src/Lite3_sdk_deploy/state_machine/quadruped/liedown_state.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* @file liedown_state.hpp
* @brief from stand state to lie down state
* @author DeepRobotics
* @version 1.0
* @date 2026-02-12
*
* @copyright Copyright (c) 2025 DeepRobotics
*
*/
#pragma once

#include "state_base.h"

namespace q{
class LieDownState : public StateBase{
private:
VecXf init_joint_pos_, init_joint_vel_, current_joint_pos_, current_joint_vel_;
double time_stamp_record_, run_time_;
VecXf goal_joint_pos_, kp_, kd_;
MatXf joint_cmd_;
float liedown_duration_ = 2.;

const float init_hipx_pos_ = Deg2Rad(0.);

void GetRobotJointValue(){
current_joint_pos_ = ri_ptr_->GetJointPosition();
current_joint_vel_ = ri_ptr_->GetJointVelocity();
run_time_ = ri_ptr_->GetInterfaceTimeStamp();
}

void RecordJointData(){
init_joint_pos_ = current_joint_pos_;
init_joint_vel_ = current_joint_vel_;
time_stamp_record_ = run_time_;
}

float GetCubicSplinePos(float x0, float v0, float xf, float vf, float t, float T){
if(t >= T) return xf;
float a, b, c, d;
d = x0;
c = v0;
a = (vf*T - 2*xf + v0*T + 2*x0) / pow(T, 3);
b = (3*xf - vf*T - 2*v0*T - 3*x0) / pow(T, 2);
return a*pow(t, 3)+b*pow(t, 2)+c*t+d;
}

float GetCubicSplineVel(float x0, float v0, float xf, float vf, float t, float T){
if(t >= T) return 0;
float a, b, c;
c = v0;
a = (vf*T - 2*xf + v0*T + 2*x0) / pow(T, 3);
b = (3*xf - vf*T - 2*v0*T - 3*x0) / pow(T, 2);
return 3.*a*pow(t, 2) + 2.*b*t + c;
}

float GetHipYPosByHeight(float h){
float l1 = cp_ptr_->thigh_len_;
float l2 = cp_ptr_->shank_len_;
if(fabs(h) >= l1 + l2) {
std::cerr << "error height input" << std::endl;
return 0;
}
float theta = -acos((l1*l1+h*h-l2*l2)/(2.*h*l1));
theta = LimitNumber(theta, cp_ptr_->fl_joint_lower_(1), cp_ptr_->fl_joint_upper_(1));
return theta;
}

float GetKneePosByHeight(float h){
float l1 = cp_ptr_->thigh_len_;
float l2 = cp_ptr_->shank_len_;
if(fabs(h) >= l1 + l2) {
std::cerr << "error height input" << std::endl;
return 0;
}
float theta = M_PI-acos((l1*l1+l2*l2-h*h)/(2*l1*l2));
theta = LimitNumber(theta, cp_ptr_->fl_joint_lower_(2), cp_ptr_->fl_joint_upper_(2));
return theta;
}

public:
LieDownState(const RobotName& robot_name, const std::string& state_name,
std::shared_ptr<ControllerData> data_ptr):StateBase(robot_name, state_name, data_ptr){
goal_joint_pos_ = Vec3f(init_hipx_pos_, GetHipYPosByHeight(0.03), GetKneePosByHeight(0.03)).replicate(4, 1);

Vec3f one_leg_kp, one_leg_kd;
one_leg_kp << cp_ptr_->swing_leg_kp_;
one_leg_kd << cp_ptr_->swing_leg_kd_;
kp_ = one_leg_kp.replicate(4, 1);
kd_ = one_leg_kd.replicate(4, 1);
joint_cmd_ = MatXf::Zero(12, 5);
joint_cmd_.col(0) = kp_;
joint_cmd_.col(2) = kd_;
liedown_duration_ = cp_ptr_->liedown_duration_;
}
~LieDownState(){}

virtual void OnEnter() {
GetRobotJointValue();
RecordJointData();
StateBase::msfb_.UpdateCurrentState(RobotMotionState::LieDown);
uc_ptr_->SetMotionStateFeedback(&StateBase::msfb_);
};

virtual void OnExit() {
}

virtual void Run() {
GetRobotJointValue();
VecXf planning_joint_pos(current_joint_pos_.rows());
VecXf planning_joint_vel(current_joint_pos_.rows());
if(run_time_ - time_stamp_record_ <= liedown_duration_){
for(int i=0;i<current_joint_pos_.rows();++i){
planning_joint_pos(i) = GetCubicSplinePos(init_joint_pos_(i), init_joint_vel_(i), goal_joint_pos_(i), 0,
run_time_ - time_stamp_record_, liedown_duration_);
planning_joint_vel(i) = GetCubicSplineVel(init_joint_pos_(i), init_joint_vel_(i), goal_joint_pos_(i), 0,
run_time_ - time_stamp_record_, liedown_duration_);
}

joint_cmd_.col(0) = kp_;
joint_cmd_.col(1) = planning_joint_pos;
joint_cmd_.col(2) = kd_;
joint_cmd_.col(3) = planning_joint_vel;
joint_cmd_.col(4).setZero();
ri_ptr_->SetJointCommand(joint_cmd_);
} else if (run_time_ - time_stamp_record_ <= 2.0 * liedown_duration_){
joint_cmd_ = MatXf::Zero(12, 5);
joint_cmd_.col(2) = kd_;
ri_ptr_->SetJointCommand(joint_cmd_);
} else {
joint_cmd_ = MatXf::Zero(12, 5);
ri_ptr_->SetJointCommand(joint_cmd_);
}
}
virtual bool LoseControlJudge() {
if (uc_ptr_->GetUserCommand()->target_mode == uint8_t(RobotMotionState::JointDamping)) return true;
return false;
}
virtual StateName GetNextStateName() {
if(uc_ptr_->GetUserCommand()->safe_control_mode!=0){
return StateName::kJointDamping;
}

if(run_time_ - time_stamp_record_ <= 2.*liedown_duration_){
return StateName::kLieDown;
}else{
if(uc_ptr_->GetUserCommand()->target_mode == uint8_t(RobotMotionState::StandingUp)){
return StateName::kStandUp;
}
}
return StateName::kLieDown;
}
};

};
Loading