Spaces:
Sleeping
Sleeping
File size: 24,291 Bytes
65da30d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 |
# rmscript - Kid-Friendly Robot Programming
rmscript is a natural language-inspired programming language designed to make robot programming accessible and fun for children. It compiles to Python code that controls the Reachy Mini robot.
## Example Usage
Create a file `hello.rmscript`:
```rmscript
DESCRIPTION Wave hello to someone
antenna up
wait 1s
antenna down
look left
look right
look center
```
Test the script using the provided runner (after starting the reachy-mini-daemon):
```
python src/reachy_mini_conversation_app/rmscript/run_rmscript.py path/to/hello.rmscript
```
## Language Syntax
### File Structure
Every rmscript file has a simple structure:
```rmscript
DESCRIPTION Wave hello to someone
# Your commands here
look left
wait 1s
```
**Key features:**
- **Tool name**: Automatically derived from the filename (e.g., `wave_hello.rmscript` → tool name is `wave_hello`)
- **DESCRIPTION** (optional): One-line description used for LLM tool registration
### Basic Commands
```rmscript
# Comments start with #
# Movement commands
look left
turn right
antenna up
head forward 10
# Wait command
wait 2s
wait 0.5s
# Camera command
picture
# Sound playback
play mysound
play othersound pause
```
### Case Insensitivity
rmscript is case-insensitive for keywords:
```rmscript
LOOK left # Same as "look left"
Look Left # Same as "look left"
WAIT 1s # Same as "wait 1s"
```
## Movement Commands
### Look (Head Orientation)
Control the robot's head orientation (pitch and yaw):
```rmscript
look left # Turn head left (30° default)
look right 45 # Turn head right 45°
look up # Tilt head up (30° default)
look down 20 # Tilt head down 20°
look center # Return to center position
# Synonyms
look straight # Same as center
look neutral # Same as center
```
**Physical Limits:**
- Pitch (up/down): ±40°
- Yaw (left/right): ±180° absolute, ±65° relative to body
### Turn (Body Rotation)
Rotate the robot's body (the head rotates together with the body):
```rmscript
turn left # Rotate body and head left (30° default)
turn right 90 # Rotate body and head right 90°
turn center # Face forward
```
**Note:** The `turn` command rotates both the body yaw and the head yaw together, since the body carries the head.
**Physical Limits:**
- Body yaw: ±160° (safe range)
### Antenna
Control the antenna positions using multiple syntaxes:
**Clock Position (Numeric 0-12):**
```rmscript
antenna both 0 # 0 o'clock = 0° (straight up)
antenna both 3 # 3 o'clock = 90° (external/right)
antenna both 6 # 6 o'clock = 180° (straight down)
antenna both 9 # 9 o'clock = -90° (internal/left)
antenna left 4.5 # Left antenna to 4.5 o'clock (135°)
```
**Clock Keywords:**
```rmscript
antenna both high # 0° (high position)
antenna both ext # 90° (external)
antenna both low # 180° (low position)
antenna both int # -90° (internal)
```
**Directional Keywords (Natural Language):**
```rmscript
antenna both up # 0° (up)
antenna both right # 90° (right/external)
antenna both down # 180° (down)
antenna both left # -90° (left/internal)
# Individual antenna control
antenna left up # Left antenna pointing up
antenna right down # Right antenna pointing down
# Even in potentially confusing cases, it works naturally:
antenna left left # Left antenna pointing left (-90°)
antenna right right # Right antenna pointing right (90°)
```
**Physical Limits:**
- Antenna angle: ±180° (safe recommended: ±120°)
### Head Translation
Move the head forward/back/left/right/up/down in space:
```rmscript
head forward 10 # Move head forward 10mm
head back 5 # Move head back 5mm
head backward 5 # Same as "back" (synonym)
head backwards 5 # Same as "back" (synonym)
head left 8 # Move head left 8mm
head right 8 # Move head right 8mm
head up 5 # Move head up 5mm
head down 3 # Move head down 3mm
```
**Physical Limits:**
- X (forward/back): ±50mm typical
- Y (left/right): ±50mm typical
- Z (up/down): +20mm / -30mm typical
### Tilt (Head Roll)
Tilt the head side-to-side:
```rmscript
tilt left 15 # Tilt head left
tilt right 15 # Tilt head right
tilt center # Return to level
```
**Physical Limits:**
- Roll: ±40°
### Wait
Pause between movements:
```rmscript
wait 1s # Wait 1 second
wait 0.5s # Wait 0.5 seconds
wait 2.5s # Wait 2.5 seconds
```
**Important:** The `s` suffix is **required** for consistency. `wait 1` will produce a compilation error.
### Picture
Take a picture with the robot's camera and add it to the conversation:
```rmscript
picture # Take a picture
```
The picture command captures a frame from the camera and returns it as a base64-encoded image.
**How it works:**
1. **During execution**: The `picture` command is queued as a movement (instant duration: 0.01s)
2. **Capture timing**: Picture is captured at the right moment in the movement sequence
3. **File storage**: Pictures are automatically saved to `/tmp/reachy_picture_YYYYMMDD_HHMMSS_microseconds.jpg`
4. **Color handling**: Images are captured in RGB format with proper color conversion
5. **LLM integration**: When called by the LLM, pictures are automatically added to the conversation
**Single picture (LLM-compatible):**
```rmscript
DESCRIPTION Check behind by taking a picture
turn left maximum
wait 0.5s
picture
wait 0.5s
turn center
```
This script:
- Turns the robot to look behind (120° left)
- Waits for movement to stabilize
- Captures a picture at the right moment
- Returns to center position
- The LLM receives the picture and can describe what it sees
**Important timing notes:**
- Add `wait` commands before `picture` to let movements complete
- The tool automatically waits for all movements + picture capture before returning
- Pictures are captured when the movement queue reaches them (not immediately)
**Multiple pictures:**
```rmscript
DESCRIPTION Look around and take pictures
look left
wait 0.5s
picture
look right
wait 0.5s
picture
look center
```
Each `picture` command captures a separate image. For single-picture scripts, the image is returned in Camera-compatible format (`b64_im`) for LLM integration. For multi-picture scripts, images are returned as an array.
**Use with `run_rmscript.py`:**
When testing scripts with `run_rmscript.py`, pictures are:
- Saved to `/tmp` with timestamped filenames
- Logged with full paths for easy access
- Displayed in the execution summary
**Use with LLM conversation:**
When called as a tool by the LLM:
- Single picture: Automatically fed to the LLM conversation (like Camera tool)
- Multiple pictures: Available as base64 data (future enhancement for multi-image display)
- Gradio UI: Picture thumbnails displayed in the conversation interface
**Example conversation:**
```
User: "Can you check what's behind you?"
→ LLM calls: check_behind()
→ Robot: turns, captures picture, returns to center
→ LLM sees the picture and responds: "I can see a desk with a laptop and some books!"
```
### Play Sound
Play sound files (.wav, .mp3, .ogg, .flac) during script execution:
```rmscript
play soundname # Play sound in background (async, continues immediately)
play soundname pause # Wait for sound to finish before continuing (blocking)
play soundname 5s # Play sound for exactly 5 seconds (blocking)
```
The script automatically searches for sound files in this order:
1. **Directory containing the .rmscript file** (highest priority - co-located sounds)
2. Current working directory
3. `sounds/` subdirectory
**Asynchronous playback (default):**
```rmscript
play intro # Starts playing, script continues immediately
look left
wait 1s
```
The sound plays in the background while movements execute.
**Blocking playback (wait for sound to finish):**
```rmscript
play mysound pause # Waits for sound to finish
# OR use synonyms:
play mysound fully
play mysound wait
play mysound block
play mysound complete
```
The script pauses until the sound finishes playing, then continues.
**Duration-limited playback:**
```rmscript
play mysound 10s # Play sound for exactly 10 seconds (blocks for 10s)
```
Plays the sound for the specified duration. If the sound is shorter, it will finish early. If longer, it will be cut off.
### Loop Sound
Loop a sound file repeatedly for a specified duration:
```rmscript
loop soundname # Loop sound for 10 seconds (default)
loop soundname 5s # Loop sound for 5 seconds
loop soundname 30s # Loop sound for 30 seconds
```
The loop command automatically repeats the sound until the duration expires. This is useful for background music or ambient sounds.
**Example use case:**
```rmscript
DESCRIPTION Greet with sound and movement
play hello pause # Play greeting sound fully
wait 0.5s
antenna both up # Wave antennas
wait 1s
antenna both down
```
**Sound files:**
- File extensions: `.wav`, `.mp3`, `.ogg`, `.flac`
- Example: `play hello` looks for `hello.wav`, `hello.mp3`, etc.
- **Best practice**: Place sound files in the same directory as your .rmscript file
- Alternative locations: working directory or `sounds/` subdirectory
## Advanced Features
### Keyword Reuse with `and`
Chain multiple directions with the same action keyword:
```rmscript
# Reuse "look" keyword
look left and up 25
# → Equivalent to: look left + look up 25
# → Merged into single command
# Different keywords - no reuse
turn left and look right
# → turn left + look right
# → Two separate commands merged
```
This creates more natural, flowing descriptions while optimizing execution.
### Qualitative Strength
Use descriptive words instead of numbers - works for both angles and distances:
```rmscript
# 5 levels of strength - values are context-aware!
# Examples:
turn left tiny # 10° (body yaw can handle larger movements)
look up tiny # 5° (head pitch limited by cone constraint)
head forward tiny # 2mm
turn left maximum # 120° (body yaw safe maximum)
look up maximum # 38° (respects ±40° pitch limit)
look left maximum # 60° (respects ±65° body-head differential)
head forward maximum # 28mm (under 30mm limit)
```
**Available Qualitative Keywords:**
- **VERY_SMALL**: minuscule, mini, verysmall, tiny
- **SMALL**: little, slightly, small, alittle
- **MEDIUM**: medium, normal, regular, standard, normally
- **LARGE**: lot, big, large, very, alot, huge, strong, strongly
- **VERY_LARGE**: verybig, enormous, verylarge, maximum
**Context-Aware Values:**
The same qualitative keyword maps to different values depending on the movement type, respecting physical limits:
| Keyword | VERY_SMALL | SMALL | MEDIUM | LARGE | VERY_LARGE |
|---------|------------|-------|--------|-------|------------|
| **Body Yaw (turn)** | 10° | 30° | 60° | 90° | 120° |
| **Head Pitch/Roll (look up/down, tilt)** | 5° | 10° | 20° | 30° | 38° |
| **Head Yaw (look left/right)** | 5° | 15° | 30° | 45° | 60° |
| **Head Translation (head forward/back/etc)** | 2mm | 5mm | 10mm | 20mm | 28mm |
| **Antennas** | 10° | 30° | 60° | 90° | 110° |
**Note:** These values are carefully chosen to stay within the robot's physical limits while maximizing safe range of motion.
### Duration Keywords
Use descriptive speed words:
```rmscript
look left superfast # 0.2 seconds
look right fast # 0.5 seconds
look up slow # 2.0 seconds
look up slowly # 2.0 seconds (synonym for slow)
look down superslow # 3.0 seconds
```
Combine with 'and':
```rmscript
turn left and look right fast
# Both movements complete in 0.5 seconds
```
### Repeat Blocks
Repeat a sequence of commands:
```rmscript
repeat 3
look left
wait 0.5s
look right
wait 0.5s
# Nested repeat blocks work too
repeat 2
antenna up
repeat 3
look left
look right
antenna down
```
**Indentation:** Use spaces or tabs consistently (tabs = 4 spaces).
### Combining Movements
Combine multiple movements into a single smooth motion:
```rmscript
# All happen simultaneously
antenna both up and look up 25 and turn left 30
```
This merges into a single `goto_target()` call with all parameters.
**Important:** The `and` keyword can only combine movement commands (turn, look, head, tilt, antenna). You **cannot** combine movements with control commands (picture, play, loop, wait) using `and`. Use separate lines instead:
```rmscript
# ❌ ERROR - Cannot combine movement with picture
look left and picture
# ✓ CORRECT - Use separate lines
look left
wait 0.5s
picture
# ❌ ERROR - Cannot combine movement with play
turn right and play mysound
# ✓ CORRECT - Use separate lines
turn right
play mysound
```
## Compiler Architecture
### Overview
The compiler uses a 5-stage pipeline:
```
Source Code → Lexer → Parser → Semantic Analyzer → Optimizer → Code Generator
```
### Stage 1: Lexer (Tokenization)
Converts source text into tokens with indentation tracking:
```python
from reachy_mini.rmscript.lexer import Lexer
source = "look left\nwait 1s"
lexer = Lexer(source)
tokens = lexer.tokenize()
# → [Token(KEYWORD_LOOK, 'look', L1:C1), Token(DIRECTION, 'left', L1:C6), ...]
```
Features:
- Python-like INDENT/DEDENT tokens
- Case-insensitive keyword matching
- Comment stripping
- Line/column tracking for error messages
### Stage 2: Parser (AST Construction)
Builds an Abstract Syntax Tree from tokens:
```python
from reachy_mini.rmscript.parser import Parser
ast = Parser(tokens).parse()
# → Program(tool_name='...', description='...', statements=[...])
```
Features:
- Keyword reuse detection for `and` chains
- Direction validation per keyword
- Repeat block nesting
- Syntax error reporting
### Stage 3: Semantic Analyzer
Applies defaults, validates ranges, and generates IR:
```python
from reachy_mini.rmscript.semantic import SemanticAnalyzer
ir, errors, warnings = SemanticAnalyzer().analyze(ast)
# → List[Action | WaitAction], List[CompilationError], List[CompilationError]
```
Features:
- Default value resolution (30° for angles, 1s for duration)
- Qualitative → quantitative conversion
- Duration keyword mapping
- Physical limit validation
- Action merging (multiple SingleActions → one Action)
### Stage 4: Optimizer
Optimizes the IR:
```python
from reachy_mini.rmscript.optimizer import Optimizer
optimized_ir = Optimizer().optimize(ir)
```
Features:
- Consecutive wait merging
- No-op action removal
### Stage 5: Code Generator
Creates executable Python functions:
```python
from reachy_mini.rmscript.codegen import CodeGenerator
executable = CodeGenerator().generate(tool_name, description, ir)
# → Callable that executes the behavior
```
Features:
- Direct execution: `executable(mini)`
- Readable code generation: `to_python_code()`
- Proper imports and function signatures
## API Reference
### rmscriptCompiler
Main compiler class.
```python
from reachy_mini.rmscript import rmscriptCompiler
compiler = rmscriptCompiler(log_level="INFO")
tool = compiler.compile(source_code)
```
**Parameters:**
- `log_level` (str): Logging level - "DEBUG", "INFO", "WARNING", "ERROR"
**Returns:**
- `CompiledTool`: Compilation result object
### verify_rmscript
Verification function that checks if rmscript code compiles without generating executable code.
```python
from reachy_mini_conversation_app.rmscript import verify_rmscript
is_valid, errors = verify_rmscript(source_code)
if not is_valid:
for error in errors:
print(error)
```
**Parameters:**
- `source_code` (str): rmscript source code to verify
**Returns:**
- `tuple[bool, list[str]]`: Tuple of (is_valid, error_messages)
- `is_valid`: True if compilation succeeds, False otherwise
- `error_messages`: List of error and warning messages (empty if valid)
**Example:**
```python
script = """
DESCRIPTION Test script
look left and picture
"""
is_valid, errors = verify_rmscript(script)
# is_valid = False
# errors = ["❌ Line 3: Cannot combine movement with 'picture' using 'and'. Use separate lines instead."]
```
### CompiledTool
Result of compilation.
```python
class CompiledTool:
name: str # Tool name
description: str # Tool description
executable: Callable # Function to execute on robot
success: bool # Compilation succeeded?
errors: List[CompilationError] # Compilation errors
warnings: List[CompilationError] # Compilation warnings
source_code: str # Original source
ir: List[Action | WaitAction] # Intermediate representation
def execute(self, mini: ReachyMini) -> None:
"""Execute the compiled behavior on the robot."""
def to_python_code(self) -> str:
"""Generate readable Python code."""
```
**Usage:**
```python
if tool.success:
# Execute on robot
with ReachyMini() as mini:
tool.execute(mini)
# Or get Python code
print(tool.to_python_code())
else:
# Handle errors
for error in tool.errors:
print(f"{error.severity} Line {error.line}: {error.message}")
```
### CompilationError
Error or warning from compilation.
```python
@dataclass
class CompilationError:
line: int # Line number (1-indexed)
column: int # Column number (1-indexed)
message: str # Error/warning message
severity: str # "ERROR" or "WARNING"
```
### Action (IR)
Intermediate representation of a movement.
```python
@dataclass
class Action:
head_pose: Optional[np.ndarray] # 4x4 transformation matrix
antennas: Optional[np.ndarray] # [left, right] angles in radians
body_yaw: Optional[float] # Body rotation in radians
duration: float # Movement duration in seconds
interpolation: str # "minjerk", "linear", "ease", "cartoon"
```
### WaitAction (IR)
Intermediate representation of a wait/pause.
```python
@dataclass
class WaitAction:
duration: float # Wait duration in seconds
```
## Examples
### Example 1: Simple Greeting
```rmscript
DESCRIPTION Greet someone warmly
# Wave with antennas
antenna up
wait 0.5s
antenna down
wait 0.5s
# Nod
look down 20
wait 0.3s
look up 20
wait 0.3s
look center
```
### Example 2: Search Pattern
```rmscript
DESCRIPTION Look around to search for something
# Scan left to right
look left 45
wait 1s
look center
wait 0.5s
look right 45
wait 1s
look center
# Check up and down
look up 30
wait 0.5s
look down 30
wait 0.5s
look center
```
### Example 3: Dance Choreography
```rmscript
DESCRIPTION Perform a fun dance routine
# Opening pose
antenna both up and look up 25
wait 1s
# Main sequence
repeat 2
# Move left
turn left 30 and look right 20
wait 0.5s
# Move right
turn right 30 and look left 20
wait 0.5s
# Ending pose
turn center and look center and antenna down
```
### Example 4: Using Qualitative Strength
```rmscript
DESCRIPTION Wave shyly at someone
# Small movements
look down slightly
antenna left up a little
wait 1s
antenna left down
look center
```
### Example 5: Complex Combination
```rmscript
DESCRIPTION Show excitement when greeting
# Quick movements
repeat 3
antenna both up superfast
antenna both down superfast
# Energetic looking around
look left and up fast
wait 0.2s
look right and up fast
wait 0.2s
look center slow
```
## Error Handling
### Compilation Errors
Errors prevent code generation:
```rmscript
# Invalid keyword
jump up
```
**Output:** `❌ Line 1: Unexpected token: 'jump'`
```rmscript
# Invalid direction for keyword
turn up
```
**Output:** `❌ Line 1: Invalid direction 'up' for keyword 'turn'`
```rmscript
# Missing indentation
repeat 3
look left
```
**Output:** `❌ Line 2: Expected indented block after 'repeat'`
### Compilation Warnings
Warnings allow compilation but alert to potential issues:
```rmscript
# Exceeds safe range
turn left 200
```
**Output:** `⚠️ Line 1: Body yaw 200.0° exceeds safe range (±160.0°), will be clamped`
```rmscript
# Exceeds physical limit
look up 50
```
**Output:** `⚠️ Line 1: Head pitch 50.0° exceeds limit (±40.0°), will be clamped`
### Handling Errors in Code
```python
tool = compiler.compile(source)
if not tool.success:
print("Compilation failed!")
for error in tool.errors:
print(f" ❌ Line {error.line}: {error.message}")
exit(1)
if tool.warnings:
print("Compilation succeeded with warnings:")
for warning in tool.warnings:
print(f" ⚠️ Line {warning.line}: {warning.message}")
# Safe to execute
tool.execute(mini)
```
## Tips and Best Practices
### 1. Use Comments
```rmscript
# Good: explain what you're doing
# Opening sequence: get attention
antenna up
wait 1s
# Main behavior: scan the room
look left 45
wait 1s
look right 45
```
### 2. Use Descriptive Tool Names
```python
# Good
# Avoid
```
### 3. Break Complex Behaviors into Steps
```rmscript
# Good: readable steps
antenna up
wait 1s
look left
wait 0.5s
look right
wait 0.5s
look center
# Avoid: too compressed
antenna up and look left and wait 1s and look right
```
### 4. Use Repeat for Patterns
```rmscript
# Good: use repeat
repeat 3
antenna up
antenna down
# Avoid: repetitive
antenna up
antenna down
antenna up
antenna down
antenna up
antenna down
```
### 5. Test with Small Values First
```rmscript
# Start conservative
look left 10
turn right 15
# Then increase if needed
look left 45
turn right 90
```
## Physical Safety Limits
rmscript validates all movements against these limits:
| Movement | Limit | Warning Threshold |
|----------|-------|------------------|
| Body yaw (turn) | ±180° | ±160° |
| Head yaw (look left/right) | ±180° absolute | ±65° relative to body |
| Head pitch (look up/down) | ±40° | ±40° |
| Head roll (tilt) | ±40° | ±40° |
| Antenna | ±90° | ±65° |
| Head X (forward/back) | ±50mm typical | - |
| Head Y (left/right) | ±50mm typical | - |
| Head Z (up/down) | +20mm / -30mm typical | - |
Exceeding these limits generates **warnings** but still compiles. The robot's hardware will clamp values to safe ranges.
## Default Values
When values aren't specified:
| Parameter | Default |
|-----------|---------|
| Angle (look, turn, tilt) | 30° |
| Distance (head translation) | 10mm |
| Antenna angle | 45° |
| Duration | 1.0s |
**Qualitative Strength Values:**
| Level | Keywords | Angle | Distance |
|-------|----------|-------|----------|
| VERY_SMALL | tiny, minuscule, mini, verysmall | 5° | 2mm |
| SMALL | little, slightly, small, alittle | 15° | 5mm |
| MEDIUM | medium, normal, regular, standard, normally | 30° | 10mm |
| LARGE | strong, lot, big, large, very, alot, huge, strongly | 45° | 20mm |
| VERY_LARGE | enormous, verybig, verylarge, maximum | 60° | 30mm |
## Troubleshooting
### Issue: "Inconsistent indentation"
**Cause:** Mixing tabs and spaces, or inconsistent indentation levels.
**Solution:** Use only spaces or only tabs (not mixed). Ensure nested blocks are indented consistently.
```rmscript
# Bad: mixed tabs and spaces
repeat 3
look left # 4 spaces
→ look right # 1 tab (shows as →)
# Good: consistent spaces
repeat 3
look left # 4 spaces
look right # 4 spaces
```
### Issue: "Expected indented block after 'repeat'"
**Cause:** No indentation after `repeat`.
**Solution:** Indent the repeat block body.
```rmscript
# Bad
repeat 3
look left
# Good
repeat 3
look left
```
### Issue: Generated code has unexpected angles
**Cause:** Sign convention confusion (left/right).
**Solution:** Remember: `left` = positive yaw, `right` = negative yaw in the generated code. `up` = negative pitch, `down` = positive pitch. This matches the robot's actual coordinate system.
## Contributing
To extend rmscript:
1. **Add new keywords**: Update `lexer.py`, `parser.py`, and `semantic.py`
2. **Add new features**: Modify the appropriate compiler stage
3. **Add tests**: Create integration tests in `tests/test_rmscript/`
4. **Update docs**: Keep this README and examples current
## License
Same as Reachy Mini SDK.
|