/** * @file greet_common.hpp * @brief Shared arm-action + Inspire-hand greeting routines (handshake / thumbup). * * Arm uses Unitree's built-in actions (G1ArmActionClient, balance-aware). * Hand uses the Inspire DDS bridge -> REQUIRES ./inspire_g1 running for fingers. */ #pragma once #include "unitree/robot/g1/arm/g1_arm_action_client.hpp" #include "unitree/robot/g1/arm/g1_arm_action_error.hpp" #include #include #include #include #include #include #include using Vec6 = Eigen::Matrix; // Inspire hand command publisher (open-loop; needs inspire_g1 running). class Hand { public: Hand() { pub_ = std::make_shared>("rt/inspire/cmd"); pub_->InitChannel(); cmd_.cmds().resize(12); } void set(const Vec6 &right, const Vec6 &left) { for (size_t i = 0; i < 6; i++) { cmd_.cmds()[i].q() = right(i); cmd_.cmds()[i + 6].q() = left(i); } pub_->Write(cmd_); } private: unitree_go::msg::dds_::MotorCmds_ cmd_; unitree::robot::ChannelPublisherPtr pub_; }; inline Vec6 gv(float p, float r, float m, float i, float tb, float tr) { Vec6 v; v << p, r, m, i, tb, tr; return v; } inline void ghold(int ms) { usleep(ms * 1000); } // q in [0,1], 0=closed 1=open; order pinky,ring,middle,index,thumb_bend,thumb_rot static const Vec6 G_OPEN = gv(1, 1, 1, 1, 1, 1); static const Vec6 G_THUMBUP = gv(0, 0, 0, 0, 1, 1); static const Vec6 G_SHAKE_READY = gv(0.85, 0.85, 0.85, 0.85, 0.85, 0.6); static const Vec6 G_SHAKE_GRIP = gv(0.2, 0.2, 0.2, 0.2, 0.25, 0.1); // Execute a built-in arm action; print a helpful message on failure. inline bool arm_do(unitree::robot::g1::G1ArmActionClient &arm, int id, const char *name) { using namespace unitree::robot::g1; std::cout << "arm: " << name << " (" << id << ") ..." << std::endl; int32_t ret = arm.ExecuteAction(id); if (ret != 0) { std::cerr << " arm action '" << name << "' failed, code " << ret; if (ret == UT_ROBOT_ARM_ACTION_ERR_ARMSDK) std::cerr << " (rt/arm_sdk occupied)"; else if (ret == UT_ROBOT_ARM_ACTION_ERR_INVALID_FSM_ID) std::cerr << " (need fsm {500,501,801})"; else if (ret == UT_ROBOT_ARM_ACTION_ERR_HOLDING) std::cerr << " (arm holding; send release 99)"; std::cerr << std::endl; return false; } return true; } // arm reaches out -> hand grips -> thumbs-up -> arm releases inline void runHandshake(unitree::robot::g1::G1ArmActionClient &arm, Hand &hand) { hand.set(G_SHAKE_READY, G_OPEN); // present an open hand if (!arm_do(arm, 27, "shake hand")) { hand.set(G_OPEN, G_OPEN); return; } ghold(300); std::cout << "hand: grip" << std::endl; hand.set(G_SHAKE_GRIP, G_OPEN); ghold(1500); std::cout << "hand: thumbs-up" << std::endl; hand.set(G_THUMBUP, G_OPEN); ghold(1200); hand.set(G_OPEN, G_OPEN); ghold(400); arm_do(arm, 99, "release arm"); } // raise right hand -> hand thumbs-up -> arm releases inline void runThumbup(unitree::robot::g1::G1ArmActionClient &arm, Hand &hand) { if (!arm_do(arm, 23, "right hand up")) { hand.set(G_OPEN, G_OPEN); return; } ghold(300); std::cout << "hand: thumbs-up" << std::endl; hand.set(G_THUMBUP, G_OPEN); ghold(2500); hand.set(G_OPEN, G_OPEN); ghold(400); arm_do(arm, 99, "release arm"); } // Shared setup: init DDS + arm client + hand, then return the arm client. inline std::shared_ptr greet_init(const std::string &iface) { unitree::robot::ChannelFactory::Instance()->Init(0, iface); auto arm = std::make_shared(); arm->Init(); arm->SetTimeout(10.f); return arm; }