← Emre Girgin

Unitree D1 Arm Setup

Building the D1 SDK and sending joint position commands from a small C++ program.

1. Install the Unitree SDK

git clone https://github.com/unitreerobotics/unitree_sdk2.git
cd unitree_sdk2
mkdir build && cd build
cmake ..
sudo make install

2. Build the D1 SDK

Download d1_sdk.zip from Unitree, then:

unzip d1_sdk.zip
cd d1_sdk
mkdir build && cd build
cmake ..
make

If the build cannot find the DDS headers, add this to CMakeLists.txt:

include_directories(/usr/local/include/ddscxx /usr/local/include/iceoryx/v2.0.2 /usr/local/lib)

3. Make the libraries visible to the linker

echo "/usr/local/lib" | sudo tee /etc/ld.so.conf.d/local-lib.conf
sudo ldconfig

If that is not enough, these sometimes help:

export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
sudo chmod +r /usr/local/lib/libddsc.so.0

4. Configure the network

Give the Ethernet interface connected to the arm a static address in the 192.168.123.0/24 subnet, for example 192.168.123.99 with netmask 255.255.255.0 and no gateway. Note the interface name with ip addr.

5. Run the bundled controller

cd <path/to/d1_sdk>/build
sudo ./arm_zero_control enp86s0   # your interface name

6. Send joint positions from the command line

The arm listens on the rt/arm_Command topic for a JSON string. The program below takes seven angles (six joints and the gripper), builds that JSON and publishes it once. Save it as examples/interactive_joint_control.cpp.

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "unitree/robot/channel/channel_publisher.hpp"
#include "unitree/common/time/time_tool.hpp"
#include "msg/ArmString_.hpp"

#define TOPIC "rt/arm_Command"

using namespace unitree::robot;
using namespace unitree::common;

int main(int argc, char *argv[]) {
    if (argc != 8) {
        std::cerr << "Usage: " << argv[0] << " a0 a1 a2 a3 a4 a5 a6" << std::endl;
        std::cerr << "Example: " << argv[0] << " 0 -60 60 0 30 0 0" << std::endl;
        return 1;
    }

    std::vector<double> angles;
    for (int i = 1; i < argc; ++i) angles.push_back(std::stod(argv[i]));

    ChannelFactory::Instance()->Init(0);
    ChannelPublisher<unitree_arm::msg::dds_::ArmString_> publisher(TOPIC);
    publisher.InitChannel();

    // {"seq":4,"address":1,"funcode":2,"data":{"mode":1,"angle0":0,"angle1":-60,...}}
    std::stringstream ss;
    ss << "{\"seq\":4,\"address\":1,\"funcode\":2,\"data\":{\"mode\":1,";
    for (size_t i = 0; i < angles.size(); ++i) {
        ss << "\"angle" << i << "\":" << angles[i] << (i + 1 == angles.size() ? "" : ",");
    }
    ss << "}}";

    unitree_arm::msg::dds_::ArmString_ msg{};
    msg.data_() = ss.str();
    std::cout << "Sending: " << msg.data_() << std::endl;
    publisher.Write(msg);
    return 0;
}

Register it in the root CMakeLists.txt of d1_sdk:

add_executable(interactive_joint_control examples/interactive_joint_control.cpp)
target_link_libraries(interactive_joint_control ${PROJECT_NAME})

Build and run:

cd d1_sdk/build
cmake ..
make -j4
sudo ./interactive_joint_control 0 -60 60 0 30 0 0