feat(skills): add taiyang servo dance skill

Add PWM-based servo control skill for RDK nodes with hardware servo support.

Changes:
- Add taiyang skill with SKILL.md and servo_dance.py script
- Update Dockerfile and Containerfile to include taiyang skill
- Update SOUL.md with skill constraints documentation

The skill controls two servos via /sys/class/pwm interface and must run on node side only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
d-robotics 2026-04-22 15:36:54 +08:00
parent e6dab8c3dd
commit 077f2eacf1
5 changed files with 155 additions and 0 deletions

View File

@ -41,7 +41,9 @@ EOF
COPY rdk-claw/SOUL.md /app/docs/reference/templates/SOUL.md COPY rdk-claw/SOUL.md /app/docs/reference/templates/SOUL.md
COPY rdk-claw/IDENTITY.md /app/docs/reference/templates/IDENTITY.md COPY rdk-claw/IDENTITY.md /app/docs/reference/templates/IDENTITY.md
COPY rdk-claw/node-remote-connect /app/skills/node-remote-connect COPY rdk-claw/node-remote-connect /app/skills/node-remote-connect
COPY rdk-claw/taiyang /app/skills/taiyang
RUN chown -R 1001:1001 /app/skills/node-remote-connect RUN chown -R 1001:1001 /app/skills/node-remote-connect
RUN chown -R 1001:1001 /app/skills/taiyang
ENV HOME=/home/user ENV HOME=/home/user
USER 1001:1001 USER 1001:1001

View File

@ -29,8 +29,12 @@ COPY IDENTITY.md /app/docs/reference/templates/IDENTITY.md
# 添加 node-remote-connect skill # 添加 node-remote-connect skill
COPY node-remote-connect /app/skills/node-remote-connect COPY node-remote-connect /app/skills/node-remote-connect
# 添加 taiyang skill
COPY taiyang /app/skills/taiyang
# 确保权限正确 # 确保权限正确
RUN chown -R node:node /app/skills/node-remote-connect RUN chown -R node:node /app/skills/node-remote-connect
RUN chown -R node:node /app/skills/taiyang
USER node USER node

View File

@ -37,3 +37,15 @@ Be the assistant you'd actually want to talk to. Concise when needed, thorough w
Each session, you wake up fresh. These files are your memory. Read them. Update them. They're how you persist. Each session, you wake up fresh. These files are your memory. Read them. Update them. They're how you persist.
If you change this file, tell the user — it's your soul, and they should know. If you change this file, tell the user — it's your soul, and they should know.
## Skills
### Taiyang! - Servo Dance
A hardware control skill that drives two servos on an RDK board via PWM signals.
**Constraints:**
- Must be executed on the Node side (the RDK board). Never run this on the cloud Gateway — there is no PWM hardware here.
- Requires PWM-capable servos physically connected to the board (`/sys/class/pwm/pwmchip0/pwm0` and `pwm1`).
When users mention "taiyang", "servo", or "servo dance", remind them this skill only works on RDK nodes with servo hardware attached.

59
rdk-claw/taiyang/SKILL.md Normal file
View File

@ -0,0 +1,59 @@
---
name: 太阳!
description: 控制 RDK 节点上的舵机挥动,让两个舵机同步或反向运动。使用 PWM 信号控制舵机角度0°-180°支持多种舞动模式。触发话太阳、舵机挥动、servo dance、PWM 舵机、双舵机控制、双舵机控制。
---
# 太阳!- 舵机挥动技能
让舵机动起来!就像太阳升起一样充满活力。
## 前置条件
- RDK 节点已连接
- PWM 接口可用(/sys/class/pwm/pwmchip0
- 两个舵机分别连接到 pwm0 和 pwm1
## 快速使用
直接调用脚本启动舵机舞动:
```bash
python3 ~/.openclaw/skills/taiyang/scripts/servo_dance.py
```
停止舞动:
```bash
pkill -f servo_dance.py
```
## 舵机控制参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| 频率 | 50Hz | 标准舵机频率20ms周期 |
| 脉宽范围 | 1-2ms | 0°-180° 对应 |
| 中心位置 | 1.5ms | 90° |
## PWM 接口路径
- pwm0: `/sys/class/pwm/pwmchip0/pwm0` → Servo 0
- pwm1: `/sys/class/pwm/pwmchip0/pwm1` → Servo 1
## 舞动模式
脚本内置三种模式循环:
1. **波浪模式** - Servo0 和 Servo1 反向运动
2. **同步模式** - 两个舵机同步运动
## 手动控制
如需手动控制单个舵机:
```bash
# 设置角度0-180
echo $((1000000 + angle * 5555)) > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
```
角度计算duty = 1000000 + (angle/180) * 1000000 (单位ns)

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python3
"""
太阳- 双舵机舞动脚本
让舵机像太阳升起一样充满活力地挥动
"""
import time
import os
import sys
PWM0 = "/sys/class/pwm/pwmchip0/pwm0"
PWM1 = "/sys/class/pwm/pwmchip0/pwm1"
PERIOD = 20000000 # 20ms = 50Hz
def init_pwm():
"""初始化两个PWM通道"""
for pwm in [PWM0, PWM1]:
try:
with open(os.path.join(pwm, "period"), "w") as f:
f.write(str(PERIOD))
with open(os.path.join(pwm, "duty_cycle"), "w") as f:
f.write("1500000") # 中心位置 90°
with open(os.path.join(pwm, "enable"), "w") as f:
f.write("1")
except Exception as e:
print(f"Error initializing {pwm}: {e}")
sys.exit(1)
def set_angle(pwm, angle):
"""设置舵机角度 (0-180°)"""
duty = int(1000000 + (angle / 180.0) * 1000000)
with open(os.path.join(pwm, "duty_cycle"), "w") as f:
f.write(str(duty))
def wave_pattern():
"""波浪模式:两舵机反向运动"""
for a in range(0, 181, 30):
set_angle(PWM0, a)
set_angle(PWM1, 180 - a)
time.sleep(0.2)
for a in range(180, -1, -30):
set_angle(PWM0, a)
set_angle(PWM1, 180 - a)
time.sleep(0.2)
def sync_pattern():
"""同步模式:两舵机同步运动"""
for a in [0, 90, 180, 90]:
set_angle(PWM0, a)
set_angle(PWM1, a)
time.sleep(0.3)
def stop_pwm():
"""停止PWM输出"""
set_angle(PWM0, 90)
set_angle(PWM1, 90)
time.sleep(0.3)
for pwm in [PWM0, PWM1]:
with open(os.path.join(pwm, "enable"), "w") as f:
f.write("0")
def main():
print("🌞 太阳!- 舵机舞动开始")
print("=" * 40)
init_pwm()
try:
while True:
print("🌊 波浪模式...")
wave_pattern()
print("🎯 同步模式...")
sync_pattern()
except KeyboardInterrupt:
print("\n停止舞动...")
stop_pwm()
print("🌞 太阳落山了,再见!")
if __name__ == "__main__":
main()