initial release

This commit is contained in:
Agnel Wang 2024-04-02 19:50:19 +08:00
commit b52c204997
13 changed files with 951 additions and 0 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"ros.distro": "noetic"
}

5
CHANGELOG.md Normal file
View File

@ -0,0 +1,5 @@
# Changelog
## 1.0.1
+ Initial release.

30
CMakeLists.txt Normal file
View File

@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.10)
project(h1_inspire_service VERSION 0.1.0)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
if (CMAKE_BUILD_TYPE MATCHES "Debug")
message("Debug mode")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -g -O0 -fPIC")
else()
message("Release mode")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3 -DNDEBUG -fPIC")
endif()
find_package(Boost COMPONENTS program_options REQUIRED)
include_directories(
/usr/local/include/ddscxx
/usr/local/include/iceoryx/v2.0.2
include
)
link_libraries(unitree_sdk2 ddsc ddscxx rt pthread)
link_libraries(unitree_idl libboost_program_options.a)
add_executable(inspire_hand inspire_ctrl.cpp)
# example
add_executable(h1_hand_example example/inspire_hand.cpp)

BIN
bin/x86_64/h1_hand_example Executable file

Binary file not shown.

BIN
bin/x86_64/inspire_hand Executable file

Binary file not shown.

80
doc/Inspire-Hand.md Normal file
View File

@ -0,0 +1,80 @@
## Dexterous Hand
Unitree H1 can be equipped with [Inspire Robotics](https://inspire-robots.com/product/frwz/)'s dexterous hand, which has **six degrees of freedom** and 12 motion joints to mimic the human hand for complex movements.
![RH56](./img/RH56.png)
##
The user can control the hand by publish the **"unitree_go::msg::dds::MotorCmds_"** message to the topic **"rt/inspire/cmd"**, and get the hand state by subscribe the **"unitree_go::msg::dds::MotorStates_"** message from the topic **"rt/inspire/state"**.
```mermaid
graph LR
A(user) --rt/inspire/cmd--> B(H1)
B --rt/inspire/state--> A
```
+ IDL Message Type
Motor data in array format, containing 12 motor data for both hands.
!!!note
Currently the dexterous hand only supports joint control, i.e. only the parameter q makes sense in the idl format. The others are reserved.
!!!
```bash
# namespace unitree_go::msg::dds_
# unitree_go::msg::dds_::MotorCmds_
struct MotorCmds_
{
sequence<unitree_go::msg::dds_::MotorCmd_> cmds;
};
# unitree_go::msg::dds_::MotorStates_
struct MotorCmds_
{
sequence<unitree_go::msg::dds_::MotorState> states;
};
```
+ Joint Motor Sequence
<div style="text-align: center;">
<table border="1">
<tr>
<td>Id</td>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
</tr>
<tr>
<td rowspan="2">Joint</td>
<td colspan="6">Right Hand</td>
<td colspan="6">Left Hand</td>
</tr>
<tr>
<td>pinky</td>
<td>ring</td>
<td>middle</td>
<td>index</td>
<td>thumb-bend</td>
<td>thumb-rotation</td>
<td>pinky</td>
<td>ring</td>
<td>middle</td>
<td>index</td>
<td>thumb-bend</td>
<td>thumb-rotation</td>
</tr>
</table>
</div>

BIN
doc/img/RH56.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

80
doc/灵巧手开发.md Normal file
View File

@ -0,0 +1,80 @@
## 灵巧手介绍
H1可搭载[Inspire Robotics](https://inspire-robots.com/product/frwz/)的仿人五指灵巧手该灵巧手具有6个自由度和12个运动关节可以模拟人手实现复杂动作。
![RH56](./img/RH56.png)
## 接口说明
**示例程序** [h1_example_hand.cpp]()
用户向 **"rt/inspire/cmd"** 话题发送 **"unitree_go::msg::dds::MotorCmds_"** 消息控制灵巧手。
**"rt/inspire/state"** 话题接受 **"unitree_go::msg::dds::MotorStates_"** 消息获取灵巧手状态。
```mermaid
graph LR
A(user) --rt/inspire/cmd--> B(H1)
B --rt/inspire/state--> A
```
+ IDL数据格式
采用数组格式的电机数据内部包含双手12个电机数据。
```note
当前灵巧手只支持关节控制即在idl格式中只有参数q有意义。其他保留。
```
```bash
# namespace unitree_go::msg::dds_
# unitree_go::msg::dds_::MotorCmds_
struct MotorCmds_
{
sequence<unitree_go::msg::dds_::MotorCmd_> cmds;
};
# unitree_go::msg::dds_::MotorStates_
struct MotorCmds_
{
sequence<unitree_go::msg::dds_::MotorState> states;
};
```
+ IDL中的关节顺序
<div style="text-align: center;">
<table border="1">
<tr>
<td>Id</td>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
</tr>
<tr>
<td rowspan="2">Joint</td>
<td colspan="6">Right Hand</td>
<td colspan="6">Left Hand</td>
</tr>
<tr>
<td>pinky</td>
<td>ring</td>
<td>middle</td>
<td>index</td>
<td>thumb-bend</td>
<td>thumb-rotation</td>
<td>pinky</td>
<td>ring</td>
<td>middle</td>
<td>index</td>
<td>thumb-bend</td>
<td>thumb-rotation</td>
</tr>
</table>
</div>

163
example/inspire_hand.cpp Normal file
View File

@ -0,0 +1,163 @@
/**
* @file inspire_hand.cpp
* @brief This is an example of how to control the Unitree H1 (Inspire) Hand using unitree_sdk2.
*/
// Inspire Hand Topic IDL Types
#include <unitree/idl/go2/MotorCmds_.hpp>
#include <unitree/idl/go2/MotorStates_.hpp>
// DDS Channel
#include <unitree/robot/channel/channel_publisher.hpp>
#include <unitree/robot/channel/channel_subscriber.hpp>
#include <unitree/common/thread/thread.hpp>
#include <eigen3/Eigen/Dense>
#include <unordered_map>
/**
* @brief Unitree H1 Hand Controller
* The user can subscribe to "rt/inspire/state" to get the current state of the hand and publish to "rt/inspire/cmd" to control the hand.
*
* IDL Types
* user ---(unitree_go::msg::dds_::MotorCmds_)---> "rt/inspire/cmd"
* user <--(unitree_go::msg::dds_::MotorStates_)-- "rt/inspire/state"
*
* @attention Currently the hand only supports position control, which means only the `q` field in idl is used.
*/
class H1HandController
{
public:
H1HandController()
{
this->InitDDS_();
}
/**
* @brief Control the hand to a specific label
*/
void ctrl(std::string label)
{
if(labels.find(label) != labels.end())
{
this->ctrl(labels[label], labels[label]);
}
else
{
std::cout << "Invalid label: " << label << std::endl;
}
}
/**
* @brief Move the fingers to the specified angles
*
* @note The angles should be in the range [0, 1]
* 0: close 1: open
*/
void ctrl(
const Eigen::Matrix<float, 6, 1>& right_angles,
const Eigen::Matrix<float, 6, 1>& left_angles)
{
for(size_t i(0); i<6; i++)
{
cmd.cmds()[i].q() = right_angles(i);
cmd.cmds()[i+6].q() = left_angles(i);
}
handcmd->Write(cmd);
}
/**
* @brief Get the right hand angles
*
* Joint order: [pinky, ring, middle, index, thumb_bend, thumb_rotation]
*/
Eigen::Matrix<float, 6, 1> getRightQ()
{
std::lock_guard<std::mutex> lock(mtx);
Eigen::Matrix<float, 6, 1> q;
for(size_t i(0); i<6; i++)
{
q(i) = state.states()[i].q();
}
return q;
}
/**
* @brief Get the left hand angles
*
* Joint order: [pinky, ring, middle, index, thumb_bend, thumb_rotation]
*/
Eigen::Matrix<float, 6, 1> getLeftQ()
{
std::lock_guard<std::mutex> lock(mtx);
Eigen::Matrix<float, 6, 1> q;
for(size_t i(0); i<6; i++)
{
q(i) = state.states()[i+6].q();
}
return q;
}
unitree_go::msg::dds_::MotorCmds_ cmd;
unitree_go::msg::dds_::MotorStates_ state;
private:
void InitDDS_()
{
handcmd = std::make_shared<unitree::robot::ChannelPublisher<unitree_go::msg::dds_::MotorCmds_>>(
"rt/inspire/cmd");
handcmd->InitChannel();
cmd.cmds().resize(12);
handstate = std::make_shared<unitree::robot::ChannelSubscriber<unitree_go::msg::dds_::MotorStates_>>(
"rt/inspire/state");
handstate->InitChannel([this](const void *message){
std::lock_guard<std::mutex> lock(mtx);
state = *(unitree_go::msg::dds_::MotorStates_*)message;
});
state.states().resize(12);
}
// DDS parameters
std::mutex mtx;
unitree::robot::ChannelPublisherPtr<unitree_go::msg::dds_::MotorCmds_> handcmd;
unitree::robot::ChannelSubscriberPtr<unitree_go::msg::dds_::MotorStates_> handstate;
// Saved labels
std::unordered_map<std::string, Eigen::Matrix<float, 6, 1>> labels = {
{"open", Eigen::Matrix<float, 6, 1>::Ones()},
{"close", Eigen::Matrix<float, 6, 1>::Zero()},
{"half", Eigen::Matrix<float, 6, 1>::Constant(0.5)},
};
};
/**
* Main Function
*/
int main(int argc, char** argv)
{
std::cout << " --- Unitree Robotics --- \n";
std::cout << " H1 Hand Example \n\n";
// Target label
std::string label = "close"; // You change this value to other labels
// Initialize the DDS Channel
std::string networkInterface = argc > 1 ? argv[1] : "";
unitree::robot::ChannelFactory::Instance()->Init(0, networkInterface);
// Create the H1 Hand Controller
auto h1hand = std::make_shared<H1HandController>();
int cnt = 0;
while (true)
{
usleep(100000);
if(cnt++ % 10 == 0)
label = label == "close" ? "open" : "close";
h1hand->ctrl(label); // Control the hand
std::cout << "-- Hand State --\n";
std::cout << " R: " << h1hand->getRightQ().transpose() << std::endl;
std::cout << " L: " << h1hand->getLeftQ().transpose() << std::endl;
std::cout << "\033[3A"; // Move cursor up 3 lines
}
return 0;
}

154
include/SerialPort.h Normal file
View File

@ -0,0 +1,154 @@
#ifndef SERIAL_PORT_H
#define SERIAL_PORT_H
#include <termios.h>
#include <sys/select.h>
#include <string>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <unistd.h>
#include <iostream>
#include <memory>
#include <chrono>
#include <queue>
void print_data(const uint8_t* data, uint8_t len)
{
for (int i = 0; i < len; i++)
{
printf("%02x ", data[i]);
}
printf("\n");
}
class SerialPort
{
public:
using SharedPtr = std::shared_ptr<SerialPort>;
SerialPort(std::string port, speed_t baudrate, int timeout_ms = 2)
{
set_timeout(timeout_ms);
Init(port, baudrate);
}
~SerialPort()
{
close(fd_);
}
ssize_t send(const uint8_t* data, size_t len)
{
// tcflush(fd_, TCIFLUSH);
ssize_t ret = ::write(fd_, data, len);
// tcdrain(fd_);
return ret;
}
ssize_t recv(uint8_t* data, size_t len)
{
FD_ZERO(&rSet_);
FD_SET(fd_, &rSet_);
ssize_t recv_len = 0;
switch (select(fd_ + 1, &rSet_, NULL, NULL, &timeout_))
{
case -1: // error
// std::cout << "communication error" << std::endl;
break;
case 0: // timeout
// std::cout << "timeout" << std::endl;
break;
default:
recv_len = ::read(fd_, data, len);
break;
}
return recv_len;
}
void recv(uint8_t* data, uint8_t head, ssize_t len)
{
// 存入队列
ssize_t recv_len = this->recv(recv_buf.data(), len);
for (int i = 0; i < recv_len; i++)
{
recv_queue.push(recv_buf[i]);
}
// 查找帧头
while (recv_queue.size() >= len)
{
if(recv_queue.front() != head)
{
recv_queue.pop();
continue;
}
break;
}
if(recv_queue.size() < len) return;
// 读取数据
for(int i = 0; i < len; i++)
{
data[i] = recv_queue.front();
recv_queue.pop();
}
}
void set_timeout(int timeout_ms)
{
timeout_.tv_sec = timeout_ms / 1000;
timeout_.tv_usec = (timeout_ms % 1000) * 1000;
}
private:
void Init(std::string port, speed_t baudrate)
{
int ret;
// Open serial port
fd_ = open(port.c_str(), O_RDWR | O_NOCTTY);
if (fd_ < 0)
{
printf("Open serial port %s failed\n", port.c_str());
exit(-1);
}
// Set attributes
struct termios option;
memset(&option, 0, sizeof(option));
ret = tcgetattr(fd_, &option);
option.c_oflag = 0;
option.c_lflag = 0;
option.c_iflag = 0;
cfsetispeed(&option, baudrate);
cfsetospeed(&option, baudrate);
option.c_cflag &= ~CSIZE;
option.c_cflag |= CS8; // 8
option.c_cflag &= ~PARENB; // no parity
option.c_iflag &= ~INPCK; // no parity
option.c_cflag &= ~CSTOPB; // 1 stop bit
option.c_cc[VTIME] = 0;
option.c_cc[VMIN] = 0;
option.c_lflag |= CBAUDEX;
ret = tcflush(fd_, TCIFLUSH);
ret = tcsetattr(fd_, TCSANOW, &option);
}
int fd_;
fd_set rSet_;
timeval timeout_;
std::queue<uint8_t> recv_queue;
std::array<uint8_t, 1024> recv_buf;
};
#endif // SERIAL_PORT_H

262
include/inspire.h Normal file
View File

@ -0,0 +1,262 @@
#ifndef INSPIRE_H
#define INSPIRE_H
#include <eigen3/Eigen/Dense>
#include <vector>
#include "SerialPort.h"
namespace inspire
{
class InspireHand
{
public:
InspireHand(SerialPort::SharedPtr serial = nullptr, id_t id = 0)
: serial_(serial), id(id)
{
if(!serial)
serial_ = std::make_shared<SerialPort>("/dev/ttyUSB0", B115200);
}
void ChangeID(uint8_t before, uint8_t now)
{
std::vector<uint8_t> cmd = {0xEB, 0x90, before, 0x04, 0x12, 0xE8, 0x03, now, 0x00};
cmd.back() = CheckSum(cmd.data(), cmd.size());
serial_->send(cmd.data(), cmd.size());
usleep(5000);
serial_->recv(recvBuff, 9);
}
/**
* @brief
* -1:
* [0, 1] 0:
*
*/
int16_t SetPosition(const Eigen::Matrix<double, 6, 1> & q)
{
// 将q限制在0到1
Eigen::Matrix<int16_t, 6, 1> q_int16 = (q * 1000).cast<int16_t>().cwiseMax(0).cwiseMin(1000);
uint8_t cmd[20];
cmd[0] = 0xEB; // head
cmd[1] = 0x90;
cmd[2] = id; // ID
cmd[3] = 0x0F; // data length
cmd[4] = 0x12; // write
cmd[5] = 0xCE; // address
cmd[6] = 0x05;
cmd[7] = q_int16(0) & 0xFF;
cmd[8] = (q_int16(0) >> 8) & 0xFF;
cmd[9] = q_int16(1) & 0xFF;
cmd[10] = (q_int16(1) >> 8) & 0xFF;
cmd[11] = q_int16(2) & 0xFF;
cmd[12] = (q_int16(2) >> 8) & 0xFF;
cmd[13] = q_int16(3) & 0xFF;
cmd[14] = (q_int16(3) >> 8) & 0xFF;
cmd[15] = q_int16(4) & 0xFF;
cmd[16] = (q_int16(4) >> 8) & 0xFF;
cmd[17] = q_int16(5) & 0xFF;
cmd[18] = (q_int16(5) >> 8) & 0xFF;
cmd[19] = CheckSum(cmd, 20);
serial_->send(cmd, 20);
usleep(5000);
serial_->recv(recvBuff, 9);
return 0;
}
/**
* @brief
*
*
*/
int16_t GetPosition(Eigen::Matrix<double, 6, 1> & q)
{
std::vector<uint8_t> cmd = {0xEB, 0x90, id, 0x04, 0x11, 0x0A, 0x06, 0x0C, 0x00};
cmd.back() = CheckSum(cmd.data(), cmd.size());
serial_->send(cmd.data(), cmd.size());
usleep(5000);
size_t len = serial_->recv(recvBuff, 20);
if(len != 20) return 1;
if(recvBuff[19] != CheckSum(recvBuff, 20)) return 2;
q(0) = (recvBuff[7] | (recvBuff[8] << 8)) / 1000.;
q(1) = (recvBuff[9] | (recvBuff[10] << 8)) / 1000.;
q(2) = (recvBuff[11] | (recvBuff[12] << 8)) / 1000.;
q(3) = (recvBuff[13] | (recvBuff[14] << 8)) / 1000.;
q(4) = (recvBuff[15] | (recvBuff[16] << 8)) / 1000.;
q(5) = (recvBuff[17] | (recvBuff[18] << 8)) / 1000.;
return 0;
}
/**
* @brief
*
*
*/
void SetVelocity(int16_t v0, int16_t v1, int16_t v2, int16_t v3, int16_t v4, int16_t v5)
{
uint8_t cmd[20];
cmd[0] = 0xEB; // head
cmd[1] = 0x90;
cmd[2] = id; // ID
cmd[3] = 0x0F; // data length
cmd[4] = 0x12; // write
cmd[5] = 0xF2; // address
cmd[6] = 0x05;
cmd[7] = v0 & 0xFF;
cmd[8] = (v0 >> 8) & 0xFF;
cmd[9] = v1 & 0xFF;
cmd[10] = (v1 >> 8) & 0xFF;
cmd[11] = v2 & 0xFF;
cmd[12] = (v2 >> 8) & 0xFF;
cmd[13] = v3 & 0xFF;
cmd[14] = (v3 >> 8) & 0xFF;
cmd[15] = v4 & 0xFF;
cmd[16] = (v4 >> 8) & 0xFF;
cmd[17] = v5 & 0xFF;
cmd[18] = (v5 >> 8) & 0xFF;
cmd[19] = CheckSum(cmd, 20);
serial_->send(cmd, 20);
usleep(5000);
serial_->recv(recvBuff, 9);
}
/**
* @brief
*
* [0, 1000] Unit: g
*/
void SetForce(uint16_t f0, uint16_t f1, uint16_t f2, uint16_t f3, uint16_t f4, uint16_t f5)
{
uint8_t cmd[20];
cmd[0] = 0xEB; // head
cmd[1] = 0x90;
cmd[2] = id; // ID
cmd[3] = 0x0F; // data length
cmd[4] = 0x12; // write
cmd[5] = 0xDA; // address
cmd[6] = 0x05;
cmd[7] = f0 & 0xFF;
cmd[8] = (f0 >> 8) & 0xFF;
cmd[9] = f1 & 0xFF;
cmd[10] = (f1 >> 8) & 0xFF;
cmd[11] = f2 & 0xFF;
cmd[12] = (f2 >> 8) & 0xFF;
cmd[13] = f3 & 0xFF;
cmd[14] = (f3 >> 8) & 0xFF;
cmd[15] = f4 & 0xFF;
cmd[16] = (f4 >> 8) & 0xFF;
cmd[17] = f5 & 0xFF;
cmd[18] = (f5 >> 8) & 0xFF;
cmd[19] = CheckSum(cmd, 20);
serial_->send(cmd, 20);
usleep(5000);
serial_->recv(recvBuff, 9);
}
/**
* @brief
*
* g, [0 - 1000], N
*/
int16_t GetForce(Eigen::Matrix<double, 6, 1> & f)
{
std::vector<uint8_t> cmd = {0xEB, 0x90, id, 0x04, 0x11, 0x2E, 0x06, 0x0C, 0x00};
cmd.back() = CheckSum(cmd.data(), cmd.size());
serial_->send(cmd.data(), cmd.size());
usleep(5000);
size_t len = serial_->recv(recvBuff, 20);
if(len != 20) return 1;
if(recvBuff[19] != CheckSum(recvBuff, 20)) return 2;
f(0) = int16_t(recvBuff[7] | (recvBuff[8] << 8)) / 1000. * 9.8;
f(1) = int16_t(recvBuff[9] | (recvBuff[10] << 8)) / 1000. * 9.8;
f(2) = int16_t(recvBuff[11] | (recvBuff[12] << 8)) / 1000. * 9.8;
f(3) = int16_t(recvBuff[13] | (recvBuff[14] << 8)) / 1000. * 9.8;
f(4) = int16_t(recvBuff[15] | (recvBuff[16] << 8)) / 1000. * 9.8;
f(5) = int16_t(recvBuff[17] | (recvBuff[18] << 8)) / 1000. * 9.8;
return 0;
}
/**
* @brief
*
*
*/
void ClearError()
{
uint8_t cmd[9];
cmd[0] = 0xEB; // head
cmd[1] = 0x90;
cmd[2] = id; // ID
cmd[3] = 0x04; // data length
cmd[4] = 0x12; // write
cmd[5] = 0xEC; // address
cmd[6] = 0x03;
cmd[7] = 0x01;
cmd[8] = CheckSum(cmd, 9);
serial_->send(cmd, 9);
usleep(5000);
serial_->recv(recvBuff, 9);
}
/**
* @brief
*
*
*/
void Calibration()
{
std::vector<uint8_t> cmd = {0xEB, 0x90, id, 0x04, 0x12, 0x2F, 0x06, 0x01, 0x00};
cmd.back() = CheckSum(cmd.data(), cmd.size());
serial_->send(cmd.data(), cmd.size());
usleep(5000);
serial_->recv(recvBuff, 9); // 第一帧
sleep(10); // 整个过程大约需要6s
serial_->recv(recvBuff, 9); // 第二帧
std::cout << "Calibration: ";
print_data(recvBuff, 9);
}
uint8_t id = 1;
private:
uint8_t CheckSum(const uint8_t* data, uint8_t len)
{
uint8_t sum = 0;
// 除应答帧头外其余数据的累加和的低字节
for (int i = 2; i < len - 1; i++)
{
sum += data[i];
}
return sum;
}
SerialPort::SharedPtr serial_;
uint8_t recvBuff[1024];
};
} // namespace inspire
#endif // INSPIRE_H

58
include/param.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef PARAM_H
#define PARAM_H
#include <stdint.h>
#include <iostream>
#include <chrono>
#include <spdlog/spdlog.h>
#include <boost/program_options.hpp>
namespace param
{
namespace po = boost::program_options;
inline std::string serial_port;
inline std::string network;
inline std::string ns;
inline float threhold;
po::variables_map helper(int argc, char** argv)
{
#ifndef NDEBUG
spdlog::set_level(spdlog::level::debug);
#else
spdlog::set_level(spdlog::level::info);
#endif
po::options_description desc("Unitree H1 Inspire Hand Serial to DDS");
desc.add_options()
("help,h", "produce help message")
("serial,s", po::value<std::string>(&serial_port)->default_value("/dev/h1_newhand"), "serial port")
("network", po::value<std::string>(&network)->default_value(""), "DDS network interface")
("namespace", po::value<std::string>(&ns)->default_value("inspire"), "DDS topic namespace")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help"))
{
std::cout << desc << std::endl;
exit(0);
}
if(ns.empty())
{
spdlog::error("Namespace cannot be empty");
exit(1);
}
return vm;
}
}
#endif // PARAM_H

116
inspire_ctrl.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "inspire.h"
#include <unitree/dds_wrapper/go2/go2_pub.h>
#include <unitree/dds_wrapper/go2/go2_sub.h>
#include "param.h"
class InspireRunner
{
public:
InspireRunner()
{
serial = std::make_shared<SerialPort>(param::serial_port, B115200);
// inspire
righthand = std::make_shared<inspire::InspireHand>(serial, 1); // 按邵博格式,先右后左
lefthand = std::make_shared<inspire::InspireHand>(serial, 2);
// dds
handcmd = std::make_shared<unitree::robot::SubscriptionBase<unitree_go::msg::dds_::MotorCmds_>>(
"rt/" + param::ns + "/cmd");
handcmd->msg_.cmds().resize(12);
handstate = std::make_unique<unitree::robot::go2::publisher::RealTimeMotorStates>(
"rt/" + param::ns + "/state");
handstate->msg_.states().resize(12);
// Start running
thread = std::make_shared<unitree::common::RecurrentThread>(
10000, std::bind(&InspireRunner::run, this)
);
}
void run()
{
// Set command
if(!handcmd->isTimeout())
{
for(int i(0); i<12; i++)
{
qcmd(i) = handcmd->msg_.cmds()[i].q();
}
righthand->SetPosition(qcmd.block<6, 1>(0, 0)); // 先右后左
lefthand->SetPosition(qcmd.block<6, 1>(6, 0));
}
// Recv state
Eigen::Matrix<double, 6, 1> qtemp;
if(righthand->GetPosition(qtemp) == 0)
{
qstate.block<6, 1>(0, 0) = qtemp;
}
else
{
for(int i(0); i<6; i++)
{
handstate->msg_.states()[i].lost()++;
}
spdlog::debug("Failed to get right hand state");
}
if(lefthand->GetPosition(qtemp) == 0)
{
qstate.block<6, 1>(6, 0) = qtemp;
}
else
{
for(int i(0); i<6; i++)
{
handstate->msg_.states()[i+6].lost()++;
}
spdlog::debug("Failed to get left hand state");
}
if(handstate->trylock())
{
for(int i(0); i<12; i++)
{
handstate->msg_.states()[i].q() = qstate(i);
}
handstate->unlockAndPublish();
}
}
unitree::common::ThreadPtr thread;
// inspire
SerialPort::SharedPtr serial;
std::shared_ptr<inspire::InspireHand> lefthand;
std::shared_ptr<inspire::InspireHand> righthand;
Eigen::Matrix<double, 12, 1> qcmd, qstate;
// dds
std::shared_ptr<unitree::robot::SubscriptionBase<unitree_go::msg::dds_::MotorCmds_>> handcmd;
std::unique_ptr<unitree::robot::go2::publisher::RealTimeMotorStates> handstate;
};
int main(int argc, char ** argv)
{
auto vm = param::helper(argc, argv);
if (param::network.empty())
{
// 在H1上需使用master service启动DDS不带参数查找程序同级目录下的parameter.json
// 否则会报错
unitree::robot::ChannelFactory::Instance()->Init();
}
else {
unitree::robot::ChannelFactory::Instance()->Init(0, param::network);
}
std::cout << " --- Unitree Robotics --- " << std::endl;
std::cout << " Inspire Hand Controller " << std::endl;
InspireRunner runner;
while (true)
{
sleep(1);
}
return 0;
}