| name | rula-reba-assessor |
| description | Rapid Upper Limb Assessment (RULA) and Rapid Entire Body Assessment (REBA) skill for posture evaluation. |
| allowed-tools | Bash(*) Read Write Edit Glob Grep WebFetch |
| metadata | {"author":"babysitter-sdk","version":"1.0.0","category":"ergonomics","backlog-id":"SK-IE-021"} |
| graph | {"domains":["domain:industrial-engineering"],"skillAreas":["skill-area:statistical-analysis","skill-area:organizational-design","skill-area:data-analysis"],"roles":["role:operations-analyst","role:research-engineer"]} |
rula-reba-assessor
You are rula-reba-assessor - a specialized skill for evaluating work postures using RULA and REBA methodologies.
Overview
This skill enables AI-powered posture assessment including:
- RULA scoring for upper extremity tasks
- REBA scoring for whole body postures
- Body segment angle measurement guidance
- Risk level classification
- Action level determination
- Photo/video-based assessment
- Comparative assessment reports
- Improvement recommendation generation
Capabilities
1. RULA Assessment
from dataclasses import dataclass
from typing import Optional
@dataclass
class RULAInput:
upper_arm_angle: float
upper_arm_abducted: bool = False
shoulder_raised: bool = False
arm_supported: bool = False
lower_arm_angle: float
working_across_midline: bool = False
working_outside_body: bool = False
wrist_angle: float
wrist_bent_from_midline: bool = False
wrist_twist: str = "mid"
neck_angle: float
neck_twisted: bool = False
neck_side_bent: bool = False
trunk_angle: float
trunk_twisted: bool = False
trunk_side_bent: bool = False
legs_supported: bool = True
muscle_use_score: int = 0
force_load_score: int = 0
def calculate_rula(inputs: RULAInput):
"""
Calculate RULA score
"""
if inputs.upper_arm_angle <= 20:
upper_arm = 1
elif inputs.upper_arm_angle <= 45:
upper_arm = 2
elif inputs.upper_arm_angle <= 90:
upper_arm = 3
else:
upper_arm = 4
if inputs.shoulder_raised:
upper_arm += 1
if inputs.upper_arm_abducted:
upper_arm += 1
if inputs.arm_supported:
upper_arm -= 1
upper_arm = max(1, min(6, upper_arm))
if 60 <= inputs.lower_arm_angle <= 100:
lower_arm = 1
else:
lower_arm = 2
if inputs.working_across_midline or inputs.working_outside_body:
lower_arm += 1
lower_arm = min(3, lower_arm)
if inputs.wrist_angle == 0:
wrist = 1
elif abs(inputs.wrist_angle) <= 15:
wrist = 2
else:
wrist = 3
if inputs.wrist_bent_from_midline:
wrist += 1
wrist = min(4, wrist)
wrist_twist = 1 if inputs.wrist_twist == "mid" else 2
table_a = get_table_a_score(upper_arm, lower_arm, wrist, wrist_twist)
if inputs.neck_angle <= 10:
neck = 1
elif inputs.neck_angle <= 20:
neck = 2
elif inputs.neck_angle <= 45:
neck = 3
else:
neck = 4
if inputs.neck_twisted:
neck += 1
if inputs.neck_side_bent:
neck += 1
neck = min(6, neck)
if inputs.trunk_angle == 0:
trunk = 1
elif inputs.trunk_angle <= 20:
trunk = 2
elif inputs.trunk_angle <= 60:
trunk = 3
else:
trunk = 4
if inputs.trunk_twisted:
trunk += 1
if inputs.trunk_side_bent:
trunk += 1
trunk = min(6, trunk)
legs = 1 if inputs.legs_supported else 2
table_b = get_table_b_score(neck, trunk, legs)
posture_a = table_a + inputs.muscle_use_score + inputs.force_load_score
posture_b = table_b + inputs.muscle_use_score + inputs.force_load_score
final_score = get_table_c_score(posture_a, posture_b)
return {
"final_score": final_score,
"group_a_score": table_a,
"group_b_score": table_b,
"posture_a": posture_a,
"posture_b": posture_b,
"component_scores": {
"upper_arm": upper_arm,
"lower_arm": lower_arm,
"wrist": wrist,
"wrist_twist": wrist_twist,
"neck": neck,
"trunk": trunk,
"legs": legs
},
"action_level": get_rula_action_level(final_score)
}
def get_rula_action_level(score):
if score <= 2:
return {"level": 1, "action": "Posture acceptable if not maintained for long periods"}
elif score <= 4:
return {"level": 2, "action": "Further investigation needed, changes may be required"}
elif score <= 6:
return {"level": 3, "action": "Investigation and changes required soon"}
else:
return {"level": 4, "action": "Investigation and changes required immediately"}
2. REBA Assessment
@dataclass
class REBAInput:
trunk_angle: float
trunk_twisted: bool = False
trunk_side_bent: bool = False
neck_angle: float
neck_twisted: bool = False
neck_side_bent: bool = False
legs_bilateral: bool = True
knee_flexion: float = 0
upper_arm_angle: float
shoulder_raised: bool = False
upper_arm_abducted: bool = False
arm_supported: bool = False
lower_arm_angle: float
wrist_angle: float
wrist_twisted: bool = False
load_kg: float = 0
shock_or_rapid: bool = False
coupling: str = "good"
static_posture: bool = False
repeated_actions: bool = False
rapid_changes: bool = False
():
inputs.trunk_angle == :
trunk =
(inputs.trunk_angle) <= :
trunk =
< inputs.trunk_angle <= :
trunk =
:
trunk =
inputs.trunk_twisted:
trunk +=
inputs.trunk_side_bent:
trunk +=
<= inputs.neck_angle <= :
neck =
:
neck =
inputs.neck_twisted inputs.neck_side_bent:
neck +=
legs = inputs.legs_bilateral
<= inputs.knee_flexion <= :
legs +=
inputs.knee_flexion > :
legs +=
table_a = get_reba_table_a(trunk, neck, legs)
inputs.load_kg < :
load =
inputs.load_kg <= :
load =
:
load =
inputs.shock_or_rapid:
load +=
score_a = table_a + load
inputs.upper_arm_angle <= :
upper_arm =
inputs.upper_arm_angle <= :
upper_arm =
inputs.upper_arm_angle <= :
upper_arm =
:
upper_arm =
inputs.shoulder_raised inputs.upper_arm_abducted:
upper_arm +=
inputs.arm_supported:
upper_arm -=
<= inputs.lower_arm_angle <= :
lower_arm =
:
lower_arm =
(inputs.wrist_angle) <= :
wrist =
:
wrist =
inputs.wrist_twisted:
wrist +=
table_b = get_reba_table_b(upper_arm, lower_arm, wrist)
coupling_scores = {: , : , : , : }
coupling = coupling_scores.get(inputs.coupling.lower(), )
score_b = table_b + coupling
table_c = get_reba_table_c(score_a, score_b)
activity =
inputs.static_posture:
activity +=
inputs.repeated_actions:
activity +=
inputs.rapid_changes:
activity +=
final_score = table_c + activity
{
: final_score,
: score_a,
: score_b,
: table_c,
: activity,
: get_reba_risk_level(final_score),
: get_reba_action_level(final_score)
}
():
score == :
score <= :
score <= :
score <= :
:
():
score == :
{: , : }
score <= :
{: , : }
score <= :
{: , : }
score <= :
{: , : }
:
{: , : }
3. Angle Measurement Guidance
def get_measurement_guidance(body_part: str):
"""
Provide guidance for measuring body segment angles
"""
guidance = {
"trunk": {
"reference": "Vertical line through hip",
"measurement_point": "Angle between trunk and vertical",
"neutral": "0 degrees (upright)",
"tips": [
"Use plumb line or vertical reference",
"Measure at shoulder level relative to hip",
"Note any twisting or side bending"
]
},
"neck": {
"reference": "Vertical line through trunk",
"measurement_point": "Angle of neck flexion/extension",
"neutral": "0-10 degrees slight flexion",
"tips": [
"Measure from ear to shoulder alignment",
"Note if looking up (extension) or down (flexion)",
"Check for rotation and side bending"
]
},
"upper_arm": {
"reference": "Vertical line through shoulder",
"measurement_point": "Angle of upper arm from vertical",
"neutral": "0-20 degrees",
"tips": [
"Measure from shoulder joint",
"Note if arm is in front or behind body",
"Check for shoulder elevation and abduction"
]
},
: {
: ,
: ,
: ,
: [
,
]
},
: {
: ,
: ,
: ,
: [
,
,
]
}
}
guidance.get(body_part.lower(), {: })
4. Comparative Assessment
def compare_postures(assessments: list, method: str = "RULA"):
"""
Compare multiple posture assessments
"""
comparisons = []
for assessment in assessments:
if method == "RULA":
result = calculate_rula(assessment['inputs'])
else:
result = calculate_reba(assessment['inputs'])
comparisons.append({
"name": assessment.get('name', 'Unnamed'),
"description": assessment.get('description', ''),
"score": result['final_score'],
"action_level": result['action_level'],
"component_scores": result.get('component_scores', {})
})
comparisons.sort(key=lambda x: x['score'])
if len(comparisons) > 1:
baseline = comparisons[-1]
best = comparisons[0]
improvement = {
"score_reduction": baseline['score'] - best['score'],
"baseline": baseline['name'],
"best_option": best['name']
}
else:
improvement =
{
: method,
: comparisons,
: improvement
}
5. Recommendation Generator
def generate_posture_recommendations(result: dict, method: str = "RULA"):
"""
Generate specific recommendations based on assessment
"""
recommendations = []
component_scores = result.get('component_scores', {})
if method == "RULA":
if component_scores.get('upper_arm', 0) >= 3:
recommendations.append({
"body_part": "Upper Arm",
"issue": "Upper arm elevation too high",
"suggestions": [
"Lower work surface",
"Raise worker platform",
"Bring work closer to body"
]
})
if component_scores.get('wrist', 0) >= 3:
recommendations.append({
"body_part": "Wrist",
"issue": "Excessive wrist deviation",
"suggestions": [
"Use ergonomic tools with inline grip",
"Adjust work angle",
"Redesign workstation layout"
]
})
if component_scores.get('neck', 0) >= 3:
recommendations.append({
"body_part": "Neck",
: ,
: [
,
,
]
})
component_scores.get(, ) >= :
recommendations.append({
: ,
: ,
: [
,
,
]
})
{
: result[],
: recommendations,
: recommendations[][] recommendations
}
Process Integration
This skill integrates with the following processes:
ergonomic-risk-assessment.js
workstation-design-optimization.js
Output Format
{
"method": "RULA",
"final_score": 5,
"action_level": {
"level": 3,
"action": "Investigation and changes required soon"
},
"component_scores": {
"upper_arm": 3,
"lower_arm": 2,
"wrist": 3,
"neck": 2,
"trunk": 3
},
"recommendations": [
{
"body_part": "Upper Arm",
"suggestions": ["Lower work surface"]
Best Practices
- Assess worst posture - Evaluate most strenuous position
- Multiple observations - Assess several cycles
- Use photos/video - Capture postures for analysis
- Train observers - Ensure consistent scoring
- Compare before/after - Validate improvements
- Consider task duration - Factor in exposure time
Constraints
- Subjective measurement requires training
- Snap assessment - may miss variations
- Does not account for all risk factors
- Use with other tools for complete analysis