🧪 Novos tutoriais em andamento — do braço robótico ao sensor
Ir para o conteúdo
Compre este produtoLoja oficialSite oficial

Controlo Web do AmazingHand ​

Controla a mão de destreza AmazingHand a partir do navegador — mão esquerda ou direita, com duas formas de a comandar:

  • Manual — 8 sliders de servo + 19 poses predefinidas + regresso ao centro com um clique
  • Teleoperação por câmara — o MediaPipe corre localmente no navegador e mapeia a tua mão nos dedos em tempo real (o vídeo nunca sai da máquina)
  • Interface bilingue — alterna entre chinês e inglês no canto superior direito; a escolha é memorizada pelo navegador

O mapa dos servos, a biblioteca de poses e as fórmulas de conversão estão todos alinhados com o AmazingHandControl (a GUI oficial).


Início rápido ​

Abre um terminal e entra na pasta Amazinghand-web:

bash
pip install -r requirements.txt

python server.py --no-connect   # recomendado: arranca o servidor, escolhe a porta na página (nada se move)

# python server.py                # deteta automaticamente a porta série e liga
# python server.py --port COM9    # usa uma porta específica
# python server.py --hand left    # ativa apenas a mão esquerda
# python server.py --dry-run      # sem porta série nenhuma, só a UI

Depois abre http://localhost:8000 no navegador.

Escolher a porta série na página ​

No topo da página há uma barra de portas:

Port  [COM3 ★ USB-Enhanced-SERIAL CH343 ▾]  [Refresh]  [Connect]  [Disconnect]  Connected to COM3
  • A lista pendente mostra todas as portas série da máquina. Os chips USB-série mais comuns (CH34x / CP210x / FTDI …) estão marcados com ★ e aparecem primeiro
  • Refresh volta a fazer a deteção (usa-o depois de ligares a placa)
  • Connect abre a porta selecionada; a mão regressa ao centro logo a seguir
  • Disconnect liberta primeiro o binário, para que os servos não fiquem bloqueados onde estão
  • Para trocar de porta, basta escolher a nova e clicar em Connect — a porta antiga é fechada automaticamente

O --no-connect foi pensado para este fluxo: o servidor arranca sem tocar em nenhuma porta série, por isso não pode agarrar a porta errada e sacudir a mão. Remove a flag se quiseres que ele se ligue no arranque.

Os registos da porta série vão para server.log (se arrancares através de um .bat, a janela passa a correr — lê o ficheiro em vez disso).

Verificação da cablagem ​

Lista os IDs de servo que encontra e verifica-os mão a mão contra o config.json.

bash
python tools/scan_servos.py --port COM9

Substitui COM9 pela tua porta real.


Mapa dos servos ​

Fonte: DEFAULT_HAND_SERVOS em AmazingHandControl/hand_logic.py e docs/zh-hans/CONFIG_FORMAT.md.

DedoID mão direitaID mão esquerda
Anelar5, 611, 12
Médio3, 413, 14
Indicador1, 215, 16
Polegar7, 817, 18

Os dois servos de um dedo fazem trabalhos diferentes ⚠️ ​

servo1 (ID ímpar)  = eixo de flexão    aberto ↔ fechado
servo2 (ID par)    = eixo de abertura  dedos juntos ↔ afastados

A UI mostra exatamente estes dois eixos. «O dedo não abre / não fecha» significa que o eixo de abertura não se está a mover — um V em tesoura vem da abertura do indicador +17 e da abertura do médio −30 (um positivo, um negativo).

São combinados no barramento:

servo1 = flexão + abertura
servo2 = flexão − abertura

Intervalo (derivado das 19 poses oficiais mais o centro):

EixoIntervaloSignificado
Flexão−50 ~ +90aberto (negativo) ↔ fechado (positivo)
Abertura−45 ~ +45juntos (negativo) ↔ afastados (positivo)

Duas regras de IDs (enganar-se nelas queima servos ou inverte o movimento):

  1. Os IDs ímpares são o primeiro servo de um dedo, os IDs pares o segundo. Verdade em ambas as mãos (esquerda 11/12, 13/14 … também alternam). O segundo servo está montado ao contrário, por isso nega-o na conversão.
  2. A mão esquerda não é 9–16, é 11–18, numerada começando pelo anelar. Cablá-la como 1–8 por ordem está sempre errado.

image.png


Conversão ​

Idêntica ao apply_pose() do AmazingHandControl (verificada pose a pose):

1) quando mirror_pose é true, troca primeiro os dois valores de servo de cada dedo
2) servo_deg = (ID ímpar ? +1 : -1) × (valor da pose − angle_offset_deg)
Mãoangle_offset_degmirror_pose
Direita0sim ⚠️
Esquerda-35.16sim

-35.16° é o desvio físico de meio entalhe dos braços dos servos da mão esquerda em relação à direita, retirado da configuração oficial.

Sobre o mirror_pose — esta versão difere da oficial

Na referência oficial, só a mão esquerda é true. Nesta versão, o módulo da mão direita também tem os pares de servos montados ao contrário, por isso a mão direita também é true.

A troca só inverte a abertura, nunca a flexão: base = (a+b)/2 não muda com a troca, enquanto side = (a−b)/2 muda de sinal. Portanto, se a abertura de uma mão sair espelhada, este é o primeiro interruptor a verificar.

O array de pose tem sempre 8 valores na ordem [ring, middle, index, thumb], dois por dedo — idêntico byte a byte ao data/hand_config.yaml oficial, por isso podes compará-los diretamente.

Exemplos práticos ​

PoseDireitaEsquerda
open [-35]*8ID5 −35 / ID6 +35ID11 +0.16 / ID12 −0.16
close [75]*8ID5 +75 / ID6 −75ID11 +110.16 / ID12 −110.16
middle [-17.578]*8ID5 −17.58 / ID6 +17.58ID11 +17.58 / ID12 −17.58

Nota que o close da mão esquerda precisa de ±110.16°, muito acima do da direita. É por isso que o config.json define o limite em ±120° (o intervalo total do SCS0009 é cerca de ±150°). Um limite de ±90 cortaria o punho da mão esquerda.


Poses predefinidas (19) ​

PosePose
openAbertawave1Aceno
close / stonePunhowave_r / wave_lDeslizar à direita / à esquerda
okOKring_closeSó o anelar
greetingCumprimentomiddle_closeSó o médio
victory / two / scissorsVitória / Doispointer_closeSó o indicador
paperPapelthumb_closeSó o polegar
one / threeUm / TrêshifiveHigh five

Para alterar uma pose, edita a secção poses do config.json; as chaves correspondem aos nomes no YAML oficial.

As mãos esquerda e direita podem usar valores diferentes ​

Dentro de uma pose, positions é partilhado pelas duas mãos. Se uma mão precisar de um conjunto diferente devido a diferenças de montagem mecânica, acrescenta uma chave right ou left a essa entrada e esta sobrepõe-se a positions:

jsonc
"close": {
  "label": "Punho",
  "positions": [75, 75, 75, 75, 75, 75, 18, 18],   // partilhado pelas duas mãos
  "right":     [ ... 8 valores ... ]                // opcional: só a mão direita
}

Dois modos ​

ModoComportamento
Mão direitaEnvia apenas para os 8 servos da mão direita
Mão esquerdaEnvia apenas para os 8 servos da mão esquerda (espelhamento + desvio de −35.16° aplicados automaticamente)

Uma mão de cada vez. As duas mãos estão no mesmo barramento; alterna com Target no topo. A alternância não perturba a outra mão — esta mantém a pose onde está.


Teleoperação por câmara ​

Os landmarks da mão do MediaPipe correm localmente no navegador (recursos em web/mediapipe/, funciona offline). Os 21 landmarks são convertidos em valores de pose para 4 dedos e transmitidos para a mão selecionada.

1. Captura primeiro três poses ​

Liga a câmara, coloca a mão na pose e clica no botão de captura dessa pose (cerca de 1 segundo de média). A ordem não importa; clica no mesmo botão outra vez para recapturar. O estado só fica verde quando as três estiverem capturadas — nada é enviado antes disso, por isso a mão fica no centro.

BotãoPoseFornece
① SpreadDedos esticados e afastados, polegar apontado para foraO extremo «estendido» da flexão + o extremo «afastado» da abertura
② TogetherDedos esticados e juntos, polegar encostado ao indicadorO extremo «junto» da abertura
③ FistFaz um punho, polegar recolhido contra a palmaO extremo «fechado» da flexão

2. Cada eixo é uma reta que passa por dois pontos medidos ​

Tanto a flexão como a abertura usam dois pontos extremos medidos para definir uma reta:

t      = (medido − endpointA) / (endpointB − endpointA)      // limitado a 0..1
alvo   = A.alvo + (B.alvo − A.alvo) × t

A vantagem: o sinal da medição de cada dedo é absorvido automaticamente — por isso não há nenhuma caixa «inverter este dedo» para assinalar. (O indicador e o anelar estão em lados opostos da linha central, e a cadeia de articulações do polegar é diferente, por isso as suas direções são mesmo opostas — dois pontos extremos medidos absorvem isso.)

Os alvos dos pontos extremos estão na secção camera do config.json:

EixoPontos extremosAnelarMédioIndicador
Flexãoestendido → punho−50 → 90−50 → 90−50 → 90
Aberturajuntos → afastados−45 → +450 → 0+45 → −45

Ambos os extremos da abertura são 0 para o dedo médio (não oscila lateralmente).

3. O polegar apenas flexiona ⚠️ ​

A CMC do polegar é uma articulação em sela (oponível), e a sua flexão está fortemente acoplada à abdução — com o polegar encostado ao indicador já está numa posição de abertura, por isso a «flexão zero» medida aí é contaminada. Forçá-la em dois eixos faz com que ambos saiam errados.

Por isso o polegar apenas flexiona; a sua abertura é sempre 0 (os seus dois servos recebem o mesmo valor). As duas poses de referência de que precisa são a ① (apontado para fora) e a ③ (apontado para a palma) — nada mais para capturar.

4. A abertura desvanece à medida que o dedo se enrola ​

A abertura final é escalada por:

side = side_measured × (1 − curl)

splayDeg é a projeção da direção MCP→PIP sobre o eixo lateral da palma, e a flexão do dedo roda exatamente em torno desse eixo — passado um certo enrolamento, a projeção troca de sinal e a «abertura» medida salta de repente para o outro extremo.

Sem este desvanecimento, esse salto atravessa servo2 = flexion − splay e arrasta o servo de ID par para o sentido inverso. (O ID ímpar recebe flexion + splay, por isso move-se no mesmo sentido, apenas mais depressa — não se nota.) O sintoma é «a meio de fechar o punho, os servos 12 e 16 invertem de repente» — o 12 é o servo de abertura do anelar esquerdo e o 16 o do indicador esquerdo, precisamente os dois cujos alvos de abertura são diferentes de zero.

Com o desvanecimento, a abertura é exatamente 0 no enrolamento total, por muito disparatada que a medição fique. E um punho deve ter os dedos juntos de qualquer forma.

5. Cores do esqueleto ​

DedoCor
Polegarlaranja
Indicadorvermelho
Médioazul
Anelaramarelo
Mindinhocinzento escuro
Ossos da palmacinzento neutro

O mindinho não é usado no mapeamento de gestos, mas a sua MCP de base (landmark 17) define o referencial de coordenadas da palma da mão direita, por isso não pode ser simplesmente descartado.

Guia rápido de afinação ​

O que queres alterarOnde
Valores alvo dos eixossecção camera do config.json
A abertura da esquerda e da direita vai em sentidos opostosdefine essa mão como -1 em camera.side_sign (1 = sem alteração)
SuavidadeSMOOTH em tracking.js (por omissão 0.35; mais baixo é mais estável mas com mais atraso)
Rapidez com que um enrolamento é detetadorecaptura as três poses

side_sign aplica-se à mão que estiver selecionada em Target. Altera-o e recarrega a página — não é preciso reiniciar o servidor:

jsonc
"side_sign": { "right": 1, "left": -1 }   // inverte todo o eixo de abertura na mão esquerda

A flexão não é afetada pelo side_sign.


Estrutura do projeto ​

Amazinghand-web/
├── config.json          mapa de servos / biblioteca de poses / sequências / limites (gerado a partir do YAML oficial)
├── server.py            backend: porta série + ficheiros estáticos + WebSocket
├── requirements.txt
├── README.md            Inglês (ficheiro original)
├── tutorials/           de · en · es · fr · it · ja · ko · pt-br · pt-pt · zh-hans · zh-hant
├── tools/
│   └── scan_servos.py   procura servos no barramento e verifica os seus IDs
└── web/
    ├── index.html · app.js · tracking.js · style.css
    ├── i18n.js          textos da UI (dicionário chinês/inglês + comutador no canto superior direito)
    └── mediapipe/       recursos do MediaPipe (incluídos localmente, offline, ~42 MB)

Protocolo frontend/backend:

jsonc
// frontend → backend   target: "left" | "right"
{ "cmd": "ports" }                                  // volta a procurar as portas série
{ "cmd": "connect",    "port": "COM3" }
{ "cmd": "disconnect" }
{ "cmd": "target", "target": "left" }
{ "cmd": "set",    "target": "left", "angles": { "pointer": [0, 0], ... } }
{ "cmd": "pose",   "target": "left", "name": "close" }
{ "cmd": "middle", "target": "left" }
{ "cmd": "torque", "target": "left", "on": true }
{ "cmd": "stop" }

// backend → frontend
{ "type": "state", "hands": {...}, "poses": {...}, "positions": {...},
  "axes": {...}, "camera": {...}, "target": "left",
  "limits": {...}, "port": { "available": [...], "current": "COM3", "connected": true } }
{ "type": "ports" | "connected" | "disconnected", "ports": [...], ... }
{ "type": "error", "msg": "connect COM_FAKE failed: the system cannot find the file specified." }

Segurança ​

Item
Limites de pose±120° (limits no config.json); as 19 poses verificadas para se manterem dentro
Deduplicação de alteraçõesalterações abaixo de 0.5° não são enviadas, para o barramento não ser inundado com ruído
Limitação de escritamínimo de 20 ms entre escritas
Paragem de emergênciao botão vermelho na UI liberta todos os servos de uma vez
Proteção à saídao binário é libertado automaticamente com Ctrl+C
Depuração de uma só mão--hand left/right

Resolução de problemas ​

O movimento está invertido → verifica se a cablagem segue «primeiro o ID ímpar»; na mão esquerda confirma também angle_offset_deg = -35.16.

O punho da mão esquerda é demasiado pequeno / os servos zumbem → o limite provavelmente ainda é ±90; verifica se o config.json tem ±120.

Um dedo não se move / «expected N motors, found M» → executa tools/scan_servos.py; os IDs da mão esquerda são os mais fáceis de ligar mal.

A posição central está errada → o valor central -17.578125 é convertido do valor bruto oficial 451/571. Para calibrar com a tua própria montagem, edita middle.positions no config.json.

O seguimento da câmara está instável → iluminação uniforme, palma virada para a câmara. SMOOTH em tracking.js (por omissão 0.35) — mais alto é mais suave mas com mais atraso.

Os gestos da câmara estão errados / um dedo não acompanha → vê a secção Teleoperação por câmara acima. Verifica primeiro três coisas:

  1. As três poses estão todas capturadas? — o estado tem de estar verde; nada é enviado antes disso e a mão fica no centro
  2. Uma direção invertida significa que essa pose foi capturada ao contrário — basta recapturar esse botão, sem alterar código
  3. O polegar não deve abrir — apenas flexiona, vê a secção 3 acima

Um servo entra em sentido inverso a meio de fechar o punho → esse servo deve ter um ID par (o eixo de abertura, por exemplo 12 / 16 na mão esquerda). Normalmente a abertura desvanece para 0 com o enrolamento e nunca salta; se vires isto, a linha side *= (1 - curl) em tracking.js não está a ter efeito, ou o curl não está a ser calculado.