Skip to content

Commit 3d7f4a5

Browse files
committed
add unitree_dds_wrapper
1 parent 6f9312d commit 3d7f4a5

File tree

13 files changed

+1137
-1
lines changed

13 files changed

+1137
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ You can install the required packages on Ubuntu 20.04 with:
1818

1919
```bash
2020
apt-get update
21-
apt-get install -y cmake g++ build-essential libyaml-cpp-dev libeigen3-dev
21+
apt-get install -y cmake g++ build-essential libyaml-cpp-dev libeigen3-dev libboost-all-dev libspdlog-dev libfmt-dev
2222
```
2323

2424
### Build examples
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// Copyright (c) 2025, Unitree Robotics Co., Ltd.
2+
// All rights reserved.
3+
4+
#pragma once
5+
6+
#include <unitree/robot/channel/channel_publisher.hpp>
7+
#include <atomic>
8+
#include <thread>
9+
#include <memory>
10+
11+
namespace unitree
12+
{
13+
namespace robot
14+
{
15+
16+
/**
17+
* @brief Base class for convenient publishing to topics.
18+
* This class provides an easy-to-use interface for publishing messages to topics.
19+
*
20+
* @tparam MessageType The type of message being published.
21+
*/
22+
template <typename MessageType>
23+
class PublisherBase : public unitree::robot::ChannelPublisher<MessageType>
24+
{
25+
public:
26+
using MsgType = MessageType;
27+
using SharedPtr = std::shared_ptr<PublisherBase<MsgType>>;
28+
29+
PublisherBase(std::string TOPIC_NAME)
30+
: unitree::robot::ChannelPublisher<MessageType>(TOPIC_NAME)
31+
{
32+
this->InitChannel();
33+
}
34+
};
35+
36+
// For details: see https://github.com/ros-controls/realtime_tools
37+
template <typename MessageType>
38+
class RealTimePublisher
39+
{
40+
public:
41+
using MsgType = MessageType;
42+
using PublisherSharedPtr = typename unitree::robot::ChannelPublisherPtr<MessageType>;
43+
44+
MessageType msg_{};
45+
46+
explicit RealTimePublisher(PublisherSharedPtr publisher)
47+
: publisher_(publisher), is_running_(false), keep_running_(true), turn_(LOOP_NOT_STARTED)
48+
{
49+
thread_ = std::thread(&RealTimePublisher::publishingLoop, this);
50+
}
51+
52+
explicit RealTimePublisher(std::string topic)
53+
: RealTimePublisher(std::make_shared<PublisherBase<MsgType>>(topic))
54+
{}
55+
56+
~RealTimePublisher()
57+
{
58+
stop();
59+
while (is_running()) {
60+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
61+
}
62+
if(thread_.joinable()) { thread_.join(); }
63+
}
64+
65+
void stop()
66+
{
67+
keep_running_ = false;
68+
}
69+
70+
/**
71+
* @brief Try to get the data lock from realtime
72+
*
73+
* To publish data from the realtime loop, you need to run trylock to
74+
* attenot to get unique access to the msg_ variable. Teylock returns
75+
* true if the lock was aquired, and false otherwise.
76+
*/
77+
bool trylock()
78+
{
79+
if(mutex_.try_lock())
80+
{
81+
if(turn_ == REALTIME) {
82+
return true;
83+
} else {
84+
mutex_.unlock();
85+
return false;
86+
}
87+
} else {
88+
return false;
89+
}
90+
}
91+
92+
/**
93+
* @brief Unlock the msg_ variable and publish it.
94+
*/
95+
void unlockAndPublish()
96+
{
97+
turn_ = NON_REALTIME;
98+
mutex_.unlock();
99+
}
100+
101+
/**
102+
* @brief Get the data lock from non-realtime.
103+
*
104+
* To publish data from the realtime loop, you need to run trylock to
105+
* attenot to get unique access to the msg_ variable. Teylock returns
106+
* true if the lock was aquired, and false otherwise.
107+
*/
108+
void lock()
109+
{
110+
// never actually lock on the lock
111+
while (!mutex_.try_lock()) {
112+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
113+
}
114+
}
115+
116+
/**
117+
* @brief Unlocks the data without publishing anything.
118+
*/
119+
void unlock() { mutex_.unlock(); }
120+
121+
protected:
122+
virtual void pre_communication() {} // something before sending the message
123+
virtual void post_communication() {} // something after sending the message
124+
125+
private:
126+
// non-copyable
127+
RealTimePublisher(const RealTimePublisher&) = delete;
128+
RealTimePublisher& operator=(const RealTimePublisher&) = delete;
129+
130+
131+
bool is_running() const { return is_running_; }
132+
133+
void publishingLoop()
134+
{
135+
is_running_ = true;
136+
turn_ = REALTIME;
137+
138+
while (keep_running_)
139+
{
140+
MsgType outgoing;
141+
142+
// Locks msg_ and copies it
143+
lock();
144+
while (turn_ != NON_REALTIME && keep_running_)
145+
{
146+
unlock();
147+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
148+
lock();
149+
}
150+
pre_communication();
151+
outgoing = msg_;
152+
turn_ = REALTIME;
153+
154+
unlock();
155+
156+
if(keep_running_) {
157+
publisher_->Write(outgoing, 0);
158+
}
159+
post_communication();
160+
}
161+
is_running_ = false;
162+
}
163+
164+
PublisherSharedPtr publisher_;
165+
std::atomic_bool is_running_;
166+
std::atomic_bool keep_running_;
167+
168+
std::mutex mutex_;
169+
170+
std::thread thread_;
171+
172+
enum { REALTIME, NON_REALTIME, LOOP_NOT_STARTED };
173+
std::atomic<int> turn_;
174+
};
175+
176+
} // namespace robot
177+
} // namespace unitree
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) 2025, Unitree Robotics Co., Ltd.
2+
// All rights reserved.
3+
4+
#pragma once
5+
6+
#include <unitree/robot/channel/channel_subscriber.hpp>
7+
#include <mutex>
8+
#include <thread>
9+
#include <spdlog/spdlog.h>
10+
11+
namespace unitree
12+
{
13+
namespace robot
14+
{
15+
/**
16+
* @brief Base class for convenient subscription to topics.
17+
* This class provides an easy-to-use interface for subscribing to topics,
18+
* but the continuous background updates may increase CPU usage.
19+
*
20+
* @tparam MessageType The type of message being subscribed to.
21+
*/
22+
template <typename MessageType>
23+
class SubscriptionBase
24+
{
25+
public:
26+
using MsgType = MessageType;
27+
using SharedPtr = std::shared_ptr<SubscriptionBase<MsgType>>;
28+
29+
SubscriptionBase(const std::string& topic, const std::function<void(const void*)>& handler = nullptr)
30+
{
31+
last_update_time_ = std::chrono::steady_clock::now() - std::chrono::milliseconds(timeout_ms_);
32+
sub_ = std::make_shared<unitree::robot::ChannelSubscriber<MessageType>>(topic);
33+
if (handler) {
34+
sub_->InitChannel(handler);
35+
} else {
36+
sub_->InitChannel([this](const void *msg){
37+
last_update_time_ = std::chrono::steady_clock::now();
38+
std::lock_guard<std::mutex> lock(mutex_);
39+
pre_communication();
40+
msg_ = *(const MessageType*)msg;
41+
post_communication();
42+
});
43+
}
44+
}
45+
46+
void set_timeout_ms(uint32_t timeout_ms) { timeout_ms_ = timeout_ms; }
47+
48+
bool isTimeout() {
49+
auto now = std::chrono::steady_clock::now();
50+
auto elasped_time = now - last_update_time_;
51+
return elasped_time > std::chrono::milliseconds(timeout_ms_);
52+
}
53+
54+
void wait_for_connection() {
55+
auto t0 = std::chrono::steady_clock::now();
56+
bool warn_info = false;
57+
while(isTimeout()) {
58+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
59+
if (!warn_info && std::chrono::steady_clock::now() - t0 > std::chrono::seconds(2)) {
60+
warn_info = true;
61+
spdlog::warn("Waiting for connection {}", sub_->GetChannelName());
62+
}
63+
}
64+
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait for stable communicaiton
65+
if (warn_info) {
66+
spdlog::info("Connected {}", sub_->GetChannelName());
67+
}
68+
}
69+
70+
MessageType msg_;
71+
protected:
72+
virtual void pre_communication() {} // something before receiving message
73+
virtual void post_communication() {} // something after receiving message
74+
75+
uint32_t timeout_ms_{1000};
76+
std::mutex mutex_;
77+
unitree::robot::ChannelSubscriberPtr<MessageType> sub_;
78+
std::chrono::steady_clock::time_point last_update_time_;
79+
};
80+
81+
82+
}; // namespace robot
83+
}; // namespace unitree
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) 2025, Unitree Robotics Co., Ltd.
2+
// All rights reserved.
3+
4+
#pragma once
5+
6+
#include <stdint.h>
7+
8+
inline uint16_t crc16_core (const uint8_t *nData, unsigned short wLength){
9+
static const uint16_t wCRCTable[] = {
10+
0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
11+
0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
12+
0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
13+
0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
14+
0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
15+
0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
16+
0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
17+
0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
18+
0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
19+
0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
20+
0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
21+
0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
22+
0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
23+
0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
24+
0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
25+
0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
26+
0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
27+
0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
28+
0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
29+
0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
30+
0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
31+
0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
32+
0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
33+
0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
34+
0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
35+
0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
36+
0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
37+
0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
38+
0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
39+
0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
40+
0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
41+
0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };
42+
43+
uint8_t nTemp;
44+
uint16_t wCRCWord = 0xFFFF;
45+
46+
while (wLength--){
47+
nTemp = *nData++ ^ wCRCWord;
48+
wCRCWord >>= 8;
49+
wCRCWord ^= wCRCTable[nTemp];
50+
}
51+
return wCRCWord;
52+
53+
} // End: CRC16
54+
55+
inline uint32_t crc32_core(uint32_t* ptr, uint32_t len){
56+
uint32_t xbit = 0;
57+
uint32_t data = 0;
58+
uint32_t CRC32 = 0xFFFFFFFF;
59+
const uint32_t dwPolynomial = 0x04c11db7;
60+
for (uint32_t i = 0; i < len; i++)
61+
{
62+
xbit = 1 << 31;
63+
data = ptr[i];
64+
for (uint32_t bits = 0; bits < 32; bits++)
65+
{
66+
if (CRC32 & 0x80000000)
67+
{
68+
CRC32 <<= 1;
69+
CRC32 ^= dwPolynomial;
70+
}
71+
else
72+
CRC32 <<= 1;
73+
if (data & xbit)
74+
CRC32 ^= dwPolynomial;
75+
76+
xbit >>= 1;
77+
}
78+
}
79+
return CRC32;
80+
}

0 commit comments

Comments
 (0)