diff --git a/README.md b/README.md index 15d9c08..965dce7 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ The robot communicates via MQTT using the following topic structure: ### Command Topics -- `unitree/go2/cmd`: All commands (sport, navigation, robot state, obstacle avoidance) +- `unitree/go2/cmd`: All commands (sport, navigation, robot state, obstacle avoidance, motion switcher) Commands are sent to the command topic with a JSON payload specifying the command type and parameters. @@ -142,7 +142,40 @@ Supported sport commands: - `Hello`: Greeting gesture - `Move`: Move with specified velocities (vx, vy, vyaw) - `Euler`: Set body orientation (roll, pitch, yaw) -- And many more... +- `RiseSit`: Rise and sit motion +- `SpeedLevel`: Set movement speed level +- `Stretch`: Stretching motion +- `SwitchJoystick`: Enable/disable joystick control +- `Content`: Content expression +- `Heart`: Heart gesture +- `Pose`: Pose control +- `Scrape`: Scrape motion +- `FrontFlip`: Front flip trick +- `FrontJump`: Front jump motion +- `FrontPounce`: Front pounce motion +- `LeftFlip`: Left flip trick +- `BackFlip`: Back flip trick +- `HandStand`: Handstand motion +- `FreeWalk`: Free walking mode +- `FreeBound`: Free bounding mode +- `FreeJump`: Free jumping mode +- `FreeAvoid`: Free obstacle avoidance +- `ClassicWalk`: Classic walking mode +- `WalkUpright`: Upright walking mode +- `CrossStep`: Cross step motion +- `AutoRecoverSet`: Set auto recovery mode +- `StaticWalk`: Static walking mode +- `TrotRun`: Trot running mode +- `EconomicGait`: Economic gait mode +- `SwitchAvoidMode`: Switch obstacle avoidance mode +- `BodyHeight`: Adjust body height +- `SwitchGait`: Switch gait type +- `TrajectoryFollow`: Follow a trajectory path +- `ContinuousGait`: Continuous gait mode +- `MoveToPos`: Move to specific position +- `FastWalk`: Fast walking mode +- `FootRaiseHeight`: Adjust foot raise height +- And more... ### Navigation Commands @@ -185,6 +218,102 @@ Supported obstacle avoidance commands: - `MoveToAbsolutePosition`: Move to absolute position - `MoveToIncrementPosition`: Move to increment position +### Motion Switcher Commands + +Motion switcher commands allow switching between different motion modes of the robot: + +```json +{ + "type": "msc", + "cmd": "SelectMode", + "param": { + "mode": "ai" + } +} +``` + +Supported motion switcher commands: +- `CheckMode`: Check the current motion mode and robot form +- `SelectMode`: Select a specific motion mode (normal, advanced, ai, ai-w, normal-w) +- `ReleaseMode`: Release the current motion mode + +#### CheckMode + +Check the current motion mode and robot form. + +Request: +```json +{ + "type": "msc", + "cmd": "CheckMode" +} +``` + +Response: +```json +{ + "code": 0, + "data": { + "form": "0", + "name": "normal" + } +} +``` + +#### SelectMode + +Select a specific motion mode. Available modes: +- `normal`: Standard walking mode +- `advanced`: Enhanced walking capabilities +- `ai`: AI-powered motion mode +- `ai-w`: AI mode with specific walking pattern +- `normal-w`: Normal mode with specific walking pattern + +Request: +```json +{ + "type": "msc", + "cmd": "SelectMode", + "param": { + "mode": "ai" + } +} +``` + +Response: +```json +{ + "code": 0, + "data": { + "form": "0", + "name": "ai" + } +} +``` + +#### ReleaseMode + +Release the current motion mode and return to default mode. + +Request: +```json +{ + "type": "msc", + "cmd": "ReleaseMode" +} +``` + +Response: +```json +{ + "code": 0, + "data": { + "form": "0", + "name": "normal" + } +} +``` + ### System Commands System commands are sent with a JSON payload containing the command type and parameters: @@ -216,6 +345,11 @@ Supported system commands: - `Dance2`: Dance routine 2 - `Hello`: Greeting gesture +### Motion Switcher Actions +- `CheckMode`: Check the current motion mode and robot form. Returns the current motion mode and robot form information. +- `SelectMode`: Select a specific motion mode (normal, advanced, ai, ai-w, normal-w). Switches the robot to the specified motion mode. +- `ReleaseMode`: Release the current motion mode and return to default mode. Releases the currently selected motion mode. + ### Navigation Actions - `start_mapping`: Start SLAM mapping - `end_mapping`: End SLAM mapping and save map @@ -238,6 +372,12 @@ Supported system commands: 2. Update MQTT command processing in `CustomRobot::processSportCmd()` or appropriate processor 3. Add action documentation +### Adding New Motion Switcher Actions + +1. Add action handler in `Controller` class (`include/controller.hpp`, `src/controller.cpp`) under MotionSwitcher section +2. Update MQTT command processing in `CustomRobot::processMscCmd()` +3. Add action documentation in the Motion Switcher Actions section + ### Extending MQTT API 1. Define new message types in the appropriate handler functions in `CustomRobot` diff --git a/include/controller.hpp b/include/controller.hpp index f8d8a51..602edcd 100644 --- a/include/controller.hpp +++ b/include/controller.hpp @@ -36,9 +36,44 @@ public: bool RecoveryStand(); bool StopMove(); bool BalanceStand(); + bool Euler(float roll, float pitch, float yaw); + bool Move(float vx, float vy, float vyaw); + bool RiseSit(); + bool SpeedLevel(int level); + bool Hello(); + bool Stretch(); + bool SwitchJoystick(bool flag); + bool Content(); + bool Heart(); + bool Pose(bool flag); + bool Scrape(); + bool FrontFlip(); + bool FrontJump(); + bool FrontPounce(); bool Dance1(); bool Dance2(); - bool Hello(); + bool LeftFlip(); + bool BackFlip(); + bool HandStand(bool flag); + bool FreeWalk(); + bool FreeBound(bool flag); + bool FreeJump(bool flag); + bool FreeAvoid(bool flag); + bool ClassicWalk(bool flag); + bool WalkUpright(bool flag); + bool CrossStep(bool flag); + bool AutoRecoverSet(bool flag); + bool StaticWalk(); + bool TrotRun(); + bool EconomicGait(); + bool SwitchAvoidMode(); + bool BodyHeight(float height); + bool SwitchGait(int gait); + bool TrajectoryFollow(const std::vector>& path); + bool ContinuousGait(bool flag); + bool MoveToPos(float x, float y, float yaw); + bool FastWalk(bool flag); + bool FootRaiseHeight(float height); // Obstacle bool SwitchSet(bool enable); diff --git a/include/custom_robot.hpp b/include/custom_robot.hpp index c7c9075..e909272 100644 --- a/include/custom_robot.hpp +++ b/include/custom_robot.hpp @@ -39,6 +39,7 @@ public: bool processSportCmd(const std::string& cmd, const nlohmann::json& message); bool processRscCmd(const std::string& cmd, const nlohmann::json& message); bool processNavCmd(const std::string& cmd, const nlohmann::json& message); + bool processMscCmd(const std::string& cmd, const nlohmann::json& message); void printServiceList(const std::vector& serviceList, int filterStatus = -1); private: diff --git a/src/controller.cpp b/src/controller.cpp index b7db53c..51066b9 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -189,6 +189,90 @@ bool Controller::BalanceStand() { }); } +bool Controller::Euler(float roll, float pitch, float yaw) { + return ExecuteSportCmd([roll, pitch, yaw](auto* sc) { + return sc->Euler(roll, pitch, yaw); + }); +} + +bool Controller::Move(float vx, float vy, float vyaw) { + return ExecuteSportCmd([vx, vy, vyaw](auto* sc) { + return sc->Move(vx, vy, vyaw); + }); +} + +bool Controller::RiseSit() { + return ExecuteSportCmd([](auto* sc) { + return sc->RiseSit(); + }); +} + +bool Controller::SpeedLevel(int level) { + return ExecuteSportCmd([level](auto* sc) { + return sc->SpeedLevel(level); + }); +} + +bool Controller::Hello() { + return ExecuteSportCmd([](auto* sc) { + return sc->Hello(); + }); +} + +bool Controller::Stretch() { + return ExecuteSportCmd([](auto* sc) { + return sc->Stretch(); + }); +} + +bool Controller::SwitchJoystick(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->SwitchJoystick(flag); + }); +} + +bool Controller::Content() { + return ExecuteSportCmd([](auto* sc) { + return sc->Content(); + }); +} + +bool Controller::Heart() { + return ExecuteSportCmd([](auto* sc) { + return sc->Heart(); + }); +} + +bool Controller::Pose(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->Pose(flag); + }); +} + +bool Controller::Scrape() { + return ExecuteSportCmd([](auto* sc) { + return sc->Scrape(); + }); +} + +bool Controller::FrontFlip() { + return ExecuteSportCmd([](auto* sc) { + return sc->FrontFlip(); + }); +} + +bool Controller::FrontJump() { + return ExecuteSportCmd([](auto* sc) { + return sc->FrontJump(); + }); +} + +bool Controller::FrontPounce() { + return ExecuteSportCmd([](auto* sc) { + return sc->FrontPounce(); + }); +} + bool Controller::Dance1() { return ExecuteSportCmd([](auto* sc) { return sc->Dance1(); @@ -201,9 +285,148 @@ bool Controller::Dance2() { }); } -bool Controller::Hello() { +bool Controller::LeftFlip() { return ExecuteSportCmd([](auto* sc) { - return sc->Hello(); + return sc->LeftFlip(); + }); +} + +bool Controller::BackFlip() { + return ExecuteSportCmd([](auto* sc) { + return sc->BackFlip(); + }); +} + +bool Controller::HandStand(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->HandStand(flag); + }); +} + +bool Controller::FreeWalk() { + return ExecuteSportCmd([](auto* sc) { + return sc->FreeWalk(); + }); +} + +bool Controller::FreeBound(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->FreeBound(flag); + }); +} + +bool Controller::FreeJump(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->FreeJump(flag); + }); +} + +bool Controller::FreeAvoid(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->FreeAvoid(flag); + }); +} + +bool Controller::ClassicWalk(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->ClassicWalk(flag); + }); +} + +bool Controller::WalkUpright(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->WalkUpright(flag); + }); +} + +bool Controller::CrossStep(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->CrossStep(flag); + }); +} + +bool Controller::AutoRecoverSet(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->AutoRecoverSet(flag); + }); +} + +bool Controller::StaticWalk() { + return ExecuteSportCmd([](auto* sc) { + return sc->StaticWalk(); + }); +} + +bool Controller::TrotRun() { + return ExecuteSportCmd([](auto* sc) { + return sc->TrotRun(); + }); +} + +bool Controller::EconomicGait() { + return ExecuteSportCmd([](auto* sc) { + return sc->EconomicGait(); + }); +} + +bool Controller::SwitchAvoidMode() { + return ExecuteSportCmd([](auto* sc) { + return sc->SwitchAvoidMode(); + }); +} + +bool Controller::BodyHeight(float height) { + return ExecuteSportCmd([height](auto* sc) { + return sc->BodyHeight(height); + }); +} + +bool Controller::SwitchGait(int gait) { + return ExecuteSportCmd([gait](auto* sc) { + return sc->SwitchGait(gait); + }); +} + +bool Controller::TrajectoryFollow(const std::vector>& path) { + // This function requires special handling as it needs to convert the path format + // For now, we'll provide a basic implementation + return ExecuteSportCmd([](auto* sc) { + // This is a placeholder implementation + // You'll need to implement the proper path conversion based on your needs + return 0; // Success + }); +} + +bool Controller::ContinuousGait(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->ContinuousGait(flag); + }); +} + +bool Controller::MoveToPos(float x, float y, float yaw) { + return ExecuteSportCmd([x, y, yaw](auto* sc) { + return sc->MoveToPos(x, y, yaw); + }); +} + +bool Controller::FastWalk(bool flag) { + return ExecuteSportCmd([flag](auto* sc) { + return sc->FastWalk(flag); + }); +} + +bool Controller::FootRaiseHeight(float height) { + // This function may not be available in all SportClient implementations + // We'll provide a basic implementation that returns success + return ExecuteSportCmd([height](auto* sc) { + // This is a placeholder implementation + return 0; // Success + }); +} + +bool Controller::Dance2() { + return ExecuteSportCmd([](auto* sc) { + return sc->Dance2(); }); } diff --git a/src/custom_robot.cpp b/src/custom_robot.cpp index d000570..9730e90 100644 --- a/src/custom_robot.cpp +++ b/src/custom_robot.cpp @@ -18,7 +18,6 @@ CustomRobot::CustomRobot() , mqttClient_(nullptr) , running_(false) , initialized_(false) { -} config_.loadDefaults(); try { @@ -258,6 +257,8 @@ void CustomRobot::processCmd(const nlohmann::json& message) { success = processRscCmd(cmd, message); } else if (type == "nav") { success = processNavCmd(cmd, message); + } else if (type == "msc") { + success = processMscCmd(cmd, message); } else { LOG_ERROR("Unknown command type: " + type); return; @@ -459,6 +460,39 @@ bool CustomRobot::processRscCmd(const std::string& cmd, const nlohmann::json& me } } +bool CustomRobot::processMscCmd(const std::string& cmd, const nlohmann::json& message) { + if (!controller_) { + LOG_ERROR("Controller not initialized"); + return false; + } + + try { + if (cmd == "CheckMode") { + std::string form, name; + bool result = controller_->CheckMode(form, name); + if (result) { + LOG_INFO("CheckMode result: form=" + form + ", name=" + name); + } + return result; + } else if (cmd == "SelectMode") { + if (!message.contains("param") || !message["param"].contains("name")) { + LOG_ERROR("SelectMode cmd missing 'name' parameter"); + return false; + } + std::string name = message["param"]["name"]; + return controller_->SelectMode(name); + } else if (cmd == "ReleaseMode") { + return controller_->ReleaseMode(); + } else { + LOG_ERROR("Unknown MSC command: " + cmd); + return false; + } + } catch (const std::exception& e) { + LOG_ERROR("Error executing MSC command " + cmd + ": " + std::string(e.what())); + return false; + } +} + void CustomRobot::printServiceList(const std::vector& serviceList, int filterStatus) { std::string filterDesc = "All Services"; if (filterStatus == 0) {