SHA256
add r1 robot project
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unitree/robot/channel/channel_factory.hpp>
|
||||
#include <unitree/robot/channel/channel_publisher.hpp>
|
||||
#include <unitree/robot/channel/channel_subscriber.hpp>
|
||||
#include <unitree/robot/b2/motion_switcher/motion_switcher_client.hpp>
|
||||
#include <unitree/idl/hg/LowCmd_.hpp>
|
||||
#include <unitree/idl/hg/LowState_.hpp>
|
||||
|
||||
namespace r1_vision {
|
||||
|
||||
using unitree_hg::msg::dds_::LowCmd_;
|
||||
using unitree_hg::msg::dds_::LowState_;
|
||||
using namespace unitree::robot;
|
||||
|
||||
inline constexpr int kNumMotors = 12;
|
||||
|
||||
inline constexpr std::array<int, kNumMotors> kJointId = {
|
||||
15, 16, 17, 18, 19,
|
||||
22, 23, 24, 25, 26,
|
||||
29, 30
|
||||
};
|
||||
|
||||
inline constexpr std::array<const char*, kNumMotors> kJointName = {
|
||||
"left_shoulder_pitch",
|
||||
"left_shoulder_roll",
|
||||
"left_shoulder_yaw",
|
||||
"left_elbow",
|
||||
"left_wrist_roll",
|
||||
"right_shoulder_pitch",
|
||||
"right_shoulder_roll",
|
||||
"right_shoulder_yaw",
|
||||
"right_elbow",
|
||||
"right_wrist_roll",
|
||||
"head_pitch",
|
||||
"head_yaw"
|
||||
};
|
||||
|
||||
inline constexpr std::array<float, kNumMotors> kKp = {
|
||||
100.f, 100.f, 100.f, 100.f, 50.f,
|
||||
100.f, 100.f, 100.f, 100.f, 50.f,
|
||||
50.f, 10.f
|
||||
};
|
||||
|
||||
inline constexpr std::array<float, kNumMotors> kKd = {
|
||||
2.f, 2.f, 2.f, 2.f, 2.f,
|
||||
2.f, 2.f, 2.f, 2.f, 2.f,
|
||||
2.f, 0.1f
|
||||
};
|
||||
|
||||
inline uint32_t Crc32Core(uint32_t* ptr, uint32_t len) {
|
||||
uint32_t xbit = 0;
|
||||
uint32_t data = 0;
|
||||
uint32_t crc = 0xFFFFFFFFu;
|
||||
constexpr uint32_t polynomial = 0x04c11db7u;
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
xbit = 1u << 31;
|
||||
data = ptr[i];
|
||||
for (uint32_t bits = 0; bits < 32; ++bits) {
|
||||
if (crc & 0x80000000u) {
|
||||
crc <<= 1;
|
||||
crc ^= polynomial;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
if (data & xbit) crc ^= polynomial;
|
||||
xbit >>= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
class RobotController {
|
||||
public:
|
||||
explicit RobotController(const std::string& network_interface) {
|
||||
ChannelFactory::Instance()->Init(0, network_interface);
|
||||
|
||||
motion_switcher_ = std::make_shared<unitree::robot::b2::MotionSwitcherClient>();
|
||||
motion_switcher_->SetTimeout(5.0f);
|
||||
motion_switcher_->Init();
|
||||
|
||||
std::string form, name;
|
||||
while (true) {
|
||||
motion_switcher_->CheckMode(form, name);
|
||||
if (name.empty()) break;
|
||||
std::cout << "[SYS] Releasing motion mode: " << name << std::endl;
|
||||
if (motion_switcher_->ReleaseMode()) {
|
||||
std::cerr << "[WARN] ReleaseMode failed" << std::endl;
|
||||
}
|
||||
::sleep(2);
|
||||
}
|
||||
|
||||
lowcmd_ = std::make_shared<ChannelPublisher<LowCmd_>>("rt/lowcmd");
|
||||
lowcmd_->InitChannel();
|
||||
|
||||
lowstate_ = std::make_shared<ChannelSubscriber<LowState_>>("rt/lowstate");
|
||||
lowstate_->InitChannel(
|
||||
std::bind(&RobotController::StateHandler, this, std::placeholders::_1), 1);
|
||||
|
||||
std::cout << "[SYS] Waiting for LowState..." << std::endl;
|
||||
while (!state_received_.load()) ::usleep(10000);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
target_q_ = current_q_;
|
||||
}
|
||||
|
||||
running_.store(true);
|
||||
writer_thread_ = std::thread(&RobotController::CommandLoop, this);
|
||||
std::cout << "[SYS] Low-level controller running at 500 Hz." << std::endl;
|
||||
}
|
||||
|
||||
~RobotController() {
|
||||
HoldCurrent();
|
||||
running_.store(false);
|
||||
if (writer_thread_.joinable()) writer_thread_.join();
|
||||
}
|
||||
|
||||
void StateHandler(const void* message) {
|
||||
const auto& state = *static_cast<const LowState_*>(message);
|
||||
const uint32_t crc = Crc32Core(
|
||||
reinterpret_cast<uint32_t*>(const_cast<LowState_*>(&state)),
|
||||
(sizeof(LowState_) >> 2) - 1);
|
||||
if (state.crc() != crc) return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (int i = 0; i < kNumMotors; ++i) {
|
||||
current_q_[i] = state.motor_state().at(kJointId[i]).q();
|
||||
}
|
||||
mode_machine_ = state.mode_machine();
|
||||
state_received_.store(true);
|
||||
}
|
||||
|
||||
void CommandLoop() {
|
||||
auto next = std::chrono::steady_clock::now();
|
||||
while (running_.load()) {
|
||||
next += std::chrono::milliseconds(2);
|
||||
|
||||
LowCmd_ cmd;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
cmd.mode_pr() = 0;
|
||||
cmd.mode_machine() = mode_machine_;
|
||||
for (int i = 0; i < kNumMotors; ++i) {
|
||||
auto& motor = cmd.motor_cmd().at(kJointId[i]);
|
||||
motor.mode() = 1;
|
||||
motor.q() = target_q_[i];
|
||||
motor.dq() = 0.0f;
|
||||
motor.kp() = kKp[i];
|
||||
motor.kd() = kKd[i];
|
||||
motor.tau() = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
cmd.crc() = Crc32Core(
|
||||
reinterpret_cast<uint32_t*>(&cmd),
|
||||
(sizeof(cmd) >> 2) - 1);
|
||||
lowcmd_->Write(cmd);
|
||||
std::this_thread::sleep_until(next);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<float, kNumMotors> CurrentQ() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return current_q_;
|
||||
}
|
||||
|
||||
float CurrentJoint(int joint) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return current_q_.at(joint);
|
||||
}
|
||||
|
||||
void SetTargets(const std::array<float, kNumMotors>& q) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
target_q_ = q;
|
||||
}
|
||||
|
||||
void SetJointTarget(int joint, float q) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
target_q_.at(joint) = q;
|
||||
}
|
||||
|
||||
void MoveJointRelative(int joint, float delta_rad, double seconds = 0.6) {
|
||||
const float start = CurrentJoint(joint);
|
||||
const float target = start + delta_rad;
|
||||
const auto t0 = std::chrono::steady_clock::now();
|
||||
|
||||
while (true) {
|
||||
const double elapsed = std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - t0).count();
|
||||
const double a = std::clamp(elapsed / seconds, 0.0, 1.0);
|
||||
const double s = a * a * (3.0 - 2.0 * a);
|
||||
SetJointTarget(joint, static_cast<float>(start + delta_rad * s));
|
||||
if (a >= 1.0) break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
|
||||
SetJointTarget(joint, target);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
}
|
||||
|
||||
void HoldCurrent() {
|
||||
SetTargets(CurrentQ());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<unitree::robot::b2::MotionSwitcherClient> motion_switcher_;
|
||||
ChannelPublisherPtr<LowCmd_> lowcmd_;
|
||||
ChannelSubscriberPtr<LowState_> lowstate_;
|
||||
std::thread writer_thread_;
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<bool> state_received_{false};
|
||||
mutable std::mutex mutex_;
|
||||
std::array<float, kNumMotors> current_q_{};
|
||||
std::array<float, kNumMotors> target_q_{};
|
||||
uint8_t mode_machine_{0};
|
||||
};
|
||||
|
||||
} // namespace r1_vision
|
||||
Reference in New Issue
Block a user