diff --git a/src/Lite3_sdk_deploy/README.md b/src/Lite3_sdk_deploy/README.md
index 3839829b..4a734e33 100644
--- a/src/Lite3_sdk_deploy/README.md
+++ b/src/Lite3_sdk_deploy/README.md
@@ -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)
**Note:**
> - 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
@@ -166,14 +168,18 @@ source install/setup.bash
ros2 run lite3_sdk_deploy rl_deploy
```
**keyboard control:**
-> - 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
**gamepad control:**
-> - 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
@@ -196,4 +202,4 @@ Verified cross-host cases:
**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).**
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.
\ No newline at end of file
+If you know how to solve it better, welcome to submit an issue or a pull request.
diff --git a/src/Lite3_sdk_deploy/include/types/custom_types.h b/src/Lite3_sdk_deploy/include/types/custom_types.h
index 648581e1..55b14928 100644
--- a/src/Lite3_sdk_deploy/include/types/custom_types.h
+++ b/src/Lite3_sdk_deploy/include/types/custom_types.h
@@ -15,6 +15,7 @@ namespace types{
WaitingForStand = 0,
StandingUp = 1,
JointDamping = 2,
+ LieDown = 3,
RLControlMode = 6,
};
@@ -23,6 +24,7 @@ namespace types{
kIdle = 0,
kStandUp = 1,
kJointDamping = 2,
+ kLieDown = 3,
kRLControl = 6,
};
diff --git a/src/Lite3_sdk_deploy/interface/robot/simulation/mujoco_simulation_ros2.py b/src/Lite3_sdk_deploy/interface/robot/simulation/mujoco_simulation_ros2.py
index 29dc3159..37d47238 100644
--- a/src/Lite3_sdk_deploy/interface/robot/simulation/mujoco_simulation_ros2.py
+++ b/src/Lite3_sdk_deploy/interface/robot/simulation/mujoco_simulation_ros2.py
@@ -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 脚本一致的初始角度"""
@@ -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):
@@ -274,4 +282,4 @@ def _publish_robot_state(self, step: int):
sim_node = MuJoCoSimulationNode()
sim_node.start()
sim_node.destroy_node()
- rclpy.shutdown()
\ No newline at end of file
+ rclpy.shutdown()
diff --git a/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface.hpp b/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface.hpp
index 9166141b..2b3bbe51 100644
--- a/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface.hpp
+++ b/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface.hpp
@@ -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()
@@ -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;
@@ -130,7 +139,7 @@ class KeyboardInterface : public UserCommandInterface
char k = std::tolower(static_cast(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;
}
@@ -227,4 +236,4 @@ class KeyboardInterface : public UserCommandInterface
<< " side=" << max_side_
<< " yaw=" << max_yaw_ << "\n";
}
-};
\ No newline at end of file
+};
diff --git a/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface_sim.hpp b/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface_sim.hpp
index 9b953e10..e419bec4 100644
--- a/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface_sim.hpp
+++ b/src/Lite3_sdk_deploy/interface/user_command/keyboard_interface_sim.hpp
@@ -98,7 +98,8 @@ 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";
}
@@ -106,6 +107,11 @@ class KeyboardInterface : public UserCommandInterface
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()
@@ -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;
@@ -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) {
@@ -281,4 +287,4 @@ class KeyboardInterface : public UserCommandInterface
<< " side:" << max_side_
<< " yaw:" << max_yaw_ << "\n";
}
-};
\ No newline at end of file
+};
diff --git a/src/Lite3_sdk_deploy/interface/user_command/retroid_gamepad_interface.hpp b/src/Lite3_sdk_deploy/interface/user_command/retroid_gamepad_interface.hpp
index f1a5f581..821d6bf0 100644
--- a/src/Lite3_sdk_deploy/interface/user_command/retroid_gamepad_interface.hpp
+++ b/src/Lite3_sdk_deploy/interface/user_command/retroid_gamepad_interface.hpp
@@ -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;
@@ -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:
diff --git a/src/Lite3_sdk_deploy/state_machine/parameters/control_parameters.h b/src/Lite3_sdk_deploy/state_machine/parameters/control_parameters.h
index 7a558dd9..ddec8bc9 100644
--- a/src/Lite3_sdk_deploy/state_machine/parameters/control_parameters.h
+++ b/src/Lite3_sdk_deploy/state_machine/parameters/control_parameters.h
@@ -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_;
};
-
diff --git a/src/Lite3_sdk_deploy/state_machine/quadruped/liedown_state.hpp b/src/Lite3_sdk_deploy/state_machine/quadruped/liedown_state.hpp
new file mode 100644
index 00000000..f5057aca
--- /dev/null
+++ b/src/Lite3_sdk_deploy/state_machine/quadruped/liedown_state.hpp
@@ -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 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;iSetJointCommand(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;
+ }
+};
+
+};
diff --git a/src/Lite3_sdk_deploy/state_machine/quadruped/q_state_machine.hpp b/src/Lite3_sdk_deploy/state_machine/quadruped/q_state_machine.hpp
index f6782527..8294eb15 100644
--- a/src/Lite3_sdk_deploy/state_machine/quadruped/q_state_machine.hpp
+++ b/src/Lite3_sdk_deploy/state_machine/quadruped/q_state_machine.hpp
@@ -16,6 +16,7 @@
#include "quadruped/standup_state.hpp"
#include "quadruped/joint_damping_state.hpp"
#include "quadruped/rl_control_state.hpp"
+#include "quadruped/liedown_state.hpp"
#include "keyboard_interface.hpp"
#include "retroid_gamepad_interface.hpp"
#include "hardware/lite3_interface.hpp"
@@ -29,6 +30,7 @@ class QStateMachine : public StateMachineBase{
std::shared_ptr standup_controller_;
std::shared_ptr rl_controller_;
std::shared_ptr joint_damping_controller_;
+ std::shared_ptr liedown_controller_;
std::shared_ptr joints_data_shadow_subscriber_;
// std::shared_ptr car_move_controller_;
@@ -79,6 +81,7 @@ class QStateMachine : public StateMachineBase{
standup_controller_ = std::make_shared(robot_name_, "standup_state", data_ptr);
rl_controller_ = std::make_shared(robot_name_, "rl_control", data_ptr);
joint_damping_controller_ = std::make_shared(robot_name_, "joint_damping", data_ptr);
+ liedown_controller_ = std::make_shared(robot_name_, "liedown_state", data_ptr);
current_controller_ = idle_controller_;
current_state_name_ = kIdle;
@@ -111,6 +114,9 @@ class QStateMachine : public StateMachineBase{
case StateName::kJointDamping:{
return joint_damping_controller_;
}
+ case StateName::kLieDown:{
+ return liedown_controller_;
+ }
default:{
std::cerr << "error state name" << std::endl;
return joint_damping_controller_;
diff --git a/src/Lite3_sdk_deploy/state_machine/quadruped/rl_control_state.hpp b/src/Lite3_sdk_deploy/state_machine/quadruped/rl_control_state.hpp
index 5451f0e1..2adc94a2 100644
--- a/src/Lite3_sdk_deploy/state_machine/quadruped/rl_control_state.hpp
+++ b/src/Lite3_sdk_deploy/state_machine/quadruped/rl_control_state.hpp
@@ -132,7 +132,9 @@ namespace q {
virtual StateName GetNextStateName() {
if (uc_ptr_->GetUserCommand()->safe_control_mode != 0) return StateName::kJointDamping;
+ if (uc_ptr_->GetUserCommand()->target_mode == uint8_t(RobotMotionState::LieDown))
+ return StateName::kLieDown;
return StateName::kRLControl;
}
};
-};
\ No newline at end of file
+};
diff --git a/src/Lite3_sdk_deploy/state_machine/quadruped/standup_state.hpp b/src/Lite3_sdk_deploy/state_machine/quadruped/standup_state.hpp
index 48152e05..bbf52191 100644
--- a/src/Lite3_sdk_deploy/state_machine/quadruped/standup_state.hpp
+++ b/src/Lite3_sdk_deploy/state_machine/quadruped/standup_state.hpp
@@ -151,6 +151,8 @@ class StandUpState : public StateBase{
}else{
if(uc_ptr_->GetUserCommand()->target_mode == uint8_t(RobotMotionState::RLControlMode)){
return StateName::kRLControl;
+ }else if(uc_ptr_->GetUserCommand()->target_mode == uint8_t(RobotMotionState::LieDown)){
+ return StateName::kLieDown;
}
}
return StateName::kStandUp;