/** * @file hand_gestures.cpp * @brief Named gestures for the Inspire RH56 hands on G1, over DDS (rt/inspire/cmd). * * Requires the service running in another terminal: ./inspire_g1 * * Usage: * ./hand_gestures # interactive menu (pick gestures repeatedly) * ./hand_gestures [iface] # run one gesture and exit * gesture = open | close | box | thumbup | handshake | combo | demo * * Per-hand DOF order: [pinky, ring, middle, index, thumb_bend, thumb_rotation] * q in [0,1]: 0 = closed, 1 = open. Indices 0-5 = right hand, 6-11 = left hand. * * NOTE: this controls the HAND (fingers) only. Raising the arm is a separate * subsystem (G1 arm joints) and is not driven here. */ #include #include #include #include #include #include #include #include #include #include using Vec6 = Eigen::Matrix; class HandController { public: HandController() { Init(); } 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); } handcmd->Write(cmd); } void setBoth(const Vec6 &q) { set(q, q); } unitree_go::msg::dds_::MotorCmds_ cmd; unitree_go::msg::dds_::MotorStates_ state; private: void Init() { handcmd = std::make_shared>("rt/inspire/cmd"); handcmd->InitChannel(); cmd.cmds().resize(12); handstate = std::make_shared>("rt/inspire/state"); handstate->InitChannel([this](const void *message) { std::lock_guard lock(mtx); state = *(unitree_go::msg::dds_::MotorStates_ *)message; }); state.states().resize(12); } std::mutex mtx; unitree::robot::ChannelPublisherPtr handcmd; unitree::robot::ChannelSubscriberPtr handstate; }; static Vec6 V(float pinky, float ring, float middle, float index, float thumb_bend, float thumb_rot) { Vec6 v; v << pinky, ring, middle, index, thumb_bend, thumb_rot; return v; } static void hold(int ms) { usleep(ms * 1000); } // --- Pose library (tune freely; values are [0,1], 0=closed 1=open) --- static const Vec6 OPEN = V(1, 1, 1, 1, 1, 1); static const Vec6 CLOSE = V(0, 0, 0, 0, 0, 0); static const Vec6 BOX = V(0.5, 0.5, 0.5, 0.5, 0.4, 0.1); static const Vec6 THUMBUP = V(0, 0, 0, 0, 1, 1); static const Vec6 SHAKE_READY = V(0.85, 0.85, 0.85, 0.85, 0.85, 0.6); static const Vec6 SHAKE_GRIP = V(0.2, 0.2, 0.2, 0.2, 0.25, 0.1); // --- Gestures (social ones use the RIGHT hand, left held open) --- static void g_pose(HandController &h, const Vec6 &q, const char *name, int ms = 1500) { std::cout << " [" << name << "]" << std::endl; h.setBoth(q); hold(ms); } static void g_thumbup(HandController &h, int ms = 2000) { std::cout << " [thumbup] (right hand)" << std::endl; h.set(THUMBUP, OPEN); hold(ms); } static void g_handshake(HandController &h) { std::cout << " [handshake] (right hand)" << std::endl; h.set(SHAKE_READY, OPEN); hold(1200); // reach out, open h.set(SHAKE_GRIP, OPEN); hold(800); // grip for (int k = 0; k < 3; k++) // a few firm "shakes" { h.set(SHAKE_GRIP * 0.85f, OPEN); hold(180); h.set(SHAKE_GRIP, OPEN); hold(180); } h.set(THUMBUP, OPEN); hold(1500); // finish with a thumbs-up h.set(OPEN, OPEN); hold(600); // relax } static void g_demo(HandController &h) { g_pose(h, OPEN, "open"); g_pose(h, CLOSE, "close"); g_pose(h, BOX, "box"); g_pose(h, OPEN, "open"); g_thumbup(h); g_handshake(h); g_pose(h, OPEN, "open"); } // Dispatch by name or menu number. Returns false only on quit. static bool runGesture(HandController &h, const std::string &g) { if (g == "open" || g == "1") g_pose(h, OPEN, "open"); else if (g == "close" || g == "2") g_pose(h, CLOSE, "close"); else if (g == "box" || g == "3") g_pose(h, BOX, "box"); else if (g == "thumbup" || g == "4") g_thumbup(h); else if (g == "handshake" || g == "5") g_handshake(h); else if (g == "combo" || g == "6") { g_thumbup(h); g_handshake(h); } else if (g == "demo" || g == "7") g_demo(h); else if (g == "q" || g == "quit" || g == "0") return false; else std::cout << " unknown option: '" << g << "'" << std::endl; return true; } static void printMenu() { std::cout << "\n=== Inspire Hand Gestures ===\n" << " 1) open\n" << " 2) close (fist)\n" << " 3) box (power grasp)\n" << " 4) thumbup (right hand)\n" << " 5) handshake (right hand, ends thumbs-up)\n" << " 6) combo (thumbup + handshake)\n" << " 7) demo (run all)\n" << " q) quit\n" << "Select: " << std::flush; } static std::string trim(std::string s) { const char *ws = " \t\r\n"; size_t a = s.find_first_not_of(ws); size_t b = s.find_last_not_of(ws); return a == std::string::npos ? "" : s.substr(a, b - a + 1); } int main(int argc, char **argv) { std::string arg1 = argc > 1 ? argv[1] : ""; std::string iface = argc > 2 ? argv[2] : ""; unitree::robot::ChannelFactory::Instance()->Init(0, iface); auto hand = std::make_shared(); usleep(300000); // let DDS match the service // Direct mode: run one gesture and exit (backward compatible). if (!arg1.empty()) { runGesture(*hand, arg1); std::cout << "done." << std::endl; return 0; } // Interactive menu: fire gestures repeatedly over one DDS connection. std::string line; while (true) { printMenu(); if (!std::getline(std::cin, line)) break; // EOF / Ctrl-D line = trim(line); if (line.empty()) continue; if (!runGesture(*hand, line)) break; } std::cout << "bye." << std::endl; return 0; }