Files
lzwc-terminal-unitreeGo2/src/custom_robot.cpp
Sucan126 d3487a2613 feat(monitor): 添加监控模块用于订阅和打印运动状态和里程计数据
添加新的Monitor类,用于订阅并打印SportModeState和Odometry消息数据
移除custom_robot.cpp中冗余的日志信息
更新CMakeLists.txt以包含新的监控模块
2025-09-30 15:53:12 +08:00

790 lines
27 KiB
C++

#include "custom_robot.hpp"
#include <thread>
#include <chrono>
#include <functional>
#include <ctime>
#include <random>
#include <sstream>
#include <iomanip>
#include <nlohmann/json.hpp>
#include <unitree/robot/channel/channel_factory.hpp>
#include <unitree/robot/go2/sport/sport_client.hpp>
#include "recharge.hpp"
#include <cmath>
namespace custom {
CustomRobot::CustomRobot()
: controller_(nullptr)
, navigation_(nullptr)
, rsc_(nullptr)
, mqttClient_(nullptr)
, running_(false)
, initialized_(false)
, traj_running_(false)
, traj_dt_(0.02f)
, traj_count_(0.0f) {
config_.loadDefaults();
try {
unitree::robot::ChannelFactory::Instance()->Init(0, config_.network_interface);
LOG_INFO("ChannelFactory initialized with interface: " + config_.network_interface);
} catch (const std::exception& e) {
LOG_ERROR("Failed to initialize ChannelFactory: " + std::string(e.what()));
}
}
CustomRobot::~CustomRobot() {
LOG_INFO("Shutting down CustomRobot");
stopTraj();
if (mqttClient_) {
mqttClient_->stopMessageProcessor();
mqttClient_->disconnect();
mqttClient_.reset();
LOG_INFO("MQTT client disconnected and cleaned up");
}
if (controller_) {
controller_->shutdown();
controller_.reset();
}
// low_controller_ = std::make_unique<LowController>();
// low_controller_->initialize();
if (rsc_) {
rsc_.reset();
}
if (navigation_) {
navigation_.reset();
}
if (recharge_) {
recharge_.reset();
}
try {
unitree::robot::ChannelFactory::Instance()->Release();
LOG_INFO("ChannelFactory released");
} catch (const std::exception& e) {
LOG_ERROR("Failed to release ChannelFactory: " + std::string(e.what()));
}
initialized_ = false;
}
bool CustomRobot::initialize() {
try {
controller_ = std::make_unique<Controller>();
controller_->initialize();
// low_controller_ = std::make_unique<LowController>();
// low_controller_->initialize();
navigation_ = std::make_unique<Navigation>();
navigation_->Init();
rsc_ = std::make_unique<unitree::robot::go2::RobotStateClient>();
rsc_->SetTimeout(3.0f);
rsc_->Init();
recharge_ = std::make_unique<Recharge>();
recharge_->Init();
if (!initializeMqtt()) {
LOG_ERROR("Failed to initialize MQTT client");
return false;
}
} catch (const std::exception& e) {
LOG_ERROR("Failed to initialize : " + std::string(e.what()));
return false;
}
initialized_ = true;
return true;
}
bool CustomRobot::start() {
if (!initialized_) {
LOG_ERROR("Cannot start: not initialized");
return false;
}
if (running_) {
LOG_WARN("CustomRobot already running");
return true;
}
std::vector<unitree::robot::go2::ServiceState> serviceList;
if (!GetServiceList(serviceList)) {
LOG_ERROR("Failed to get service list");
return false;
}
if (!SwitchService("sport_mode", false)) {
LOG_ERROR("Failed to stop sport_mode service");
return false;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
if (!controller_->start()) {
LOG_ERROR("Failed to start robot controller");
return false;
}
// if (!low_controller_->start()) {
// LOG_ERROR("Failed to start low-level controller");
// return false;
// }
running_ = true;
LOG_INFO("CustomRobot started successfully");
return true;
}
void CustomRobot::trajControl()
{
if (!controller_) {
LOG_ERROR("Controller not available for dynamic trajectory.");
return;
}
float vx = 0.3f;
float delta = 0.06f;
traj_count_ += traj_dt_;
std::vector<unitree::robot::go2::PathPoint> path;
for (int i=0; i<30; i++) {
unitree::robot::go2::PathPoint p;
float var = (traj_count_ + i * delta);
p.timeFromStart = i * delta;
p.x = vx * var;
p.y = 0.6 * sin(M_PI * vx * var);
p.yaw = 2 * 0.6 * vx * M_PI * cos(M_PI * vx * var);
p.vx = vx;
p.vy = M_PI * vx * (0.6 * cos(M_PI * vx * var));
p.vyaw = - M_PI * vx * 2 * 0.6 * vx * M_PI * sin(M_PI * vx * var);
path.push_back(p);
}
if (!controller_->TrajectoryFollow(path)) {
LOG_WARN("Failed to send dynamic trajectory command.");
}
}
bool CustomRobot::startTraj()
{
if (traj_running_) {
LOG_WARN("Dynamic trajectory is already running.");
return true;
}
LOG_INFO("Starting dynamic trajectory control...");
traj_running_ = true;
traj_count_ = 0.0f;
traj_th_ = unitree::common::CreateRecurrentThread(static_cast<int64_t>(traj_dt_ * 1000000), std::bind(&CustomRobot::trajControl, this));
return true;
}
void CustomRobot::stopTraj()
{
if (traj_running_) {
LOG_INFO("Stopping dynamic trajectory control...");
traj_running_ = false;
if (traj_th_) {
traj_th_.reset();
}
}
}
bool CustomRobot::GetServiceList(std::vector<unitree::robot::go2::ServiceState>& serviceList) {
if (!initialized_ || !rsc_) {
LOG_ERROR("Robot not initialized or stateClient is null");
return false;
}
try {
int32_t ret = rsc_->ServiceList(serviceList);
if (ret != 0) {
LOG_ERROR("Failed to get service list, error code: " + std::to_string(ret));
return false;
}
// LOG_INFO("Successfully retrieved service list with " + std::to_string(serviceList.size()) + " services");
// for (const auto& service : serviceList) {
// std::string statusStr = (service.status == 1) ? "ACTIVE" : "INACTIVE";
// LOG_INFO("Service: " + service.name + " | Status: " + statusStr + " | Protect: " +
// (service.protect ? "YES" : "NO"));
// }
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in getServiceList: " + std::string(e.what()));
return false;
}
}
bool CustomRobot::SwitchService(const std::string& serviceName, bool enable) {
if (!initialized_ || !rsc_) {
LOG_ERROR("Robot not initialized or stateClient is null");
return false;
}
try {
int32_t status;
int32_t ret = rsc_->ServiceSwitch(serviceName, enable ? 1 : 0, status);
if (ret != 0) {
LOG_ERROR("Failed to switch service " + serviceName + ", error code: " + std::to_string(ret));
return false;
}
LOG_INFO("Successfully switched service " + serviceName + " to " + (enable ? "enabled" : "disabled"));
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in switchService: " + std::string(e.what()));
return false;
}
}
bool CustomRobot::SetReportFreq(int32_t interval, int32_t duration) {
if (!initialized_ || !rsc_) {
LOG_ERROR("Robot not initialized or stateClient is null");
return false;
}
try {
int32_t ret = rsc_->SetReportFreq(interval, duration);
if (ret != 0) {
LOG_ERROR("Failed to set report frequency, error code: " + std::to_string(ret));
return false;
}
LOG_INFO("Successfully set report frequency: interval=" + std::to_string(interval) + ", duration=" + std::to_string(duration));
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in setReportFreq: " + std::string(e.what()));
return false;
}
}
bool CustomRobot::initializeMqtt() {
try {
std::string broker = config_.mqtt_broker;
int port = config_.mqtt_port;
std::string clientId = generateRandomClientId();
mqttClient_ = std::make_unique<MqttClient>(broker, port, clientId);
using namespace std::placeholders;
mqttClient_->setMessageCb(std::bind(&CustomRobot::onMqttMessage, this, _1, _2));
mqttClient_->setConnectionCb(std::bind(&CustomRobot::onMqttConnection, this, _1));
mqttClient_->startMessageProcessor();
if (!mqttClient_->connect(config_.mqtt_username, config_.mqtt_password)) {
LOG_ERROR("Failed to connect to MQTT broker");
return false;
}
std::string controlTopic = config_.topic_prefix + "/" + config_.topic_cmd;
mqttClient_->subscribe(controlTopic, 1);
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception during MQTT initialization: " + std::string(e.what()));
return false;
}
}
void CustomRobot::onMqttMessage(const std::string& topic, const std::string& payload) {
try {
nlohmann::json message = nlohmann::json::parse(payload);
std::string controlTopic = config_.topic_prefix + "/" + config_.topic_cmd;
if (topic == controlTopic) {
processCmd(message);
}
} catch (const std::exception& e) {
LOG_ERROR("Error processing MQTT message: " + std::string(e.what()));
}
}
void CustomRobot::onMqttConnection(bool connected) {
if (connected) {
LOG_INFO("MQTT client connected to broker");
} else {
LOG_WARN("MQTT client disconnected from broker");
}
}
void CustomRobot::processCmd(const nlohmann::json& message) {
try {
if (!message.contains("type") || !message.contains("cmd")) {
LOG_ERROR("Invalid command format: missing 'type' or 'cmd' field");
return;
}
std::string type = message["type"];
std::string cmd = message["cmd"];
LOG_INFO("Processing cmd - type: " + type + ", cmd: " + cmd);
bool success = false;
if (type == "oac") {
success = processOacCmd(cmd, message);
} else if (type == "sport") {
success = processSportCmd(cmd, message);
} else if (type == "rsc") {
success = processRscCmd(cmd, message);
} else if (type == "nav") {
success = processNavCmd(cmd, message);
} else if (type == "msc") {
success = processMscCmd(cmd, message);
} else if (type == "low") {
success = processLowCmd(cmd, message);
} else if (type == "recharge") {
success = processRechargeCmd(cmd, message);
} else {
LOG_ERROR("Unknown command type: " + type);
return;
}
LOG_INFO(cmd + ", success: " + std::string(success ? "true" : "false"));
} catch (const std::exception& e) {
LOG_ERROR("Error processing cmd: " + std::string(e.what()));
}
}
bool CustomRobot::processLowCmd(const std::string& cmd, const nlohmann::json& message)
{
// if (!low_controller_)
// {
// LOG_ERROR("LowController not initialized");
// return false;
// }
//
// try
// {
// if (cmd == "requestAutoCharge")
// {
// if (!message.contains("param") || !message["param"].contains("enable"))
// {
// LOG_ERROR("requestAutoCharge cmd missing 'enable' parameter");
// return false;
// }
// bool enable = message["param"]["enable"];
// low_controller_->requestAutoCharge(enable);
// return true;
// }
// else if (cmd == "powerOff")
// {
// low_controller_->powerOff();
// return true;
// }
// else
// {
// LOG_ERROR("Unknown Low command: %s", cmd.c_str());
// return false;
// }
// }
// catch (const std::exception& e)
// {
// LOG_ERROR("Error executing Low command %s: %s", cmd.c_str(), e.what());
// return false;
// }
LOG_WARN("LowController functionality is currently disabled");
return false;
}
bool CustomRobot::processRechargeCmd(const std::string& cmd, const nlohmann::json& message) {
if (!recharge_) {
LOG_ERROR("Recharge module not initialized");
return false;
}
try {
if (cmd == "start") {
LOG_INFO("Starting recharge process...");
std::string result = recharge_->StartRecharge();
LOG_INFO("Recharge process finished with result: " + result);
return result == "aruco_success";
} else if (cmd == "stop") {
LOG_INFO("Stopping recharge process...");
return recharge_->StopRecharge();
} else {
LOG_ERROR("Unknown recharge command: " + cmd);
return false;
}
} catch (const std::exception& e) {
LOG_ERROR("Error executing recharge command " + cmd + ": " + std::string(e.what()));
return false;
}
}
bool CustomRobot::processOacCmd(const std::string& cmd, const nlohmann::json& message) {
if (!controller_) {
LOG_ERROR("Controller not initialized");
return false;
}
try {
if (cmd == "SwitchSet") {
if (message.find("param") == message.end() || message["param"].find("enable") == message["param"].end()) {
LOG_ERROR("Missing 'enable' parameter for Switch command");
return false;
}
bool enable = message["param"]["enable"];
return controller_->SwitchSet(enable);
} else if (cmd == "Move") {
if (!message.contains("param")) {
LOG_ERROR("Move cmd missing 'param'");
return false;
}
auto param = message["param"];
return controller_->ObstacleMove(param["x"], param["y"], param["yaw"]);
} else if (cmd == "UseRemoteCommandFromApi") {
if (!message.contains("param") || !message["param"].contains("enable")) {
LOG_ERROR("UseRemoteCommandFromApi cmd missing 'enable' parameter");
return false;
}
bool enable = message["param"]["enable"];
return controller_->UseRemoteCommandFromApi(enable);
} else if (cmd == "MoveToAbsolutePosition") {
if (!message.contains("param")) {
LOG_ERROR("MoveToAbsolutePosition cmd missing 'param'");
return false;
}
auto param = message["param"];
if (!param.contains("x") || !param.contains("y") || !param.contains("yaw")) {
LOG_ERROR("MoveToAbsolutePosition missing x, y, or yaw parameters");
return false;
}
float x = param["x"];
float y = param["y"];
float yaw = param["yaw"];
return controller_->MoveToAbsolutePosition(x, y, yaw);
} else if (cmd == "MoveToIncrementPosition") {
if (!message.contains("param")) {
LOG_ERROR("MoveToIncrementPosition cmd missing 'param'");
return false;
}
auto param = message["param"];
if (!param.contains("x") || !param.contains("y") || !param.contains("yaw")) {
LOG_ERROR("MoveToIncrementPosition missing x, y, or yaw parameters");
return false;
}
float x = param["x"];
float y = param["y"];
float yaw = param["yaw"];
return controller_->MoveToIncrementPosition(x, y, yaw);
} else {
LOG_ERROR("Unknown OAC command: " + cmd);
return false;
}
} catch (const std::exception& e) {
LOG_ERROR("Error executing OAC command " + cmd + ": " + std::string(e.what()));
return false;
}
}
bool CustomRobot::processNavCmd(const std::string& cmd, const nlohmann::json& message) {
if (!navigation_) {
LOG_ERROR("Navigation not initialized");
return false;
}
try {
if (cmd == "start_mapping") {
return navigation_->startMapping();
} else if (cmd == "end_mapping") {
if (!message.contains("param") || !message["param"].contains("address")) {
LOG_ERROR("end_mapping cmd missing 'address' parameter");
return false;
}
std::string address = message["param"]["address"];
return navigation_->endMapping(address);
} else if (cmd == "initialize_pose") {
if (!message.contains("param")) {
LOG_ERROR("initialize_pose cmd missing 'param'");
return false;
}
auto& param = message["param"];
const std::vector<std::string> keys = {"x", "y", "z", "q_x", "q_y", "q_z", "q_w", "address"};
for (const auto& key : keys) {
if (!param.contains(key)) {
LOG_ERROR("initialize_pose missing parameter: " + key);
return false;
}
}
return navigation_->initializePose(param["x"], param["y"], param["z"], param["q_x"], param["q_y"], param["q_z"], param["q_w"], param["address"]);
} else if (cmd == "pose_navigation") {
if (!message.contains("param")) {
LOG_ERROR("pose_navigation cmd missing 'param'");
return false;
}
auto& param = message["param"];
if (!param.contains("targetPose") || !param.contains("mode") || !param.contains("speed")) {
LOG_ERROR("pose_navigation missing 'targetPose', 'mode', or 'speed'");
return false;
}
auto& targetPose = param["targetPose"];
const std::vector<std::string> pose_keys = {"x", "y", "z", "q_x", "q_y", "q_z", "q_w"};
for (const auto& key : pose_keys) {
if (!targetPose.contains(key)) {
LOG_ERROR("pose_navigation.targetPose missing parameter: " + key);
return false;
}
}
return navigation_->poseNavigation(targetPose["x"], targetPose["y"], targetPose["z"], targetPose["q_x"], targetPose["q_y"], targetPose["q_z"], targetPose["q_w"], param["mode"], param["speed"]);
} else if (cmd == "pause_navigation") {
return navigation_->pauseNavigation();
} else if (cmd == "resume_navigation") {
return navigation_->resumeNavigation();
} else if (cmd == "close_slam") {
return navigation_->closeSlam();
} else {
LOG_ERROR("Unknown Nav command: " + cmd);
return false;
}
} catch (const std::exception& e) {
LOG_ERROR("Error executing Nav command " + cmd + ": " + std::string(e.what()));
return false;
}
}
bool CustomRobot::processRscCmd(const std::string& cmd, const nlohmann::json& message) {
try {
if (cmd == "list") {
LOG_INFO("Received service list query command");
std::vector<unitree::robot::go2::ServiceState> serviceList;
if (!GetServiceList(serviceList)) {
LOG_ERROR("Failed to get service list");
return false;
}
LOG_INFO("Successfully retrieved service list with " + std::to_string(serviceList.size()) + " services");
int filterStatus = -1;
if (message.contains("param") && message["param"].contains("status")) {
filterStatus = message["param"]["status"];
LOG_INFO("Filtering services by status: " + std::to_string(filterStatus));
}
printServiceList(serviceList, filterStatus);
return true;
} else if (cmd == "switch") {
if (!message.contains("param") ||
!message["param"].contains("name") ||
!message["param"].contains("enable")) {
LOG_ERROR("Service switch command missing required parameters (name, enable)");
return false;
}
std::string serviceName = message["param"]["name"];
bool enable = message["param"]["enable"];
bool result = SwitchService(serviceName, enable);
if (result) {
LOG_INFO("Service switch completed successfully");
}
return result;
} else {
LOG_ERROR("Unknown service command: " + cmd);
return false;
}
} catch (const std::exception& e) {
LOG_ERROR("Error processing service cmd: " + std::string(e.what()));
return false;
}
}
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<unitree::robot::go2::ServiceState>& serviceList, int filterStatus) {
std::string filterDesc = "All Services";
if (filterStatus == 0) {
filterDesc = "INACTIVE Services Only";
} else if (filterStatus == 1) {
filterDesc = "ACTIVE Services Only";
}
LOG_INFO("=== Service Status Report (" + filterDesc + ") ===");
int matchCount = 0;
for (const auto& service : serviceList) {
if (filterStatus != -1 && service.status != filterStatus) {
continue;
}
std::string statusStr = (service.status == 1) ? "ACTIVE" : "INACTIVE";
LOG_INFO("Service: " + service.name + " | Status: " + statusStr + " | Protect: " +
(service.protect ? "YES" : "NO"));
matchCount++;
}
if (filterStatus != -1) {
LOG_INFO("Found " + std::to_string(matchCount) + " services matching filter criteria");
}
LOG_INFO("=== End Service Report ===");
}
bool CustomRobot::processSportCmd(const std::string& cmd, const nlohmann::json& message) {
if (!controller_) {
LOG_ERROR("Controller not initialized");
return false;
}
try {
if (cmd == "Damp") {
return controller_->Damp();
} else if (cmd == "BalanceStand") {
return controller_->BalanceStand();
} else if (cmd == "StopMove") {
return controller_->StopMove();
} else if (cmd == "StandUp") {
return controller_->StandUp();
} else if (cmd == "StandDown") {
return controller_->StandDown();
} else if (cmd == "RecoveryStand") {
return controller_->RecoveryStand();
} else if (cmd == "Euler") {
return controller_->Euler(message["param"]["roll"], message["param"]["pitch"], message["param"]["yaw"]);
} else if (cmd == "Move") {
return controller_->Move(message["param"]["vx"], message["param"]["vy"], message["param"]["vyaw"]);
} else if (cmd == "Sit") {
return controller_->Sit();
} else if (cmd == "RiseSit") {
return controller_->RiseSit();
} else if (cmd == "SpeedLevel") {
return controller_->SpeedLevel(message["param"]["level"]);
} else if (cmd == "Hello") {
return controller_->Hello();
} else if (cmd == "Stretch") {
return controller_->Stretch();
} else if (cmd == "SwitchJoystick") {
return controller_->SwitchJoystick(message["param"]["flag"]);
} else if (cmd == "Content") {
return controller_->Content();
} else if (cmd == "Heart") {
return controller_->Heart();
} else if (cmd == "Pose") {
return controller_->Pose(message["param"]["flag"]);
} else if (cmd == "Scrape") {
return controller_->Scrape();
} else if (cmd == "FrontFlip") {
return controller_->FrontFlip();
} else if (cmd == "FrontJump") {
return controller_->FrontJump();
} else if (cmd == "FrontPounce") {
return controller_->FrontPounce();
} else if (cmd == "Dance1") {
return controller_->Dance1();
} else if (cmd == "Dance2") {
return controller_->Dance2();
} else if (cmd == "LeftFlip") {
return controller_->LeftFlip();
} else if (cmd == "BackFlip") {
return controller_->BackFlip();
} else if (cmd == "FreeWalk") {
return controller_->FreeWalk();
} else if (cmd == "FreeBound") {
return controller_->FreeBound(message["param"]["flag"]);
} else if (cmd == "FreeJump") {
return controller_->FreeJump(message["param"]["flag"]);
} else if (cmd == "FreeAvoid") {
return controller_->FreeAvoid(message["param"]["flag"]);
} else if (cmd == "WalkUpright") {
return controller_->WalkUpright(message["param"]["flag"]);
} else if (cmd == "CrossStep") {
return controller_->CrossStep(message["param"]["flag"]);
} else if (cmd == "start_traj") {
return startTraj();
} else if (cmd == "stop_traj") {
stopTraj();
return true;
}
else {
LOG_ERROR("Unknown Sport command: " + cmd);
return false;
}
} catch (const std::exception& e) {
LOG_ERROR("Error executing Sport command " + cmd + ": " + std::string(e.what()));
return false;
}
}
std::string CustomRobot::generateRandomClientId() const {
std::string baseId = config_.mqtt_client_id;
std::string safeBaseId;
for (char c : baseId) {
if (std::isalnum(c) || c == '_' || c == '-') {
safeBaseId += c;
}
}
if (safeBaseId.empty()) {
safeBaseId = "unitree_go2_client";
}
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(1000, 9999);
std::stringstream ss;
ss << safeBaseId << "_" << std::time(nullptr) << "_" << dis(gen);
return ss.str();
}
} // namespace custom