refactor(devices): remove legacy robot stack and stale tests

This commit is contained in:
lgv 2026-06-30 16:05:47 +08:00
parent 7e803452e1
commit 32fe139015
23 changed files with 6 additions and 1781 deletions

View File

@ -5,6 +5,5 @@ add_subdirectory(microphone)
add_subdirectory(dexhand) add_subdirectory(dexhand)
add_subdirectory(biohead) add_subdirectory(biohead)
add_subdirectory(arm) add_subdirectory(arm)
add_subdirectory(robot)
add_subdirectory(canbus) add_subdirectory(canbus)
add_subdirectory(motor) add_subdirectory(motor)

View File

@ -18,6 +18,8 @@ namespace cmvr::device {
Microphone, Microphone,
Motor, Motor,
MotorSystem, MotorSystem,
MujocoViewer,
MujocoWorld,
Robot, Robot,
Speaker, Speaker,
}; };
@ -46,6 +48,10 @@ namespace cmvr::device {
return "Motor"; return "Motor";
case DeviceKind::MotorSystem: case DeviceKind::MotorSystem:
return "MotorSystem"; return "MotorSystem";
case DeviceKind::MujocoViewer:
return "MujocoViewer";
case DeviceKind::MujocoWorld:
return "MujocoWorld";
case DeviceKind::Robot: case DeviceKind::Robot:
return "Robot"; return "Robot";
case DeviceKind::Speaker: case DeviceKind::Speaker:

View File

@ -12,17 +12,3 @@ target_link_libraries(px_6ax_gen3
) )
install(TARGETS px_6ax_gen3 LIBRARY DESTINATION lib) install(TARGETS px_6ax_gen3 LIBRARY DESTINATION lib)
add_executable(px_6ax_gen3_test
src/px_6ax_gen3_test.cpp
)
target_link_libraries(px_6ax_gen3_test PRIVATE
cmvr_es::device::px_6ax_gen3
cmvr_es::common
cmvr_es::proto
glog
gtest
gtest_main
pthread
)

View File

@ -1,163 +0,0 @@
#include "gtest/gtest.h"
#include "../include/px_6ax_gen3.h"
#include "cmvr/config/dexhand_config/dexhand_config.pb.h"
#include "common/config/config_files.h"
#include <chrono>
#include <cstdint>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
namespace {
using DexHand = cmvr::device::AbstractDexHand;
using PX6AXGen3 = cmvr::device::PX6AXGen3;
DexHand::FingerType parseFingerType(const std::string& value) {
if (value == "PINKY") {
return DexHand::FingerType::PINKY;
}
if (value == "RING") {
return DexHand::FingerType::RING;
}
if (value == "MIDDLE" || value == "MIDDLE_FINGER") {
return DexHand::FingerType::MIDDLE;
}
if (value == "THUMB") {
return DexHand::FingerType::THUMB;
}
if (value == "PALM") {
return DexHand::FingerType::PALM;
}
return DexHand::FingerType::INDEX;
}
DexHand::TactileRegion parseTactileRegion(const std::string& value) {
if (value == "FINGER") {
return DexHand::TactileRegion::FINGER;
}
if (value == "PAD") {
return DexHand::TactileRegion::PAD;
}
if (value == "THUMB_MIDDLE") {
return DexHand::TactileRegion::THUMB_MIDDLE;
}
if (value == "PALM_PAD") {
return DexHand::TactileRegion::PALM_PAD;
}
return DexHand::TactileRegion::TIP;
}
struct StopGuard {
std::shared_ptr<PX6AXGen3> hand;
~StopGuard() {
if (!hand) {
return;
}
try {
hand->stop();
} catch (...) {
}
}
};
DexHand::ResultantForce readFirstValidResultantForce(PX6AXGen3& hand,
const DexHand::FingerType finger,
const DexHand::TactileRegion region,
const int max_attempts,
const std::chrono::milliseconds retry_interval) {
std::string last_error;
for (int attempt = 0; attempt < max_attempts; ++attempt) {
try {
return hand.getResultantForce(finger, region);
} catch (const std::exception& ex) {
last_error = ex.what();
}
if (attempt + 1 < max_attempts) {
std::this_thread::sleep_for(retry_interval);
}
}
throw std::runtime_error("Failed to read PX6AXGen3 resultant force: " + last_error);
}
DexHand::ResultantForce readStateResultantForce(PX6AXGen3& hand) {
cmvr::device::DexHandState state;
hand.getState(state);
return DexHand::ResultantForce{0, 0, state.hands[0].force};
}
} // namespace
TEST(PX6AXGen3Test, PrintResultantForceOnly) {
cmvr::config::DexHandRootConfig root_config;
ASSERT_TRUE(cmvr::ConfigHelper::loadConfigFile("devices/dexhand/dexhand.pb.txt", root_config));
const cmvr::config::DexHandDeviceConfig* device_config_ptr = nullptr;
for (const auto& cfg : root_config.dexhand().dexhands()) {
if (cfg.id() == "paxini_tip_1" && cfg.has_px_6ax_gen3()) {
device_config_ptr = &cfg;
break;
}
}
ASSERT_NE(device_config_ptr, nullptr);
auto test_config = device_config_ptr->px_6ax_gen3();
test_config.set_id(device_config_ptr->id());
ASSERT_FALSE(test_config.serial_port().empty());
auto hand = std::make_shared<PX6AXGen3>(test_config);
ASSERT_NO_THROW(hand->init());
ASSERT_NO_THROW(hand->start());
StopGuard stop_guard{hand};
EXPECT_EQ(hand->state(), DexHand::Status::STREAMING);
const DexHand::FingerType finger = parseFingerType(test_config.tactile_finger());
const DexHand::TactileRegion region = parseTactileRegion(test_config.tactile_region());
const int warmup_ms = test_config.poll_interval_ms() > 0
? test_config.poll_interval_ms() * 5
: 200;
const int iterations = 1000000;
const int read_interval_ms = test_config.poll_interval_ms() > 0
? test_config.poll_interval_ms()
: 10;
if (warmup_ms > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(warmup_ms));
}
bool read_once = false;
for (int iteration = 0; iteration < iterations; ++iteration) {
DexHand::ResultantForce resultant_force{};
ASSERT_NO_THROW(resultant_force = readFirstValidResultantForce(
*hand,
finger,
region,
10,
std::chrono::milliseconds(100)));
DexHand::ResultantForce state_force{};
ASSERT_NO_THROW(state_force = readStateResultantForce(*hand));
std::cout << "[PX6AXGen3Test] iter=" << (iteration + 1)
<< " resultant_fx=" << resultant_force.fx
<< " resultant_fy=" << resultant_force.fy
<< " resultant_fz=" << resultant_force.fz
<< " state_resultant_fz=" << state_force.fz
<< std::endl;
read_once = true;
if (read_interval_ms > 0 && iteration + 1 < iterations) {
std::this_thread::sleep_for(std::chrono::milliseconds(read_interval_ms));
}
}
EXPECT_TRUE(read_once);
}

View File

@ -7,17 +7,3 @@ add_library(cmvr_es::device::rh56dftp_dexhand ALIAS rh56dftp_dexhand)
target_link_libraries(rh56dftp_dexhand PRIVATE cmvr_es::hardware cmvr_es::proto -lmodbus) target_link_libraries(rh56dftp_dexhand PRIVATE cmvr_es::hardware cmvr_es::proto -lmodbus)
install(TARGETS rh56dftp_dexhand LIBRARY DESTINATION lib) install(TARGETS rh56dftp_dexhand LIBRARY DESTINATION lib)
add_executable(rh56dftp_dexhand_test
src/rh56dftp_dexhand_test.cpp
)
target_link_libraries(rh56dftp_dexhand_test PRIVATE
cmvr_es::device::rh56dftp_dexhand
cmvr_es::common
cmvr_es::proto
glog
gtest
gtest_main
pthread
)

View File

@ -1,135 +0,0 @@
#include "gtest/gtest.h"
#include "../include/rh56dftp_dexhand.h"
#include "cmvr/config/dexhand_config/dexhand_config.pb.h"
#include "common/config/config_files.h"
#include <chrono>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <thread>
#include <vector>
namespace {
using DexHand = cmvr::device::AbstractDexHand;
using RH56DexHand = cmvr::device::RH56DFTPDexhand;
const char* tactileRegionToString(const DexHand::TactileRegion region) {
switch (region) {
case DexHand::TactileRegion::TIP: return "TIP";
case DexHand::TactileRegion::FINGER: return "FINGER";
case DexHand::TactileRegion::PAD: return "PAD";
case DexHand::TactileRegion::THUMB_MIDDLE: return "THUMB_MIDDLE";
case DexHand::TactileRegion::PALM_PAD: return "PALM_PAD";
}
return "UNKNOWN";
}
struct StopGuard {
std::shared_ptr<RH56DexHand> hand;
~StopGuard() {
if (!hand) {
return;
}
try {
hand->stop();
} catch (...) {
}
}
};
} // namespace
TEST(RH56DFTPDexhandLatencyTest, ReadConfiguredRegionAndMeasureLatency) {
cmvr::config::DexHandRootConfig root_config;
ASSERT_TRUE(cmvr::ConfigHelper::loadConfigFile("devices/dexhand/dexhand.pb.txt", root_config));
const cmvr::config::DexHandDeviceConfig* device_config_ptr = nullptr;
for (const auto& cfg : root_config.dexhand().dexhands()) {
if (cfg.id() == "hand2" && cfg.has_rh56dftp()) {
device_config_ptr = &cfg;
break;
}
}
ASSERT_NE(device_config_ptr, nullptr);
auto hand_config = device_config_ptr->rh56dftp();
hand_config.set_id(device_config_ptr->id());
ASSERT_FALSE(hand_config.ip().empty());
const auto finger = DexHand::FingerType::RING;
const auto region = DexHand::TactileRegion::TIP;
const int iterations = 20000;
const int warmup_ms = 200;
const int read_interval_ms = 10;
auto hand = std::make_shared<RH56DexHand>(hand_config);
ASSERT_NO_THROW(hand->init());
ASSERT_NO_THROW(hand->start());
StopGuard stop_guard{hand};
EXPECT_EQ(hand->state(), DexHand::Status::STREAMING);
ASSERT_NO_THROW(hand->setTactilePollingRegion(finger, region));
if (warmup_ms > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(warmup_ms));
}
double last_pressure_sum = 0.0;
double last_pressure_peak = 0.0;
int point_count = 0;
std::string sensor_name;
for (int i = 0; i < iterations; ++i) {
const auto region_data = hand->getSensorData(finger, region);
ASSERT_TRUE(region_data.valid());
EXPECT_EQ(region_data.finger, finger);
EXPECT_EQ(region_data.region, region);
if (point_count == 0) {
point_count = region_data.view.pointCount();
sensor_name = region_data.name == nullptr ? "" : region_data.name;
}
last_pressure_sum = 0.0;
last_pressure_peak = 0.0;
for (int index = 0; index < region_data.view.pointCount(); ++index) {
const double pressure = static_cast<double>(region_data.view.data[index].fz);
last_pressure_sum += pressure;
if (pressure > last_pressure_peak) {
last_pressure_peak = pressure;
}
}
std::cout << std::fixed << std::setprecision(3)
<< "[RH56DFTPDexhandLatencyTest] iter=" << (i + 1)
<< "/" << iterations
<< " sensor=" << sensor_name
<< " pressure_sum=" << last_pressure_sum
<< " pressure_peak=" << last_pressure_peak
<< std::endl;
if (read_interval_ms > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(read_interval_ms));
}
}
std::cout << std::fixed << std::setprecision(3)
<< "[RH56DFTPDexhandLatencyTest] id=" << hand_config.id()
<< " ip=" << hand_config.ip()
<< " port=" << hand_config.port()
<< " finger=INDEX"
<< " region=" << tactileRegionToString(region)
<< " sensor=" << sensor_name
<< " iterations=" << iterations
<< " points=" << point_count
<< " last_pressure_sum=" << last_pressure_sum
<< " last_pressure_peak=" << last_pressure_peak
<< " read_interval_ms=" << read_interval_ms
<< "\n";
EXPECT_GT(point_count, 0);
}

View File

@ -1,3 +0,0 @@
#add_subdirectory(ti5_robot)
#add_subdirectory(c701)
#add_subdirectory(aubo_robot)

View File

@ -1,170 +0,0 @@
//
// Created by xtkuang on 2025/5/8.
//
#ifndef CMVR_ES_ABSTRACT_ROBOT_H
#define CMVR_ES_ABSTRACT_ROBOT_H
#pragma once
#include "json/json.h"
#include "../abstract_device.h"
#include "common/types/arm/arm_types.h"
#include "algorithms/motion_planner/base_motion/cartesian_velocity/twist_limiter/include/cartesian_twist_limiter.h"
#include "cmvr/common/geometry.pb.h"
#include "cmvr/msgs/motor.pb.h"
#include "common/base/logging/logger.h"
#include <Eigen/Dense>
namespace cmvr::device{
class AbstractRobot: public AbstractDevice {
public:
AbstractRobot() = default;
~AbstractRobot() override=default;
DeviceKind kind() const noexcept override { return DeviceKind::Robot; }
virtual int getDOF() { CMVR_LOG(ERROR) << "[AbstractRobot] getDOF is not implemented"; return 0; }
virtual std::vector<std::string> getJointNames() { CMVR_LOG(ERROR) << "[AbstractRobot] getJointNames is not implemented"; return {}; }
virtual std::unordered_map<std::string,double> getJointQ() const { CMVR_LOG(ERROR) << "[AbstractRobot] getJointQ is not implemented"; return {}; }
/**
*
* @param joint_qs joint_qs joint name
*/
virtual void getJointQ(std::unordered_map<std::string,double> &joint_qs) const { CMVR_LOG(ERROR) << "[AbstractRobot] getJointQ(out) is not implemented"; joint_qs.clear(); }
virtual std::vector<std::string> getLinkNames() { CMVR_LOG(ERROR) << "[AbstractRobot] getLinkNames is not implemented"; return {}; }
virtual void getJointsState(std::vector<JointState>& states) = 0;
virtual void getState(RobotState &state) { CMVR_LOG(ERROR) << "[AbstractRobot] getState is not implemented"; state = RobotState{}; }
virtual math::Pose3d getTransform(std::string &bask_link, std::string &target_link) { CMVR_LOG(ERROR) << "[AbstractRobot] getTransform is not implemented"; return {}; }
virtual void torqueOn() { CMVR_LOG(ERROR) << "[AbstractRobot] torqueOn is not implemented"; }
virtual void torqueOn(const std::string &joint_name) { CMVR_LOG(ERROR) << "[AbstractRobot] torqueOn(joint) is not implemented: " << joint_name; }
virtual void torqueOff() { CMVR_LOG(ERROR) << "[AbstractRobot] torqueOff is not implemented"; }
virtual void torqueOff(const std::string &joint_name) { CMVR_LOG(ERROR) << "[AbstractRobot] torqueOff(joint) is not implemented: " << joint_name; }
virtual void eStop() { CMVR_LOG(ERROR) << "[AbstractRobot] eStop is not implemented"; }
/**
* @brief point-to-point
* @details /使
* @param joints rad
* @param max_vel rad/s
* @param max_acc rad/s^2
*/
virtual void moveJ(std::vector<double> &joints, double max_vel=0.5, double max_acc=0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] moveJ(vector<double>) is not implemented"; }
/**
* @brief
* @param cmd + `vel` 使
* @param vel rad/s
* @param acc rad/s^2
*/
virtual void moveJ(std::vector<JointPoint> &cmd, double vel=0.5, double acc=0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] moveJ(vector<JointPoint>) is not implemented"; }
virtual void speedJ(std::vector<JointVelocityCommand> &cmd) { CMVR_LOG(ERROR) << "[AbstractRobot] speedJ(commands) is not implemented"; }
virtual void speedJ(double vel) { CMVR_LOG(ERROR) << "[AbstractRobot] speedJ(double) is not implemented"; }
/**
* @brief 姿
* @details IK `moveJ`
* @param base_link link
* @param ee_link link
* @param pose 姿
* @param vel rad/s
* @param acc rad/s^2
*/
virtual void moveJ(const std::string &base_link, const std::string &ee_link, cmvr::common::Pose3d pose,double vel = 0.5, double acc = 0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] moveJ(pose) is not implemented"; }
virtual void moveDeltaJ(const std::string &base_link, const std::string &ee_link, cmvr::common::Pose3d delta_pose,double vel = 0.5, double acc = 0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] moveDeltaJ is not implemented"; }
virtual void moveJ_IK(math::Pose3d &pose, double vel=0.5, double acc=0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] moveJ_IK is not implemented"; }
virtual bool moveL(const std::vector<double> &pose,
double speed = 0.25,
double acceleration = 1.2,
double jerk = 5.0,
const std::vector<double> &qd_max = std::vector<double>(7, 2.5),
bool asynchronous = false) {
CMVR_LOG(ERROR) << "[AbstractRobot] moveL is not implemented";
return false;
}
virtual bool speedL(const std::vector<double> &xd,
double acceleration = 0.25,
double time = 0.0,
cmvr::CartesianFrame frame = cmvr::CartesianFrame::Base) {
CMVR_LOG(ERROR) << "[AbstractRobot] speedL is not implemented";
return false;
}
virtual Eigen::Matrix<double, 6, 1> getSpeedLCommandTwistBase() {
return Eigen::Matrix<double, 6, 1>::Zero();
}
virtual void stopSpeedL() { CMVR_LOG(ERROR) << "[AbstractRobot] stopSpeedL is not implemented"; }
virtual void speedJ(std::string &joint_name, RobotJointIndexDirection dir, double vel, double acc=0.5) { CMVR_LOG(ERROR) << "[AbstractRobot] speedJ(joint) is not implemented: " << joint_name; }
virtual void followJointTrajectory(std::vector<std::vector<double>> &traj, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] followJointTrajectory(double) is not implemented"; }
virtual void followJointTrajectory(std::vector<std::vector<JointPoint>> &traj, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] followJointTrajectory(JointPoint) is not implemented"; }
virtual void followPoseTrajectory(std::vector<math::Pose3d> &traj, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] followPoseTrajectory is not implemented"; }
/**
* @brief
* @details
* @param joints rad
* @param dt s
*/
virtual void servoJ(std::vector<double> &joints, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] servoJ(vector<double>) is not implemented"; }
/**
* @brief
* @param joints
* @param dt s
*/
virtual void servoJ(std::vector<JointPoint> &joints, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] servoJ(vector<JointPoint>) is not implemented"; }
/**
* @brief
* @param joints
* @param vel
* @param dt s
*/
virtual void servoJ(std::vector<JointPoint> &joints, double vel, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] servoJ(vector<JointPoint>, vel) is not implemented"; }
/**
* @brief 姿
* @details IK `moveJ`
* @param base_link link
* @param ee_link link
* @param pose 姿
* @param vel
* @param acc
*/
virtual void servoJ(const std::string &base_link, const std::string &ee_link, cmvr::common::Pose3d pose,double vel = 0.1, double acc = 0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] servoJ(pose) is not implemented"; }
virtual void servoDeltaJ(const std::string &base_link, const std::string &ee_link, cmvr::common::Pose3d delta_pose,double vel = 0.1, double acc = 0.1) { CMVR_LOG(ERROR) << "[AbstractRobot] servoDeltaJ is not implemented"; }
virtual void servoL(math::Pose3d &pose, double dt) { CMVR_LOG(ERROR) << "[AbstractRobot] servoL is not implemented"; }
virtual void calibrateZeroQ(const std::string &joint_name) = 0;
void setToolFrame(const std::string& toolFrame)
{
toolFrame_ = toolFrame;
}
virtual std::string getToolFrame() const { return toolFrame_; }
virtual cmvr::common::Pose3d fk(const std::string &base_link, const std::string &ee_link) = 0;
virtual cmvr::common::Pose3d fk(bool is_tcp = true) = 0;
virtual std::vector<double> ik(const std::string &base_link, const std::string &ee_link,cmvr::common::Pose3d pose) = 0;
protected:
int dof_{};
RobotState state_{};
std::string toolFrame_;
};
}
#endif //CMVR_ES_ABSTRACT_ROBOT_H

View File

@ -1,35 +0,0 @@
# Aubo SDK add_library
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(AUBO_SDK_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/AuboSdk/linux/include)
set(AUBO_SDK_LIB_DIR ${CMAKE_SOURCE_DIR}/third_party/AuboSdk/linux/lib)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(AUBO_SDK_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/AuboSdk/win/include)
set(AUBO_SDK_LIB_DIR ${CMAKE_SOURCE_DIR}/third_party/AuboSdk/win/lib)
endif()
# aubo_robot SDK
add_library(aubo_robot SHARED src/aubo_robot.cpp)
# aubo_robot ${CMAKE_CURRENT_SOURCE_DIR} SDK include
target_include_directories(aubo_robot
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR} #
${AUBO_SDK_INCLUDE_DIR} # SDK
)
# aubo_robot link_directories
target_link_directories(aubo_robot
PRIVATE
${AUBO_SDK_LIB_DIR} # SDK
)
# SDK
target_link_libraries(aubo_robot
PRIVATE
aubo_sdk
robot_proxy
cmvr_es::proto
)
#
add_library(cmvr_es::device::aubo_robot ALIAS aubo_robot)

View File

@ -1,51 +0,0 @@
//
// Created by linbo on 2025/11/7.
//
#ifndef CMVR_ES_AUBO_ROBOT_H
#define CMVR_ES_AUBO_ROBOT_H
#include "devices/robot/abstract_robot.h"
#include "aubo_sdk/rpc.h"
#include "cmvr/config/aubo_robot_config/aubo_robot_config.pb.h"
#include "cmvr/msgs/robot_detail.pb.h"
using namespace arcs::common_interface;
using namespace arcs::aubo_sdk;
namespace cmvr::device
{
template<int DOF>
class AuboRobot: public AbstractRobot {
public:
explicit AuboRobot(const config::AuboRobotConfig& cfg);
~AuboRobot();
std::string typeName() const override { return "AuboRobot"; }
bool init () override;
void torqueOn() override;
void torqueOff() override;
void getJointsState(std::vector<JointState>& states) override;
void moveJ(std::vector<JointPoint>& cmd, double vel, double acc) override;
void moveL(math::Pose3d& pose, double vel, double acc) override;
void calibrateZeroQ(const std::string& joint_name) override;
cmvr::common::Pose3d fk(const std::string &base_link, const std::string &ee_link) override;
std::vector<double> ik(const std::string &base_link, const std::string &ee_link,cmvr::common::Pose3d pose) override;
private:
static void waitForRobotMode(const RobotInterfacePtr& robot_interface,
const RobotModeType& target_mode);
static int waitArrival(const RobotInterfacePtr& impl);
private:
std::string ip_;
int port_;
std::string username_;
std::string password_;
std::shared_ptr<RpcClient> rpc_cli_;
};
}
#endif //CMVR_ES_AUBO_ROBOT_H

View File

@ -1,272 +0,0 @@
#include "common/base/logging/logger.h"
//
// Created by linbo on 2025/11/7.
//
#include "devices/robot/aubo_robot/include/aubo_robot.h"
using namespace std;
using namespace cmvr::device;
template<int DOF>
AuboRobot<DOF>::AuboRobot(const config::AuboRobotConfig &cfg)
{
try {
id_ = cfg.id();
ip_ = cfg.ip();
port_ = cfg.port() > 0 ? cfg.port() : 30004;
username_ = cfg.username();
password_ = cfg.password();
} catch (std::exception &e) {
CMVR_LOG(ERROR) << "[AuboRobot] Failed to parse config: " << e.what();
}
}
template<int DOF>
AuboRobot<DOF>::~AuboRobot()
{
if (rpc_cli_)
{
// 接口调用: 退出登录
rpc_cli_->logout();
// 接口调用: 断开连接
rpc_cli_->disconnect();
}
}
template<int DOF>
bool AuboRobot<DOF>::init ()
{
try {
//初始化AuboSDK
rpc_cli_ = std::make_shared<RpcClient>();
// 接口调用: 设置 RPC 超时
rpc_cli_->setRequestTimeout(1000);
// 接口调用: 连接到 RPC 服务
rpc_cli_->connect(ip_, port_);
// 接口调用: 登录
rpc_cli_->login(username_, password_);
return true;
} catch (const std::exception& e) {
CMVR_LOG(ERROR) << "[AuboRobot] init failed: " << e.what();
rpc_cli_.reset();
return false;
}
}
template<int DOF>
void AuboRobot<DOF>::waitForRobotMode(const RobotInterfacePtr& robot_interface,
const RobotModeType& target_mode)
{
// 接口调用: 获取当前机械臂的模式
auto current_mode = robot_interface->getRobotState()->getRobotModeType();
while (current_mode != target_mode) {
CMVR_LOG(INFO) << "机械臂当前模式:" << current_mode;
std::this_thread::sleep_for(std::chrono::seconds(1));
current_mode = robot_interface->getRobotState()->getRobotModeType();
}
}
template<int DOF>
void AuboRobot<DOF>::torqueOn()
{
// 接口调用: 获取机器人的名字
auto robot_name = rpc_cli_->getRobotNames().front();
auto robot_interface = rpc_cli_->getRobotInterface(robot_name);
// 接口调用: 设置负载
double mass = 0.0;
std::vector<double> cog(3, 0.0);
std::vector<double> aom(3, 0.0);
std::vector<double> inertia(6, 0.0);
robot_interface->getRobotConfig()->setPayload(mass, cog, aom, inertia);
// 接口调用: 获取机械臂当前模式
auto robot_mode = robot_interface->getRobotState()->getRobotModeType();
if (robot_mode == RobotModeType::Running) {
CMVR_LOG(INFO) << "机械臂已松刹车,处于运行模式";
} else {
// 接口调用: 机械臂发起上电请求
robot_interface->getRobotManage()->poweron();
// 等待机械臂进入空闲模式
waitForRobotMode(robot_interface, RobotModeType::Idle);
CMVR_LOG(INFO) << "机械臂上电成功,当前模式:"
<< robot_interface->getRobotState()->getRobotModeType()
;
// 接口调用: 机械臂发起松刹车请求
rpc_cli_->getRobotInterface(robot_name)->getRobotManage()->startup();
// 等待机械臂进入运行模式
waitForRobotMode(robot_interface, RobotModeType::Running);
CMVR_LOG(INFO) << "机械臂松刹车成功,当前模式:"
<< robot_interface->getRobotState()->getRobotModeType()
;
}
}
template<int DOF>
void AuboRobot<DOF>::getJointsState(std::vector<JointState>& states)
{
try {
// 接口调用: 获取机器人的名字
auto robot_name = rpc_cli_->getRobotNames().front();
auto robot_interface = rpc_cli_->getRobotInterface(robot_name);
auto rebotState = robot_interface->getRobotState();
states.clear();
JointState state;
//获取关节位置和速度
for (int i = 0; i < 6; i++) {
state.velocity = rebotState->getJointSpeeds()[i];
state.position = rebotState->getJointPositions()[i];
states.push_back(state);
}
} catch (exception &e) {
CMVR_LOG(ERROR) << "[AuboRobot] getJointsState failed: " << e.what();
states.clear();
}
}
template<int DOF>
void AuboRobot<DOF>::torqueOff()
{
// 接口调用: 获取机器人的名字
auto robot_name = rpc_cli_->getRobotNames().front();
auto robot_interface = rpc_cli_->getRobotInterface(robot_name);
// 接口调用: 机械臂断电
robot_interface->getRobotManage()->poweroff();
// 等待机械臂进入断电模式
waitForRobotMode(robot_interface, RobotModeType::PowerOff);
CMVR_LOG(INFO) << "机械臂断电成功,当前模式:"
<< robot_interface->getRobotState()->getRobotModeType()
;
}
template<int DOF>
void AuboRobot<DOF>::moveJ(std::vector<JointPoint>& cmd, double vel, double acc)
{
try
{
//检查参数
if (cmd.size() != dof_)
{
CMVR_LOG(ERROR) << "[AuboRobot](moveJ)Wrong number of cmd";
return;
}
//调用aubo sdk接口实现moveJ
// 接口调用: 获取机器人的名字
auto robot_name = rpc_cli_->getRobotNames().front();
auto robot_interface = rpc_cli_->getRobotInterface(robot_name);
// 接口调用: 设置机械臂的速度比率
robot_interface->getMotionControl()->setSpeedFraction(0.3);
// 接口调用: 关节运动
// 关节角,单位: 弧度 (此处为6关节)
std::vector<double> joint_angle;
for (int i = 0; i < cmd.size(); i++)
{
joint_angle.emplace_back(cmd[i].rad);
}
robot_interface->getMotionControl()->moveJoint(
joint_angle, acc, vel, 0, 0);
}
catch (std::exception& e)
{
CMVR_LOG(ERROR) << "[AuboRobot] moveJ failed: " << e.what();
}
}
template<int DOF>
void AuboRobot<DOF>::calibrateZeroQ(const std::string& joint_name)
{
}
template<int DOF>
cmvr::common::Pose3d AuboRobot<DOF>::fk(const std::string &base_link, const std::string &ee_link)
{
cmvr::common::Pose3d pose;
return pose;
}
template<int DOF>
std::vector<double> AuboRobot<DOF>::ik(const std::string &base_link, const std::string &ee_link,cmvr::common::Pose3d pose)
{
std::vector<double> ik;
return ik;
}
template<int DOF>
int AuboRobot<DOF>::waitArrival(const RobotInterfacePtr& impl) {
const int max_retry_count = 5;
int cnt = 0;
// 接口调用: 获取当前的运动指令 ID
int exec_id = impl->getMotionControl()->getExecId();
// 等待机械臂开始运动
while (exec_id == -1) {
if (cnt++ > max_retry_count) {
return -1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
exec_id = impl->getMotionControl()->getExecId();
}
// 等待机械臂动作完成
while (impl->getMotionControl()->getExecId() != -1) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
return 0;
}
template <int DOF>
void AuboRobot<DOF>::moveL(math::Pose3d& pose, double vel, double acc)
{
// 接口调用: 获取机器人的名字
auto robot_name = rpc_cli_->getRobotNames().front();
auto robot_interface = rpc_cli_->getRobotInterface(robot_name);
// 接口调用: 设置机械臂的速度比率
robot_interface->getMotionControl()->setSpeedFraction(0.75);
// 接口调用: 设置工具中心点TCP相对于法兰盘中心的偏移
std::vector<double> tcp_offset(6, 0.0);
robot_interface->getRobotConfig()->setTcpOffset(tcp_offset);
// 接口调用: 直线运动到位置
std::vector<double> pose1;
pose1.emplace_back(pose.position.x);
pose1.emplace_back(pose.position.y);
pose1.emplace_back(pose.position.z);
pose1.emplace_back(pose.euler.rx);
pose1.emplace_back(pose.euler.ry);
pose1.emplace_back(pose.euler.rz);
robot_interface->getMotionControl()->moveLine(pose1, acc, vel, 0.025, 0);
// 阻塞
auto ret = waitArrival(robot_interface);
if (ret == 0) {
CMVR_LOG(INFO)<<"直线运动到位置成功!";
} else {
CMVR_LOG(INFO)<<"直线运动到位置失败!";
}
}
template class cmvr::device::AuboRobot<6>;

View File

@ -1,50 +0,0 @@
add_library(c701 SHARED
${CMAKE_CURRENT_SOURCE_DIR}/motor/motor_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/motor/protocol/ti5_motor_sdo_response.cpp
${CMAKE_CURRENT_SOURCE_DIR}/motor/protocol/ti5_motor_tpdo1.cpp
${CMAKE_CURRENT_SOURCE_DIR}/motor/protocol/ti5_motor_tpdo2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/motor/protocol/ti5_motor_rpdo1.cpp)
target_include_directories(c701 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_library(cmvr_es::robot::c701 ALIAS c701)
target_link_libraries(c701
PRIVATE
cmvr_es::device::canbus
protobuf
glog
)
# --------------------------------------------------------
# Unit test
# --------------------------------------------------------
include_directories(
${CMAKE_SOURCE_DIR}/third_party/gtest/1.17.0/include
)
link_directories(
${CMAKE_SOURCE_DIR}/third_party/gtest/1.17.0/lib
)
add_executable(motor_controller_test
${CMAKE_CURRENT_SOURCE_DIR}/motor/motor_controller_test.cpp
)
target_link_libraries(motor_controller_test
PRIVATE
cmvr_es::device::canbus
cmvr_es::robot::c701
c701
gtest
gtest_main
pthread
glog
cmvr_es::proto
)

View File

@ -1,406 +0,0 @@
#include "common/base/logging/logger.h"
//
// Created by lgv on 2025/7/17.
//
#include "robot/c701/motor/motor_controller.h"
#include "robot/c701/motor/protocol/ti5_motor_sdo_response.h"
#include "canbus/canopen/register.h"
#include "robot/c701/motor/protocol/ti5_motor_tpdo1.h"
#include "robot/c701/motor/protocol/ti5_motor_tpdo2.h"
using namespace cmvr::robot::c701;
using namespace cmvr::robot::motor;
using namespace cmvr::device;
using namespace cmvr::msgs;
using namespace cmvr::msgs;
ErrorCode MotorController::Init(cmvr::device::AbstractCanbus *can_client, bool enable_log) {
if (!can_client->init()) {
CMVR_LOG(ERROR) << "[MotorController] CAN client init failed";
return ErrorCode::CAN_CLIENT_ERROR_BASE;
}
if (!can_client->start()) {
CMVR_LOG(ERROR) << "[MotorController] CAN client start failed";
return ErrorCode::CAN_CLIENT_ERROR_BASE;
}
auto ret = ErrorCode::OK;
// 初始化 message_manager_
message_manager_ = std::make_shared<device::MessageManager<msgs::RobotDetail> >();
// NMT
message_manager_->AddSendProtocolData<NmtRequestProtocol<RobotDetail>, false>();
//sync
message_manager_->AddSendProtocolData<SyncProtocol<RobotDetail>, false>();
for (const auto node_id: node_ids_) {
//nmt
message_manager_->AddRecvProtocolData<NmtResponseProtocol<RobotDetail>, false>(node_id);
//sdo
message_manager_->AddSendProtocolData<SdoRequestProtocol<RobotDetail>, false>(node_id);
message_manager_->AddRecvProtocolData<Ti5MotorSdoResponse, false>(node_id);
//TPDO
message_manager_->AddRecvProtocolData<Ti5MotorTPDO1,false>(node_id);
message_manager_->AddRecvProtocolData<Ti5MotorTPDO2,false>(node_id);
//RPDO
message_manager_->AddSendProtocolData<Ti5MotorRPDO1,false>(node_id);
}
// 初始化 sender
can_sender_ = std::make_shared<device::CanSender<msgs::RobotDetail> >();
ret = can_sender_->Init(can_client, enable_log);
if (ret != ErrorCode::OK) {
CMVR_LOG(ERROR) << "Failed to init can sender.";
return ret;
}
// 初始化 receiver
can_receiver_ = std::make_shared<CanReceiver<msgs::RobotDetail> >();
ret = can_receiver_->Init(can_client, message_manager_.get(), enable_log);
if (ret != ErrorCode::OK) {
CMVR_LOG(ERROR) << "Failed to init can receiver.";
return ret;
}
// nmt
nmt_command_ = dynamic_cast<NmtRequestProtocol<RobotDetail> *>(
message_manager_->GetMutableProtocolDataById(NmtRequestProtocol<RobotDetail>::ID));
if (nmt_command_ == nullptr) {
CMVR_LOG(ERROR) << "Ti5 Motor NMT Request Protocol does not exist in the MessageManager!";
return ErrorCode::CANBUS_ERROR;
}
can_sender_->AddMessage(nmt_command_->ID, nmt_command_, true);
// sync
sync_command_ = dynamic_cast<SyncProtocol<RobotDetail> *>(
message_manager_->GetMutableProtocolDataById(SyncProtocol<RobotDetail>::ID));
if (sync_command_ == nullptr) {
CMVR_LOG(ERROR) << "Ti5 Motor NMT Request Protocol does not exist in the MessageManager!";
return ErrorCode::CANBUS_ERROR;
}
// can_sender_->AddMessage(sync_command_->ID, sync_command_, false);
// sdo
for (auto node_id: node_ids_) {
sdo_commands_[node_id] = dynamic_cast<SdoRequestProtocol<RobotDetail> *>(
message_manager_->GetMutableProtocolDataById(SdoRequestProtocol<RobotDetail>::ID(node_id)));
if (sdo_commands_[node_id] == nullptr) {
CMVR_LOG(ERROR) << "Ti5 Motor SDO Request Protocol does not exist in the MessageManager!";
return ErrorCode::CANBUS_ERROR;
}
can_sender_->AddMessage(sdo_commands_[node_id]->ID(), sdo_commands_[node_id], true);
}
// pdo1
for (auto node_id: node_ids_) {
rpdo1_commands_[node_id] = dynamic_cast<Ti5MotorRPDO1 *>(
message_manager_->GetMutableProtocolDataById(Ti5MotorRPDO1::ID(node_id)));
if (rpdo1_commands_[node_id] == nullptr) {
CMVR_LOG(ERROR) << "Ti5 Motor RPDO1 Protocol does not exist in the MessageManager!";
return ErrorCode::CANBUS_ERROR;
}
can_sender_->AddMessage(rpdo1_commands_[node_id]->ID(), rpdo1_commands_[node_id], true);
}
// need sleep to ensure all messages received
CMVR_LOG(INFO) << "Motor Controller is initialized.";
return ErrorCode::OK;
}
ErrorCode MotorController::Start() {
auto ret = ErrorCode::OK;
ret = can_sender_->Start();
if (ret != ErrorCode::OK) {
CMVR_LOG(ERROR) << "Failed to start can sender.";
return ret;
}
ret = can_receiver_->Start();
if (ret != ErrorCode::OK) {
CMVR_LOG(ERROR) << "Failed to start can receiver.";
return ret;
}
return ret;
}
void MotorController::SetMode(uint8_t node_id, msgs::RunMode mode) {
cur_mode_ = mode;
// 1 : 先设置模式
auto data = static_cast<uint32_t>(mode);
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,OPERATION_MODE_6060,SUB_INDEX_0,data);
// 2 : 状态机步进 —— Shutdown0x06
controlword_t cw = {};
cw.quick_stop = 1;
cw.enable_voltage = 1;
SeedSdoRequest(node_id, CS_WRITE_TWO_BYTES, CONTROL_WORD_6040, SUB_INDEX_0, cw.value,20);
// 3 : 状态机步进 —— Switch On & Enable Operation0x0F
cw.switch_on = 1;
cw.enable_operation = 1;
SeedSdoRequest(node_id, CS_WRITE_TWO_BYTES, CONTROL_WORD_6040, SUB_INDEX_0, cw.value,20);
switch (mode) {
case RUN_MODE_PROFILE_POSITION: {
// 4 : 设置目标位置(为当前位置)
auto cur_pos = GetRobotDetail()->motors().at(node_id).position();
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TARGET_POSITION_607A,SUB_INDEX_0,cur_pos);
// 5 : 触发位置运动new_set_point 翻转)
cw.new_set_point = 1;
cw.change_set_immediately = 1;
SeedSdoRequest(node_id, CS_WRITE_TWO_BYTES, CONTROL_WORD_6040, SUB_INDEX_0, cw.value);
// 6 : 清除 new_set_point必须不清除则无法再次触发新目标
cw.new_set_point = 0;
SeedSdoRequest(node_id, CS_WRITE_TWO_BYTES, CONTROL_WORD_6040, SUB_INDEX_0, cw.value);
break;
}
case RUN_MODE_CYCLIC_SYNC_POSITION: {
// 设置目标位置为当前位置
auto cur_pos = GetRobotDetail()->motors().at(node_id).position();
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TARGET_POSITION_607A,SUB_INDEX_0,cur_pos);
//3 : 使能 15
cw.enable_operation = 1;
cw.switch_on = 1;
SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,CONTROL_WORD_6040,SUB_INDEX_0,cw.value);
break;
}
default:
// TODO: Handle unspecified or unknown mode
break;
}
}
void MotorController::ConfigTPDO1(uint8_t node_id) {
//TDPO1 配置 状态字 和 控制字
// 1: 失能 pdo
uint32_t cob_id = TPDO1_BASE_ID_180 + node_id;
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO1_COMM_1800,SUB_INDEX_1,cob_id | (1U << 31));
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,TPDO1_MAP_1A00,SUB_INDEX_0,0);
// 2: 配置为异步
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,TPDO1_COMM_1800,SUB_INDEX_2,ASYNC_MANUFACTURER_SPECIFIC);
// 3配置约束时间 unit:0.1ms
SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,TPDO1_COMM_1800,SUB_INDEX_3,1);
// 4 : 配置周期发送时间 unit : ms 0 为 数据改变时发送
SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,TPDO1_COMM_1800,SUB_INDEX_5,1000);
// 5 :映射控制字
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO1_MAP_1A00,SUB_INDEX_1,CONTROL_WORD_6040 << 16 | SUB_INDEX_0 << 8 | 16);
//6 : 映射状态字
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO1_MAP_1A00,SUB_INDEX_2,STATUS_WORD_6041 << 16 | SUB_INDEX_0 << 8 | 16);
//7 : 映射模式
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO1_MAP_1A00,SUB_INDEX_3,MODE_DISPLAY_6061 << 16 | SUB_INDEX_0 << 8 | 8);
//8 映射错误码
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO1_MAP_1A00,SUB_INDEX_4,ERROR_CODE_603F << 16 | SUB_INDEX_0 << 8 | 16);
//9 写入该PDO映射对象总个数
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,TPDO1_MAP_1A00,SUB_INDEX_0,4);
//10 使能
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO1_COMM_1800,SUB_INDEX_1,cob_id | (0U << 31));
}
void MotorController::ConfigTPDO2(uint8_t node_id) {
// 1: 失能 pdo
uint32_t cob_id = TPDO2_BASE_ID_280 + node_id;
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO2_COMM_1801,SUB_INDEX_1,cob_id | (1U << 31));
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,TPDO2_MAP_1A01,SUB_INDEX_0,0);
// 2: 配置为异步
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,TPDO2_COMM_1801,SUB_INDEX_2,ASYNC_MANUFACTURER_SPECIFIC);
// 3配置约束时间 unit:0.1ms
SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,TPDO2_COMM_1801,SUB_INDEX_3,10000);
// 4 : 配置周期发送时间 unit : ms
SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,TPDO2_COMM_1801,SUB_INDEX_5,1000);
// 5 :映射当前位置
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO2_MAP_1A01,SUB_INDEX_1,ACTUAL_POSITION_6064 << 16 | SUB_INDEX_0 << 8 | 32);
//6 : 映射当前速度
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO2_MAP_1A01,SUB_INDEX_2,ACTUAL_SPEED_606C << 16 | SUB_INDEX_0 << 8 | 32);
//9 写入该PDO映射对象总个数
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,TPDO2_MAP_1A01,SUB_INDEX_0,2);
//10 使能
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,TPDO2_COMM_1801,SUB_INDEX_1,cob_id | (0U << 31));
}
void MotorController::ConfigRPDO1(uint8_t node_id,bool start) {
// 1: 失能 pdo
uint32_t cob_id = RPDO1_BASE_ID_200 + node_id;
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,RPDO1_COMM_1400,SUB_INDEX_1,cob_id | (1U << 31));
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,RPDO1_MAP_1600,SUB_INDEX_0,0);
// 2: 配置为
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,RPDO1_COMM_1400,SUB_INDEX_2,SYNC_EVENT_DRIVEN);
// // 3配置约束时间 unit:0.1ms
// SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,RPDO1_COMM_1400,SUB_INDEX_3,10);
//
// // 4 : 配置周期发送时间 unit : ms 0 为 数据改变时发送
// SeedSdoRequest(node_id,CS_WRITE_TWO_BYTES,RPDO1_COMM_1400,SUB_INDEX_5,0);
// 5 :映射位置
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,RPDO1_MAP_1600,SUB_INDEX_1,TARGET_POSITION_607A << 16 | SUB_INDEX_0 << 8 | 32);
//6 : 映射控制字
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,RPDO1_MAP_1600,SUB_INDEX_2,CONTROL_WORD_6040 << 16 | SUB_INDEX_0 << 8 | 16);
if (start) {
//7 写入该PDO映射对象总个数
SeedSdoRequest(node_id,CS_WRITE_ONE_BYTE,RPDO1_MAP_1600,SUB_INDEX_0,2);
//8 使能
SeedSdoRequest(node_id,CS_WRITE_FOUR_BYTES,RPDO1_COMM_1400,SUB_INDEX_1,cob_id | (0U << 31));
}
}
void MotorController::SetPPTargetPosBySdo(uint8_t node_id, int32_t pos) {
controlword_t cw = {};
cw.switch_on = 1;
cw.enable_voltage = 1;
cw.enable_operation = 1;
cw.quick_stop = 1;
cw.change_set_immediately = 1;
// 1. 设置目标位置
SeedSdoRequest(node_id, CS_WRITE_FOUR_BYTES, TARGET_POSITION_607A, SUB_INDEX_0, pos);
// 2. 设置触发位bit4 = 1
cw.new_set_point = 1;
SeedSdoRequest(node_id, CS_WRITE_TWO_BYTES, CONTROL_WORD_6040, SUB_INDEX_0, cw.value);
// 3. 清除触发位bit4 = 0准备下一次触发
cw.new_set_point = 0;
SeedSdoRequest(node_id, CS_WRITE_TWO_BYTES, CONTROL_WORD_6040, SUB_INDEX_0, cw.value);
}
void MotorController::SetPPTargetPosByPdo(uint8_t node_id, int32_t pos) {
// 触发目标位置运动
controlword_t cw;
cw.value = 0x0F;
cw.new_set_point = 1;
cw.change_set_immediately = 1;
rpdo1_commands_[node_id]->SetTargetPos(pos);
rpdo1_commands_[node_id]->SetCtrlWord(cw.value);
can_sender_->Update(rpdo1_commands_[node_id]->ID());
std::this_thread::sleep_for(std::chrono::milliseconds(10));
cw.new_set_point = 0;
rpdo1_commands_[node_id]->SetCtrlWord(cw.value);
can_sender_->Update(rpdo1_commands_[node_id]->ID());
}
void MotorController::SetTargetPosition(uint8_t node_id, double angle_rad) {
auto cmd = (angle_rad * RADTODEG) / 360.0 * GearRatio * 65536.0;
switch (cur_mode_) {
case RUN_MODE_CYCLIC_SYNC_POSITION:
SetCSPTargetPosByPdo(node_id,static_cast<int32_t>(cmd));
break;
case RUN_MODE_PROFILE_POSITION:
SetPPTargetPosByPdo(node_id,static_cast<int32_t>(cmd));
// SetPPTargetPosBySdo(node_id,static_cast<int32_t>(cmd));
break;
}
}
void MotorController::ConfigProfile(uint8_t node_id, uint32_t speed, uint32_t accel, uint32_t decel) {
SeedSdoRequest(node_id, CS_WRITE_FOUR_BYTES, PROFILE_SPEED_6081, SUB_INDEX_0, speed);
SeedSdoRequest(node_id, CS_WRITE_FOUR_BYTES, PROFILE_ACCELERATION_6083, SUB_INDEX_0, accel);
SeedSdoRequest(node_id, CS_WRITE_FOUR_BYTES, PROFILE_DECELERATION_6084, SUB_INDEX_0, decel);
}
void MotorController::SetCSPTargetPosByPdo(uint8_t node_id, int32_t pos) {
rpdo1_commands_[node_id]->SetTargetPos(pos);
rpdo1_commands_[node_id]->SetCtrlWord(0x0F);
can_sender_->Update(rpdo1_commands_[node_id]->ID());
}
void MotorController::SeedNmtRequest(uint8_t node_id, msgs::NmtCommand command, uint32_t delay_ms) {
nmt_command_->RequestService(node_id,command);
can_sender_->Update(nmt_command_->ID);
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
}
void MotorController::SeedSdoRequest(uint8_t node_id, msgs::CommandSpecifier cs, msgs::ObIndex index, msgs::ObSubIndex sub_index, uint32_t data, uint32_t delay_ms) {
sdo_commands_[node_id]->SetFrameData(cs,index,sub_index,data);
can_sender_->Update(sdo_commands_[node_id]->ID());
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
}

View File

@ -1,101 +0,0 @@
//
// Created by lgv on 2025/7/17.
//
#pragma once
#include <utility>
#include "canbus/can_client/socket/socket_can_client_raw.h"
#include "can_comm/can_sender.h"
#include "canbus/can_comm/can_receiver.h"
#include "canbus/can_comm/can_sender.h"
#include "canbus/can_comm/message_manager.h"
#include "cmvr/msgs/error_code.pb.h"
#include "cmvr/msgs/robot_detail.pb.h"
#include "canbus/canopen/sdo_request_protocol.h"
#include "robot/c701/motor/protocol/ti5_motor_rpdo1.h"
#include "canbus/canopen/sync_protocol.h"
#include "canbus/canopen/nmt_response_protocol.h"
#include "canopen/nmt_request_protocol.h"
namespace cmvr {
namespace robot {
namespace c701 {
class MotorController {
public:
MotorController(std::vector<uint8_t> node_ids):node_ids_(node_ids) {}
MotorController(const MotorController &) = delete;
MotorController &operator=(const MotorController &) = delete;
/**
* @brief Destructor.
*/
virtual ~MotorController() = default;
msgs::ErrorCode Init(cmvr::device::AbstractCanbus *can_client,bool enable_log);
msgs::ErrorCode Start();
std::unique_ptr<msgs::RobotDetail> GetRobotDetail() {
auto data_ptr = std::make_unique<msgs::RobotDetail>();
message_manager_->GetSensorData(data_ptr.get());
return data_ptr;
}
void SeedNmtRequest(uint8_t node_id,msgs::NmtCommand command,uint32_t delay_ms = 10);
void SeedSdoRequest(uint8_t node_id,msgs::CommandSpecifier cs, msgs::ObIndex index,msgs::ObSubIndex sub_index, uint32_t data,uint32_t delay_ms = 10);
// 设置软件限位(上限和下限)
void SetPositionLimits(uint8_t node_id, int32_t lower_limit, int32_t upper_limit);
void ConfigProfile(uint8_t node_id, uint32_t speed, uint32_t accel, uint32_t decel);
void ConfigTPDO1(uint8_t node_id);
void ConfigTPDO2(uint8_t node_id);
// 目标位置 607A + 控制字 6040
void ConfigRPDO1(uint8_t node_id,bool start);
void SetTargetPosition(uint8_t node_id,double angle_rad);
void SetMode(uint8_t node_id,msgs::RunMode mode);
private:
static constexpr double GearRatio = 101.0; // 电机减速比
static constexpr double RADTODEG = 180.0 / 3.1415926;
std::vector<uint8_t> node_ids_{}; // 要控制的电机 id
// std::map<uint8_t,motor::Ti5MotorSdoRequestProtocol*> control_commands_{};
// nmt
device::NmtRequestProtocol<msgs::RobotDetail>* nmt_command_{nullptr};
//sync
device::SyncProtocol<msgs::RobotDetail>* sync_command_{nullptr};
// sdo
std::map<uint8_t,device::SdoRequestProtocol<msgs::RobotDetail>*> sdo_commands_{};
// rpdo1
std::map<uint8_t,robot::motor::Ti5MotorRPDO1*> rpdo1_commands_{};
std::shared_ptr<device::CanReceiver<msgs::RobotDetail>> can_receiver_{nullptr};
std::shared_ptr<device::CanSender<msgs::RobotDetail>> can_sender_{nullptr};
std::shared_ptr<device::MessageManager<msgs::RobotDetail>> message_manager_{nullptr};
msgs::RunMode cur_mode_{msgs::RunMode::RUN_MODE_UNSPECIFIED};
void SetPPTargetPosBySdo(uint8_t node_id,int32_t pos);
void SetPPTargetPosByPdo(uint8_t node_id,int32_t pos);
void SetCSPTargetPosByPdo(uint8_t node_id,int32_t pos);
};
}
}
}

View File

@ -1,87 +0,0 @@
//
// Created by lgv on 2025/7/18.
//
#include <gtest/gtest.h>
#include "robot/c701/motor/motor_controller.h"
#include "canbus/can_comm/can_sender.h"
#include "canbus/can_comm/message_manager.h"
#include "cmvr/msgs/robot_detail.pb.h"
#include "canbus/canopen/sdo_request_protocol.h"
#include "canbus/can_client/socket/socket_can_client_raw.h"
// #include "cmvr/msgs/ti5_motor.pb.h"
using cmvr::msgs::RobotDetail;
using cmvr::msgs::ErrorCode;
using cmvr::device::CanSender;
using cmvr::device::MessageManager;
using cmvr::robot::c701::MotorController;
using cmvr::device::SdoRequestProtocol;
using cmvr::device::SocketCanClientRaw;
using namespace cmvr::msgs;
TEST(MotorControllerTest, MotorControllerCmdTest) {
std::vector<uint8_t> node_ids = {0x03};
MotorController controller(node_ids);
cmvr::config::SocketCanConfig can_config;
can_config.set_channel_id(0);
SocketCanClientRaw can_client(can_config);
can_client.start();
auto result = controller.Init(&can_client, false);
controller.Start();
for (uint8_t node_id: node_ids) {
controller.ConfigTPDO1(node_id);
controller.ConfigTPDO2(node_id);
controller.ConfigRPDO1(node_id,true);
controller.ConfigProfile(node_id,1000,1000,1000);
controller.SeedNmtRequest(node_id,NMT_ENTER_PRE_OPERATIONAL);
controller.SeedNmtRequest(node_id,NMT_START_REMOTE_NODE);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
}
for (uint8_t node_id: node_ids) {
controller.SetMode(node_id,RUN_MODE_CYCLIC_SYNC_POSITION);
controller.SetTargetPosition(node_id,3.14);
// controller.SyncStart();
// controller.SetTarget(node_id,3.14,1000);
}
while (true) {
// controller.SetNmtRequest(test_id,NMT_RESET_NODE);
// controller.SeedSdoRequest(test_id,CS_READ_REQUEST,ACTUAL_POSITION_6064,SUB_INDEX_0,0);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
EXPECT_EQ(result, ErrorCode::CANBUS_ERROR);
}

View File

@ -1,27 +0,0 @@
//
// Created by lgv on 2025/7/28.
//
#include "robot/c701/motor/protocol/ti5_motor_rpdo1.h"
#include "common/base/logging/logger.h"
using namespace cmvr::msgs;
using namespace cmvr::device;
using namespace cmvr::robot::motor;
void Ti5MotorRPDO1::UpdateData(uint8_t *data) {
std::lock_guard<std::mutex> lock(mutex_);
data[0] = target_pos_ & 0xFF;
data[1] = target_pos_ >> 8 & 0xFF;
data[2] = target_pos_ >> 16 & 0xFF;
data[3] = target_pos_ >> 24 & 0xFF;
data[4] = ctrl_word_ & 0xFF;
data[5] = ctrl_word_ >> 8 & 0xFF;
// data[4] = target_vel_ & 0xFF;
// data[5] = target_vel_ >> 8 & 0xFF;
// data[6] = target_vel_ >> 16 & 0xFF;
// data[7] = target_vel_ >> 24 & 0xFF;
}

View File

@ -1,60 +0,0 @@
//
// Created by lgv on 2025/7/28.
//
#pragma once
#include "canbus/can_comm/protocol_data.h"
#include "cmvr/msgs/robot_detail.pb.h"
#include <mutex>
namespace cmvr {
namespace robot {
namespace motor {
class Ti5MotorRPDO1 : public device::ProtocolData<cmvr::msgs::RobotDetail> {
public:
static constexpr uint32_t BASE_ID = msgs::RPDO1_BASE_ID_200;
static uint32_t ID(uint8_t node_id) {
return BASE_ID + node_id;
}
uint32_t ID() const{
return BASE_ID + node_id_;
}
explicit Ti5MotorRPDO1(uint8_t node_id) : node_id_(node_id) {}
void UpdateData(uint8_t *data) override;
int32_t GetLength() const override {
return 0x06;
}
uint32_t GetPeriod() const override {
return 1000 * 1; // 5 ms
}
void SetTargetPos(int32_t position) {
std::lock_guard<std::mutex> lock(mutex_);
target_pos_ = position;
}
void SetTargetVel(int32_t velocity) {
std::lock_guard<std::mutex> lock(mutex_);
target_vel_ = velocity;
}
void SetCtrlWord(uint16_t ctrl_word) {
std::lock_guard<std::mutex> lock(mutex_);
ctrl_word_ = ctrl_word;
}
private:
mutable std::mutex mutex_;
uint8_t node_id_{0};
int32_t target_pos_{0};
int32_t target_vel_{0};
uint16_t ctrl_word_{0};
};
}
}
}

View File

@ -1,40 +0,0 @@
#include "common/base/logging/logger.h"
//
// Created by lgv on 2025/7/24.
//
#include "ti5_motor_sdo_response.h"
using namespace cmvr::robot::motor;
using namespace cmvr::msgs;
void Ti5MotorSdoResponse::ParseSdoData(const msgs::SdoFrame &sdo_response,
cmvr::msgs::RobotDetail *sensor_data) const {
// 通过 node_id 获取对应的电机状态Ti5_MotorStatus
auto &motors_map = *sensor_data->mutable_motors();
auto *motor_status = &motors_map[this->node_id_];
// 先把基础的 sdo_response 拷贝进去
motor_status->mutable_sdo_response()->CopyFrom(sdo_response);
switch (sdo_response.index()) {
case msgs::CONTROL_WORD_6040:
motor_status->set_ctrl_word(sdo_response.data());
break;
case msgs::STATUS_WORD_6041:
motor_status->set_status_word(sdo_response.data());
break;
case msgs::ACTUAL_POSITION_6064:
motor_status->set_position(static_cast<int32_t>(sdo_response.data()));
CMVR_LOG(INFO) << "pos = " << motor_status->position();
break;
}
// CMVR_LOG(INFO) << "Parsed motor SDO for node " << int(this->node_id_)
// << ": command=" << int(sdo_response.cs())
// << ", index=" << sdo_response.index()
// << ", subindex=" << int(sdo_response.sub_index())
// << ", data=" << sdo_response.data();
}

View File

@ -1,26 +0,0 @@
//
// Created by lgv on 2025/7/24.
//
#pragma once
#include "canbus/canopen/sdo_response_protocol.h"
#include "cmvr/msgs/robot_detail.pb.h"
namespace cmvr {
namespace robot {
namespace motor {
class Ti5MotorSdoResponse : public device::SdoResponseProtocol<cmvr::msgs::RobotDetail> {
public:
explicit Ti5MotorSdoResponse(uint8_t node_id)
: SdoResponseProtocol<cmvr::msgs::RobotDetail>(node_id) {
}
protected:
void ParseSdoData(const msgs::SdoFrame &sdo_response,
cmvr::msgs::RobotDetail *sensor_data) const override;
};
}
}
}

View File

@ -1,36 +0,0 @@
//
// Created by lgv on 2025/7/25.
//
#include "ti5_motor_tpdo1.h"
#include "common/base/logging/logger.h"
#include "canbus/canopen/register.h"
using namespace cmvr::msgs;
using namespace cmvr::device;
using namespace cmvr::robot::motor;
void Ti5MotorTPDO1::Parse(const std::uint8_t *bytes, int32_t length, msgs::RobotDetail *sensor_data) const {
if (length < 7) {
CMVR_LOG(WARNING) << "Motor TPDO1 Response Protocol: data length too short: " << length;
return;
}
auto &motors_map = *sensor_data->mutable_motors();
auto *motor_status = &motors_map[this->node_id_];
motor_status->set_ctrl_word(bytes[1] << 8 | bytes[0]);
motor_status->set_status_word(bytes[3] << 8 | bytes[2]);
motor_status->set_run_mode(static_cast<RunMode>(bytes[4]));
motor_status->set_error_state(bytes[6] << 8 | bytes[5]);
statusword_t st{};
st.value = motor_status->status_word();
if (st.op_mode_specific > 0) {
CMVR_LOG(INFO) << st.op_mode_specific ;
}
}

View File

@ -1,32 +0,0 @@
//
// Created by lgv on 2025/7/25.
//
#pragma once
#include "canbus/can_comm/protocol_data.h"
#include "cmvr/msgs/robot_detail.pb.h"
namespace cmvr {
namespace robot {
namespace motor {
class Ti5MotorTPDO1 : public device::ProtocolData<cmvr::msgs::RobotDetail> {
public:
static constexpr uint32_t BASE_ID = msgs::TPDO1_BASE_ID_180;
static uint32_t ID(uint8_t node_id) {
return BASE_ID + node_id;
}
uint32_t ID() const{
return BASE_ID + node_id_;
}
explicit Ti5MotorTPDO1(uint8_t node_id) : node_id_(node_id) {}
void Parse(const std::uint8_t *bytes, int32_t length, msgs::RobotDetail *sensor_data) const override;
private:
uint8_t node_id_{0};
};
}
}
}

View File

@ -1,26 +0,0 @@
//
// Created by lgv on 2025/7/25.
//
#include "ti5_motor_tpdo2.h"
#include "common/base/logging/logger.h"
using namespace cmvr::msgs;
using namespace cmvr::device;
using namespace cmvr::robot::motor;
void Ti5MotorTPDO2::Parse(const std::uint8_t *bytes, int32_t length, msgs::RobotDetail *sensor_data) const {
if (length < 8) {
CMVR_LOG(WARNING) << "Motor TPDO1 Response Protocol: data length too short: " << length;
return;
}
auto &motors_map = *sensor_data->mutable_motors();
auto *motor_status = &motors_map[this->node_id_];
motor_status->set_position(bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0]);
motor_status->set_speed(bytes[7] << 24 | bytes[6] << 16 | bytes[5] << 8 | bytes[4]);
CMVR_LOG(INFO) << "Motor ID " << this->node_id_ << "pos = " << motor_status->position() << " speed = " << motor_status->speed();
}

View File

@ -1,32 +0,0 @@
//
// Created by lgv on 2025/7/25.
//
#pragma once
#include "canbus/can_comm/protocol_data.h"
#include "cmvr/msgs/robot_detail.pb.h"
namespace cmvr {
namespace robot {
namespace motor {
class Ti5MotorTPDO2 : public device::ProtocolData<cmvr::msgs::RobotDetail> {
public:
static constexpr uint32_t BASE_ID = msgs::TPDO2_BASE_ID_280;
static uint32_t ID(uint8_t node_id) {
return BASE_ID + node_id;
}
uint32_t ID() const{
return BASE_ID + node_id_;
}
explicit Ti5MotorTPDO2(uint8_t node_id) : node_id_(node_id) {}
void Parse(const std::uint8_t *bytes, int32_t length, msgs::RobotDetail *sensor_data) const override;
private:
uint8_t node_id_{0};
};
}
}
}