otto新增动作和AI自定义编程动作MCP工具 (#1365)

* otto v1.4.0 MCP

1.使用MCP协议控制机器人
2.gif继承lcdDisplay,避免修改lcdDisplay

* otto v1.4.1 gif as components

gif as components

* electronBot v1.1.0 mcp

1.增加electronBot支持
2.mcp协议
3.gif 作为组件
4.display子类

* 规范代码

1.规范代码
2.修复切换主题死机bug

* fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug

* 1.增加robot舵机初始位置校准
2.fix(mcp_sever) 超出范围异常捕获类型  bug

* refactor: Update Electron and Otto emoji display implementations

- Removed GIF selection from Kconfig for Electron and Otto boards.
- Updated Electron and Otto bot versions to 2.0.4 in their respective config files.
- Refactored emoji display classes to utilize EmojiCollection for managing emojis.
- Enhanced chat label setup and status display functionality in both classes.
- Cleaned up unused code and improved initialization logging for emoji displays.

* Rename OTTO_ICON_FONT.c to otto_icon_font.c

* Rename OTTO_ICON_FONT.c to otto_icon_font.c

* refactor: Update Otto emoji display configurations and functionalities

- Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays.
- Bumped Otto robot version to 2.0.5 in the configuration file.
- Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase.
- Enhanced servo sequence handling and added support for executing custom servo sequences.
- Improved logging and error handling for servo sequence execution.

* refactor: Update chat label long mode for Electron and Otto emoji displays

- Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays.
- Improved consistency in chat label setup across both implementations.

* Update Otto robot README with new actions and parameters
This commit is contained in:
小鹏
2025-11-02 18:04:06 +08:00
committed by GitHub
parent 1f602fa3a0
commit e39a46c1a0
9 changed files with 939 additions and 183 deletions

View File

@ -2,6 +2,7 @@
#include <algorithm>
#include "freertos/idf_additions.h"
#include "oscillator.h"
static const char* TAG = "OttoMovements";
@ -198,6 +199,39 @@ void Otto::Execute(int amplitude[SERVO_COUNT], int offset[SERVO_COUNT], int peri
vTaskDelay(pdMS_TO_TICKS(10));
}
//---------------------------------------------------------
//-- Execute2: 使用绝对角度作为振荡中心
//-- Parameters:
//-- amplitude: 振幅数组(每个舵机的振荡幅度)
//-- center_angle: 绝对角度数组0-180度作为振荡中心位置
//-- period: 周期(毫秒)
//-- phase_diff: 相位差数组(弧度)
//-- steps: 步数/周期数(可为小数)
//---------------------------------------------------------
void Otto::Execute2(int amplitude[SERVO_COUNT], int center_angle[SERVO_COUNT], int period,
double phase_diff[SERVO_COUNT], float steps = 1.0) {
if (GetRestState() == true) {
SetRestState(false);
}
// 将绝对角度转换为offsetoffset = center_angle - 90
int offset[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
offset[i] = center_angle[i] - 90;
}
int cycles = (int)steps;
//-- Execute complete cycles
if (cycles >= 1)
for (int i = 0; i < cycles; i++)
OscillateServos(amplitude, offset, period, phase_diff);
//-- Execute the final not complete cycle
OscillateServos(amplitude, offset, period, phase_diff, (float)steps - cycles);
vTaskDelay(pdMS_TO_TICKS(10));
}
///////////////////////////////////////////////////////////////////
//-- HOME = Otto at rest position -------------------------------//
///////////////////////////////////////////////////////////////////
@ -224,7 +258,7 @@ void Otto::Home(bool hands_down) {
}
}
MoveServos(500, homes);
MoveServos(700, homes);
is_otto_resting_ = true;
}
@ -389,7 +423,7 @@ void Otto::ShakeLeg(int steps, int period, int dir) {
int homes[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
// Changes in the parameters if left leg is chosen
if (dir == 1) {
if (dir == LEFT) {
shake_leg1[2] = 180 - 35;
shake_leg1[3] = 180 - 58;
shake_leg2[2] = 180 - 120;
@ -420,6 +454,14 @@ void Otto::ShakeLeg(int steps, int period, int dir) {
vTaskDelay(pdMS_TO_TICKS(period));
}
//---------------------------------------------------------
//-- Otto movement: Sit (坐下)
//---------------------------------------------------------
void Otto::Sit() {
int target[SERVO_COUNT] = {120, 60, 0, 180, 45, 135};
MoveServos(600, target);
}
//---------------------------------------------------------
//-- Otto movement: up & down
//-- Parameters:
@ -588,6 +630,29 @@ void Otto::Flapping(float steps, int period, int height, int dir) {
Execute(A, O, period, phase_diff, steps);
}
//---------------------------------------------------------
//-- Otto gait: WhirlwindLeg (旋风腿)
//-- Parameters:
//-- steps: Number of steps
//-- period: Period (建议100-800毫秒)
//-- amplitude: amplitude (Values between 20 - 40)
//---------------------------------------------------------
void Otto::WhirlwindLeg(float steps, int period, int amplitude) {
int target[SERVO_COUNT] = {90, 90, 180, 90, 45, 20};
MoveServos(100, target);
target[RIGHT_FOOT] = 160;
MoveServos(500, target);
vTaskDelay(pdMS_TO_TICKS(1000));
int C[SERVO_COUNT] = {90, 90, 180, 160, 45, 20};
int A[SERVO_COUNT] = {amplitude, 0, 0, 0, amplitude, 0};
double phase_diff[SERVO_COUNT] = {DEG2RAD(20), 0, 0, 0, DEG2RAD(20), 0};
Execute2(A, C, period, phase_diff, steps);
}
//---------------------------------------------------------
//-- 手部动作: 举手
//-- Parameters:
@ -599,16 +664,15 @@ void Otto::HandsUp(int period, int dir) {
return;
}
int initial[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
int target[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
if (dir == 0) {
target[LEFT_HAND] = 170;
target[RIGHT_HAND] = 10;
} else if (dir == 1) {
} else if (dir == LEFT) {
target[LEFT_HAND] = 170;
target[RIGHT_HAND] = servo_[RIGHT_HAND].GetPosition();
} else if (dir == -1) {
} else if (dir == RIGHT) {
target[RIGHT_HAND] = 10;
target[LEFT_HAND] = servo_[LEFT_HAND].GetPosition();
}
@ -629,9 +693,9 @@ void Otto::HandsDown(int period, int dir) {
int target[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
if (dir == 1) {
if (dir == LEFT) {
target[RIGHT_HAND] = servo_[RIGHT_HAND].GetPosition();
} else if (dir == -1) {
} else if (dir == RIGHT) {
target[LEFT_HAND] = servo_[LEFT_HAND].GetPosition();
}
@ -639,111 +703,248 @@ void Otto::HandsDown(int period, int dir) {
}
//---------------------------------------------------------
//-- 手部动作: 挥手
//-- 手部动作: 挥手
//-- Parameters:
//-- period: 动作周期
//-- dir: 方向 LEFT/RIGHT/BOTH
//-- dir: 方向 LEFT/RIGHT/BOTH
//---------------------------------------------------------
void Otto::HandWave(int period, int dir) {
void Otto::HandWave(int dir) {
if (!has_hands_) {
return;
}
if (dir == LEFT) {
int center_angle[SERVO_COUNT] = {90, 90, 90, 90, 160, 135};
int A[SERVO_COUNT] = {0, 0, 0, 0, 20, 0};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), 0};
Execute2(A, center_angle, 300, phase_diff, 5);
}
else if (dir == RIGHT) {
int center_angle[SERVO_COUNT] = {90, 90, 90, 90, 45, 20};
int A[SERVO_COUNT] = {0, 0, 0, 0, 0, 20};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, 0, DEG2RAD(90)};
Execute2(A, center_angle, 300, phase_diff, 5);
}
else {
int center_angle[SERVO_COUNT] = {90, 90, 90, 90, 160, 20};
int A[SERVO_COUNT] = {0, 0, 0, 0, 20, 20};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), DEG2RAD(90)};
Execute2(A, center_angle, 300, phase_diff, 5);
}
}
//---------------------------------------------------------
//-- 手部动作: 大风车
//-- Parameters:
//-- steps: 动作次数
//-- period: 动作周期(毫秒)
//-- amplitude: 振荡幅度(度)
//---------------------------------------------------------
void Otto::Windmill(float steps, int period, int amplitude) {
if (!has_hands_) {
return;
}
if (dir == BOTH) {
HandWaveBoth(period);
return;
}
int servo_index = (dir == LEFT) ? LEFT_HAND : RIGHT_HAND;
int current_positions[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
current_positions[i] = servo_[i].GetPosition();
} else {
current_positions[i] = 90;
}
}
int position;
if (servo_index == LEFT_HAND) {
position = 170;
} else {
position = 10;
}
current_positions[servo_index] = position;
MoveServos(300, current_positions);
vTaskDelay(pdMS_TO_TICKS(300));
// 左右摆动5次
for (int i = 0; i < 5; i++) {
if (servo_index == LEFT_HAND) {
current_positions[servo_index] = position - 30;
MoveServos(period / 10, current_positions);
vTaskDelay(pdMS_TO_TICKS(period / 10));
current_positions[servo_index] = position + 30;
MoveServos(period / 10, current_positions);
} else {
current_positions[servo_index] = position + 30;
MoveServos(period / 10, current_positions);
vTaskDelay(pdMS_TO_TICKS(period / 10));
current_positions[servo_index] = position - 30;
MoveServos(period / 10, current_positions);
}
vTaskDelay(pdMS_TO_TICKS(period / 10));
}
if (servo_index == LEFT_HAND) {
current_positions[servo_index] = HAND_HOME_POSITION;
} else {
current_positions[servo_index] = 180 - HAND_HOME_POSITION;
}
MoveServos(300, current_positions);
int center_angle[SERVO_COUNT] = {90, 90, 90, 90, 90, 90};
int A[SERVO_COUNT] = {0, 0, 0, 0, amplitude, amplitude};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), DEG2RAD(90)};
Execute2(A, center_angle, period, phase_diff, steps);
}
//---------------------------------------------------------
//-- 手部动作: 双手同时挥手
//-- 手部动作: 起飞
//-- Parameters:
//-- period: 动作周期
//-- steps: 动作次数
//-- period: 动作周期(毫秒),数值越小速度越快
//-- amplitude: 振荡幅度(度)
//---------------------------------------------------------
void Otto::HandWaveBoth(int period) {
void Otto::Takeoff(float steps, int period, int amplitude) {
if (!has_hands_) {
return;
}
int current_positions[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
current_positions[i] = servo_[i].GetPosition();
} else {
current_positions[i] = 90;
}
Home(true);
int center_angle[SERVO_COUNT] = {90, 90, 90, 90, 90, 90};
int A[SERVO_COUNT] = {0, 0, 0, 0, amplitude, amplitude};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), DEG2RAD(-90)};
Execute2(A, center_angle, period, phase_diff, steps);
}
//---------------------------------------------------------
//-- 手部动作: 健身
//-- Parameters:
//-- steps: 动作次数
//-- period: 动作周期(毫秒)
//-- amplitude: 振荡幅度(度)
//---------------------------------------------------------
void Otto::Fitness(float steps, int period, int amplitude) {
if (!has_hands_) {
return;
}
int target[SERVO_COUNT] = {90, 90, 90, 0, 160, 135};
MoveServos(100, target);
target[LEFT_FOOT] = 20;
MoveServos(400, target);
vTaskDelay(pdMS_TO_TICKS(2000));
int C[SERVO_COUNT] = {90, 90, 20, 90, 160, 135};
int A[SERVO_COUNT] = {0, 0, 0, 0, 0, amplitude};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, 0, 0};
Execute2(A, C, period, phase_diff, steps);
}
//---------------------------------------------------------
//-- 手部动作: 打招呼
//-- Parameters:
//-- dir: 方向 LEFT=左手, RIGHT=右手
//-- steps: 动作次数
//---------------------------------------------------------
void Otto::Greeting(int dir, float steps) {
if (!has_hands_) {
return;
}
if (dir == LEFT) {
int target[SERVO_COUNT] = {90, 90, 150, 150, 45, 135};
MoveServos(400, target);
int C[SERVO_COUNT] = {90, 90, 150, 150, 160, 135};
int A[SERVO_COUNT] = {0, 0, 0, 0, 20, 0};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, 0, 0};
Execute2(A, C, 300, phase_diff, steps);
}
else if (dir == RIGHT) {
int target[SERVO_COUNT] = {90, 90, 30, 30, 45, 135};
MoveServos(400, target);
int C[SERVO_COUNT] = {90, 90, 30, 30, 45, 20};
int A[SERVO_COUNT] = {0, 0, 0, 0, 0, 20};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, 0, 0};
Execute2(A, C, 300, phase_diff, steps);
}
int left_position = 170;
int right_position = 10;
}
current_positions[LEFT_HAND] = left_position;
current_positions[RIGHT_HAND] = right_position;
MoveServos(300, current_positions);
// 左右摆动5次
for (int i = 0; i < 5; i++) {
// 波浪向左
current_positions[LEFT_HAND] = left_position - 30;
current_positions[RIGHT_HAND] = right_position + 30;
MoveServos(period / 10, current_positions);
// 波浪向右
current_positions[LEFT_HAND] = left_position + 30;
current_positions[RIGHT_HAND] = right_position - 30;
MoveServos(period / 10, current_positions);
//---------------------------------------------------------
//-- 手部动作: 害羞
//-- Parameters:
//-- dir: 方向 LEFT=左手, RIGHT=右手
//-- steps: 动作次数
//---------------------------------------------------------
void Otto::Shy(int dir, float steps) {
if (!has_hands_) {
return;
}
current_positions[LEFT_HAND] = HAND_HOME_POSITION;
current_positions[RIGHT_HAND] = 180 - HAND_HOME_POSITION;
MoveServos(300, current_positions);
if (dir == LEFT) {
int target[SERVO_COUNT] = {90, 90, 150, 150, 45, 135};
MoveServos(400, target);
int C[SERVO_COUNT] = {90, 90, 150, 150, 45, 135};
int A[SERVO_COUNT] = {0, 0, 0, 0, 20, 20};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), DEG2RAD(-90)};
Execute2(A, C, 300, phase_diff, steps);
}
else if (dir == RIGHT) {
int target[SERVO_COUNT] = {90, 90, 30, 30, 45, 135};
MoveServos(400, target);
int C[SERVO_COUNT] = {90, 90, 30, 30, 45, 135};
int A[SERVO_COUNT] = {0, 0, 0, 0, 0, 20};
double phase_diff[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), DEG2RAD(-90)};
Execute2(A, C, 300, phase_diff, steps);
}
}
//---------------------------------------------------------
//-- 手部动作: 广播体操
//---------------------------------------------------------
void Otto::RadioCalisthenics() {
if (!has_hands_) {
return;
}
const int period = 1000;
const float steps = 8.0;
int C1[SERVO_COUNT] = {90, 90, 90, 90, 145, 45};
int A1[SERVO_COUNT] = {0, 0, 0, 0, 45, 45};
double phase_diff1[SERVO_COUNT] = {0, 0, 0, 0, DEG2RAD(90), DEG2RAD(-90)};
Execute2(A1, C1, period, phase_diff1, steps);
int C2[SERVO_COUNT] = {90, 90, 115, 65, 90, 90};
int A2[SERVO_COUNT] = {0, 0, 25, 25, 0, 0};
double phase_diff2[SERVO_COUNT] = {0, 0, DEG2RAD(90), DEG2RAD(-90), 0, 0};
Execute2(A2, C2, period, phase_diff2, steps);
int C3[SERVO_COUNT] = {90, 90, 130, 130, 90, 90};
int A3[SERVO_COUNT] = {0, 0, 0, 0, 20, 0};
double phase_diff3[SERVO_COUNT] = {0, 0, 0, 0, 0, 0};
Execute2(A3, C3, period, phase_diff3, steps);
int C4[SERVO_COUNT] = {90, 90, 50, 50, 90, 90};
int A4[SERVO_COUNT] = {0, 0, 0, 0, 0, 20};
double phase_diff4[SERVO_COUNT] = {0, 0, 0, 0, 0, 0};
Execute2(A4, C4, period, phase_diff4, steps);
}
//---------------------------------------------------------
//-- 手部动作: 爱的魔力转圈圈
//---------------------------------------------------------
void Otto::MagicCircle() {
if (!has_hands_) {
return;
}
int A[SERVO_COUNT] = {30, 30, 30, 30, 50, 50};
int O[SERVO_COUNT] = {0, 0, 5, -5, 0, 0};
double phase_diff[SERVO_COUNT] = {0, 0, DEG2RAD(-90), DEG2RAD(-90), DEG2RAD(-90) , DEG2RAD(90)};
Execute(A, O, 700, phase_diff, 40);
}
//---------------------------------------------------------
//-- 展示动作:串联多个动作展示
//---------------------------------------------------------
void Otto::Showcase() {
if (GetRestState() == true) {
SetRestState(false);
}
// 1. 往前走3步
Walk(3, 1000, FORWARD, 50);
vTaskDelay(pdMS_TO_TICKS(500));
// 2. 挥挥手
if (has_hands_) {
HandWave(LEFT);
vTaskDelay(pdMS_TO_TICKS(500));
}
// 3. 跳舞(使用广播体操)
if (has_hands_) {
RadioCalisthenics();
vTaskDelay(pdMS_TO_TICKS(500));
}
// 4. 太空步
Moonwalker(3, 900, 25, LEFT);
vTaskDelay(pdMS_TO_TICKS(500));
// 5. 摇摆
Swing(3, 1000, 30);
vTaskDelay(pdMS_TO_TICKS(500));
// 6. 起飞
if (has_hands_) {
Takeoff(5, 300, 40);
vTaskDelay(pdMS_TO_TICKS(500));
}
// 7. 健身
if (has_hands_) {
Fitness(5, 1000, 25);
vTaskDelay(pdMS_TO_TICKS(500));
}
// 8. 往后走3步
Walk(3, 1000, BACKWARD, 50);
}
void Otto::EnableServoLimit(int diff_limit) {