Unitree Go1 を使ってみよう！（その３　ソフト編）


今回はソフトウェア編です。

雑多なメモ状態ですが、参考になりましたら幸いです。（2022/3 に届いた Proモデルでの情報）

SDKの使ってみた様子/ROS/音声関係/環境などについて書いています。

（どうやらスピーカやらWiFi 5GHz -> 2.4GHz 変更などノウハウがまとまったドキュメントがUnitree からもらえるぽいので、購入された方はサポートに聞いてみましょう。私も取得しました。）



:::note info

2024/5 追記

こちらの方の記事によりますと、2023年暮れ～以降の出荷バージョンから何やら対策がなされたとの事です。

https://qiita.com/Manyan3/items/cfab10bfacd1e646a171

私の記事は2022年のものなので、どこまで同じことができるかわかりませんが、参考のために残しておきます。

:::



<br>
<br>



前回　その２　ハード編→

https://qiita.com/devemin/items/baaa0d260a35e991ac39


前々回　その１　スタート編→

https://qiita.com/devemin/items/c31e106a6e3661db1b1e

追加

https://qiita.com/devemin/items/1708176248a1928f3b88


# はじめに

ハード編にも書きましたが、内部のラズパイ・Jetson Nano にソフト追加したりするのには、USB-Ethernet 接続するのがシンプルに使えそうですが、 **安易に apt update とかすると、最初の環境から変化していくので、標準ソフトへの影響が未知なのでよくなさそうです。**

私は内部ボードのソフト構成はあまり変えず、追加のホストからLAN 経由でコントロールしています。



# Unitree 公式リポジトリ

[Unitree github リポジトリ](https://github.com/unitreerobotics)

[unitree_legged_sdk](https://github.com/unitreerobotics/unitree_legged_sdk)
[UnitreeCameraSDK](https://github.com/unitreerobotics/UnitreecameraSDK)　（これはPro も公式対応）

[unitree_ros](https://github.com/unitreerobotics/unitree_ros)
[unitree_ros_to_real](https://github.com/unitreerobotics/unitree_ros_to_real)

# ソフト構成
ラズパイもJetson Nano も、~/Unitree/autostart/ に各種バイナリがあり、~/Unitree/sdk/ にSDKが置いてある。
SDKは公式github にあるが、unitree_legged_sdk 等、ハードのversion によって構造体の宣言が異なって挙動が変わるものもあるよう。
また、faceLightSDK(LED) や ultraSoundSDK_Nano/ultraSoundSDK_Raspi(超音波距離センサ)など、まだ公式github に無いものもある。



# 公式 SDK

### unitree_legged_sdk

https://github.com/unitreerobotics/unitree_legged_sdk


https://www.youtube.com/watch?v=wXFQqrSywTU


各example のようす
Pro では、High-level モードの example_walk.cpp は動きました。(cmd.levelFlag = 0x00; を追加する)
また、IMU/モータ角度値などの状態値は取得できる模様。

トルク・速度・位置制御の Low-level モードの example_torque / velocity / position .cpp は動きませんでした。
Pro でも動かせた方がいたら是非お教えください。

cmd.levelFlag = 0x00～0xFF までインクリメントして試しましたが、やはり Lo-level モードは動かず。


（Pro / Edu でここら辺の挙動を変えてるのかな？あくまで推測だが、CRC チェックサム処理をPro / Edu で挙動を変えて制限してる？）

example_walk.cpp

https://twitter.com/devemin/status/1511214370390372353?s=20&t=QiKopClU0CW4uiXbEJQR2Q


最新(2022/4/5 時点) version 3.5.1 と、過去のもので、 include/comm.h の構造体の中身が一部異なるため、
古いバージョンは、実行するとデータが正しく取得できない。（実機のファームversion によってここらは変わりそう）

そういえば High-level モードで backflip モーション実行を試しましたが、さすがにこれは封印されてるのかな？バク転は実行されませんでしたｗA1 では出来るのと、Go1 の紹介ムービーでもしてたから微かに期待していたのですがｗ　Edu の方がモーター強そう？なので出来るのかどうかは不明
（バックフリップはできたので後述）

https://twitter.com/devemin/status/1539786524140916738?s=20&t=muDYqNvGFNP0_ax4lEix0g

### バックフリップ

バックフリップはアンロック操作が必要でした。

https://twitter.com/devemin/status/1547389922545471488?s=20&t=yup4C8NNj5za_oMmJb1E9A


（バックフリップ（バク転）は負荷で脚が折れるよ～という海外の方のツイを見かけましたので、自己責任でやりたい方はDM下さい）



### UnitreecameraSDK

https://github.com/unitreerobotics/UnitreecameraSDK

各example のようす

gstremer の配信がexampleで実行されてるが、それをコマンドで再現したものもツイートに書いた。

https://twitter.com/devemin/status/1509783551948226560?s=20&t=AXIAgeMawIz5XEyBa5R1Qg

https://twitter.com/devemin/status/1504303559718105092?s=20&t=AXIAgeMawIz5XEyBa5R1Qg


使いやすいようexample 追加のプルリクを出しました！

https://twitter.com/devemin/status/1518465625760624641?s=20

https://github.com/unitreerobotics/UnitreecameraSDK/pull/5


実行前にカメラ関連プロセスをkill する必要があります。

https://github.com/unitreerobotics/UnitreecameraSDK/blob/ecd2058ba3f033af11c2d2a0306b44cc8d819332/examples/example_putImagetrans.cc#L45



# カメラ

https://twitter.com/devemin/status/1511053541741568000?s=20&t=AXIAgeMawIz5XEyBa5R1Qg




### faceLightSDK


### ultraSoundSDK_Nano/ultraSoundSDK_Raspi





### ROS

https://github.com/unitreerobotics/unitree_ros

https://github.com/unitreerobotics/unitree_ros_to_real



Unitree Legged SDK の High level mode については Pro モデルでも動いたので、unitree_ros, unitree_ros_to_real とも実機で動かせました。

現在つくばチャレンジ2022 トライしており、これを利用しております。

https://twitter.com/devemin/status/1436012596813987843?s=20&t=AXIAgeMawIz5XEyBa5R1Qg


# MQTT コマンド　使用方法

MQTT モータ・各種コマンド
（基本的に、スマホアプリ・ブラウザで行えるコマンドです。）

取得情報
（スマホアプリ・ブラウザから得られる情報（つまりMQTT コマンドからの情報）は、 unitree_legged_sdk で得られるものより精度が劣ります（float -> int 等））

参考ソース


<details><summary>mqtt python 例 </summary><div>

```

# free use



import paho.mqtt.client as paho

import time
import struct

def on_connect(mqttc, obj, rc, a):
    mqttc.subscribe("$SYS/#", 0)
    print("rc: "+str(rc))

def on_message(mqttc, obj, msg):
    print("on_message: " + msg.topic+" "+str(msg.qos)+" "+str(msg.payload))

def on_publish(mqttc, obj, mid):
    print("mid: "+str(mid))

def on_log(mqttc, obj, level, string):
    print("on_log: " + string)




if __name__ == '__main__':
    mqttc = paho.Client()
    mqttc.on_message = on_message
    mqttc.on_connect = on_connect
    mqttc.on_publish = on_publish


    #mqttc.connect("192.168.12.1", 1883, 60)
    mqttc.connect("192.168.123.161", 1883, 60)



    #bytesarr = bytearray([0x03, 0xca]);
    #mqttc.publish("controller/current_action", bytesarr, 0)
    #time.sleep(1)



    #移動・回転
    #lx, rx, ry, ly
    
    #ヨコ移動
    #bytesarr = bytearray([0x26, 0x73, 0x2a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

    #移動＋回転
    #bytesarr = bytearray([0xcc, 0xcc, 0x4c, 0xbd, 0xcc, 0xcc, 0x4c, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

    #ry(からだ前後傾け) と移動の複合はできないぽい
    #bytesarr = bytearray([0x26, 0x73, 0x2a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);


    #0.16回転
    #bytesarr = bytearray([0x00, 0x00, 0x00, 0x00, 0x26, 0x73, 0x2a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
    #bytesarr = bytearray([0x00, 0x00, 0x00, 0x00, 0x26, 0x73, 0x2a, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

    #0.3回転
    #bytesarr = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

    #bytesarr  = bytearray([0x00, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 
    #print(bytesarr)
    #mqttc.publish("controller/stick", bytesarr, 0)
    #time.sleep(2)


    #右移動
    bytesarr = bytearray([0xcc, 0xcc, 0x4c, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(0.3)

    #ストップ
    bytesarr  = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(1)



    #左移動
    bytesarr = bytearray([0xcc, 0xcc, 0x4c, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(0.3)

    #ストップ
    bytesarr  = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(1)



    #前移動
    bytesarr = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x4c, 0x3e]);
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(0.3)

    #ストップ
    bytesarr  = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(1)



    #後ろ移動
    bytesarr = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0x4c, 0xbe]);
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(0.3)

    #ストップ
    bytesarr  = bytearray([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 
    mqttc.publish("controller/stick", bytesarr, 0)
    time.sleep(1)



    #LED(BLUE)
    bytesarr = bytearray([0x00, 0x00, 0xff]);
    mqttc.publish("face_light/color", bytesarr, 0)
    time.sleep(1)

    #LED(YELLOW)
    bytesarr = bytearray([0xff, 0xff, 0x00]);
    mqttc.publish("face_light/color", bytesarr, 0)
    time.sleep(1)


    '''
    #command
    #dance1, dance2, straightHand1, damping, standUp, standDown, recoverStand, stand, walk, run, climb  (from chunk-3caf6fe5.e09948eb.js) 

    #other ?
    #jump, backflip
    
    
    mqttc.publish("controller/action", "standUp", 0)
    time.sleep(0.3)

    mqttc.publish("controller/action", "standDown", 0)
    time.sleep(0.3)

    '''



    mqttc.loop_forever()
```
</div>
</details>




# ほかに入っていたSDK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478524/b17e2688-a448-f4ed-8d51-1e59d940e2da.png)

faceLightSDK_Nano

ultraSoundSDK_Raspi


外部にラズパイを接続してみましたが、そこからでもLED / Legged_SDK / CameraSDK は実行できました。ultraSoundSDK_Raspi は Jetson の方にも類似名で入っていますが、どうやら超音波センサはシリアル接続？センサがつながってる実機でないと実行時にエラーになりました。


# スピーカ

（ちょっとここゴチャゴチャ書いてしまいましたが、このスピーカの項目の最後の方の、ネットワーク再生の設定のが一番シンプルに、他端末から再生できるようになりました。）

https://twitter.com/devemin/status/1519501200344829953?s=20

192.168.123.13 の頭部Nano にUSB接続と思われるスピーカがついています。マイクもあるはず

ps aux | grep wsaudio
sudo kill xxxx (wsaudio のプロセスをキル)

aplay ./testdog.wav
再生できないときがあったので下記

aplay -l
aplay --device=hw:2,0 ./testdog1.wav 

USB audio の順番が変わる等か？

pulseaudio --kill
pulseaudio --start

alsamixer (F6 押すとUSB Audio Device の音量変更できる。これを変えると、スマホアプリでのPeripherals->Audio Device のところの機能の音量が変わった)


頭部Nano はUSB端子が外に出てないので、ファイルの転送には scp を使いましょう

scp ./testdog.wav unitree@192.168.123.13:~/ 


https://www.google.com/search?q=%E7%8A%AC+wave&sxsrf=ALiCzsYrwuZqo8UEOuX9VWIz-PEQ91zM7g%3A1651120001570&ei=gRdqYui6IsPihwPayb-YDg&ved=0ahUKEwio_7Kp9bX3AhVD8WEKHdrkD-MQ4dUDCA4&uact=5&oq=%E7%8A%AC+wave&gs_lcp=Cgdnd3Mtd2l6EAMyBAgjECcyBggAEAgQHjoHCAAQsAMQHjoJCAAQsAMQCBAeSgQIQRgBSgQIRhgAUCVY2AhgwwloAXAAeACAAWmIAbUBkgEDMS4xmAEAoAEByAECwAEB&sclient=gws-wiz

音量調整（これは設定間違えるとよくわからなくなったので注意）

pactl set-sink-volume @DEFAULT_SINK@ +5%
たぶんこっち？？？
pactl set-sink-volume 0 +5%
//pactl set-sink-volume 1 +5%
https://qiita.com/Hayao0819/items/1f01aa39db7326ff69ee
sink#1 の Volume front-left / front-right が音量に連動して、600% 以上とかまで上がりました。（のはたまたまバグ？）
さすがにそれで再生したらスピーカが壊れそうなノイジーな音になりましたので、ほどほどに。
（これは、デフォルトシンクを設定した上で、 aplay ./testdog.wav とオプション無しで実行した場合に、600%とかが適応されるみたい）


ネットワーク経由で別端末から再生するには、こちらを利用して、 /etc/pulse/default.pa を編集しました。

http://bluewidz.blogspot.com/2018/04/oslinux-virualboxdebian-8.html

PULSE_SERVER=192.168.123.13 aplay ./testdog.wav 


https://qiita.com/propella/items/4699eda71cd742cba8d3


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478524/49d403d9-6e8e-8453-876b-b0919b1dfbd1.png)

Mono / Stereo のファイルで再生出来たりできなかったり・・・謎

本体で再生出来ていても、ネットワーク経由のができなくなったり・・・

何か標準出力が変わる？

https://qiita.com/propella/items/4699eda71cd742cba8d3

aplay -l ではなく aplay -L で見て、


aplay --device=plughw:2,0 ./testdog1.wav 

なら、Mono / Stereo とも再生できた


ここを参考に、
頭部Nano の /etc/pulse/default.pa
最後尾に、

set-default-sink alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo
追加して、

pulseaudio --kill
pulseaudio --start
したらネットワーク再生もできた！



https://wiki.archlinux.jp/index.php/PulseAudio/%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB#.E3.83.87.E3.83.95.E3.82.A9.E3.83.AB.E3.83.88.E3.81.AE.E5.87.BA.E5.8A.9B.E3.82.BD.E3.83.BC.E3.82.B9.E3.82.92.E8.A8.AD.E5.AE.9A



paplay -s 192.168.123.13 ./dog1.wav 

でもいけた


別に用意したラズパイとかなら、AAC / *.m4a ファイルはこれで再生できた。mp3とかも
（deb パッケージをオフラインでコピーすれば、192.168.123.161 のラズパイでもインストールできるかもしれないけど、ライブラリが多かったのでせず。wave なら aplay/paplay で別ソフトインストール無しで再生できるので、wave に変換するのが吉）
（先に 192.168.123.13 の wsaudio は kill する必要あり）

sudo apt install mplayer

PULSE_SERVER=192.168.123.13 mplayer ./yobikomikun/yobikomikun.m4a 


http://abc002.blogspot.com/2013/05/raspberrypimplayeraac.html



# PointCloud2 出力ノード

192.168.123.161 ラズパイの~/Unitree/autostart/camerarosnode/cameraRosNode/startNode.sh　

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478524/d8a2e619-7d4e-19ed-c128-3e315e1a9507.png)

ソースを見る限り、UnitreecameraSDK の StartCapture(false,true) で、圧縮なし、共有メモリONにして、rect image / Depth 取得を同時に行ってるぽい？

https://twitter.com/devemin/status/1523643723472846850

ビルド

ssh 192.168.123.14
cd
mkdir ./copy_camerarosnode
cp -r ~/Unitree/autostart/camerarosnode/* ./copy_camerarosnode/
cd ~/Unitree/autostart/camerarosnode/cameraRosnode/src/unitree_camera/
mkdir build
cd build
cmake ..
make
ls ./devel/lib/unitree_camera/




# その他
Mujoco
https://twitter.com/devemin/status/1455419485737807873?s=20&t=AXIAgeMawIz5XEyBa5R1Qg


# JetPack バージョン


```
192.168.123.13
The authenticity of host '192.168.123.13 (192.168.123.13)' can't be established.
ECDSA key fingerprint is SHA256:5d2GAwdV3M599daXFj6/UhO7ZzLA7DXRtRA5B7/iQ10.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.123.13' (ECDSA) to the list of known hosts.
unitree@192.168.123.13's password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.9.140-tegra aarch64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

298 packages can be updated.
205 updates are security updates.

Last login: Mon Nov  8 15:06:09 2021 from 192.168.123.163

unitree@nano2gb:~$ cat /etc/nv_tegra_release 
# R32 (release), REVISION: 4.4, GCID: 23942405, BOARD: t210ref, EABI: aarch64, DATE: Fri Oct 16 19:44:43 UTC 2020

```


```
192.168.123.14

ubuntu@rpi4ubuntu2004:~$ ssh unitree@192.168.123.14
unitree@192.168.123.14's password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.9.201-tegra aarch64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

262 packages can be updated.
162 updates are security updates.

Last login: Wed Dec 15 23:57:40 2021 from 192.168.123.163

unitree@unitree-desktop:~$ sudo cat /etc/nv_tegra_release 
[sudo] password for unitree: 
# R32 (release), REVISION: 5.0, GCID: 25531747, BOARD: t210ref, EABI: aarch64, DATE: Fri Jan 15 22:55:35 UTC 2021
```


```
192.168.123.15

unitree@192.168.123.15's password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.9.201-tegra aarch64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.

To restore this content, you can run the 'unminimize' command.

262 packages can be updated.
162 updates are security updates.

Last login: Wed Dec 15 19:02:28 2021 from 192.168.123.14

unitree@unitree-desktop:~$ cat /etc/nv_tegra_release 
# R32 (release), REVISION: 5.0, GCID: 25531747, BOARD: t210ref, EABI: aarch64, DATE: Fri Jan 15 22:55:35 UTC 2021

```


# timezone
/etc/timezone 変えても意味なし
/etc/localtime のシンボリックリンクを ln -s で変える

https://qiita.com/azusanakano/items/b39bd22504313884a7c3


# Web サーバー
nginx のリバースプロキシ？でWebSocket, mqttプロトコルで内外データやり取りをしているようです。
192.168.123.161:8080 - firmware アップデート用
192.168.123.161:80 - 各種データやり取り用

nginx 設定
/mqtt
/upload
/map
/audio
/cam1 - cam5
/human


mqtt explorer が便利でした。

mqtt (mosquitto server) 192.168.123.161:1883
controller/current_action
controller/action (dance1, dance2, straightHand1, damping, standUp, standDown, recoverStand, stand, walk, run, climb (jump?), (backflip?) )
controller/stick
face_light/color

https://twitter.com/devemin/status/1505714528843014149?s=20&t=AXIAgeMawIz5XEyBa5R1Qg

https://twitter.com/devemin/status/1505720547279212544?s=20&t=AXIAgeMawIz5XEyBa5R1Qg




# パケットハック

内蔵のラズパイ・Jetson Nano にパケット解析の WireShark ソフト追加するにあたって、オフラインで apt パッケージを移動して行いました。
また、USB-LAN モジュールなど使えば、直接ラズパイ・Jetson NanoをInternet に繋げられると思います。
（ただ、内部で動いてるソフトウェア・ROS関係の環境を壊さないように注意が必要かと思いますので、安易にソフト追加せず、必要なら外部ホストを用意したほうが良いかなと個人的に思ってます。）


WireShark を入れてパケットハック。
このソフト、Lua 言語でパケットデータをスクリプト処理するように拡張できて便利。

https://twitter.com/devemin/status/1502628723237199872?s=20&t=6J8f4Dxl5a1t917W3jWW6w




# ポート調査

netstat -putona 

で、各端末のポート接続状況が読み取れます。192.168.123.161 のラズパイ上で見ると全てつながってるぽい


# アップデート

公式サイトにアップデートファイルがあります。 Nano, Raspi 両方のソフトが入っていますので、解凍すれば買う前にある程度ソフト構成が見られます。