Text Generation
Transformers
Safetensors
step3p5
conversational
custom_code
Eval Results

Question : Real-world use cases for Step-3.5-Flash

#24
by Geodd - opened

Hi everyone 👋
Curious how people are actually using Step-3.5-Flash in real applications.
If you’re running it in production or experimenting seriously, we’d love to hear:

Post your comment below

Hello @Geodd I was curious about this model but I was in no hurry to test it for myself, since I've never heard of stepfun-ai. Also, I have a lot of projects in development so my GPU rig stays rather busy doing actual work, usually with Minimax 2.1 as my daily driver. I did however give this model a try and I must say I'm rather impressed. Scratch that, I'm a bit blown away be the ability of this model to get to work and code. I honestly didn't think anything would ever give me reason to spin down Minimax 2.1 but this model did. I've been putting it through its paces HEAVILY. It is coding non-stop for me. After a few quick hitter projects, I decided to let it loose on a bigger project. It took all the documentation, asked a bunch of clarifying questions, created an 8 phase plan with about 10-12 tasks per phase and now it's ripping through phases and tasks. Did I mention how stupid fast this model is? I may never fire up Minimax M2.1 again. GLM 4.7 will be deleted to get some space back.

Hello @Geodd I was curious about this model but I was in no hurry to test it for myself, since I've never heard of stepfun-ai. Also, I have a lot of projects in development so my GPU rig stays rather busy doing actual work, usually with Minimax 2.1 as my daily driver. I did however give this model a try and I must say I'm rather impressed. Scratch that, I'm a bit blown away be the ability of this model to get to work and code. I honestly didn't think anything would ever give me reason to spin down Minimax 2.1 but this model did. I've been putting it through its paces HEAVILY. It is coding non-stop for me. After a few quick hitter projects, I decided to let it loose on a bigger project. It took all the documentation, asked a bunch of clarifying questions, created an 8 phase plan with about 10-12 tasks per phase and now it's ripping through phases and tasks. Did I mention how stupid fast this model is? I may never fire up Minimax M2.1 again. GLM 4.7 will be deleted to get some space back.

Wow the team who built the model will be very happy to see this comment! Running a 200b model locally is challenging, what does your rig setup look like and any quantisation ?

I've run the Int8 and Int4 GGUF. The safetensors probably won't fit comfortably on my rig, which has 3x RTX PRO 6000 Blackwell. This model is too big to fit in two cards and vllm won't split across 3 GPU. when i get the 4th gpu, i'll be able to run this and some others, using vllm or sglang and I can ditch llama.cpp for good.

Very good model, it uses very little amount of just 210Gb RAM in Q8 quality(context size 131072 the max possible in obabooga, in cli possible more=more ram). The little example of its work below:

#My Hardware# Intel Xeon E5-2699v4 LGA2011-3 22 cores 44 threads (2016) $110 # Gigabyte C612 chipset 12 RAM slots VGA motherboard year 2016 $150 # Samsung-Hynix ECC RAM 12x64Gb=768Gb ~$900 # VGA monitor # IKEA chair # Run: Trillions Deepseeks, Kimis in Q5-Q6, 400-500billions in BF16

Prompt: Lets invent something to draw in real life!
Ai (result after thinking which is too long):

Open-Source XY Plotter: Complete Documentation & Software Pipeline

1. Project Overview

This project details the construction of a homemade XY plotter – a computer-controlled drawing machine that reproduces images on paper using a motorized pen. The system consists of:

  • Mechanical hardware: An X‑Y gantry driven by stepper motors, with a pen‑lift mechanism.
  • Electronics: An Arduino‑based controller running GRBL firmware.
  • Software: A Python image‑to‑G‑code pipeline that converts raster/vector images into drawing commands.

Block diagram of system (Note: In actual documentation, insert a visual block diagram showing: Image → Python script → G‑code → Serial → Arduino GRBL → Motors/Pen)


2. Mechanical Design

2.1. Bill of Materials (BOM)

Part Specification Qty Notes
Frame 20 × 20 mm aluminum extrusion (or PVC/wood) ~4 m Cut to size: two lengths for Y‑rails (e.g., 600 mm), two for ends
X‑axis beam 20 × 20 mm extrusion or carbon fiber rod 1 Length = Y‑travel (e.g., 400 mm)
Stepper motors NEMA 17, 1.8°/step, 1.5 A 2 High torque recommended
Motor drivers A4988 or DRV8825 2 Microstepping up to 1/16
Belts & pulleys GT2 timing belt, 2 mm pitch; GT2 pulley 20 T ~2 m + 2 Belt length depends on frame size
Linear bearings LM8UU (for 8 mm shaft) or SC8UU (for 8 mm rail) 4–6 For Y‑rails and X‑carriage
Shafts/Rails 8 mm smooth rod or linear rail 2 × Y‑travel For Y‑axis motion
Pen mechanism Micro servo (MG90S) or solenoid (5 V) 1 Servo for pressure control, solenoid simpler
Pen holder 3D‑printed or custom bracket 1 Holds pen vertically, adjustable height
Power supply 12 V DC, 5 A (or 24 V for faster motors) 1 Switch‑mode, with appropriate connector
Arduino board Arduino Uno or Nano 1
CNC shield Arduino Uno CNC shield V3 1 For easy driver/endstop wiring
Endstops Mechanical micro switches (NO) 2 (optional) For homing; optional if no homing
Misc M3/M4 screws, nuts, brackets, wires – As needed

2.2. Mechanical Layout

Top view (Y‑axis beam moves front‑back, X‑carriage moves left‑right):

     Y (front)
      ^
      |
      |   +-----------------+   <-- Y‑axis beam (moves on Y‑rails)
      |   |  X‑carriage     |
      |   |  (pen up/down)  |
      |   +--------+--------+
      |            |
      |            | X (right)
      +------------+-----> X
      |
      |  Paper (origin at bottom‑left)

Side view (pen vertical movement):

      Z (pen up/down)
      ^
      |
      |   Servo/Solenoid
      |   +-----+
      |   | Pen |
      |   +-----+
      |     |
      +-----+--- X‑carriage
      |
      |  X‑beam (horizontal)
      |
      |  Y‑rails (vertical)
      |
      +--- Paper

Key design points:

  • Y‑axis: The beam spans the desired drawing width. Two parallel rails (smooth rods or linear rails) guide the beam’s motion via bearings. A stepper motor drives the belt attached to the beam.
  • X‑axis: A carriage slides on the beam (using bearings or a second belt). Another stepper moves the carriage left‑right.
  • Pen axis: The pen is mounted on the carriage with its tip just touching the paper. A servo rotates an arm to lift/drop the pen; a solenoid pushes/pulls directly. Adjustable height for different pens.
  • Frame rigidity: Use cross‑bracing to prevent wobble. All moving parts should have minimal backlash.

2.3. Assembly Steps

  1. Build the base frame (rectangular) with extrusions.
  2. Mount Y‑rails vertically (parallel, same plane). Ensure they are level and square.
  3. Install Y‑axis bearings on the beam and attach beam to Y‑rails.
  4. Attach Y‑motor and belt to beam (pulley on motor, idler on opposite end).
  5. Build X‑carriage: mount bearings on beam, attach belt.
  6. Mount X‑motor on one end of the beam.
  7. Install pen mechanism on carriage. Adjust so pen tip just touches paper when “down”.
  8. Wire electronics (see section 3).
  9. Test motion manually (with power off) to ensure smooth travel.

3. Electronics & Firmware

3.1. Wiring Diagram

Arduino + CNC Shield
    |--- Stepper X (X‑axis)   -> A4988 X (X1,X2)
    |--- Stepper Y (Y‑axis)   -> A4988 Y (Y1,Y2)
    |--- Servo/Solenoid       -> Spindle enable (or free PWM pin)
    |--- Power (12 V) to V‑Mot
    |--- Arduino USB → Computer
  • Stepper connections: A4988 STEP and DIR to CNC shield pins, ENABLE to ground (always enabled) or use ENABLE pin.
  • Servo: Connect signal to any PWM pin (e.g., D9 on Uno). Power from Arduino 5 V if small servo (MG90S); for larger, use separate 5 V.
  • Solenoid: Connect via transistor (e.g., TIP120) to a digital pin; include flyback diode.
  • GRBL’s spindle control: If using digital spindle, set spindle_type=1 in GRBL config. The spindle enable pin (usually D11 on Uno) will be toggled by M3/M5.

3.2. GRBL Firmware

  1. Install Arduino IDE, add GRBL library or upload grbl sketch (from https://github.com/gnea/grbl).

  2. Configure GRBL (using Universal G-code Sender or grbl Console):

    • $0=80 (step pulse, microseconds, usually 10–20)
    • $1=25 (step idle delay, ms)
    • $2=0 (step port invert mask)
    • $3=0 (dir port invert mask)
    • $4=0 (step enable invert)
    • $5=0 (limit pins invert)
    • $6=0 (probe pin invert)
    • $10=1 (status report mask)
    • $11=0.010 (junction deviation, mm)
    • $12=0.002 (arc tolerance, mm)
    • $13=0 (report inches)
    • $20=0 (soft limits enable)
    • $21=0 (hard limits enable)
    • $22=1 (homing cycle enable) – 0 if no homing
    • $23=0 (homing dir invert mask)
    • $24=25.0 (homing locate feed rate, mm/min)
    • $25=500.0 (homing search feed rate, mm/min)
    • $26=250 (homing switch debounce, ms)
    • $27=1.0 (homing switch pull‑off distance, mm)
    • $100=80.0 (X steps/mm) – calculate from your hardware
    • $101=80.0 (Y steps/mm)
    • $110=500.0 (X max rate, mm/min)
    • $111=500.0 (Y max rate)
    • $120=300.0 (X acceleration, mm/sec²)
    • $121=300.0 (Y acceleration)
    • $130=200.0 (X max travel, mm) – set to your drawing area
    • $131=200.0 (Y max travel)
  3. Steps per mm calculation:

    • For GT2 belt with 20‑tooth pulley:
      steps_per_mm = (200 steps/rev * microsteps) / (2 mm/pitch * 20 teeth)
      Example: 1/16 microstep ⇒ (200*16)/(2*20) = 80 steps/mm.
    • For lead screw: (steps * microsteps) / screw_pitch.
  4. Spindle (pen) setup: In GRBL, spindle_type=1 for digital (on/off). The spindle output pin will be HIGH when pen down (M3), LOW when up (M5). You may need to invert depending on wiring: set spindle_invert=1 in config.

  5. Test motion using Universal G‑code Sender (UGS) or grbl Console. Send G1 X10 Y10 F500 to check both axes move correctly.


4. Software Pipeline

4.1. High‑Level Workflow

Input image (PNG/JPG)
     |
     v
[Image preprocessing]  (grayscale, threshold, edge/contour)
     |
     v
[Vector extraction]   (contour tracing, polyline simplification)
     |
     v
[Scaling]            (map to machine coordinate space, flip Y)
     |
     v
[G‑code generation]  (insert M3/M5, convert to G0/G1)
     |
     v
Serial transmission  (send to GRBL line‑by‑line with flow control)
     |
     v
Physical drawing

4.2. Image Processing Options

  1. Contour mode (default): Convert image to binary (threshold), extract external contours. Good for line art, silhouettes.
  2. Edge mode: Use Canny edge detection, then find contours from edges.
  3. Dither mode (advanced): Ordered dithering → stippling (draw dots). Not covered in base code but can be added.

We’ll implement contour mode using OpenCV.


5. Python Implementation

5.1. Dependencies

pip install opencv-python numpy pyserial

5.2. Main Script: plotter.py

#!/usr/bin/env python3
"""
XY Plotter: Convert image to G-code and send to GRBL.
"""

import cv2
import numpy as np
import serial
import time
import argparse
import sys

def load_and_preprocess(image_path, threshold=128):
    """Load image, grayscale, binary threshold, invert if needed."""
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    if img is None:
        raise FileNotFoundError(f"Cannot load {image_path}")
    # Binarize: dark pixels become foreground (1), light background (0)
    _, binary = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY_INV)
    return binary

def extract_contours(binary, epsilon_factor=0.01):
    """Find and approximate contours to polylines."""
    contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    polylines = []
    for cnt in contours:
        if len(cnt) < 2:
            continue
        # Approximate to reduce points (epsilon as fraction of arc length)
        arc_len = cv2.arcLength(cnt, closed=True)
        epsilon = epsilon_factor * arc_len
        approx = cv2.approxPolyDP(cnt, epsilon, closed=True)
        # Convert to list of (x, y) tuples
        polyline = [tuple(pt[0]) for pt in approx]
        polylines.append(polyline)
    return polylines

def scale_to_machine(polylines, machine_width, machine_height, img_width, img_height):
    """Scale image coordinates to machine mm coordinates. Flips Y to have origin bottom-left."""
    scale_x = machine_width / img_width
    scale_y = machine_height / img_height

    scaled_polylines = []
    for poly in polylines:
        new_poly = []
        for (x, y) in poly:
            mx = x * scale_x
            my = y * scale_y
            # Flip Y: machine Y increases upward; image Y increases downward
            my = machine_height - my
            new_poly.append((mx, my))
        scaled_polylines.append(new_poly)
    return scaled_polylines

def generate_gcode(polylines, feed_rate=1000, pen_up_delay=0.1, pen_down_delay=0.1):
    """Generate G-code string with pen control via M3 (digital on) and M5 (off)."""
    gcode_lines = []
    gcode_lines.append("G21 ; mm units")
    gcode_lines.append("G90 ; absolute positioning")
    gcode_lines.append("G94 ; feed rate per minute")

    for i, poly in enumerate(polylines):
        if len(poly) < 2:
            continue
        # Pen up before moving to start
        gcode_lines.append("M5 ; pen up")
        # Rapid move to first point
        x0, y0 = poly[0]
        gcode_lines.append(f"G0 X{x0:.3f} Y{y0:.3f} F{feed_rate}")
        # Pen down
        gcode_lines.append("M3 S1 ; pen down")
        # Draw the polyline
        for (x, y) in poly[1:]:
            gcode_lines.append(f"G1 X{x:.3f} Y{y:.3f} F{feed_rate}")
        # Lift pen after contour
        gcode_lines.append("M5 ; pen up")
        # Optional short pause
        if pen_up_delay > 0:
            gcode_lines.append(f"G4 P{pen_up_delay:.3f}")

    # End program
    gcode_lines.append("M5 ; ensure pen up")
    gcode_lines.append("M2 ; program end")
    return "\n".join(gcode_lines)

def send_gcode(ser, gcode_str, wait_for_ok=True):
    """Send G-code line by line, waiting for 'ok' if required."""
    lines = gcode_str.strip().split('\n')
    for line in lines:
        # Strip comments and empty lines
        clean = line.split(';')[0].strip()
        if not clean:
            continue
        # GRBL expects line ending with \n
        ser.write((clean + '\n').encode())
        if wait_for_ok:
            # Wait for 'ok' or 'error'
            while True:
                resp = ser.readline().decode().strip()
                if resp == 'ok':
                    break
                elif resp.startswith('error'):
                    print(f"GRBL error: {resp}")
                    break
                # else ignore other status reports
        # Small delay to buffer
        time.sleep(0.01)

def main():
    parser = argparse.ArgumentParser(description="XY Plotter Image to G-code")
    parser.add_argument('image', help='Input image file')
    parser.add_argument('--width', type=float, required=True, help='Machine X travel (mm)')
    parser.add_argument('--height', type=float, required=True, help='Machine Y travel (mm)')
    parser.add_argument('--port', default='COM3', help='Serial port (e.g., COM3 or /dev/ttyUSB0)')
    parser.add_argument('--baud', type=int, default=115200, help='Baud rate')
    parser.add_argument('--threshold', type=int, default=128, help='Binarization threshold 0-255')
    parser.add_argument('--epsilon', type=float, default=0.01, help='Contour simplification factor (0-0.1)')
    parser.add_argument('--feed', type=int, default=1000, help='Feed rate (mm/min)')
    args = parser.parse_args()

    # 1. Preprocess
    binary = load_and_preprocess(args.image, args.threshold)
    h, w = binary.shape
    # 2. Extract contours
    polylines = extract_contours(binary, args.epsilon)
    print(f"Found {len(polylines)} contours")
    # 3. Scale to machine
    machine_polylines = scale_to_machine(polylines, args.width, args.height, w, h)
    # 4. Generate G-code
    gcode = generate_gcode(machine_polylines, feed_rate=args.feed)
    # Optionally save G-code file for inspection
    with open('output.gcode', 'w') as f:
        f.write(gcode)
    print("G-code saved to output.gcode (length: {} lines)".format(len(gcode.splitlines())))

    # 5. Send to GRBL
    try:
        ser = serial.Serial(args.port, args.baud, timeout=1)
        time.sleep(2)  # Wait for Arduino reset
        # Initialize GRBL: clear buffer
        ser.write(b'\r\n\r\n')
        time.sleep(0.5)
        ser.flushInput()
        print(f"Connected to {args.port} at {args.baud} baud")
        # Optional: query status to ensure alive
        ser.write(b'?')
        time.sleep(0.1)
        print("Status:", ser.readline().decode().strip())
        # Send G-code
        print("Sending G-code...")
        send_gcode(ser, gcode, wait_for_ok=True)
        print("Done.")
    except serial.SerialException as e:
        print(f"Serial error: {e}")
        sys.exit(1)
    finally:
        if 'ser' in locals() and ser.is_open:
            ser.close()

if __name__ == '__main__':
    main()

5.3. Usage Examples

# Basic usage: draw a black-and-white logo
python plotter.py logo.png --width 200 --height 200 --port COM3

# With higher simplification (fewer points) and slower feed
python plotter.py photo.jpg --width 300 --height 200 --epsilon 0.02 --feed 600

Tips:

  • Adjust --threshold to get desired features. Use 0 for original inverted binary? Actually we invert because we want dark pixels as drawing. If your image has light lines on dark background, change thresholding.
  • Use --epsilon to reduce point count; too high loses detail.
  • Set --feed based on motor capabilities; start低速 (200–500 mm/min) for testing.

6. Calibration & Testing

  1. Mechanical limits: Ensure the plotter does not hit frame edges. Manually jog axes using UGS to find safe travel area.
  2. Steps/mm verification: Move a known distance (e.g., 100 mm) and measure actual travel. Adjust $100/$101 in GRBL accordingly.
  3. Pen height: The pen should lightly touch paper when down; adjust mounting so it doesn’t drag or skip.
  4. Test pattern: Draw a simple square or circle. Check for:
    • Backlash: Corners may be rounded. Improve belt tension/anti‑backlash mechanisms.
    • Scaling: If drawing is too small/large, adjust machine dimensions in scale_to_machine.
    • Skew: If axes not perpendicular, square the frame.
  5. G‑code flow: If motors stutter, reduce feed rate or acceleration in GRBL ($120, $121).

7. Advanced Enhancements

  • Hatching/Fill: For solid areas, generate parallel line fills (requires contour interior detection).
  • Multi‑color: Multiple pen holders with automatic tool change (complex).
  • Web interface: Use Flask/Django to upload images and control remotely.
  • Optimization: Solve TSP for contour order to minimize travel; use networkx or ortools.
  • Variable line width: Control pen pressure via servo angle (map to grayscale intensity).

8. Troubleshooting Table

Symptom Possible Cause Fix
Motors don’t move Wiring incorrect, GRBL not configured, power off Check connections, verify GRBL $ settings, ensure 12 V to drivers
Stuttering/skipping Feed rate too high, acceleration too high Lower $110/$111 (max rate) and $120/$121 (acceleration)
Drawings are upside‑down Y‑axis inverted In software, flip Y; or set $3=4 (Y dir invert) in GRBL
Pen marks too light/heavy Pen height wrong, servo not full travel Adjust pen mounting; calibrate servo angle range in GRBL spindle settings
G‑code not executing, error alarms G‑code syntax error, serial buffer overflow Ensure G‑code lines terminated with \n; add waits for ok; check GRBL config
Contours look pixelated/low detail epsilon too large Decrease --epsilon value; use smaller microstepping? No, just more points
Machine moves beyond paper Soft limits disabled or set incorrectly Set $130/$131 to machine travel, enable soft limits ($20=1) or use endstops

9. Safety & Best Practices

  • Power: Use a fused power supply. Never connect/disconnect steppers while powered.
  • Pen: Use a suitable pen (technical pen, ball‑point) that doesn’t leak. Secure it tightly.
  • Supervision: Never leave the plotter unattended while running; motors can overheat or jam.
  • Noise: Steppers can be noisy; consider stepper dampers or a enclosure.

10. Conclusion

You now have complete documentation to build a functional XY plotter: a mechanical design with readily available parts, electronics with GRBL firmware, and a Python pipeline that transforms images into G‑code. Start with a simple frame (maybe cardboard or wood) to validate the system, then refine mechanics for precision. Experiment with different image processing parameters to achieve artistic effects. Let’s create art! 🎨

Remember: iterate, calibrate, and share your improvements with the open‑source community.

This is the first model I've actually successfully used on my production Java codebase (few thousand classes). Running IQ4_XS quants on 120k context (Q8 KV cache).

Absolutely in love with the model, perfectly coherent, great at understanding the codebase, haven't had one faulty tool call in probably a few hundred calls. Resolved a real-life issue with minimal support (I knew what the issue was, so I decided it would be a great test for the model and surprisingly it aced it mostly without my help).

I'm actually kinda shocked the AI channels and discussions online haven't been talking more about this model. The fact I'm running this instead of Minimax 2.1 or GLM 4.7 should say a LOT about it's capability. This is a blazing fast, super competent model that knows how to code. This is the kind of model local LLM people should be talking about non-stop. It's that good.

I agree, this is a bit less in the spotlight. I was comparing with MiniMax M2.1 and found this is more accurate in complex programming challenges and is smaller. Though I use the Q6 quantized model. I have only one issue integrating into programming tools like Claude Code/Kilo Code as for tool calls it always returns 500 (running with llama-server), probably have to check the jinja template. But nevertheless great work guys, big RESPECT!

@meszarz2003 use my autoparser branch/PR till it gets merged. I'm using it and it works perfectly.

Sign up or log in to comment