inspiredhand/coordinated_actions/coordinated_actions.cpp

1035 lines
39 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <array>
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstdio>
#include <ctime>
#include <chrono>
#include <vector>
#include "coordinated_actions/coordinated_actions.h"
#include "inspire_hand/hand_actions.h"
#include "robot_action.h"
#include "tools/tools.h"
namespace coordinated_actions {
constexpr double PI = 3.14159265358979323846;
using tools::get_cwd;
using tools::clear_screen;
using tools::sleep_ms;
using tools::get_actions_record_path;
namespace {
// 记录当前“机械臂关节 + 灵巧手角度”到同一个记录文件:
// 格式: timestamp, j0..j5 (deg), h0..h5 (0~1000), "comment"
static bool record_coordinated_pose(RobotController &rc, const std::string &comment)
{
std::array<double, 6> joints_deg{};
if (!rc.getJointPositionsDeg(joints_deg)) {
std::cerr << "record_coordinated_pose: 获取关节位姿失败\n";
return false;
}
auto hand_angles = hand_actions::get_angle_act_service();
if (hand_angles.size() != 6) {
hand_angles.assign(6, 1000); // 如果读取失败,用全伸直作为占位
}
const std::string path = get_actions_record_path();
std::ofstream ofs(path.c_str(), std::ios::app);
if (!ofs) {
std::cerr << "record_coordinated_pose: 无法打开文件: " << path << std::endl;
return false;
}
auto t = std::time(nullptr);
ofs << t;
for (int i = 0; i < 6; ++i) ofs << "," << joints_deg[i];
for (int i = 0; i < 6; ++i) ofs << "," << static_cast<int>(hand_angles[i]);
ofs << "," << '"' << comment << '"' << std::endl;
ofs.close();
return true;
}
static bool delete_coordinated_file()
{
const std::string path = get_actions_record_path();
return std::remove(path.c_str()) == 0;
}
// 将联合记录文件清空为 0 字节(如果存在则截断,不存在则创建空文件)
static bool clear_coordinated_file()
{
const std::string path = get_actions_record_path();
std::ofstream ofs(path.c_str(), std::ios::trunc);
return static_cast<bool>(ofs);
}
// 手势菜单使用的力传感基准,避免在每次轮询时重复采集
std::vector<double> g_force_baseline(6, 0.0);
void set_force_baseline_ready()
{
// 在轮询外采集基准数据
std::cout << "采集力传感器基准数据中,请稍后..." << std::endl;
if (!hand_actions::capture_force_baseline(g_force_baseline, 8, 50)) {
std::cout << "采集基准失败,稍后重试..." << std::endl;
sleep_ms(200);
}
std::cout << "采集基准数据完毕" << std::endl;
}
bool run_console_menu(RobotController &rc, robot_action::RobotParams &params)
{
clear_screen();
std::cout << "控制台菜单 (输入 q 返回手势控制):\n";
std::cout << " 1) 重新设置\n";
std::cout << " 2) 示教模式与记录\n";
while (true) {
std::cout << "请输入选项: ";
std::string input;
if (!std::getline(std::cin, input)) {
return false;
}
if (input == "1") {
std::cout << "请选择要设置的对象:0 = 机械臂速度, 1 = 灵巧手速度, 2 = 传感器校准: ";
std::string opt;
if (!std::getline(std::cin, opt)) {
return false;
}
if (opt == "0") {
robot_action::adjust_speeds(rc, params);
} else if (opt == "1") {
hand_actions::adjust_hand_speeds();
} else if (opt == "2") {
hand_actions::calibrate_hand_sensors();
sleep_ms(15000);
} else {
std::cout << "输入无效,保持当前设置。\n";
}
clear_screen();
std::cout << "控制台菜单 (输入 q 返回手势控制):\n";
std::cout << " 1) 重新设置速度\n";
std::cout << " 2) 示教模式与记录\n";
} else if (input == "2") {
run_teach_mode(rc);
clear_screen();
std::cout << "控制台菜单 (输入 q 返回手势控制):\n";
std::cout << " 1) 重新设置速度\n";
std::cout << " 2) 示教模式与记录\n";
} else if (input == "q" || input == "Q") {
clear_screen();
return true;
} else if (input.empty()) {
continue;
} else {
std::cout << "输入无效,请重新输入。\n";
}
}
}
} // namespace
std::string force_trigger_selection()
{
constexpr double trigger_threshold = 40.0;
constexpr int wave_index = 1;
constexpr int rps_index = 2;
constexpr int grasp_index = 3;
constexpr int console_index = 5;
constexpr int exit_index = 0;
auto forces = hand_actions::get_force_service();
if (forces.size() != 6) {
return std::string();
}
auto delta_at = [&](int index) -> double {
if (index < 0 || index >= 6) return 0.0;
return hand_actions::force_delta_abs(forces, g_force_baseline, index);
};
double exit_delta = delta_at(exit_index);
if (exit_delta >= trigger_threshold) {
return std::string("exit");
}
double wave_delta = delta_at(wave_index);
if (wave_delta >= trigger_threshold) {
return std::string("wave");
}
double rps_delta = delta_at(rps_index);
if (rps_delta >= trigger_threshold) {
return std::string("rps");
}
double grasp_delta = delta_at(grasp_index);
if (grasp_delta >= trigger_threshold) {
return std::string("grasp");
}
double console_delta = delta_at(console_index);
if (console_delta >= trigger_threshold) {
return std::string("console");
}
return std::string();
}
void wave_and_handshake(RobotController &rc)
{
clear_screen();
std::cout << "挥手与握手演示" << std::endl;
std::cout << "机械臂将摆动第4关节 ±25°任意非小拇指受力触发伸手10 秒内所有手指受力则执行灵巧手挥手,小拇指受力随时退出。" << std::endl;
std::array<double,6> preset_deg = {0.0, 90.0, -90.0, -15.0, 0.0, -90.0};
auto preset_rad = degrees_to_radians(preset_deg);
if (!rc.moveJoints(preset_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad())) {
std::cout << "机械臂移动失败无法开始。1 秒后返回手势等待。" << std::endl;
sleep_ms(1000);
return;
}
hand_actions::set_speed_service(400,400,400,400,400,400);
hand_actions::set_force_service(300,300,300,300,300,300);
hand_actions::set_angle_service(1000,1000,1000,1000,1000,1000);
sleep_ms(1000);
std::vector<double> baseline;
if (!hand_actions::capture_force_baseline(baseline, 12, 50)) {
std::cout << "无法获取力传感基准2 秒后返回手势等待。" << std::endl;
sleep_ms(2000);
return;
}
const int pinky_index = 0;
const std::array<int,5> handshake_indices = {1, 2, 3, 4, 5};
const double trigger_threshold = 40.0;
const double exit_threshold = 40.0;
const int switch_interval_ms = 600;
const int wait_all_ms = 8000; // 等待所有手指受力的最长时间
int open_angles[6] = {1000,1000,1000,1000,1000,1000};
int shake_angles[6] = {50,50,50,50,400,600};
bool swing_left = true;
auto last_switch = std::chrono::steady_clock::now();
std::array<double,6> base_rad = preset_rad;
double base_joint = base_rad[4];
double amplitude = 25.0 * PI / 180.0;
// 伸手关节位姿(用户提供的关节角度,单位度)
std::array<double,6> extend_deg = {-120.0, -15.0, -82.0, 69.0, -27.0, 53.0};
auto extend_rad = degrees_to_radians(extend_deg);
while (true){
auto forces = hand_actions::get_force_service();
if (forces.size() != 6) {
sleep_ms(80);
continue;
}
// 小拇指触发整个演示退出
double pinky_delta = hand_actions::force_delta_abs(forces, baseline, pinky_index);
if (pinky_delta >= exit_threshold){
std::cout << "检测到小拇指受力,结束挥手演示。" << std::endl;
break;
}
// 检测除小拇指以外任意手指的受力,触发伸手
double max_hand_delta = 0.0;
for (int idx : handshake_indices){
double delta = hand_actions::force_delta_abs(forces, baseline, idx);
if (delta > max_hand_delta) {
max_hand_delta = delta;
}
}
if (max_hand_delta >= trigger_threshold){
std::cout << "检测到非小拇指受力,机械臂伸出准备握手..." << std::endl;
rc.motionAbort();
if (!rc.moveJoints(extend_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad())) {
std::cout << "伸手位姿移动失败,结束挥手演示。" << std::endl;
break;
}
sleep_ms(1000);
//重新采集
std::vector<double> extend_baseline;
if (!hand_actions::capture_force_baseline(extend_baseline, 10, 50)) {
std::cout << "无法获取力传感基准,结束挥手演示。" << std::endl;
break;
}
auto start_wait = std::chrono::steady_clock::now();
bool all_fingers = false;
while (true) {
auto now = std::chrono::steady_clock::now();
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_wait).count();
if (elapsed_ms >= wait_all_ms) {
break;
}
auto forces2 = hand_actions::get_force_service();
if (forces2.size() != 6) {
sleep_ms(80);
continue;
}
bool any_ok = false;
for (int idx : handshake_indices) {
double d = hand_actions::force_delta_abs(forces2, extend_baseline, idx);
if (d >= trigger_threshold) {
any_ok = true;
}
}
// 握手过程中,只要检测到至少一根手指受力,就去执行挥手
if (any_ok) {
all_fingers = true;
break;
}
sleep_ms(80);
}
if (all_fingers) {
std::cout << "检测到所有手指受力,执行灵巧手握手动作。" << std::endl;
hand_actions::set_angle_service(shake_angles[0],shake_angles[1],shake_angles[2],shake_angles[3],shake_angles[4],shake_angles[5]);
sleep_ms(4000);
hand_actions::set_angle_service(open_angles[0],open_angles[1],open_angles[2],open_angles[3],open_angles[4],open_angles[5]);
sleep_ms(2000);
break; // 完成一次示范后结束演示
} else {
std::cout << "未在 10 秒内检测到所有手指受力,返回挥手姿态继续挥手。" << std::endl;
rc.moveJoints(base_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
swing_left = true;
last_switch = std::chrono::steady_clock::now();
}
}
// 正常挥手
auto now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_switch).count() >= switch_interval_ms){
std::array<double,6> target = base_rad;
target[4] = base_joint + (swing_left ? amplitude : -amplitude);
//非阻塞
rc.moveJoints(target, ABS, false, rc.getJointSpeedRad(), rc.getJointAccRad());
swing_left = !swing_left;
last_switch = now;
}
sleep_ms(80);
}
rc.motionAbort();
rc.moveJoints(base_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
hand_actions::set_angle_service(open_angles[0],open_angles[1],open_angles[2],open_angles[3],open_angles[4],open_angles[5]);
std::cout << "演示结束。" << std::endl;
sleep_ms(1000);
}
namespace {
static bool replay_coordinated_file(RobotController &rc, const std::string &path)
{
std::ifstream ifs(path.c_str());
if (!ifs) {
std::cout << "无法打开记录文件: " << path << std::endl;
return false;
}
std::string line;
int idx_line = 0;
while (std::getline(ifs, line)) {
++idx_line;
if (line.empty()) continue;
std::stringstream ss(line);
std::string token;
if (!std::getline(ss, token, ',')) continue; // timestamp 忽略
std::array<double, 6> joints_deg{};
bool ok_joint = true;
for (int i = 0; i < 6; ++i) {
if (!std::getline(ss, token, ',')) { ok_joint = false; break; }
try { joints_deg[i] = std::stod(token); }
catch (...) { ok_joint = false; break; }
}
if (!ok_joint) {
std::cerr << "解析第" << idx_line << "行关节数据失败\n";
continue;
}
int hand_vals[6] = {1000,1000,1000,1000,1000,1000};
bool ok_hand = true;
for (int i = 0; i < 6; ++i) {
if (!std::getline(ss, token, ',')) { ok_hand = false; break; }
try { hand_vals[i] = std::stoi(token); }
catch (...) { ok_hand = false; break; }
}
if (!ok_hand) {
std::cerr << "解析第" << idx_line << "行灵巧手角度数据失败\n";
continue;
}
std::string comment;
std::getline(ss, comment); // 余下部分视为注释(可能含引号)
std::array<double, 6> joints_rad{};
for (int i = 0; i < 6; ++i) joints_rad[i] = joints_deg[i] * PI / 180.0;
std::cout << "复现第" << idx_line << ",注释: " << comment << "\n";
bool moved = rc.moveJoints(joints_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
if (!moved) {
std::cerr << "移动到记录点 " << idx_line << " 失败\n";
std::cout << "是否继续下一个点?(y/n): ";
std::string yes;
if (!std::getline(std::cin, yes)) yes = "n";
if (yes != "y" && yes != "Y") { ifs.close(); return false; }
} else {
hand_actions::set_angle_service(hand_vals[0], hand_vals[1], hand_vals[2],
hand_vals[3], hand_vals[4], hand_vals[5]);
std::cout << "完成第" << idx_line << "个点\n";
}
}
ifs.close();
std::cout << "顺序复现结束。\n";
return true;
}
} // namespace
void play_rps_game(RobotController &rc)
{
bool moved = robot_action::init_place(rc);
if (!moved) {
std::cout << "移动到起始姿态失败无法开始游戏。2 秒后返回手势等待。" << std::endl;
sleep_ms(2000);
return;
}
//进行重构,这里需要机械臂的动作而不只是灵巧手
clear_screen();
std::cout << "石头剪刀布 - 力觉交互模式" << std::endl;
std::cout << "食指: 玩家胜利, 无名指: 玩家失败, 中指: 平局, 小指: 退出" << std::endl;
int rock[6] = {0,0,0,0,200,700};
int scissors[6] = {0,0,1000,1000,200,800};
int paper[6] = {1000,1000,1000,1000,1000,1000};
int result = 1;
std::vector<double> baseline;
if (!hand_actions::capture_force_baseline(baseline, 12, 50)) {
std::cout << "无法获取力传感基准,返回菜单。" << std::endl;
sleep_ms(1200);
}
std::cout << "初始基准值:";
for (std::size_t i = 0; i < baseline.size(); ++i) {
std::cout << " f" << i << "=" << baseline[i];
}
std::cout << std::endl;
const double trigger_threshold = 40.0;
const int pinky_index = 0;
const int ring_index = 1;
const int middle_index = 2;
const int index_index = 3;
int total_rounds = 0;
int player_wins = 0;
int ties = 0;
auto print_score = [&]() {
int player_losses = total_rounds - player_wins - ties;
std::cout << "总局数: " << total_rounds
<< " | 玩家胜: " << player_wins
<< " | 平局: " << ties
<< " | 玩家负: " << player_losses << std::endl;
};
bool exit_requested = false;
int ready_gesture[6] = {1000,1000,1000,1000,800,0};
rc.setJointSpeedDeg(240.0);
rc.setJointAccDeg(400.0); //加速移动
while (true) {
robot_action::init_place(rc); //恢复初始位置
hand_actions::set_angle_service(ready_gesture[0],ready_gesture[1],ready_gesture[2],ready_gesture[3],ready_gesture[4],ready_gesture[5]);
if (total_rounds!=0) {
std::cout << "请通过手指反馈上一轮结果(食指胜、无名指负、中指平、小指退出)。" << std::endl;
/*sleep_ms(1000);
if (!hand_actions::capture_force_baseline(baseline, 8, 40)) {
std::cout << "基准刷新失败,沿用上一轮数据。" << std::endl;
}*/
sleep_ms(1000);
hand_actions::capture_force_baseline(baseline, 10, 30);
bool decided = false;
while (!decided) {
auto forces_now = hand_actions::get_force_service();
double delta_pinky = hand_actions::force_delta_abs(forces_now, baseline, pinky_index);
double delta_ring = hand_actions::force_delta_abs(forces_now, baseline, ring_index);
double delta_middle = hand_actions::force_delta_abs(forces_now, baseline, middle_index);
double delta_index = hand_actions::force_delta_abs(forces_now, baseline, index_index);
if (delta_pinky >= trigger_threshold) {
std::cout << "检测到退出手势,准备结束游戏。" << std::endl;
exit_requested = true;
decided = true;
} else if (delta_index >= trigger_threshold) {
++player_wins;
std::cout << "记录上一轮:玩家胜利。" << std::endl;
print_score();
decided = true;
} else if (delta_ring >= trigger_threshold) {
std::cout << "记录上一轮:玩家落败。" << std::endl;
print_score();
decided = true;
} else if (delta_middle >= trigger_threshold) {
++ties;
std::cout << "本轮平局。" << std::endl;
print_score();
decided = true;
} else {
sleep_ms(80);
}
}
if (exit_requested) {
break;
}
}
std::cout << "倒计时开始 (回合" << (total_rounds + 1) << ")" << std::endl;
hand_actions::action_full_fist();
// 先将机械臂伸到出拳姿态
std::array<double,6> extend_deg = {-120.0, -38.0, -0.5, 28, -57.0, 56.3};
auto extend_rad = degrees_to_radians(extend_deg);
rc.moveJoints(extend_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
// 在出拳前通过第4号关节下标 4上下摇摆两次模拟人晃动手腕
const int wrist_index = 4;
double base_wrist = extend_rad[wrist_index];
double shake_amp = 10.0 * PI / 180.0; // 摆动幅度 10 度
for (int cycle = 0; cycle < 2; ++cycle) {
std::array<double,6> target_up = extend_rad;
target_up[wrist_index] = base_wrist + shake_amp;
rc.moveJoints(target_up, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
sleep_ms(300);
std::array<double,6> target_down = extend_rad;
target_down[wrist_index] = base_wrist - shake_amp;
rc.moveJoints(target_down, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
sleep_ms(300);
}
// 摆动结束后回到基准伸出姿态再出拳
rc.moveJoints(extend_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
auto seed = static_cast<unsigned>(std::chrono::steady_clock::now().time_since_epoch().count());
std::srand(seed);
int choice = std::rand() % 3;
if (choice == 0) {
hand_actions::set_angle_service(scissors[0],scissors[1],scissors[2],scissors[3],scissors[4],scissors[5]);
std::cout << "机器人出:剪刀" << std::endl;
} else if (choice == 1) {
hand_actions::set_angle_service(rock[0],rock[1],rock[2],rock[3],rock[4],rock[5]);
std::cout << "机器人出:石头" << std::endl;
} else {
hand_actions::set_angle_service(paper[0],paper[1],paper[2],paper[3],paper[4],paper[5]);
std::cout << "机器人出:布" << std::endl;
}
sleep_ms(3000);
total_rounds++;
hand_actions::action_full_extend();
}
total_rounds--;
std::cout << "游戏结束。" << std::endl;
std::cout << "总局数: " << total_rounds
<< " | 玩家胜: " << player_wins
<< " | 平局: " << ties
<< " | 玩家负: " << (total_rounds - player_wins - ties) << std::endl;
if (exit_requested && total_rounds > 0) {
int player_losses = total_rounds - player_wins - ties;
if (player_wins > player_losses)
result = 1; // player won
else if (player_wins < player_losses)
result = -1; // player lost
else
result = 0; // tie
}
if(result==1){ //胜利点赞
std::array<double,6> preset_deg = {0,0,0,0,90,-90};
auto preset_rad = degrees_to_radians(preset_deg);
rc.moveJoints(preset_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
hand_actions::set_angle_service(0,0,0,0,1000,1000);
}
else if(result==0){
std::array<double,6> preset_deg = {0,0,0,0,0,-90};
auto preset_rad = degrees_to_radians(preset_deg);
rc.moveJoints(preset_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
hand_actions::set_angle_service(0,0,0,0,1000,1000);
}
else if(result==-1){
std::array<double,6> preset_deg = {0,0,0,0,-90,-90};
auto preset_rad = degrees_to_radians(preset_deg);
rc.moveJoints(preset_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
hand_actions::set_angle_service(0,0,0,0,1000,1000);
}
sleep_ms(2000);
const config::AppConfig &cfg = config::get_default_app_config();
rc.setJointAccDeg(cfg.robot.joint_acc_deg_s2); //恢复默认加速度
rc.setJointSpeedDeg(cfg.robot.joint_speed_deg_s); //恢复默认速度
}
void run_teach_mode(RobotController &rc)
{
// 进入示教模式时,检查记录文件是否非空,决定本次会话是清空后记录还是直接追加
{
const std::string path = get_actions_record_path();
std::ifstream ifs(path.c_str(), std::ios::binary);
if (ifs) {
ifs.seekg(0, std::ios::end);
std::streampos sz = ifs.tellg();
ifs.close();
if (sz > 0) {
while (true) {
std::cout << "检测到联合示教记录文件非空: " << path << "\n";
std::cout << "请选择 (c: 清空后重新记录, a: 在末尾追加, 回车默认 a): ";
std::string choice;
if (!std::getline(std::cin, choice)) choice.clear();
if (choice.empty() || choice == "a" || choice == "A") {
std::cout << "已选择在原有记录后追加新点。\n";
break;
} else if (choice == "c" || choice == "C") {
if (clear_coordinated_file()) {
std::cout << "记录文件已清空,本次会话将从头开始记录。\n";
} else {
std::cout << "清空记录文件失败,本次会话将继续在原文件后追加。\n";
}
break;
} else {
std::cout << "输入无效,请重新输入。\n";
}
}
}
}
}
sleep_ms(1000);
while (true) {
std::cout << "示教模式(机械臂 + 灵巧手):\n";
std::cout << " l : 显示当前状态 (机械臂 + 灵巧手)\n";
std::cout << " rj : 机械臂关节相对移动(逐关节输入步进, 回车保持原值)\n";
std::cout << " dj : 机械臂关节绝对移动(逐关节输入目标角度, 回车保持原值)\n";
std::cout << " rc : 机械臂笛卡尔相对移动(逐轴输入步进, 回车保持原值)\n";
std::cout << " dc : 机械臂笛卡尔绝对移动(逐轴输入目标值, 回车保持原值)\n";
std::cout << " ha : 灵巧手角度移动(逐关节输入角度, 回车保持原值)\n";
std::cout << " rec : 记录当前机械臂 + 灵巧手为一个示教点\n";
std::cout << " del : 删除联合记录文件\n";
std::cout << " replay : 顺序复现联合记录(机械臂+灵巧手)\n";
std::cout << " q : 返回上级菜单\n";
std::cout << "请输入命令: ";
std::string mode;
if (!std::getline(std::cin, mode)) break;
if (mode.empty()) continue;
if (mode == "q") break;
clear_screen();
if (mode == "rec") {
std::cout << "请输入当前示教点注释(可空):";
std::string comment;
std::getline(std::cin, comment);
if (record_coordinated_pose(rc, comment)) {
std::cout << "已记录当前机械臂 + 灵巧手状态到: "
<< get_actions_record_path() << "\n";
} else {
std::cerr << "记录失败\n";
}
continue;
}
if (mode == "ha") {
auto hand_angles = hand_actions::get_angle_act_service();
int cur[6] = {1000,1000,1000,1000,1000,1000};
if (hand_angles.size() == 6) {
for (int i = 0; i < 6; ++i) cur[i] = static_cast<int>(hand_angles[i]);
}
int new_vals[6];
std::cout << "灵巧手关节角度设置(0~1000),每行一个关节,直接回车保持原值。\n";
for (int i = 0; i < 6; ++i) {
std::cout << "关节 " << i << " 当前角度=" << cur[i] << ",请输入新角度: ";
std::string line;
if (!std::getline(std::cin, line)) { new_vals[i] = cur[i]; continue; }
if (line.empty()) {
new_vals[i] = cur[i];
continue;
}
try {
int v = std::stoi(line);
if (v < 0 || v > 1000) {
std::cout << "输入超出范围,保持原值 " << cur[i] << "\n";
new_vals[i] = cur[i];
} else {
new_vals[i] = v;
}
} catch (...) {
std::cout << "输入无效,保持原值 " << cur[i] << "\n";
new_vals[i] = cur[i];
}
}
hand_actions::set_angle_service(new_vals[0], new_vals[1], new_vals[2],
new_vals[3], new_vals[4], new_vals[5]);
std::cout << "已下发角度命令: (" << new_vals[0] << "," << new_vals[1] << "," << new_vals[2]
<< "," << new_vals[3] << "," << new_vals[4] << "," << new_vals[5] << ")" << std::endl;
sleep_ms(500);
continue;
}
if (mode == "l") {
std::array<double, 6> cur_deg;
if (rc.getJointPositionsDeg(cur_deg)) {
std::cout << "当前关节位置: ";
for (int i = 0; i < 6; ++i) {
double v = cur_deg[i];
if (std::fabs(v) < 1e-6) v = 0.0; // 消除极小浮点误差
std::cout << v << (i == 5 ? "\n" : ", ");
}
} else {
std::cout << "获取关节位姿失败\n";
}
// 追加灵巧手当前角度(通过 TIO 信号量读取)
auto hand_angles = hand_actions::get_angle_act_service();
if (!hand_angles.empty()) {
std::cout << "灵巧手当前角度: ";
for (std::size_t i = 0; i < hand_angles.size(); ++i) {
std::cout << hand_angles[i] << (i + 1 == hand_angles.size() ? "\n" : ", ");
}
}
CartesianPose cp;
if (rc.getTcpPose(cp)) {
std::cout << "TCP(mm): x=" << cp.tran.x << " y=" << cp.tran.y << " z=" << cp.tran.z;
std::cout << " RPY(deg): rx=" << (cp.rpy.rx * 180.0 / PI)
<< " ry=" << (cp.rpy.ry * 180.0 / PI)
<< " rz=" << (cp.rpy.rz * 180.0 / PI) << "\n";
} else {
std::cout << "获取 TCP 位姿失败\n";
}
continue;
}
if (mode == "del") {
if (delete_coordinated_file()) {
std::cout << "已删除记录文件: " << get_actions_record_path() << "\n";
} else {
std::cout << "记录文件不存在或删除失败: " << get_actions_record_path() << "\n";
}
continue;
}
if (mode == "replay") {
std::string cwd = get_cwd();
std::cout << "当前工作路径为: " << cwd << "\n";
std::cout << "请输入记录文件相对路径(回车使用默认: "
<< get_actions_record_path() << "): ";
std::string input;
if (!std::getline(std::cin, input)) break;
std::string path;
if (input.empty()) {
path = get_actions_record_path();
} else {
// 将用户输入视为相对于当前工作目录的相对路径
path = cwd + std::string("/") + input;
}
replay_coordinated_file(rc, path);
continue;
}
if (mode == "rj" || mode == "dj") {
bool relative = (mode == "rj");
std::array<double, 6> cur_deg;
if (!rc.getJointPositionsDeg(cur_deg)) { std::cerr << "获取当前关节位姿失败\n"; continue; }
std::array<double, 6> target_deg = cur_deg;
std::cout << (relative
? "机械臂关节相对移动:逐关节输入步进(单位度),直接回车表示保持不变。\n"
: "机械臂关节绝对移动:逐关节输入目标角度(单位度),直接回车表示保持不变。\n");
for (int i = 0; i < 6; ++i) {
std::cout << "关节 " << i << " 当前角度=" << cur_deg[i]
<< (relative ? ",请输入步进: " : ",请输入目标角度: ");
std::string line;
if (!std::getline(std::cin, line)) break;
if (line.empty()) continue; // 保持原值
try {
double v = std::stod(line);
if (relative) target_deg[i] = cur_deg[i] + v;
else target_deg[i] = v;
} catch (...) {
std::cout << "输入无效,保持原值 " << cur_deg[i] << "\n";
}
}
std::array<double, 6> target_rad{};
for (int i = 0; i < 6; ++i) target_rad[i] = target_deg[i] * PI / 180.0;
bool ok = rc.moveJoints(target_rad, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad());
std::cout << "移动返回: " << (ok ? "成功" : "失败") << std::endl;
continue;
}
if (mode == "rc" || mode == "dc") {
bool relative = (mode == "rc");
CartesianPose cp;
if (!rc.getTcpPose(cp)) { std::cerr << "获取当前 TCP 位姿失败\n"; continue; }
std::array<double, 6> cur_vals = {
cp.tran.x,
cp.tran.y,
cp.tran.z,
cp.rpy.rx * 180.0 / PI,
cp.rpy.ry * 180.0 / PI,
cp.rpy.rz * 180.0 / PI
};
std::array<double, 6> target_vals = cur_vals;
std::cout << (relative
? "笛卡尔相对移动:逐轴输入步进 (x/y/z 单位 mm, rx/ry/rz 单位 deg),直接回车表示保持不变。\n"
: "笛卡尔绝对移动:逐轴输入目标值 (x/y/z 单位 mm, rx/ry/rz 单位 deg),直接回车表示保持不变。\n");
const char *axis_names[6] = {"x", "y", "z", "rx", "ry", "rz"};
for (int i = 0; i < 6; ++i) {
std::cout << axis_names[i] << " 当前=" << cur_vals[i]
<< (relative ? ", 输入步进: " : ", 输入目标: ");
std::string line;
if (!std::getline(std::cin, line)) break;
if (line.empty()) continue;
try {
double v = std::stod(line);
if (relative) target_vals[i] = cur_vals[i] + v;
else target_vals[i] = v;
} catch (...) {
std::cout << "输入无效,保持原值 " << cur_vals[i] << "\n";
}
}
std::array<double, 6> offset = {0, 0, 0, 0, 0, 0};
// 统一用 "目标 - 当前" 计算偏移rc 使用 INCR, dc 使用 ABS
for (int i = 0; i < 6; ++i) {
offset[i] = target_vals[i] - cur_vals[i];
}
bool ok = rc.moveCartesianOffset(offset, relative ? INCR : ABS,
rc.getLineSpeed(), rc.getLineAcc());
std::cout << "移动返回: " << (ok ? "成功" : "失败") << std::endl;
continue;
}
std::cout << "无效命令\n";
}
}
bool perform_grasp_task(RobotController &rc)
{
clear_screen();
std::cout<<"开始执行抓握"<<std::endl;
const config::AppConfig &cfg = config::get_default_app_config();
const auto &grasp_cfg = cfg.grasp;
double descend = grasp_cfg.grasp_descend_mm;
if (descend <= 1.0) {
descend = 30.0;
}
//加速
rc.setJointSpeedDeg(grasp_cfg.grasp_joint_speed_deg);
rc.setJointAccDeg(grasp_cfg.grasp_joint_acc_deg);
const double line_speed = rc.getLineSpeed();
const double line_acc = rc.getLineAcc();
std::cout<<"A点位置:";
for (const auto &v : grasp_cfg.grasp_a.joint_deg) {
std::cout<<v<<", ";
}
std::cout<<std::endl;
std::cout<<"B点位置:";
for (const auto &v : grasp_cfg.grasp_b.joint_deg) {
std::cout<<v<<", ";
}
std::cout<<std::endl;
std::cout<<"抓取下降距离:"<<descend<<" mm"<<std::endl;
// A/B 点采用多策略运动:关节 -> 直线 -> 圆弧,避免奇异点导致直接失败
if (!robot_action::move_to_joint_with_strategies(rc, grasp_cfg.grasp_a.joint_deg)) {
std::cerr << "移动到抓取 A 点失败,终止抓取任务。" << std::endl;
return false;
}
if (!rc.moveZRelative(-descend, line_speed, line_acc)) {
std::cerr << "下降失败,终止抓取任务。" << std::endl;
return false;
}
// hand_actions::action_grasp_test();
// sleep_ms(3000);
bool grasp_ok = hand_actions::action_quick_grasp_once();
sleep_ms(500);
if (!grasp_ok) {
std::cerr << "警告:未检测到明显抓取力,可能没有抓住物体,但继续执行搬运流程。" << std::endl;
}
rc.moveZRelative(descend, line_speed, line_acc);
if (!robot_action::move_to_joint_with_strategies(rc, grasp_cfg.grasp_b.joint_deg)) {
std::cerr << "移动到抓取 B 点失败,终止抓取任务。" << std::endl;
return false;
}
if (!rc.moveZRelative(-descend, line_speed, line_acc)) {
std::cerr << "放置下降失败,终止抓取任务。" << std::endl;
return false;
}
hand_actions::action_full_extend();
sleep_ms(2000);
rc.moveZRelative(descend, line_speed, line_acc);
sleep_ms(3000);
//速度改回去
rc.setJointSpeedDeg(cfg.robot.joint_speed_deg_s);
rc.setJointAccDeg(cfg.robot.joint_acc_deg_s2);
return true;
}
void run_interaction_loop(RobotController &rc, robot_action::RobotParams &params)
{
std::cout << "开始预热" << std::endl;
std::array<double, 6> current_rad{};
if (rc.getJointPositions(current_rad)) {
auto warmup_target = current_rad;
warmup_target[4] += 10.0 * PI / 180.0;
auto attempt_move = [&](const std::array<double, 6> &target) -> bool {
if (rc.moveJoints(target, ABS, true, rc.getJointSpeedRad(), rc.getJointAccRad())) {
return true;
}
errno_t err = rc.getLastErrorCode();
if (err == -12) {
std::cout << "检测到错误 -12忽略并继续初始化。" << std::endl;
return true;
}
std::cerr << "预热关节移动失败,错误码: " << err << std::endl;
return false;
};
if (!attempt_move(warmup_target))
return;
if (!attempt_move(current_rad))
return;
} else {
std::cerr << "无法获取当前关节位姿,跳过关节预热动作。" << std::endl;
}
auto print_menu = []() {
std::cout << "手势控制提示:\n";
std::cout << " - 无名指 触发挥手+握手演示\n";
std::cout << " - 中指 触发石头剪刀布\n";
std::cout << " - 食指 触发抓取演示\n";
std::cout << " - 大拇指旋转 触发进入控制台菜单 (1:重设速度, 2:示教模式)\n";
std::cout << " - 小拇指 退出程序\n";
};
bool instructions_shown = false;
coordinated_actions::set_force_baseline_ready();
while (true) {
robot_action::init_place(rc);
hand_actions::action_full_extend();
sleep_ms(2000);
const std::string selection = force_trigger_selection();
if (selection.empty()) {
if (!instructions_shown) {
clear_screen();
print_menu();
instructions_shown = true;
}
sleep_ms(100);
continue;
}
instructions_shown = false;
if (selection == "exit") {
clear_screen();
std::cout << "检测到退出手势,正在结束程序...\n";
sleep_ms(1200);
break;
}
if (selection == "wave") {
wave_and_handshake(rc);
clear_screen();
print_menu();
coordinated_actions::set_force_baseline_ready();
continue;
}
if (selection == "rps") {
play_rps_game(rc);
clear_screen();
print_menu();
coordinated_actions::set_force_baseline_ready();
continue;
}
if (selection == "console") {
if (!run_console_menu(rc, params)) {
break;
}
clear_screen();
print_menu();
coordinated_actions::set_force_baseline_ready();
continue;
}
if (selection == "grasp") {
perform_grasp_task(rc);
sleep_ms(1000);
clear_screen();
print_menu();
coordinated_actions::set_force_baseline_ready();
continue;
}
}
}
} // namespace coordinated_actions